pax_global_header00006660000000000000000000000064134311140470014510gustar00rootroot0000000000000052 comment=38bf681f18f80259337bbb060f7040103416ba8c fastnetmon-1.1.4/000077500000000000000000000000001343111404700136715ustar00rootroot00000000000000fastnetmon-1.1.4/.github/000077500000000000000000000000001343111404700152315ustar00rootroot00000000000000fastnetmon-1.1.4/.github/CONTRIBUTING.md000066400000000000000000000002321343111404700174570ustar00rootroot00000000000000Hello! Please review our [developer guides](https://github.com/pavel-odintsov/fastnetmon/blob/master/docs/DEVELOPER_GUIDES.md) before commiting changes. fastnetmon-1.1.4/.github/ISSUE_TEMPLATE.md000066400000000000000000000015311343111404700177360ustar00rootroot00000000000000# If you want to solve your issue please read following information below First of all, please check following steps: * Do you have latest FastNetMon version? If not, please upgrade to 1.1.3 version. * Do we have similar tickets already? Please check [bug tracker](https://github.com/pavel-odintsov/fastnetmon/issues) and [Mailing list](https://groups.google.com/forum/#!forum/fastnetmon) about similar issues. If it does not help, please fill information below: * Your operating system name and version? * Please attach your /etc/fastnetmon.conf configuration file * What capture engine are you using: netflow, sflow, miror? * If you are using netflow or sflow, please specify version, vendor name, model name and firmware of agent device. * Please attach /var/log/fastnetmon.log Then please describe your issue as detailed as possible! Thanks you :) fastnetmon-1.1.4/.gitignore000066400000000000000000000000501343111404700156540ustar00rootroot00000000000000*.pyc __pycache__ *.DS_Store src/build/ fastnetmon-1.1.4/.gitmodules000066400000000000000000000001761343111404700160520ustar00rootroot00000000000000[submodule "src/juniper_plugin/netconf"] path = src/juniper_plugin/netconf url = https://github.com/Juniper/netconf-php.git fastnetmon-1.1.4/.travis.yml000066400000000000000000000006311343111404700160020ustar00rootroot00000000000000sudo: required dist: trusty language: cpp compiler: - gcc - clang before_install: - sudo apt-get update; true before_script: - sudo -i -E perl $TRAVIS_BUILD_DIR/src/fastnetmon_install.pl --use-git-master - cd $TRAVIS_BUILD_DIR/src script: mkdir -p build; cd build; cmake ..; make notifications: email: recipients: - pavel.odintsov@gmail.com on_success: change on_failure: always fastnetmon-1.1.4/HAPPY_CUSTOMERS.md000066400000000000000000000006301343111404700165370ustar00rootroot00000000000000### Hello! Here you could find happy tool customers! - Crea Nova Datacenter Finland, CREANOVA.ORG - Datahata Datacenter Belarus, DATAHATA.BY - DatHost AB, DATHOST.NET - FastVPS Eesti OU - Internet Service Provider MYVIRTUALSERVER.COM - Moscow Datacenter COLOCAT.RU - OneTelecom LTD, ONETELECOM.OD.UA - SysEleven GmbH, SYSELEVEN.DE Do not hesitate to add your company! Pull requests with new companies! :) fastnetmon-1.1.4/LICENSE000066400000000000000000000431721343111404700147050ustar00rootroot00000000000000GNU 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. FastNetMon - High Performance DDoS sensor software Copyright 2017 FastNetMon LTD 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. {signature of Ty Coon}, 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. fastnetmon-1.1.4/README.md000066400000000000000000000143761343111404700151630ustar00rootroot00000000000000![logo](https://fastnetmon.com/wp-content/uploads/2018/01/cropped-new_logo_3var-e1515443553507-1-300x146.png) Community Edition =========== FastNetMon - A high performance DoS/DDoS load analyzer built on top of multiple packet capture engines (NetFlow, IPFIX, sFlow, SnabbSwitch, netmap, PF_RING, PCAP). What do we do? -------------- We detect hosts in the deployed network sending or receiving large volumes of traffic, packets/bytes/flows, per second and perform a configurable action to handle that event. These configurable actions include notifying you, switching off the server, or blackholing the client. Flow is one or more ICMP, UDP, or TCP packets which can be identified via their unique src IP, dst IP, src port, dst port, and protocol fields. Integration with flow systems ----------------------------- At a very high level integration with FastNetMon is fairly simple. In both cases the work flow is the same and the main difference being the port numbers provided. The port numbers are configurable. #### sFlow Configure the IP of the server running FastNetMon using port 6343. This port number is configurable. #### Netflow Configure the IP of the server running FastNetMon using port 2055. This port number is configurable. License: GPLv2 Official [mirror at GitLab](https://gitlab.com/fastnetmon/fastnetmon) Project ------- - [Official site](https://fastnetmon.com) - [Mailing list](https://groups.google.com/forum/#!forum/fastnetmon) - [Slack](https://join.slack.com/t/fastnetmon/shared_invite/MjM3NDUwNzY4NjA5LTE1MDQ4MzE5NTAtYmU4MjYyYWNiZQ) - [FastNetMon Advanced, Commercial Edition](https://fastnetmon.com/fastnetmon-advanced/) - If you want add an [idea](https://fastnetmon.fider.io/) - Chat: #fastnetmon at irc.freenode.net [web client](https://webchat.freenode.net/) - Detailed reference in Russian: [link](https://fastnetmon.com/wp-content/uploads/2017/07/FastNetMon_Reference_Russian.pdf) Follow us at social media: ------- - [Twitter](https://twitter.com/fastnetmon) - [LinkedIn](https://www.linkedin.com/company/fastnetmon/) - [Facebook](https://www.facebook.com/fastnetmon/) Supported packet capture engines -------------------------------- - NetFlow v5, v9 - IPFIX - ![sFlow](http://sflow.org/images/sflowlogo.gif) v4 (since 1.1.3), v5 - Port mirror/SPAN capture with PF_RING (with ZC/DNA mode support [need license](http://www.ntop.org/products/pf_ring/)), SnabbSwitch, NETMAP and PCAP You can check out the [comparison table](https://fastnetmon.com/docs/capture_backends/) for all available packet capture engines. Complete integration with the following vendors -------------------------------- - [Juniper integration](src/juniper_plugin) - [A10 Networks Thunder TPS Appliance integration](src/a10_plugin) - [MikroTik RouterOS](src/mikrotik_plugin) Please use only recent versions of RouterOS! Travis status: ![Travis](https://travis-ci.org/pavel-odintsov/fastnetmon.svg?branch=master) Features -------- - Complete [BGP Flow Spec support](https://fastnetmon.com/docs/bgp_flow_spec/), RFC 5575 - Process and distinguish incoming and/or outgoing traffic - Trigger block/notify script if an IP exceeds defined thresholds for packets/bytes/flows per second - Thresholds can be configured per-subnet with the hostgroups feature - [Announce blocked IPs](https://fastnetmon.com/docs/exabgp_integration/) via BGP to routers with [ExaBGP](https://github.com/Exa-Networks/exabgp) - GoBGP [integration](https://fastnetmon.com/docs/gobgp-integration/) for unicast IPv4 announcements (you will need to build support for this manually). - Full integration with [Graphite](https://fastnetmon.com/docs/graphite_integration/) and [InfluxDB](https://fastnetmon.com/docs/influxdb_integration/) - API (you will need to build support for this manually) - [Redis](https://fastnetmon.com/docs/redis/) integration - [MongoDB](https://fastnetmon.com/docs/mongodb/) integration - Deep Packet Inspection (DPI) for attack traffic - netmap support (open source; wire speed processing; only Intel hardware NICs or any hypervisor VM type) - SnabbSwitch support (open source, very flexible, LUA driven, very-very-very fast) - Filter NetFlow v5 flows or sFLOW packets with LUA scripts (useful for excluding particular ports) - Supports L2TP decapsulation, VLAN untagging and MPLS processing in mirror mode - Works on server/soft-router - Detects DoS/DDoS in as little as 1-2 seconds - [Tested](https://fastnetmon.com/docs/performance_tests/) up to 10Gbps with 12Mpps on an Intel i7-3820 processor with an Intel 82599 NIC - Complete plug-in support - Capture attack fingerprints in PCAP format - [Complete support](https://fastnetmon.com/docs/detected_attack_types/) for most popular attack types Running Fastnetmon ------------------ ### Supported platforms - Linux (Debian 6/7/8/9, CentOS 6/7, Ubuntu 12.04, 14.04, 16.04) - FreeBSD 9, 10, 11: [official port](https://www.freshports.org/net-mgmt/fastnetmon/). - Mac OS X Yosemite (only 1.1.2 release) ### Supported architectures - x86 64-bit (recommended) - x86 32-bit ### Hardware requirements - At least 1 GB of RAM for compilation purposes ### Router integration instructions - [Juniper MX Routers](https://fastnetmon.com/docs/junos_integration/) ### Distributions supported - We are part of the [CloudRouter](https://cloudrouter.org/cloudrouter/2015/07/09/fastnetmon.html) distribution - We are part of the [official FreeBSD ports collection](https://freshports.org/net-mgmt/fastnetmon/) - [Docker image](https://fastnetmon.com/fastnetmon-community-docker-install/) - [Automatic install script for Debian/Ubuntu/CentOS/Fedora/Gentoo](https://fastnetmon.com/install/) - [Automatic install script for Mac OS X](https://fastnetmon.com/fastnetmon-macos/) - [Manual install on Slackware](https://fastnetmon.com/fastnetmon-community-slackware-install/) - [Manual install on VyOS](https://fastnetmon.com/fastnetmon-community-install-on-vyos-1-1-5/) Screenshots ------------ Main program: ![Main screen image](docs/images/fastnetmon_screen.png) Example CPU load on Intel i7-2600 with Intel X540/82599 NIC at 400Kpps load: ![Cpu consumption](docs/images/fastnetmon_stats.png) Example deployment scheme: ![Network diagramm](docs/images/network_map.png) Example of [notification email](https://fastnetmon.com/docs/attack_report_example/) about detected attack: Author: [Pavel Odintsov](http://uk.linkedin.com/in/podintsov/) fastnetmon-1.1.4/THANKS.md000066400000000000000000000032201343111404700152000ustar00rootroot00000000000000Thanks file! For all peoples which helped this project :) - Luke Gorrie for SnabbSwitch and help with lightning speed packet processing :) - Vicente De Luca for redis_prefix and InfluxDB optimization - Ronan Daly for Slack integration script - Andrei Ziltsov / FastVPS Eesti OU for testing and patience :) And for syncing GoBGP's gRPC intergration with upstream. - Luca Deri for PF_RING toolkit! - Max Dobladez for Mikritik API support in notify script handler. - Eric Chou and Rich Groves for A10 Networks Thunder TPS Appliance integration plugin - Elliot Morales Solé for improvements for ExaBGP integration - Roberto Bertó for Docker images and docs about Junos - Alfredo Cardigliano for helping me with PF_RING libraries :) - To flowd project for awesome parsers for netflow v5/v9 https://code.google.com/p/flowd/ - Roland Dobbins rdobbins at arbor.net for mitivating to add flow support - waszi for testing DNA/ZC mode - Martin Stoyanov for guides for Slackware - Andreas Begemann for debugging issue https://github.com/pavel-odintsov/fastnetmon/issues/90 - Anatoliy Poloz for VMs with FreebSD 9, 10, 11 - Cojacfar / https://github.com/Cojacfar help with documentation transaltion! - Thomas Mangin for help with ExaBGP integration - aabc for ipt_NETFLOW very useful tool for testing netflow plugin - Denis Denisov for FreeBSD rc script - Alexei Takaseev for AltLinux packages - Ben Agricola for fixed CentOS 6 init script without daemonize option - Dmitry Marakasov for FreeBSD port - Dmitry Baturin for huge help with building iso image with VyOS - mdpuma for help with Gentoo installer - Dmitry Kaminsky for help with configuration sanity checks and fixing redis bug fastnetmon-1.1.4/debian/000077500000000000000000000000001343111404700151135ustar00rootroot00000000000000fastnetmon-1.1.4/debian/changelog000066400000000000000000000002461343111404700167670ustar00rootroot00000000000000fastnetmon (1.1-2) UNRELEASED; urgency=medium * Initial release of Debian package -- Pavel Odintsov Sat, 30 May 2015 15:10:21 -0400 fastnetmon-1.1.4/debian/compat000066400000000000000000000000021343111404700163110ustar00rootroot000000000000008 fastnetmon-1.1.4/debian/control000066400000000000000000000024531343111404700165220ustar00rootroot00000000000000Source: fastnetmon Maintainer: Pavel Odintsov Section: net Priority: optional Standards-Version: 4.0.0 # debhelper 8 - squeeze, 9 - wheezy/jessie Build-Depends: clang, cmake, debhelper (>= 8), libboost-all-dev, libboost-regex-dev, libboost-system-dev, libboost-thread-dev, libgeoip-dev, libgpm-dev, liblog4cpp5-dev, libncurses5-dev, libpcap-dev Homepage: https://fastnetmon.com/ Package: fastnetmon Architecture: any Depends: ${misc:Depends}, ${shlibs:Depends} Description: Very fast DDoS analyzer with sflow/netflow/mirror support FastNetMon is a very high performance distributed denial-of-service attack (DDoS attack) detector built on top of multiple packet capture engines: NetFlow, IPFIX, sFLOW. . It can detect hosts in your networks sending or receiving large volumes of packets/bytes/flows per second. It can call an external script to notify you, switch off a server, or blackhole the client. . To enable sFLOW, simply specify IP of the server running FastNetMon and specify (configurable) port 6343. To enable netflow, simply specify IP of the server running FastNetMon and specify (configurable) port 2055. fastnetmon-1.1.4/debian/copyright000066400000000000000000000406331343111404700170540ustar00rootroot00000000000000Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Upstream-Name: FastNetMon Source: https://github.com/pavel-odintsov/fastnetmon Files: * Copyright: 2013-2017 Pavel Odintsov License: GPL-2 Files: src/actions/gobgp_api_client.proto Copyright: 2015-2017 Nippon Telegraph and Telephone Corporation License: Expat Files: src/concurrentqueue.h Copyright: 2013-2015, Cameron Desrochers License: BSD-2-clause Files: src/netflow_plugin/netflow.h Copyright: 2004,2005 Damien Miller License: ISC Files: src/netmap_plugin/netmap_includes/net/netmap*.h Copyright: 2011-2014 Matteo Landi, Luigi Rizzo 2011-2014 Università di Pisa License: BSD-2-clause Files: src/sflow_plugin/sflow.h Copyright: 2002-2011 InMon Corp License: sflow Files: src/tests/lru_cache/lru_cache.* Copyright: 2004-2011 by Patrick Audley License: GPL-2 Files: debian/* Copyright: 2015-2017 Pavel Odintsov 2017 Benjamin Drung 2017 Patrick Matthäi License: GPL-2 License: BSD-2-clause Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: . - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. . 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 HOLDER 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. License: Expat Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: . The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. . THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. License: GPL-2 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, version 2 of the License. . This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See 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. . On Debian systems, the complete text of the GNU General Public License version 2 can be found in the `/usr/share/common-licenses/GPL-2' file. License: ISC Permission to use, copy, modify, and distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. . THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. License: sflow Licensed under the terms of the InMon sFlow licence: http://www.inmon.com/technology/sflowlicense.txt . LICENSE AGREEMENT . PLEASE READ THIS LICENSE AGREEMENT ("AGREEMENT") CAREFULLY BEFORE REPRODUCING OR IN ANY WAY UTILIZING THE sFlow(R) SOFTWARE ("SOFTWARE") AND/OR ANY ACCOMPANYING DOCUMENTATION ("DOCUMENTATION") AND/OR THE RELATED SPECIFICATIONS ("SPECIFICATIONS"). YOUR REPRODUCTION OR USE OF THE SOFTWARE AND/OR THE DOCUMENTATION AND/OR THE SPECIFICATIONS CONSTITUTES YOUR ACCEPTANCE OF THE TERMS AND CONDITIONS OF THIS AGREEMENT. IF YOU DO NOT AGREE TO BE BOUND BY THE TERMS AND CONDITIONS OF THIS AGREEMENT, YOU MAY NOT REPRODUCE OR IN ANY WAY UTILIZE THE SOFTWARE OR THE DOCUMENTATION OR THE SPECIFICATIONS. . 1. Definitions. . "Documentation" means the user manuals, training materials, and operating materials, if any, InMon provides to Licensee under this Agreement. . "InMon" means InMon Corporation, its affiliates and subsidiaries. . "Intellectual Property Rights" means any trade secrets, patents, including without limitation any patents covering the Software, copyrights, know-how, moral rights and similar rights of any type under the laws of any governmental authority, domestic or foreign, including all applications and registrations relating to any of the foregoing. . "Licensee Hardware" means all computers, routers, or other equipment owned or controlled by or on behalf of Licensee. . "Products" means any and all software applications, computers, routers, or other equipment manufactured by or on behalf of Licensee for the purpose of resale or lease to any other third party, or otherwise made available by Licensee free of charge. . "Software" means the sFlow(R) software programs, in source or binary code format, that Licensee licenses from InMon under this Agreement and any bug fixes or error corrections which InMon may provide to Licensee. . "Specifications" means the published specifications provided or otherwise made available by InMon at: http://www.sflow.org. . "Trademark" means InMon's "sFlow(R)" trademark. . 2. License Grant. . 2.1 Software, Documentation and Specifications License Grant. InMon hereby grants to Licensee, under all of InMon's Intellectual Property Rights therein, a perpetual (subject to InMon's termination rights under Section 7 below), nonexclusive, royalty-free, worldwide, transferable, sublicensable license, to: (i) use and reproduce the Software, the Documentation, and the Specifications; (ii) modify the Software; (iii) implement the Specifications in the Products; (iv) install the Software, or software in which the Specifications have been implemented, on Licensee Hardware and Products, and (v) distribute any Products that include the Software, the Documentation, or software in which the Specifications have been implemented. . 2.2 Trademark License. InMon hereby grants to Licensee a perpetual (subject to InMon's termination rights under Section 7 below), nonexclusive, royalty-free, worldwide, transferable, sublicensable license to use the Trademark on or in connection with the Software, the Documentation, the Specifications and any software that implements the Specifications. . 2.3 Restrictions. Licensee agrees that it will not use the Software in a way inconsistent with the license granted in Section 2.1. Further, Licensee agrees that, in exercising its rights under the license granted to it in this Agreement, Licensee will: (i) strictly adhere to and fully comply with the Specifications; (ii) use the Trademark, and no other mark, to identify the Software, the Documentation, the Specifications and any Products that implement the Specifications; (iii) place, in a font or graphic design designated by InMon, the phrase "sFlow(R)" on any technical documentation, sales/marketing materials, catalogs, or other such materials relating to products it manufactures or markets which it has configured to be compatible with the Software or otherwise implement the Specifications; (iv) in connection with any Products shipped to or sold in other countries that include the Software or any software that implements the Specifications, comply with the patent and trademark laws and practice of such other country; and (v) not alter or impair any acknowledgment of copyright or trademark rights of InMon that may appear in or on the Software, the Documentation or the Specifications. In the event InMon determines that Licensee is not complying with its obligations under clauses (i)-(v) above, InMon shall notify Licensee of such non-compliance, and if Licensee fails to correct such non-compliance within three (3) months, InMon may immediately terminate this Agreement as provided under paragraph 7 below and pursue any and all actions and remedies as it deems necessary, including, but not limited to breach of contract. . 3. Ownership. Except for the license expressly granted in Section 2, Inmon hereby retains all right, title, and interest in and to the Trademark and all its Intellectual Property Rights in the Software, the Documentation and the Specifications. Licensee obtains no rights hereunder in the Trademark, Software, Documentation or Specifications by implication, estoppel or otherwise. Licensee acknowledges that the Trademark, Software, Documentation and Specifications are being licensed and not sold under this Agreement, and that this Agreement does not transfer title in the Trademark, Software, Documentation or Specifications, or any copy thereof, to Licensee. . 4. Support. Inmon shall have no obligation under this Agreement to (a) supply maintenance or support, bug fixes or error corrections to the Licensed Software, (b) supply future versions of the Licensed Software or (c) provide Licensed Software development tools to Licensee. . 5. Warranty. INMON HEREBY DISCLAIMS ALL WARRANTIES, EITHER EXPRESS, IMPLIED OR STATUTORY, WITH RESPECT TO THE TRADEMARK, THE SOFTWARE, THE DOCUMENTATION, THE SPECIFICATIONS. OR OTHERWISE, INCLUDING BUT NOT LIMITED TO IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT OF ANY INTELLECTUAL PROPERTY RIGHTS. . 6. Limitation of Liability. IN NO EVENT SHALL INMON OR ITS SUPPLIERS OR LICENSORS BE LIABLE FOR ANY CONSEQUENTIAL, INCIDENTAL, SPECIAL, INDIRECT OR EXEMPLARY DAMAGES WHATSOEVER, WHETHER RELATED TO OR ARISING OUT OF THIS AGREEMENT, THE TRADEMARK, THE SOFTWARE, THE DOCUMENTATION, THE SPECIFICATIONS, OR OTHERWISE, INCLUDING WITHOUT LIMITATION, DAMAGES FOR LOSS OF PROFITS, BUSINESS INTERRUPTION, LOSS OF DATA, COSTS OF PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES OR FOR ANY CLAIM OR DEMAND AGAINST LICENSEE BY ANY OTHER PARTY, OR OTHER PECUNIARY LOSS, EVEN IF INMON HAS BEEN ADVISED OF OR KNOWS OF THE POSSIBILITY OF SUCH DAMAGES. . 7. Term and Termination. The term of this Agreement will begin on the Effective Date, which shall be deemed to be the date of delivery of the Software and/or Documentation and/or Specifications to Licensee, and shall continue indefinitely unless and until terminated by Licensee's giving written notice of termination to InMon, or by InMon pursuant to InMon's termination rights as set forth in Section 2.3 above. Upon any termination of this Agreement, Licensee shall cease exercising its license rights under this Agreement, including the right to distribute Products that incorporate the Software or Documentation or that implement the Specifications. The rights and obligations contained in Sections 1, 3, 5, 6, 7, and 8 shall survive any termination of this Agreement. . 8. General Provisions. . 8.1 Assignment. This Agreement shall be binding upon and inure to the benefit of the parties hereto and their permitted successors and permitted assigns. InMon will have the right to assign this Agreement without notice to Licensee. Licensee may assign or transfer (whether by merger, operation of law or in any other manner) any of its rights or delegate any of its obligations hereunder without the prior written consent of InMon, provided the assignee assumes in writing all of Licensee's obligations hereunder. . 8.2 Notices. All notices permitted or required under this Agreement shall be in writing and shall be delivered in person or mailed by first class, registered or certified mail, postage prepaid, to the address of the party specified in this Agreement or such other address as either party may specify in writing. Such notice shall be deemed to have been given upon receipt. . 8.3 Non-Waiver. No term or provision hereof shall be deemed waived, and no breach excused, unless such waiver or consent shall be in writing and signed by the party claimed to have waived or consented. Any consent or waiver, whether express or implied, shall not constitute a consent or waiver of, or excuse for any separate, different or subsequent breach. . 8.4 Independent Contractor. The parties' relationship shall be solely that of independent contractors, and nothing contained in this Agreement shall be construed to make either party an agent, partner, representative or principal of the other for any purpose. . 8.5 Choice of Law and Forum. This Agreement shall be governed by and construed under the laws of the State of California, without giving effect to such state's conflict of laws principles. The parties hereby submit to the personal jurisdiction of, and agree that any legal proceeding with respect to or arising under this Agreement shall be brought in, the United States District Court for the Northern District of California or the state courts of the State of California for the County of San Francisco. . 8.6 U.S. Government Licenses. The Software and Documentation are considered a "commercial item" as that term is defined at 48 C.F.R 2.101, or "commercial computer software" and "commercial computer software documentation" as such terms are used in 48 C.F.R 12.212 of the Federal Acquisition Regulations and its successors, and 48 C.F.R. 227.7202 of the DoD FAR Supplement and its successors. . 8.7 Severability. If any provision of this Agreement is held to be unenforceable under applicable law, then such provision shall be excluded from this Agreement and the balance of this Agreement shall be interpreted as if such provision were so excluded and shall be enforceable in accordance with its terms. The court in its discretion may substitute for the excluded provision an enforceable provision which in economic substance reasonably approximates the excluded provision. . 8.8 Compliance With Law. Licensee shall comply with all applicable laws and regulations (including privacy laws and regulations) having application to or governing its use and/or operation of the Software and agrees to indemnify and hold InMon harmless from and against any claims, damages, losses or obligations suffered or incurred by InMon arising from its failure to so comply. . 8.9 Entire Agreement; Amendment. This Agreement constitutes the final, complete and entire agreement between the parties with respect to the subject matter hereof, and supersedes any previous proposals, negotiations, agreements, or arrangements, whether verbal or written, made between the parties with respect to such subject matter. This Agreement shall control over any additional or conflicting terms in any of Licensee's purchase orders or other business forms. This Agreement may only be amended or modified by mutual agreement of authorized representatives of the parties in writing. . InMon Corp. 1 Sansome Street, 35th Floor, San Francisco, CA 94104 Phone: (415) 946-8901 URL: www.inmon.com Email: info@inmon.com fastnetmon-1.1.4/debian/rules000077500000000000000000000002271343111404700161740ustar00rootroot00000000000000#!/usr/bin/make -f %: dh $@ --buildsystem=cmake --sourcedirectory=src override_dh_auto_configure: dh_auto_configure -- -DDISABLE_PF_RING_SUPPORT=yes fastnetmon-1.1.4/debian/source/000077500000000000000000000000001343111404700164135ustar00rootroot00000000000000fastnetmon-1.1.4/debian/source/format000066400000000000000000000000141343111404700176210ustar00rootroot000000000000003.0 (quilt) fastnetmon-1.1.4/debian/watch000066400000000000000000000001661343111404700161470ustar00rootroot00000000000000version=3 opts=dversionmangle=s/\+dfsg// \ https://github.com/pavel-odintsov/fastnetmon/releases .*/v(\d\S+)\.tar\.gz fastnetmon-1.1.4/docs/000077500000000000000000000000001343111404700146215ustar00rootroot00000000000000fastnetmon-1.1.4/docs/AMAZON.md000066400000000000000000000001651343111404700161320ustar00rootroot00000000000000Sorry, we've removed and deprecated Amazon images because they had ZERO interest. Let us know if you still need them fastnetmon-1.1.4/docs/API.md000066400000000000000000000001301343111404700155460ustar00rootroot00000000000000We moved this page to our site [here](https://fastnetmon.com/fastnetmon-community-api/) fastnetmon-1.1.4/docs/ATTACK_REPORT_EXAMPLE.md000066400000000000000000000001311343111404700203530ustar00rootroot00000000000000We moved this page to our new [site](https://fastnetmon.com/docs/attack_report_example/) fastnetmon-1.1.4/docs/BGP_FLOW_SPEC.md000066400000000000000000000001201343111404700172050ustar00rootroot00000000000000We migrated this page to our [site](https://fastnetmon.com/docs/bgp_flow_spec/) fastnetmon-1.1.4/docs/BUILDING_DEB_PACKAGE.md000066400000000000000000000001741343111404700201270ustar00rootroot00000000000000We deprecated any custom Debian packages. Please use upstream packages instead: https://packages.debian.org/sid/fastnetmon fastnetmon-1.1.4/docs/BUILDING_FREEBSD_KERNEL_FOR_NETMAP.md000066400000000000000000000001421343111404700223010ustar00rootroot00000000000000Page was moved to our [site](https://fastnetmon.com/installing-netmap-module-for-freebsd-kernel/) fastnetmon-1.1.4/docs/BUILDING_VYOS_ISO.md000066400000000000000000000001541343111404700177320ustar00rootroot00000000000000We do not support VyOS images with pre-installed FastNetMon. Ask VyOS project directly about this feature. fastnetmon-1.1.4/docs/BUILD_BOOST.md000066400000000000000000000001511343111404700167450ustar00rootroot00000000000000We deprecated this guide. Please us automatic [install script](https://fastnetmon.com/install/) instead. fastnetmon-1.1.4/docs/CAPTURE_BACKENDS.md000066400000000000000000000001201343111404700174710ustar00rootroot00000000000000Page was moved to our [new site](https://fastnetmon.com/docs/capture_backends/) fastnetmon-1.1.4/docs/DETECTED_ATTACK_TYPES.md000066400000000000000000000001151343111404700203340ustar00rootroot00000000000000Page moved to our [site](https://fastnetmon.com/docs/detected_attack_types/) fastnetmon-1.1.4/docs/DEVELOPER_GUIDES.md000066400000000000000000000001371343111404700175310ustar00rootroot00000000000000We moved this page to our [site](https://fastnetmon.com/fastnetmon-community-developer-notes/) fastnetmon-1.1.4/docs/DEV_VERSION.md000066400000000000000000000001351343111404700167650ustar00rootroot00000000000000Page moved to our new [site](https://fastnetmon.com/fastnetmon-community-developer-version/) fastnetmon-1.1.4/docs/DOCKER_INSTALL.md000066400000000000000000000001361343111404700173000ustar00rootroot00000000000000We moved this page to our [site](https://fastnetmon.com/fastnetmon-community-docker-install/) fastnetmon-1.1.4/docs/EXABGP_INTEGRATION.md000066400000000000000000000001221343111404700177470ustar00rootroot00000000000000We moved this page to our [site](https://fastnetmon.com/docs/exabgp_integration/) fastnetmon-1.1.4/docs/EXABGP_INTEGRATION_WITHOUT_SOCAT.md000066400000000000000000000001501343111404700221240ustar00rootroot00000000000000We moved this page to our [site](https://fastnetmon.com/fastnetmon-community-and-exabgp-without-socat/) fastnetmon-1.1.4/docs/FINE_TUNING.md000066400000000000000000000001261343111404700167470ustar00rootroot00000000000000Migrated page to our [site](https://fastnetmon.com/fastnetmon-community-fine-tuning/) fastnetmon-1.1.4/docs/FreeBSD_INSTALL.md000066400000000000000000000002341343111404700175420ustar00rootroot00000000000000Since release 1.1.2 FastNetMon was added into official FreeBSD ports. Please use version from [ports](https://freshports.org/net-mgmt/fastnetmon/) instead. fastnetmon-1.1.4/docs/GOBGP.md000066400000000000000000000001251343111404700157770ustar00rootroot00000000000000We moved this page to our new [site](https://fastnetmon.com/docs/gobgp-integration/) fastnetmon-1.1.4/docs/GRAPHITE_INTEGRATION.md000066400000000000000000000001201343111404700202020ustar00rootroot00000000000000We moved this page to [site](https://fastnetmon.com/docs/graphite_integration/) fastnetmon-1.1.4/docs/IMPORTANT.md000066400000000000000000000001351343111404700165170ustar00rootroot00000000000000If you are looking for something here, please check our [site](https://fastnetmon.com/docs/) fastnetmon-1.1.4/docs/INFLUXDB_INTEGRATION.md000066400000000000000000000001241343111404700202160ustar00rootroot00000000000000We moved this page to our [site](https://fastnetmon.com/docs/influxdb_integration/) fastnetmon-1.1.4/docs/INSTALL.md000066400000000000000000000001021343111404700162420ustar00rootroot00000000000000We moved this page to our [site](https://fastnetmon.com/install/) fastnetmon-1.1.4/docs/INSTALL_RPM_PACKAGES.md000066400000000000000000000002241343111404700202230ustar00rootroot00000000000000We deprecated rpm packages. Please use automatic [install script](https://github.com/pavel-odintsov/fastnetmon/blob/master/docs/INSTALL.md) instead fastnetmon-1.1.4/docs/JUNOS_INTEGRATION.md000066400000000000000000000001211343111404700176760ustar00rootroot00000000000000We moved this page to our [site](https://fastnetmon.com/docs/junos_integration/) fastnetmon-1.1.4/docs/LUA_SUPPORT.md000066400000000000000000000001331343111404700170150ustar00rootroot00000000000000We moved this page to our [site](https://fastnetmon.com/fastnetmon-community-lua-support/) fastnetmon-1.1.4/docs/MAC_OS_INSTALL.md000066400000000000000000000001061343111404700173270ustar00rootroot00000000000000We moved page to our [site](https://fastnetmon.com/fastnetmon-macos/) fastnetmon-1.1.4/docs/MANUAL_INSTALL.md000066400000000000000000000001261343111404700173050ustar00rootroot00000000000000Please use our suggested automatic [installer tool](https://fastnetmon.com/install/). fastnetmon-1.1.4/docs/MEMORY_CONSUMPTION.md000066400000000000000000000001231343111404700200450ustar00rootroot00000000000000This page was moved to our [site](https://fastnetmon.com/docs/memory_consumption/) fastnetmon-1.1.4/docs/MONGODB.md000066400000000000000000000001071343111404700162260ustar00rootroot00000000000000We moved thos page to our [site](https://fastnetmon.com/docs/mongodb/) fastnetmon-1.1.4/docs/MULTIPE_INSTANCES.md000066400000000000000000000001221343111404700176640ustar00rootroot00000000000000This page was moved to our [site](https://fastnetmon.com/docs/multipe_instances/) fastnetmon-1.1.4/docs/NETMAP_INSTALL.md000066400000000000000000000001221343111404700173100ustar00rootroot00000000000000We moved this page to our [site](https://fastnetmon.com/netmap-install-on-linux/) fastnetmon-1.1.4/docs/PACKAGES_INSTALL.md000066400000000000000000000000621343111404700175050ustar00rootroot00000000000000Sorry but we do not offer binary packages anymore fastnetmon-1.1.4/docs/PERFORMANCE_TESTS.md000066400000000000000000000001211343111404700176600ustar00rootroot00000000000000We moved this page to our [site](https://fastnetmon.com/docs/performance_tests/) fastnetmon-1.1.4/docs/REDIS.md000066400000000000000000000001011343111404700160010ustar00rootroot00000000000000Page was moved to our [site](https://fastnetmon.com/docs/redis/) fastnetmon-1.1.4/docs/RELEASENOTES.md000066400000000000000000000002001343111404700170240ustar00rootroot00000000000000We do not maintain this page anymore. Please use standard [release page](https://github.com/pavel-odintsov/fastnetmon/releases) fastnetmon-1.1.4/docs/SEGFAULT_INVESTIGATION.md000066400000000000000000000001261343111404700204570ustar00rootroot00000000000000We moved this page to our [site](https://fastnetmon.com/docs/segfault_investigation/) fastnetmon-1.1.4/docs/SLACKWARE_INSTALL.md000066400000000000000000000001351343111404700176440ustar00rootroot00000000000000Page was moved to our [site](https://fastnetmon.com/fastnetmon-community-slackware-install/) fastnetmon-1.1.4/docs/UPDATE.md000066400000000000000000000001361343111404700161250ustar00rootroot00000000000000We moved this page to our [site](https://fastnetmon.com/fastnetmon-community-update-process/) fastnetmon-1.1.4/docs/VYOS_BINARY_ISO_IMAGE.md000066400000000000000000000001201343111404700204140ustar00rootroot00000000000000Sorry, we deprecated VyOS support and you need to install FastNetMon manually. fastnetmon-1.1.4/docs/VyOS_INSTALL.md000066400000000000000000000001451343111404700171710ustar00rootroot00000000000000We moved this page to our [site](https://fastnetmon.com/fastnetmon-community-install-on-vyos-1-1-5/) fastnetmon-1.1.4/docs/images/000077500000000000000000000000001343111404700160665ustar00rootroot00000000000000fastnetmon-1.1.4/docs/images/fastnetmon_screen.png000066400000000000000000010313461343111404700223210ustar00rootroot00000000000000PNG  IHDR}sRGB pHYs%%IR$iTXtXML:com.adobe.xmp Pavel Odintsov 1 D$@IDATx`E! ̈́(Ez)4 EMHW"@@+ UBʵo%p3of۷odqqqZB D"@ D"@ D #f"@ D"@ D"@ 7D"@ D"@ D";Ou'5"@ D"@ D"@M D"@ D"@ D)dSI!D"@ D"@ D! D$M@Bl8{A6"@ D"@ D"dqqqZGJ/_E*JG|:!tܹ=C߯oP ;*$f@aDʠg=xN.kSVV0ױL&ؔkpo!i}D|7 ͯVߡE9 yaobJhp%By {W'a^D"@ D"@ D8 D`GŠdL6K ߚX1=CEanCT[Ɣ@H0X "jb]Ub(#qzЦNh~ ,cCcİ#:-yz?߮!BQ}`];ޜ24l,1ڍQD"@ D"@ Db|1C@LRiO .9eSH͹Uul4@DDA.V QZqc* Y#P1] УUwe& 6`-j ʹ4񽐴z25\ =x(3 '-]c7($u|hFD"@ D"@ D aNCʐZρ:wayca_tRF6؁ecEHSTvK` 3eևyoTiV>" -3Aހ_rۡe\үiUNL >N!9Sxj$w*g`m!D"@ D"@ 9O)oK5xoT}O]0Գ;Ġ&ٶ6 Dg peF'g~oreBѝ'7;gl~ edM$7lMD"@ D"@ D mN+dkg׭ .*{q랮;DҥX,&% ُ X퀳*HO&1wv9%tcM{ųtѳ]ĮpJb(YnK͢=hP(Kf{ߙYx>>*6C?RqxG~UP`G|NC:)\W9oӺV}?ʕ{kĦ,ʹI/3C0z6T% D"@ D"@&Z)/v Q6*+ۘb \}#$SGϡ*&e!_0hU)M UH3arkrk->ݍN̸8jDQqxs"3H:Xhq,L:Opٱ;*ILb4k-5~~;TCId1E]5 #2 WT?_ @5.&1;ygl7'll}ƃh]LL ϨaC#Vked2 а\S]k"f領XfAXa,RNGS6Ū`_9{DN"@ D"@ D ;w/ѲmtQ %caU=ntĺO+Fxl~J1-*,d &8 Tqp@H}k/n̪E+|"VV A!q4DaWd {sC^$A{LOH#km /&,4K2[,X]"Jr/u`T0K/`;jȢA>.m>FӸ:?}Oiqp78h36%u_^kz|qU-'2U Vx "@ D"@ D";"F1|Ì| a.Y~Wd#(A*Tyk*0zg8y4Q82%"P$<tjhbQr<ʈM0<A[YttjΆw_qx3@zMn5*b/=f4%*2蟧q#_,T/g]zo,ozL?b /$F+$`>Ԋ%U䌽J-oK3EG&n}%+Dr@dA8M· 'ah 著&>+yhYswÜ6n Ko||mo1/W%lCb}&{-`̪5U?9uzct5!R$V D"@ D"@a.ZGj2\!֝xt)/4Db&1SNs״CҴv`2h-E]ٷfb&R_G E_p1Ձa&7'o)]WD8q,P/,uG$8] _<2kc@ |9^xe\Z.i(xc>7?6. 6^9F ==b_g%пD"@ D"@ D2|bѝ -ODȾu17r^caĥ'5rjw`Bgi}FD\$"Iv 8ovĠҾ9’mME' e̟x 'ump/VMs㹗>RBi=ѯp(ٺ@*+lsB%*ȋ)g-ϔµ25z_xo 桯Hh]k>)kK1]/4gw~`j Yqr[y]`/O)ZD"@ D"@ $`m5tX{&N0):CuL=; %̝r8݅u8 G^yWNů?Dߍչk+N+B͙@SQ4_.{&ɃVg,K+.G uZ{]vwۥvCzCtv5)>zУ ԮNp!}v\$~TT*o.Vn<&]{ ]@Ckn2ӾWG Lw{P|~])S]b<H$K @kK!t YX`ꡠɦB D"@ D"@ro%,γ"|}Nq3ֶ =\ƟjytGxIkPF C?ĉEex5E- ~`,n˻d‡6StLٶ2J" T!*偰O &`ZXxps+:~T>]6K0"@ D"@ D"kT 9j(^!+טxt `|\;~C u~->zǛk~I)Phyx>~{GLi \'6ݫ%Qa {bi]3[Ş|>b-k܄2P~&59q)h r/'KZ|j*ۖ%K .4-wwöklF= ~:FkwnY/ui\qxL~! p y}({Q6T"@ D"@ DOwIv_S%"lʦп-s'OlmHƪC͢׷**&',ǕKzc#4ðeѨmLvY'IœYn&QRKߺ"vŞ푟I/aPhR? :Qg5K_Z~fRh?>~Yg(|p D"@ D"@ s [\Ox㠶d%ą2X?#3$qjbJRAzYC<FX'NcV#/I<xCvqa=~;pp#d;W4h]>̌l |\AjCjV5᳕iA9= [}>ؒcwp`tc7L!㗯^2'}IevTS9Z!D"@ D"@ 9M7}8zsV5C57 q [~3=2 gܟ)}lJS6Jgu>on U:V⌒̂V\ *BV1 R>ok /ٟuvEwDڥmɵLɔ֯(J:84U,+d =0 )Hα*ipۗP.Oore1  Jf&uu tp-6jPEOٟ>^׌CG;zT H* D"@ D"@@^#dlWe8p:V0Tѿno6 قx^h\.ID$Kʺq>90 I\NeU!1̇xjUetm.˶a)pC>K\dp[ o `swڥuL˰b?Yyu ,{E/έԷq|XK-ms沫M?VM:♯?ALCUU&v.=Y*@{Kq2=,YvsNp/n{mVW6!W^"@ D"@ DZ8}GP5MbLW"w4 -uݡg]ĕ+P-Ph8>#;nOTHb}Ѭz j!S>kWqdBtln'ߴ*am0O'k·pV_KS>j`/!sҭ691&fig 06`!I"ysgLO`ۂ X94v92iX7"@_ntsˮ!-1'&-]} TxJ )eM&D"@ D"@ Dp2ԉ4* D"@ D"@ DH@ @$D"@ D"@ D"??}A"@ D"@ D"@-DA D"@ D"@ C   D"@ D"@ DH@ @$D"@ D"@ D"??}A"@ D"@ D"@-DA D"@ D"@ C   D"@ D"@ DH@ @$D"@ D"@ D"??}A"@ D"@ D"@-DA D"@ D"@ C@?&D"@ Dj2TZ-pҥ\ԭWHy}>}ڴV D-mU ȌI+!2|K3WǔUcB}GĬA ^UG|^KƔAvoFbծޚ1]8 >\/=Uz2`<71E("F}ʁ@dz+ɫKķɝ >- 2޺︫^w{m4ՍwG|xha_#ZH)e8ޮY\gtw|3>_GD@S ̞25d2!?Y0;SZjxhP WiLWL#X\gĂ)tpd@0}fwmn#Z~ޭk!h0/2JUl SCf#GO^ݡ={Fy7Οį?忻+,GJ &CD?tV?j#l'ݿ4s]6UL4X:gOesVx3pQ7,*od_X{m.ə )e%G`mU&2cu2,ZμІ3xvy1 .My Zy>vةUú#A7v2boLM_#^x/|:7ϧIouP-(Ǐh'٤ ߢUNJPTa%+.nVVF1;*Fvir((f4QSVD[tݒiJ!{E)j ^(B ѓu}?L|W"/Ԡ@JPVwD|, 9޳HG;ݿ\7F6 [>cؐ E "8h͔)\,LTNhh(ADtiT|= WOBBNvmK*e_&o laI J Jȫ}VqX4qlر7MCvf+bVFow/] zw`qUinh ||x Ja&"8>fq_cr'3q&n>|L^#-vh G셕5{t /ӑՠ(Z =}i6_<ڮ1ؑi,M}hL%͟L+OTPgAe\N: ن}AyBIiܫbjuZʄkkPTZ'yVnK/6WԱx&"VrU<5i2f1Gy|?L`y E' Oظ,KaH5Ux=_}j7~)>긏F L?ytǬTg7GH%-oѶTrT' pf&zxgK$DkRDAeM~yhMD9pv2/EeZDw&-ߊM >ft*iHKCʐZ*mV|h57SbѮc4(4!/KAGLh"@G_]*7Vٻ̌2uY2CgPLE `NVosĝ gUc^-YZ}h"M1}EzYSCMqmteFog庐4v^*zHM]vkDv, UxmyS8Bcc2~?Ӈ_Rl>#;[]5/?V~ǡNDJH pcy2viW':M̶i"@2 WL}߿ތޮ~Ef"Mku 3cA%庍`- ED O˯]R.8vB1)Ck 4.y\8BԵ;Dҥt9l !_8Yu8UǶTs;Y{SuH=m͞ *p¸`/N+dixҳ]Į`o,nz.)hLT^^@16Aͣ[anJXmuY>˶G)R[ן@@w]_ɲW/7 ) '/,/k.g4Aiw6-;!!*2e:T d<|7C@LdAgôY7ڝ8gI%g$,bȐs/l-IpZq6#ǿ-=[+> 1E(͸֯BR_ }ڕJu̿- ֖GUbfdS]^y '8\О~y*>rG/1*iTJGnؿ!5bf>%2Xѹs("IYH2ڥ`w!cËػjFe[`}}jʆ& /~S^Qu𑝘϶^FFyܯpFfѹGxKy>\9{XT 8+˃|UGvs<,I:Q"fcwu3͖[m;? eв8i-;pd 9>/TPe?.~ Sm|,펨{#ki`6oVXL6r>Vi(SPx4ILƶf`]Cse-mB-H~v*d>N{aA1ӹ4W2+ !MߺmZyQT8{b0+qK} ZJGʳ3gE[0iyJ*"|P׃Cޠ"@sڬ^X0xYYg|j_gn4,U[/tVh]cӠ'E" }kPATh:_Lri/ѲCATpBP2a.>9k{CDt4[e7wS03^6'>I8LOS6pUJ&8&Up>\=2aߋڋC6߮ϯ?p!N1!rH0q(ga{ ڂa ^CVߦ߭9 3|t/_Ta(T %X]UuFҪ| w8wpp0 J'볰^\yCxC|_p!.:i6pc,o>,'lgXBGxKr4}ŭVsg|Efenڲ`wu'7}1{٘脱Pj>V$H.|.|vI53u%= AnmIqwgǫ {rO)Mf/0N Hu9lj1)&=3xk-@wgerfШ;/8Oz{Ptr'.ywgFs 3anG;÷V FVBi;GHB 9m d?3z*Ϳd*3͢q E_S8 Bcs}wW"Il&q{]8W+#f EB( TW_<}3Mik8i:^f{H؜TvI$|LTY_L dad?+=PLaֿo:/͙:`h-\~e6 {]mc24`ǛlYS|6 zҐ0gXӢ pI[h7m~MP_;_/7*:_oޮ*9{.gPXI.P獫B=&`v(!" KOWBQF䡓{z&yjuYttjPv}f6f)ql/"L4C.)2a|b|GD 2;Ο_VJ:M LZ?Ջ(K/o㯣\>ʗ0їdzCGep`󯸞_+)_"Xn{{P.1FtuqswRwmrGayjY 3CFonuS]8\LOuBlxw mf1^wje'̛L->8=BByO&ݿCiKu\i[+ E`i(6/CF7LfMk .cY֢u;!#+ÌǫʦУ oA+aLgkϾ5ŗo,uѦaR@4ܼ~Ǭ X t[N,8yq ۃ_9?Syw}iyoZsZu>[éNG*>FR'rrn<>4Ccl9`X~5\ TvģXzݶ?[׌dI[LZ(*{ޫ甙 d4]沌MMUrs47xVsUtίs맡Hf0g L Mb&j?Ʒ`} LJ G#ykܚ\%:03ג]k2%nm_LU>mxZǼ=>{Ϗg+Ȓgz,scjUX2>NeSra@#Y 0|f<)v2I.|.lvI9q:0dd枟6ϛы4zAt9]0*dci`j\ljtEe"x?e`"u6ߘr_u).tSyj3I5nq67RVz7qV9gб^^2-<0׮}ٚwϤ9fVe_7' -W? zfoE C4x5<.(%pɾ,2(nbp?aore YVX/ݱ0_M▆ߓrk_a-dg\X zrFowE$w PFa1Ĉs]#]%G^px7-!}ޫZk g/VIsg!< ׻Hjؕy}2jzqmd9 VXż2txʇ{X׎;dzL}iay x`X0lYݍגC-hh9I VLElghoa:\y |I3R-t}Z[ 8]E<zdFo)ҷRj]_:87^'oW?@yC[D؇N.[󢷈w6ϱ95qB$ř~sK ;]ۡoS˦y{@z K,ud#ӗ!.w}X.RC L|2ψ/Ic~r}6jW8lqnW >Rڜ3$J_,\ORoK'ԗˁsQ7'U=JCiqrڲgHKi]~\ +ek3z[-~K63>R=_ؿW3;_|%py$Vq/CbaSIvƜ]oM=>]L~^Ke!u_^忋 0UPn[:{+sTT*oRh|\3ף=fI`rL)#8]nӿ0fѤU5MZvw;&Oo6ᧆ24N,_q. b:[FG*>Mf[?fc"Vd;x> 5|x YM[;'V([W1A= Ro"%ۚ|~)6p^տD_;LG`uE֍hm7'[–z([{l.A[_'Ud0uW >wwf,dzTC)?ݿz=6Y7tVlhzoMq]\ %uoί"FoGzK9>R=_=B}7!jIHBAR[r8(S+~@/iwVLJU1÷XK^{0gx3&r?bfkPF C?ĉŪ$M$^e3`Xr&0U߃)܋&.?x=T9yW9Fs4YqKNބ'yMsJv^QQ_MIbHJw{TAd<(NpoIHITmfNhgP9ai(҇7qf c]<|_z1qfiK*}c 6JǩVjnp=+EáOD&d/ zM/3nnq+Qs]#R{-FpJ JA]lO|;C1R+r1?]|HEH$^/(?~"<εϟgWTmrRvKq r/ Ta6o8' {fXOu=G狜_~(H@IDATl!d$z^/)x&ƙ2-yj.M/sy\sT,_, 'fϝ63MFvMQ%Y8Yt#LZ5)ݒlƀKil*8U}K(r덛'̘:>qO &94u/%U'Y91}Óng)7GAǏmkqaV?UZQo b\_ƶV3;݂& V[lFo+^,SҰk\y27ɨ:&Vq췘p&IK*%~U/C Sd-"AS( ؋S"rw5L_U8,oCdYW*E#'\mgl)jt,8fD9x\7?/Wd\'G*} :)ϭ^=x+[_^dG(3qy\V_yeyj$[=K 1ow~!ܱx2;6A*uRe*z[~ k*G\ Di^E/qqqZQ:t_zCO\]w.F_)\ݣ?֢}[9rћk|&FT7d:_G.^6\s$j=.Mł?/4 i 6xQBT>;rqܵv'&o5bidWxߝTf6:q=tlRo6UH*H3OO {kNRܭ^9O/|yReL\:v^<ڗW#(6 8=9O%V/TT'R={?-8hO]TyxMMl""e(ȉ쬮w-qzeֆI97;QY2>,ڜ=Py3gOL n̜[G*>"!fv-):e&T=:W3svۇ<ls, h "TP7'5}a@><ſͥH^:*Y _7(cj" /;kږt%Th)Ir:OגKT۪Ws!mfl᣽ 闅qmbUaZld :Uql[ujcc5J W.?).ӓM1mF`$ԉgܲJ-SF66bmj8{ܿ ex*>RA"djnv K߮("Raޓsa&j! w^pIpMRHA c&{sג ̸{j,| |004|3n:;|8K>sL߷A*\߱ ~\Ľ6}Jυ,xMJJ3wRH%uUR-zw~AwK\{∏{ܞR\ e_x{㴮뜮NF/|ur|)b}ޛ'C¥]4t ۙ:OV T.yj0p϶}}^{VIkǣRA-N *ob.{>ȸ %-t@V ^KWquܸv%Pz]T .E˷lx]ۯ.XOA]{q !5e')ozofz)ൿ_1RxK, GTT83_ȕiP ,,Fb2Gw5q QϠ|s(%H=e mK|\qnUpLڲ71_RϢTq{stOPL=V1 -B`.Ӟ0 ۜe3x.نB8Ԩizқ8K (RL~jJM޸ru5)ƤiXp|8\W!pQD/zWA`S~+,X\9T|By)ъPY8y;qa![e"usؓ'gG\s,"gHVT߮FVmo75b}5v3H람#4-KG~U>v{=$) ORb>e%gT#C.oR?.(+΅jmfuByEh|Hþә"޻ryٺ]Oq)y~w ,W_RT>,gmgUc@Plӏ-]yhc÷6 1Q(*X7zIFC"Cܣ7E 2*{dΌ?[cS0fXVҧt~E~FQTXU*"b2@+DtxD6y24`ͧ#^@/Mvw#:/#J7]YIh}pa05llJ]u7Ey )ٟUnfU "ēẁF_Qh8GKӐ|8 ^^Hq^X*12i3|18"u(=Y3S#< ?Uꉯ{~nwPAcx*0~9@r(,?3_х<2JnRT>s=F*ff5${u ,{E\L}7E74a3R{\;+KH[b~}}Zwa)6NRY }#FÕ4A%۟AO)qt1Swd:pʮĬ$S3cUpf![<<_gd}cuxm&>|\Ʒ]?7V_>/KƙRPt,Ù|vw\K5]Z;YeVADwED$Y\\ۚvYq "T{2*$onIXhFBt0ܹVXR )tĿQj!q: dϓX`N*c8iy;#-o-kW@Lv A,zNc5N\C_4Zv?kWqdBsٿaPEL};|lfPV]W%#uRc6c";[Mt/w޸︫?q-Om9?z.F:OX{l?7ͥv:U_4S{\rqqa<%pRF#:,H>MU`ogoa2zװbtTJ3or!.&Zl ?Y,)[} e$LFtQE输{D'0z<,'-1ubFћ^3zkc!|1+m6o?H" D'Ͼ6VD!~.&z){Oکʼ;bO1y9tֈmD&]V﹵Monj(k`Z\y[cK΄em /\Gh6gX8@rf^^ݚ}'wiOr@G .h\"Cciq_0iy8Q_qhRmݔNYYZyD\H=v*NlD_ 4.ꖋB.I_F D1w> !⌂-`93~TǖC|tq-̻VfmTu,ly@ABL [eQ<3|Al&g57c) Uf73k`#^Ħ>'< o$"SENc0z5oރa$ ~Elq0exWB+AТ/q3Hxej6:E(/[}>sƽԥ^$}Ǐǫצ`V(%C05St?q?/jEY>5Fno;1pni DFxd1h؟{ G7=2'*@FipO{ak DTw;Ŏ4Y|1㢉EmX^[FBf`c_ypiK76ѨBJӰh7g5Ҏ@N\koׂ޴BQ ;i^_J ƊQ s1{9?:;]w^{]}UW9nllMmdTN6& (iL-Pe$m͇t)FID"@:,:̧ίǻGѳ O"8pv^z,ضjm^kꚇ7qx*6_ `ͮqS)|W&V^iSB)ji4K3n^Kr&V<[Ʈٳ/1r_/`룎3(Ŋ2AAe\lU?R~ }ƫW~63q,5 K؀_kw`%$0zpv&c!~{ed|9@kD"@jgҸ+J&X.hހ-82k ^U@˕m),>;_d]k&0}XC?KLA1ܩ0#֏0S)GD  j 62K+_RW6/U}_|ٱ~wcN?r'cxװubC^oT}QoEsT g!uK&N|.zO?b؞1|v(axYEϸߐ~ D)i\[[W7"UEFo4Z!{0?{E.B j*"% Ai Q O@E(E)E 4ґB Kmw{^nwg͛ξyo847:+߫k3bͧίa!7&bݲ.1z͖=>[4z l93| 1.fx./7%#{t+6B\@+_Zy) DdX{3ԧ9s)OSsFcfVw{Bvr\[#:ş;2÷"'Ii"@ l}"ޯ A哘;Rߡja75HL  [:meo%Rř ,>nąs'6~yɧUPԹ&:4q^6TXCH;E2Z9lȞ+7aiQ_g|\z6SY# h0Y?"ōfNjY)mד׉bMw2*6BFY[¢e?yr,eE6D~f~P%;c"_~ͼ`z!F݊a㪙U?hK+}sYVouQs)|O6HU~o˩+ojyo4<Zݯ@{ǯL ;=z^Mzтћ/rNfɓ ]Fjt 4J5N"@ vkf>̦.U i?|6کk;d Y[>BiJߋ_F9'7ktnZ) QL&}{DSv~3 ğG<}c'Gʩx(ŇS |fBP Eä5h0&<+gcmNE@0_ c rNot( rS)_Æ-1.1ݭ_w0B^h>}k[dG[=;z-3GB4 AGDX*¥!ZܮȥRM\Ook, N0]3f?MUԫCbg1ybJZɑGr!~׮)DT>F#Bx8o^nI!b2aֳ(5ۃ`sF[׫Əg(w}UٶjR]uhK+}\d9KAu/̾-۹ՃqS~ (l}Kz?ćgbL^xhtrX/W$sTUԞХDvqJvVpU=\d3pݫK>w;~2'w2,f6V2?e-osP X.F3⃫Eߔ)%D"@db֥߃XU' Pq20z3fGDx((Pq]dF25Sϔ!fca#@ zQQ}Xv4v[#.#k>,7nUy1O\h7g#v"~oŋu<W @7n<.-fv}M9mMTӧ_s'<|+zSș6.% E-#ra&_>f`BsAzHg,:j@~ց А7*KJ%#"$":!5˅ _Y:yhSVY> ϒ@!-|k ߼ވ!qֱI&aɹ#S>^NZJYYVFCp,ލ{߭`RxnKf&Y6ytktRD ``jw8bZkM bcǗwgQ>ЇM=&++}Tnv 3}dTvWydZ]íҶqht,| rQHaD*zӟX<~;k:'ɧD"@5l `_<҈B(!1ҜaF+C-7&kf(5 ["|/o0wkCy7 sDAmko;]7EKPr;w*3W3"#Q$2 "Bu_$A-[9KSwQb]d#A-kX?"M$o9ۘ[ilO/b7o$rU`k\ŵT>:ڎfpaS}N@_;ov\ugB!x{gQT,y sVȜAYFj[&qD^Oځs&bs_udS׮Y/2"Vfo*W CI[hr, j"n9s$uvݿs\l]5ڼ *U|Q.\owV}ʌʺ7yX9w`_(3ŰWL_z CET_RzT6ʗVW^=aBkXWtKϟf 5 Yy.ɈFP6^8m G*S#];dp\>\]o1Ur2Pڽ^4^rG6Qk=~߃w)fUKEnֹgݹqO[dcSӃEx ?QXRb7:XG婀N0)f}sj㧭{)8qzY+aAJԲvrxjy.giy߅4 ?ۢ]gFV_clo; ez/127z+DFL.Ҥ#L˝~3*~\ M=+栢&Gȃ^ ]d3\nƢuj46M,ciy˨ΦB/ʅ7|w/cWS1X>.an)d~6.&̠v=O Շv0ʗVx# N} pͺ]kǐOlŠ}PIhQNh=[7lGӲˮR}hy<;'PڙZ=x^5CBە:op檌_ ބhG̐羚 zvuF]x#Gs&H ɌZn$&:Sb8 ϛWr)˸G^O\EUxs>c_m*3ޤ1-[<# ]X[5u@kY9ݧߧˎ;$#mgw i XF,"3clhEڠma /aA: 6gFZӝ: E꿀|p;O8[UeFt)B̌cߠ3R @%OYG;x%v@IxfMw,*Zy!]-\8( 'ѥCZ+:QB %f~w>u'ʗVO k1Òw`e)먋w>q+C8=xdӢrZ SPpKhp]N?] /Jʙh9I;JC}ǜ E47QWp_7"aSZƯK䢲!6jb1>rjԟUHsENYCJ͂~GM0MdUՌK>\|5-ыIs'k xMm63?h9,_W'r6"bPUC!EFFd+y}n$ݑ}2Uӝ㺣:#gySS}Vݑ%~fPʧw{V;*&$.?Sŧoë163뾎\k VN 7[L;0'ok2XB3p-ٮ(Ma*SfG֫/@Y>j6ڙjzyr. ۫mcFo0&*c#2*l3s^_8dX֠fOE^~d1  D"ʇ|OT%yf-:Nء;efϞaAG|X!ם{}|i6Qn!<ţ+ϿjI9WI4`~يᅳIny'RUD\c?YGZ"*ksB7*3p=WL6Yc[c`1x}@7tlYZWd ؙ$Nt;vmF5F㒛!~Uz}0J-qU.%-Ƌ6WHU16Z/D\m:kn| {fSd"KvU~HibB)cov]rWL_Jp ~[Jd)ʩ$;9Tޥ<) yhGeE1'˗sV;ǟZӴlQsih7MXx>4%ol{ݬCg%`c)LeJ"@#O/o){Ul {R,Z}p#*)9F*QQ۴&5Qnc8e2 >Yy&܍{awC[_ks+r*$歜)B v1/1i~+О@GC^ِ\3ZCth6Ơ_F Y:u".mCv;/`,YiybwC%D"@T͍0(Gs-Σ!5ܼyߝ;Wߩo(kttyʁxaEQ;ط~f=ZQ(*;aTSTA44:xqԃB= 2lt˟5s lSwTa+>)=R19ܳy8Zr r01.ߩ])J<6ܿ}QIwFC2fs.䐍zrwYrW 9X.]YJBța|iڽɨHP-)Ϗr{{ܽ>;Qr]2aߛz5+sy4}qsyKٝU;:ydnжdF&yCg^_d Ĕ| :TG' D̜H'yZ_WDzaLnO>˿E.3C!ѢQ- Nߐ5äoix't [ILiY鬴WHA񀧙r7w (/)3/jܴ(CHT|"Y6 _HEyV lGk퟉p-ie4FLhrexcQZ ŭם"IK+,>Ty0ʗV-m]JC*J{CyV??nGeE{-J7/>uuO-,;ګ<ϦTLێ|<ӵPHbŎ.-~^c#kw. D" 'r:Ern(fc-tidulz̗}U3|k271Qk; EdY.U \@C yU/2 zS {Nd7wViPɩQt\(>飣Q$cj1hp h"(Ϧѝj+||ķ+Wy_86EJјBov/1{~}K9Kq&a<*gm ѥU..:eGfɍfl. ,;a8.#D"@ K]}4%/ oMŒS0&q}@2C0WжdcBί^..s>7T*^wMB9 ̷/A粲 [`/5/˅jGyO6c@}|ϳ]U#LgW{uLM^pUjBo^N&a*;Vs0RGFD"@V<kcdszf.n-P9t }?XU'ћ.Mu<;br!!6F`It4o59qæWSA])V}۽x녧۝1[k|iQѹ?گ/A\ LTmȈo=Q|&F{X^,W.~O6vQ 0K*y 49RӥpQ\?XJUhX?=z#Ǘ¥j̷'$.d~XeJ}h4IϑΔy'm/ʳ㍜*L}+`m7ୡ3|юXlo8/g;C };voV;^ՅŽࣴ<̞D=F:CdD;fgF.c4$(/ D" _;h{f-vo)vޏ#MU*TF6Q +M#fH&7igq9<{_CpTUFNЍ8bg b}?ХVf۶I&MʰoKI&#@C۹[QUY7P& p,ΦDTT3[; \ʃ|4(Zނ:i ><T4e)Gz}/]w8(ቢzBYۏ}p2rժvFY]VEMfe;~ GNDžkA(X1ԪUe>slݟɑf ecEzp9s-,K]Cq16[zaDxɑlZ6[ӂxy ĸDiJZUPӝssĀ:uN`Op!J7:h.8X竑XWta!bFIύLGo;O=/oA/3;ޔSyvh]/n:|%sk'HT;BZaT }l;>6KhGyOca_ٛ }w;{'ګ<_ s}tG6+`΢-l|Gnb~ٱ[}JFw[į\ǯ#G:hBG*(]cnerlR%D"JrzsHnb|-3hǑʾΰiHوҨT9v2e>Zy 1EY@Q#9PQh>xJr&[50Zt`ׅ~[c1y19WWdzkzr\(V= @h _6^vgv) b!@IDATW)Ӣ~ }O6BiA8"3I # |i?_=W,1B˩L<\Nwe7oDiШhձ,쑿 _Ֆ.6D=Y۷/2,xX:NZ}ۄ`lT9 ‰+w9K?Fx'^WC>lj찶upYRtWpp4z~08$`V.waF֘[6.8WzS0A+4$l; ; Zxo\\ªwN_e f~׏ݝʱ62/Ѹ,U}RsM5 刺z%9*(>cM|s0YE ^ {CxDdYρVRJvvyxߤDr,%M'B[GMQjFپJIsv SNU;鎜@,loy?،3w=̽?K(oѦsg]v*4_z=Lh *۶;`|=XˊA,QkFok(;uא 8:"ȣ_"@ :]LL4ް#cSMƅm:W="lie;:gn^~5 J Of4ƀwڢRP\/p| >=!羇?L d}Ϩ J.0uYPcyv`4^ChhBLv~->]gb;MPe. f踜 'wnWh:5E ?g!~Ir ࿁&G[V|wYTz_,8JaaHMW#1)I(t׸\(d{p`Kx:nb39 όI䡭Y|í+MJ́0*үߡ/ZՎA`2Fs8m{uQhXALn=C&uGq2eF]6NBQ0WL&ENڝ+Hڵ?m9"@L ߏݧ"@ˎ7hVrh<xDPyDn4e"@ Dxꄸ"@@`0k0VriC*jT D"@  2|?,wA D"@ D"@ d"P[6דi%Ǔ"@M"@ D"}_ .t D )i0xtru׃M+9$M"g3pJ"@ D-&D"@ D"@ D_ W'J2 D"@ D"@ Do70 D"@ D"@ D}Ad"@ D"@ D"@o)a"@ D"@ D"@2|*$D"@ D"@ D ÷SD"@ D"@ D" dUI D"@ D"@ ~#@o D"@ D"@ D $"@ D"@ D"@F ~CO "@ D"@ D"@/TI& D"@ D"@ D&D"@ D"@ D_ ÷/L"@ D"@ D"@2| =%L D"@ D"@  @o_P%D"@ D"@ D"7dzJ"@ D"@ D"@|A ߾J2 D"@ D"@ Do70 D"@ D"@ D}Ad"@ D"@ D"@o)a"@ D"@ D"@2|*$D"@ D"@ D ÷SD"@ D"@ D" dUI D"@ D"@ ~#@o D"@ D"@ D $"@ D"@ D"@F ~CO "@ D"@ D"@/TI& D"@ D"@ D&D"@ D"@ D_ ÷/L"@ D"@ D"@2| =%L D"@ D"@  @o_P%D"@ D"@ D"7dzJ"@ D"@ D"@|A ߾J2 D"@ D"@ Do70 D"@ D"@ D}Ad"@ D"@ D"@o)a"@ D"@ D"@2|*$D"@ D"@ D ÷SD"@ D"@ D" dUI D"@ D"@ ~#@o D"@ D"@ D $"@ D"@ D"@F ~CO "@ D"@ D"@/TI& D"@ D"@ D&D"@ D"@ D_ ÷/L"@ D"@ D"@R"PaC%&tnŧH!s%4lגd$%%yI/sr"@ Dv8#D"@<'@oJ'MԈHgВqvI@?FP`(U  2U9f"`]vcLmwpf,DZu*ʢpV.]cbM?Y֨B~Ң2 4.ï$9b b+!$]mx4@I/XK\nZwJ;甕^# |oXN,H c"@ D[1ihTK^Ѣ, :ڌplV w>QkҺ/Tz40@u&5CϡkȈWBFaw+?eF2bcg/8?w`V I # n toZz4 zv;nW8ToEJIjd0sq/((;ݯg銑/^qu$6|<;.uE(2$ھ F:f]= k[z(e<=xS=)H*(UȢHUT2qDA A>"0 V`{ś/95j>AywȐ'@o"@peM~9 Ah .#"C.S }Pb9Ga6ڐy +s8]0GDDN1Zfr9̌2?Hԃdcsot(eKs8ՇklFoA_O8bm}+0bTlG }$}Sd0PЯfAY7/i|&5P nh>`Zhm6"Qћu\Y6.܆wFǚE262F6o3SXfn! Zeri]@NT!Mgwoޫ'?ˌ2qއ_:eQb2YJSʻ O3;Md6J72F7f^h#D"@<%G lB~w7`~'a L 6iܽ\Ge<ߡl4j26|VoXf|VVr@Rl|x^U>DN8F~t&Yfn-Էl93| /ycd]̈QB<-d 7&bݲ!!'o<-iD|\bͧT.K~ 3,,9xX9#3|[;Bre#Q-uЪ~Vw1ؾ4ޡ 7WWgX߮sA^w }ay5Iˆ DxE@&J,jP&IY]ݿ7o}OmY:E [78GB$26 p.Rnٽ/ ǕÛ4ziQcL+"_|HNNFRRfzVBltE*{5HL>x3ӳBÆ"%؉#_/}ӭѦwuOaDآ׏ 1jV v/w!K[Z\t3z}0YR@ (1_o_J{͜Ԃ1EdFu54Ge1H0gTl7n] JʱgJg>Yݺ N`m.%"v1G ~yXݩ@]Fj 'J5HdKгu}pB筜|iU?{)'#_1Kz뼏ٮlDvư{,O1WL '\[׭څsw%b;\\p1M9Ր "DjìgQrc挶l8W) ^}5Lؘh>nxLBp=$_>N”u ό\jh4*a0&<+gcmkA]3oyKVBmOY/͇d>3qI|q(&w皎F'bcۣ3TMpj }lF-~_RQAӆͨefN۩͝.-T5aC[}Үj, N0]3>MUCbg1y]8Xct).I.='0Tteù_Gޮ6Zx*`ҷt1aeﴰn}fc[={VD3V,: h^US-kSRC9go4x^o:zysUKEѤ},kn|if-$v'iev'߹ů#Q%TǾ8iRS iVkaGQR;}ceX+?3o9GA$t"@|L  >\A\~2i;LSۼtQ;E21#0u!9 =$ fV: G- Sș6.%3r9.`{7*ސ7* [qˈA)ȟZR5)"Ex',V8_Cc;gHX :_.~X=6+.ЛY9j {W='lԧ"gʗfZ L;.r4Jr1(+Sǫ3Y)Ui\+ ə}o(-~it-&ϓjxzP9,y'o(t1!HϽ+7z*y Dd% b0(,My lYjJʰrWj*ĺ;p,4ӡp(\("s9w5q3z >=uE+EM62L"c.M)V쩞?BH7Awo5U\c#|/73( d}6f԰&[ yF2=`wpc/#eW@iHz(SEyv+wb<"ZnfqwV}ʌ\ty,{96ѴJ`**5Yr6VrzZSA$z/މxM>tdR׮8ɵ/2uݍ[ 8^ZҒdFPJ:oqqm&qD0 ~,3C_Z#a.Ew@+m^_k)Z+;Ǐ|(!l&5^,gQ:.ʢZP>?"hΠ~`~ k,t +N0Xdqȧ.WOt szg\Y %Ydr;}poh^Vvި}Eyw_C1j;Mosu8s-,Ӡ-sâEjدPe=޸*JؾR]UhMA: Dl$/P[oX]\1a4K&zۍ>@Äf8f]ɉV 7l Tt::6.Id#욇Zl$O6-85Sql\0+I%DR.G\S$'kH/H\S|̲!T p;iL"an,~u)Z{ ߴsE9'@#0b$%tz&c^lAKIbb& v[DsPQTl}}涿ԧ!Ӥ 4i!ÈSf_%ny0e3wnƢLj46M6fZ22R_ 9-G˲yKlްJRЯkވ+P700K-zw^l6GIVX>$|.c秃0ds7wMl6`5ɵ}o۫Z .K 9T] ցʇeΘ?lCb}߶xkxc2'Sʻ;Z=L2snDuNh4xF|Ptgs{}3X3Z/OЋʩ"@@6v÷uA1۬o‡f(tޢ񘳺{*-Ac_m*3F1]pl`, >ZǾ3a;0h2ĔuEQqDex/ axT*z$s!9Ps5gf5 x6)}Lفu4N.lq?bP3-(>\Ұ⣶}K-KRDNEf 7Q]e]mܨۯwm`K?Vt c/_h,yK9 WW+C6 2g73(r.O+9r,^vύz,_fo7j*(i@'e_-+Te##=:RZk(Q<O]A51l%,Hr:u6v҈mAvcB6ܗ <=wE{Ur)h!ʗo'îf'4h&Mcه>ބ`u.[IwS ˻k ]:YZ|9vqZ҈,c\3 \~VRym |]Ń]#u R9uz("@|LOo|֬3z; 7:|O/®M+T c2*s`_0SPiDseH5!q'y}ڕ-ivx}ϝs?rJs<}9v&R/R5zI܌lBL;uwq,!$G$dY}^7F2z.1>|9=Uwfpyn}>LRo\fIk%>5C-ʩ"tLͅW|-)m~a] |kQLl);k},!b#t| Gټnhf;dYgAk5 vgO^rYX{3{yʹ|-m ;-`%IѱObi͚Eˡd: Y.ۈAmygҒ=CP2cQOE^~B'&l;+[\ծmx+ʗ@GlfΕ CpF:F>7uM}UyϚ}WOYsϟLf{Z !yKű۠}nBjaa|PR8j5 ]V(-$fVsmr "@?gfWNGp 2MT2"g6shPYh Bn ?͎SےUg)O9wo^Uf8gYLœCg֢cvNٳgiXt]дnqQL7exՎ =JNVuf5=O'sSHPu$p[Y&B /\V|l]FcžT :UbՄ-D9aW;99Ò6sh֗8g+j&w'ҞwYc[c`1x}@7tlYZWd963QIt]"K|]_l>(u0ia?<#H;G|@)Em۫ޕ )ɡ%^b҆ӊ?.*yWw0dWlrw'l-[zμűkW=]&q:+7eЦ7]@Bd&mTN%G K/oO,W58|iF!3|_9W'3#_-#);rܽqU S8FӶ O"~sN+|1%o!wϲsj-ŢEw{>+[f X/3^OqGnV{Kn.2/5/[Хqv1F[ϧ;#qTvE22yPT[MtIX4ڕw{+Orz2 wʚYū4C‰0L:Pw2Z EMi6XZx߇,:6<ՠKг ˬ3ADot,$j6Rv]rBN(3vyj͊[?aBt3lt˟s lS,S h[U糺&yC3/zG&]^)xS!G~UC1%!]GEG25ײEB/M‚|׻Ģqx{wtgj5m{e{-}^^Lr>@S7ʻӌ>Bξsuø)2DV(as |``BhADfTn]S%HT $DZ&>NH.cpዸ!_/F-t <&m4ףJ)(F溫I]d7j*} Z21̸:ܺ|i2z? ih]Bz(4>⶿J拿ݬabb\?ד(1QlU%r8:,vL}1VHZ93Z4Wa+]fBȥ|"Yf6 _)ӵdMؿUαůbVa' kwՌѧ%EVB+9T4{$'5Gyɖ8tʻ\e_\:, G19&۳X -JթrY+;IAl("@|B/oor :i9+",NvG6_|C DU2n8("!wPCpLӆ=˩eISh^T M ڕFʵ0 ԻI}-ȅhX ?rA8;vG'Ek?n4 (T05l\G+[qhc>>abRGNkY|ķ+4idulz}UiԷ+^Zu5$֩t{ȝI 0KMʢ"f GժNK.GZ5xO\iϬr|̞ٓwVg%Mj6 gUyww?GZHF9G-ÃSq5*.P DhF@ŞEٵXE-6mmq/>KdMcC}4Y_Ee#e;g--bg+csAUoȇ0KvShIWF]ѥ]ɷh5O3bc`vqn?ZS/uˉXW m_ ɿyQ̚UdlETq}Sq(~x]̆-'K6X%;ݮZcKй t-P6Zq$SƘ_s@$5RdBTYo`l'؈Ƥ,aX?>2̥\/:1BVhU.》ʗ_3EzdZvH3ާY): ~l |O~ fp_YG4k(6 DdNMy&9#Oc.e|>-8a';]h4X&Xxi?G.BYٵϞ/V]>Jxװ{d[Mŋu$]?d{қ.Ѿ:/GEn~} 2ѕ;xG.cSS ̯7B9Zj,,DNFŊr}c ͺzy+H5{ge}na;Ӣt]fGϋ`)׎t۽rhOCx p덹z1#=R$mmfAׇUk^3ŌNjp0> W pD%k!{4ajb}?ХU7f`1[#5P'2jQN} H;;t_2od5Mb]r.W6UNDz]1J)=޹U}%ۮi8tn-aD8WM }ңˢR٪xe;~ GNDžkA(X1Y|eNWiv65Ži .bwxy?OҖ1.:ۙ1 7igq9<{_CpTU$3PЍ8bgw(Z>W/?Hw' WZhlg4?sqJqDQdGV+aQڽ pA̛x4DH\3덜|iQ?sZ1azG_?`WuR$:Ee3)xy7Sʻ9 |h;w+ܳ}cJHn C=w=8klJg|vG Πc1h5+ݸzi?7! Ddn6#d *>ZFN+JpOXBvf5\n>Ҷ܂AKUMaP6G훌#S:B?F(kXzpaԈ}5Twp9^~J*gy$e_rs9q u`Xn.5*̜ǚiP~T6<{7oDi4ШhձL :g-g+]ޠt KKL~h3i,VYe99Ӣ>!0 d>F;qQu :FCBtz<@+} xu~X_\e&n<R{N:JGm{xWOP0b)QٟSoIYnY;!%P:&c̐^ңyl@ h4 I+G|o[cz2bXPD +[#F<#Ie~'\ Ou'y{r`E] 5D$4ATAPz3&4*JP4RARH@D5@\+{5wIr|vggߙyvgg盖}O_ϼtP#ӻk3QƞNyu-ۈ8Nx$nRQC04 xs^lzGVO6$`@~kQTN\X!ޓx3h"@ܖ5JT5$pʔ #NU_aH8J,g$K=j̯YN[ S^Kw/g_0CC,/k)2oĦh3 Ujq ޛ1 )&ìD6ON'!e3$?8sv\&5 fGGbRbET5خI1sWSFKM60oj}Ė|tr\vN$Y`p~l!TeZ/9eRG|xI,öJ~]Χ Sm#lKM rwN<->laE WGE~,O\x+[0ZV yl׆sNK<ͳQ;VJcfciJr~rI4p0f~a+"V YEg9UXR`D o&h0s](T$^=SS2#zr+V@;K8g|ISVeZOM& fV kW~~Hes]>qtz_DMZ%hK 8kX\C`| 톖uBQD zARGx?:4D6E|Vbz)p&~q@œ*!`@IDAT@``!Q;l$Mx$ukVB",/hW}\9_N^u)#wR9A%"?x;?ToZ^VT&psw(D"``-L95IlKtlœ܉@A&@ }J; D"@rCS^t($w!0aWX}x3N>(`-['e\p4S< Dhuq&^:DŽyRD Dw! UGnHLVYM]测lu_; E5TD>w-lR/7Ia"@ D"@ D"@, 9jEP&O#7z'a0=#Dw kH^:@܍/w#"@ Dx3#T!~wR2mj+2Sp| #_N"@-P23L~D"[AVtp<S D"&hqK7hLM, _]DţX7w6ō"@ D"@ DJ S D"@ D"@ J:;K"D"@ D"@ DPd.7M D"@ D"@Td;K"D"@ D"@ DPd.7M D"@ D"@Td;K"D"@ D"@ DPd.7M D"@ D"@T*OM"@ D"@@@fh$%%!>>>ϣD D"P{wx7(SX 'ЮYS_1i$=SPVNk^:<޶[o E2EF n&C!Aq8@H@'KGU_ τC#w^S\v y<ґEͥ]w+ 53^L`LOq W$Z":3:<}!x+23Y6jq~:}fCt]?deyXm~ D;aaaZ;筞҄ wJ h~xr;D^dկqȒ[jJ]mj:M Y1Av'mƠ1f;&‚8_yXw֑v&}?Wi<_Ň.Zuur%D zoґ18{-[{Qz]aelmzynᔮVSNetj )|ܠ6(hDNx=^F6zst7aXKd{L-ڇJ3F՝^U?Ie~~͕"@N^IlF㷞W% U掅J·ͣo%jqFav*M V*F 1컟Z#x.']#/S0P;F u0`Ky8y;5!V˿2LYE`Īt,ɅO%Kcя0W~9Rj":Oqrj}zy"n.tIA '])2CQο0 ;܊\10y8 '.^-{h13~̤/ՙVCRokZ'_KD"@u5}0Bۄ[YZR*}[DtU>m/NУl.&S HE P`*`мy1jqQ>,t8$Dɽr.] Tl<*>+'‚>M3?c̱KhĥK8{1$DfxE]͐~vXKǙ_"@7^ ^:'m2\q:f v%x~6hVxa֑Q Eܵ\bH{NK/wtuv:/Pldh):1{McJGDx2^ N:&D;{FomعmXl9Fݵ\6Ɵ~N3xBn.!`bѧSV`S%~-1Pt 5} ƙųC D4c]~8Z[Zs{V#zԔFI8 eGfEuh b:dŨmNË/^r@n}oAEyhxF~k߻ͳtl@'8Y47j+3.~b)tX\sއ ͊^>1p΋ۖ6 Mf)NalNnQRyI ( ƵKs~lϳchx8Z־0>,ACV V]nކU1N:$Ui?coӘsq08ћa:PA0Yt˝Vx1AZ\<y%ʧy&D8ɏMSC7iU—3-)&4]Zt`r/y-aYeUC'NNsӼtz+Sײ%pxd'DszodM&!H.ͿGJnfH;/}.S/ͰQg(sr;+EjxAyo?^o?GuJeha1;,Z#g7pjYm[2- Wvc\ &HFAyv /G_nŧ3ٿ~i^A%Ú8w~0tk|uZ?⥖Ƣi_eA.h8'--7-'GHX~2<$air V2_2[ыP:}cQWEU|; :Oo"Q~(ehX.-UEa}VuB9cd0D͈C -c nc&kl0,!!2+<ώHX.g,b*X63z߀LJxi+3zS2zL܊ NOEᶟ`/Gn9q`T*_#ze^:'?]ӓ+6FuT"U`wMoɘ/78uAdߘЫy譗6g#D M8.S{_i(`~c}ҢK8*VVS|YRR nT#"@%9wC h13[?Er|c숷tpU(*(ą!¼!rmL<*Mwx AzaC|_ 0ۈy A³'eCҼyQJy/ULU4| 1R# XeB`a?>a|o6FұIuhY+LTgKQX-ˆdBHˌ;u1Uozlq l6LND珷ӸzWAa158ӯ`&Uu]ټ6*F޽s3C堑"@@nnaX|"EOh#CfT@(PLkUy(2^rn^8Ef&I.0LNV_1rU80 #}UTPhg_g=9C}"@<;7&.mN[1Xo-텘xQU#JpQ.30R_σX1Ctm'b(TP+şøUJB:/6aU;ٴV~hwL#w!_X^74y޺ JV1a0`[WGC#ouF&T-S-YGcKGlU 6RԉSsvDוb̸rG6@-iKw }co@)]ʄeOR AV[|zAҕ&4("@Elۼ!Z2ٯg}S_٨!kM!1X5̰0Rl>2h6@.xc%w0v&/6^#WU#Y7K}^: "x7\)^-peef U3?`RlZw岒p}gj[gsNOre.ThX ! nu ϳIzy阈pnaIKÆϑǎg\IZS GBi8vå7w/[#Iy8Ruh qaX0lOί:7oX gqA-ud6u%3z Bk/Zp XG<>|X%Llh7 m{ .W0*lv("@Ek^9Y6,j{thݪ+% ^2ŽrZ ӍKU~tImQNg "I C}]Tt fC6@?7 !.I=Aʧ: D\5|+>kE +ovR!YX[L oVh Lt 1( 6MJ+H)vgsI 5yQ3cIo܄c qKlz,.o;cF=5*)sxwtұ 9"x7\y(,pV${IZTf^i2K\![CprY :CKfh"%/1o:h^_aΫ'a/ =cC3ƒEj…0x82]A@~%뽑0q+ȏuSGO~캴8eejw,)jlZm+~%$ o~Рx2GP}0^:<:zOv/B^:igX+X\yD E`INBRʠg=di~x*&ʧV# D029*sz5^g6&[|#5~-EhG`ǘFb?ǭ0@Pܼx_JU(Q)0[ڼt8]D@$aO+!&/?ޯy~u5$MCTw)R_0Y# t9#B'I/kדU?i8@<;3aqf*{1澗4K˔Yx~#,=9Kǁ|(::pJ<_9*!-ۊ!QNÇYv`PRÚl~}aV D$3|E3l//8Iꈽٔ=_L>uF-YNDݦ sP2'~M#JY zKNꔰh]'6T O@5=sGw0J Wqp9Xrye T\?o;`ʧYD"T[Ue*I8Ocvi5B te0{ LxㅬΦKXc4/AǙұ鲥c>p@+;jšc^:FY;Q䥯&ak6GБSq2fx;ˎa˟{M& b^Y^:hwY;b>c9޷"5~Eh?^E(Y q vZV2o^V*x#!_Xڦo_+Mխ' @}YvJE3wtwΉ _[~xۧOdy@4KD"@O@;![,R^%5aǕЎAyߠEiiؤ__XcЎzu~F۩ŦesM:ǿ+Ѻu$ ,ok%C';T,|*mVұHvUGOѻvwVb`CiaTmD y阇MDx.^ ^:[ ewqh4mg5XrȜ*e֮"SDԈ+DM]o6Nv]Q7GbYL9o`x \l!stycm^~oX/ipEdڗx@5;)॓s5RSk;ѫkˋAd T7mCF\ǚMDL dnauA^ Z2Ey*4+(?D"Gv;0gD!-3K "1"aJ/RϭB+D̐Pz̠[v \wP:0e-y.KƶqgԵrgOc8u. @j͝Kꩬn<mdąbCWd!<^:qn"qziX //m%*mLx1tkuA0jQ9)(R>vB03-dϡ qcL"x7x(]1ewUۿV{ej -5H^ĢGpr\D}bڶ 5v ;?7)늎!hXZp  pH~PAS ^p`o-^yc47NǮ y:\ѡ%']Pn]4]əq&̼[F'2Y>I0BkaKYUo,G|~B*,|OoF`ut% ls}IǮ{p9v7+xݿkQ~Q>q D ߥ+TD YOlxC`P1gi };0;+򵄿gدxV(7@ʼ*. ^Θ&Uj!oi;~yc'STGoNnzpeOEGzat;.YGᯗ51F 3/;A)"@<:AqF*h7#gD.U/~x7e`>R?2KOf_GдT# ;H/e k?ȼyܮϳ /{{^|Nig뉳=BiC˔d~:?{*[O_)⥓uHCVWֽ"HV=V*ZY錨a(}.+2ˢnKk%ONAk,(q1B}߳YGqS91\~xO-9"@@.}W uB,sr7MmZJڐ6chDkZmʌ8t:\eϛ9U^N1qS\?iI>/YԐt=L~"qp_tygaҳԷOm>t DSzoqUvðbj>-gON6p"-66戎,xn4[ 8-8Vo17Yug7^3/sVya'K[-N\W2N_|b2-8PD K2MY;"0w.Ə_V9yr:G 9@ 9$D"@`J;1MfV5tti(('O D!;P(D"@]Zb\K<ؐs[/%ۦmCns3FMrI*[GkDD"@uJbW4,z/Aŭ5w,P"l/YTxքjXYf./2eJJX ұT&"@<@7x64rw8dBfN7\ƭ{~ ^hQE7p2# >Wfs6<%"EHP ME'j쫦c5f ͉z ۚ1@B6X= D"0 ߽n;Kf4z;GG~=Qh95;/]oap|c Fo5׍E̽ZKXo  -tV螃 ^|x0\Q$֭$ܺ{-y,LB/)CǷLjЗFJZz%]{pL_ gйṒjqzx> v>Ju1"87xtҺ(ul%&lָT}σo4%)y7͘(r8#2FTn}n4l/$-XTs*VKt̰e}pbwN7:p& rj^:KUr,ݍ.,ć =P^G fч*"87xto*dݜLMqȓ39yBh''kV(wC}GK Qԝu EɻAEfpJts̍Y!D"@rCohoȸw/DŽE1ǿ SHlGe4%QR}#s:41 A2bTtΩy#2nCw,cw4t Ͼ?Tܻ}6ұ D8U^p geq]a}m7WЧ3(SoŠտe+V4+gkӐn\1Ͽ|!QuJ?7(^8B$Ϧ sm-kh;}Z^/F@2p69p\ƀ\աe$IyU6L*dmhZń"iipx,bY(z "P8db$?>/7O GN_ܤUuT_şlF0 Тk3|{ϋ/n B(L7z ^\;m:IO1nP\;}[6oVƏxDxh_e yYPy|1Nccp(ge<5S\u 0zW&}cQWF\ŷ`T6/Ho|ѼVG)#Xи&Hȁ"@rCC>,ٷxmDDpGqO[i.#EWjo%yTJd]vŇ rD-ش|0q$^9ɞt~u Nbm =^:vSDxg :OfwBo`yx<[Lh(d BP_8/CԌ8,7K'/蔍M3.ղx H  D`;/d&l&[:vO퍧s;f=NN@S<߅kF#"hbF>+NT?3; yyWyYdzWKh_[2zL܊ VLOEᶟ`/n=\ ş#f. "6Otkk6ҏ@ ke=: NݚLƬ<^hs>Xh3p6bj.@&q)\/4F}a0|б>i%itV^]TF_T5<"@rdIΝPZ O&_/;G+9]!\*6 qa0/~D@PY6NlgH;{W`KǁL!emDؼKϠZI3pP֋uॣ(<콡*$,&7>pB7G&+@j.,,r?ߌw>]K*3)EJZJ Ez~@waƑ&x:x;I;G@<R>xI#rs hm+Teb8bѯJWmom|.n̸l}^|W.A,ĽyQJƖLU4| 1Rc| 7`˛ ܮ_5GZaE(:+2aX-T{ױ-3_W\FF@ocDtxIzuV c#l?@Ihh-#tҗ7O]A09I}sıN|`/qtN# S*U-σX1Ctm'b(T0Ӌ?qCךy5\+ Ga6/Ovgh_ oW q-5E?m [t֨o (KP?]N-ŽphDj5FT"2Hr\wʧFK MxJ;)b=- 7?<b@Sup ? Ϗ)Md Je m\"=_Ka8iM^:Nl@+=GF'֫F oVF ϋtDA!D x{CH-fFo5RdUdO;]Ex]N*Q[i.o3z ay~( V2ˎ]:R<3R%nCܔ}h@v:N0 p"'wᛇ0bY%aWUc ϸ~ VA^/T`Ȱ7nE)|X%Llh7 m{ .W0*lv("@Eֿs(^mQFgZfW{ɝXzCCn=b_+$ziŏxS :>sw&JfjU^:΄iWws֝عu+{vbߡj'X@IDATH'^nbTS9ztzKp7B皢a9?!1p=L<p"/N߄ _m1Wunݐ ٔ/ '5@/ZMN >MqP6=*-Ac2w65V׍T9TDOtq,MaMd-n :$ o^/@LL [*IRy~sbrՃ s92A1TEBXXw@ʡb_N}*֣I Cn5RYH_.mk?D%ǹpC y@@A^X+-bBz)h*E평տŴx.f؈:sziRBtX#Пm&}A=&ȨY/>07!QP1m8m2X6,dێƘѯ~icOx}lk?bżt,bBDx,}oY%Ykx^g{~%YN#=j^ЙM"_;x(V7|&Vjx,=WM<]64XlVN=e%$B˶Nz}Ic")y DXL?`Pu-$c`B}7/;Quٝfwh6G߈Niq*PL@&_R23z;0#_oy/بW% G?G&wl4yWua?ڂ+B2' qIPy[tlsugXrPV!-ЖK "@+`gnNV k-9}M(-Rf؞>B-Bin㚇/3jx|o%~Рx2'?{;9/4AUP)8rkʅG*ӯn<d}vy7[Eb{{\ <) >/PxP~y|Upp?Z:k+OhWB׶`;Ib8MJ' =ZM-SXȑ"@rdN+>O=doۯhmH' Tڬ͓jOC8K'px$PR*żtxpoN57.el묯p·=c^k&t=zd;VɜQxY}]$ܗ5Ӧ~֍q.wN拌hyc}Y/t5~-Lc}A[n Ctf*Q/xPwӲO!xuE=КA8zV37@=_p#Т 3KXf#ߌJ]]*l&DZ\ ^Z_O"@C*'eD!0ꉴPAZ߳SW<ۏ_Fo e^jdzB/dgLvF79w--aB^|x؉c2Q[2iSb^#/kΤK"AhQeMn/hKNt#{WyKGuh >P{TUv=x,\]de3T+s)ڿI(񯀪u?dIeֻ̐am7u=_7 ĬWCca-W-}xYv8?l=qW~0~u(,G9Y;ZVL+^e$+JG ,xF0>2ˢnKk%9ONAk,(q1B}߳YGqS9[!Zr D\$PuؙxM+z^ wBR҆yl{m|X MqBy39݋)>&n'M09);G#jfKc^:4 M3~Yo'6C^:YDŽ|")x7xt{nqXw+#0acn@x g Bl{r#:BqvI5X52AZ*Hd2n]#pEa̒/CnΘnK>/mh7ƙTѻ,/P>N1K %KғUDYG6ѡe;To Vީ)H1xu#;4f5"6Nl?i\N4$cn'ZhQ>@BD"aaa\8<5P" ~*-Tܽq Gw/7Iي&<úU+t@^iq|9yEK--& BxsD!W?S[DnhY'K*|0 C!xE`ὡiӢѰzcVӤ?o„E[r涞 y. fV kRS yz1Qf[ E@xp횅 &z Q; Mtj]HO;0iW WBCƳ)K}qTΥ٪3DAD"<\7|;E"@ 94雭#x8kE _~Sj D"W*` "@ 9I@[xR GSJ D"@܅NP< D"@ D"@ D 2|sH"D"@;L$/wdDq"yMW^ "@ I@Z3G"D"@ TYz 17/lM'@o1%"@ nAt@ D"@ D"@ D^h^$I"@ D"@ D"@܂6P$ D"@ D"@ D2|"I:D"@ D"@ D""A D"@ D"@ I!D"@ D"@ D @o  "@ D"@ D"@E ߼H D"@ D"@ D[Ă"A D"@ D hki]ʒ4"@p;M|&V!## 8k6hLM B Hǃ[ 8oSy`ٶ[o E2EF n&C!Aq8@HGpѣЦI2G]1_ѿ"&[w}r5:^1T@qogșH8H{YT, y[k4=4X~d|V',w&2a,]W&e%VeI$!!rWQ#(cB+Yyg=f9]zy<=5z&D"&22ȁ;C,U` 24_ͩ/Ou_j:]7~cUF}oy0Q{W#ݗƁ|K\gھ}ڡNDJc^nƲ=17_=/9]d-}3_ko}?ןVudK@!% ZI6kvZBխeRgjuk@_ ՍFXzgMS:e`X} Y^윯jO( 𒣔9iU}·Tm-k^r\džpT,˔ގT',A1lyI[J&"@ +!TD3uƴ cނ[brA}#9c3R|Xs>O)YzC(F^?Fv*n+VA `C+S $e}b_2"@ V|zc&S0H ˳ZIVD.U+!˦A-Y33Sz˝u4 hD=QU0pl=\uָ" \XqݼsgOAzѸfY`3_F(7sfZ/9r]y;Wp& NN)(Y)Z4#~h3x}3[Tf3J- G!T$1MYq6lL8QRuT{$J௓^у,ooy;˅+ۇ7 &xc|)NF^Ɏ>$G,W!+*"65el2Spkyls3%9$ D"4߽fm-B-:鐬=-fnRqa%))H\=vHgpF!m0eT]tvHr/9\ҧIիɸzc/}Uql926C_9.bB|qJLYT]k6-Js2ku4lQ4JMxɱ="P Qyn}*2-yRKYgQzo˾?{˳^2SRҲ?dEY3&0},8 ol*'T)D0ʍb٨kdlSz[m+1PTHLcD*)E$t'D"*>̔r?pп%Z8ܳqln+v:vdVGK}yT<" \[Ga>7&C7/>Xas5FvjW}F r27 [^*l}Qoڋ6,g' rz KD@a&G;yr6C-)=Szl<[D;+4QinfKӿ~N~~L [ۇoI>U!3 r#/96$^VzKo@ D50 ءU751f6 v:?YC0'cׂYjM@\R'p:^-3jQ^|xɑ彡iwMu؝/9Ά؝buC:n^ױsoyɱ DpeGCX~'ÖG0'lڼe rc>2%pQ,YK~x*Q^[<;$@۸t U Gkۇ/n>Ukd|i_xŬӧbVvlt 1 OPIQt|sIˠݖڪU\rqG\ vH7QG 8ۏ[o aO3wqdqj00ԟL0cAʡyhޡt̋/9жY-8Mpr\yyz{BD.;ʞmXT 'K"@ %CS^!PN':ICߣo-yR||ڿb9%C2"X1 VO)[l%GeoӲJm ۆQ=X("2Dsn[ۇʽ|j)ýbfGL kqs&N߾q |q6Z>xXtÀ QRrH^y,PA8r1ur.12C1Y^=.Z֡_h17gD2Jdkk8Osen0;/h] Ճ6HȂ"@S}+>S_AKBttJf$$>뫸] l$o\Ƀ:ʋ/9c-[xLfdl[fřpJVx8K>U v4%AFНT@piyΟnu| U$ǾD켳3oCJ f.=5 B/hD w .xC*f؋ʻ;(<F('JWى|Cr7*ek,'os,ޓ)[[ b}Joshϡ؍Xvwf:~M4/8Qn ZPWl4[RE5d5|_ᬬG€yFFֈ{г0m6; 6f \D]b/+/4F劬Q|?١.i1%QtRst2Jv'AOD"?dMr'|5?B+8wd'7 [գin@uPN%.4> RT C 78D4,*wX@:/9Ne-Vga-G?GJ =w6 ybW\^rL"@dԧg]hrΩæ;W0.U)ش&;G]c!!x8$#ROeL(Cw)-E& lUAv6N )3_D tΔ<#Z֌TB)YjȚel/8;/[9y'W>\xfc4iJ=7 *<GdgyG泹<:Jӎ9&jBJMv?T$N M@]Ü߱`Edڼ føBEӡFm~o 2#*t؅uR6?l%v,6MAYFV/D!FgTn*Q]}L>͋ 0nM[Ј5L]rچ!CVʾ<>r(/tBM]jg]4멙#FuKdb#Lj!廼^7-aV߸C̓a#-'FF;tXu%4|*; D7W]>L-\._xX7ɂ[ޕ󶬈 j='t3uǵxG#/ێ5=`\KHRލ>j{K$ DP\uFgB'/I*&sƳQ9g@LzHn>3\T8yGɿ`|j D  { Pn]4hXQreB=Vv=_Q @p`X'sfFzr@Y[zcyT-SzOh54T^r\ ڭoڸ[7ndصnʳaڊv:Q/9<"`p<>`y)X'm&-~ϙ?y03qek6ّc>[%^ʠn.e{myzzʒAoz*%`CA0m3o|`9Kۇ)_ +VUjtF&ə`Jr,Cc&R |!T/}wDűs.כ;)&;/^e%(-ƪj/*[ aGeC#(\<, ~W@S6!{93Hg#s8y䯞yof3s%S""@|U|['1/E:cXþ[-[1$"^mRˡdȸ{!V-ЃbnfRؑLvؑ#8 éw>{0{xwxL4o nO5DwWkoLRH@BgyN}*s֛Ta啭IJb୎}MYA\YO &9EIs;UuQzPA?X]P{D;۶D; q6;0١j*N$7;] Z%wS]D"p?WŷEY;&?_E%P^3anx/ue)H|?Na WRHp@ȕN>EPzh3޼8ǯNlWھ̲'Kyq)PrL@!塓=MzZNwI!I1/YCtdqɦHTBN"@gޣ.,U]uH\ GW壘w#ceu6EgaFp9yRC 3_ z5ƌ;ZY/WNGt= ^r\|"<̭>eJW,2wFUpUv'ަ&t%Oi31b[k'Wsf!нf ^%rm4nxF`+_an4>>K}ǼekBxB[5;?󅷵s˧~{|a&wP&Y(HCkoH3G +z*W8~WH)s>1o?!-U&gl~C47B"@x.7;g*ҕ?w~xv(,{~|'k=Iyɘ1c*Ƽ#gʎQ,{ G0xɱ'~@VH~v3/9NFjy]Z$މǻp^B;]Mi0u$"iT-+wWN-$AYuRvγrهE4F6knڀK9'UDӦڄ/}D>Og|K <($Ynx2{,~o+Ual5^[T vVi Qo$2|9sv S+iv쨅\(抈"@Tz RѫWJIXR@<ۯм|Xe@,=hPR9DʶlbYғ#ϷOCc炾;|*{xq']A&CEN#&f-K1|0ߝ?uc6="Px *yէyZghY3{|H(̤? \vd6wm棅&[yW:fj,}E;aG;'r&b`TV0XUR__r=d0=ojʧ=ty"cYnjz G~g2xnWn{XlzqiP[Xb Mf nzJ^].hUE+="@@>f9W:Icn--jrtD8S(w~*.>N zLYe}Ao)4w\I2D;Wk8q& 7ߨ߹?z{D1S f*MC,q!R%XGAx>n<1M2XW6y#MWzPv^qجp61EE1hͥcy6J^rНOWyȳ>MerݫjbޒX@4+3K:웑SD+rHi˳7,Ӄ޿&oZ+wXA "DO,-y"X4\, = 8%\x"Q^ >pJa`+_,}3zw$_(_JgQp3~ya m)EVĆnGU;Dtھz5ͫ><֞cIg?zY]k: :$l;Gr٬Vuæ|*" DN)KWض,ֺ4 y=A_CKoMuJ©Fl߇PS? 3̻k-\{p^rD5s鲔'|ddͧbU'̒5KQhA&鑸z ->m`K  TczXMGkkcFȧδgpvf[WoDm^ ̯Aٟzx~_nQiaY~APg!?M)2"Zft?_ڇi^WY{8+/,_$' /fdխTn4($MқguyRoYԉ~uTқvx% ]iFs19aj I {P\XN@߹`|j, D  ؟>,^*x񒣈Mם4'zd*[B/9p7]~k{L g}*aWL锴Ya,Ƙuނ?q)`g 72.ŏ͠9u{qZZM1:Ξ RW/ >O=)wUN^Ŷ@>Җi"HɔLk3ڏڳ3r(+v<ˍ%2s;Aaȷoz*ִ>{3xK˨hhcq6fĉzr ѷt|*  DHyS>D *;n8*V@`lN:n\:?ً!tmGVB1Zd¹/lFRzqQ?4biR*3Kä:-kG|ɒQ;|oΩ!xɱ="@!> &Ǣ~2(D3^߿ǘ,z~b3؅eҜ’fB*(8$$ʊ\%G8AUZwυ럤3%֒"@P'&V`Lz!M^n-?kG]ZJn4r;Dl (.cȲbg$=2t^r2]23_Em)|KF9kڲ%ǥ;plGȲL8]>Ay*ZÖKd!DȇrMeM+Oc9}0BQGsXēu&GWkɔO,R=`A&rA95+)3w . B+f] (n^4#tŊ#q`*}啩fz[>/ DuN+ Q1)BDGYF$Un"j*sɕeӠfҙ)MzA: 4x(k*8{6~͇>,( r1ݼsgOAzѸfY`3_F(7ssIK';Wp& NN)(Y)Z4#~h3x}3Pu/9"Pp .z~-sycZ/_^Վb]_ΔLagzx;~ܙҏͫd^eǣCf_ ( @@s}8xvƘ6ebq6lL8QRuT{$J௓ yb̎>$پG,W!+*"65eLf n0<6`n&$d D"@&׬MxEECETz;ûPJԜZ3.Q8d "%G"vɷQ,Aˆ#1u$aG^r$4)z5WoŲ/r=?0)ͼ8_&f+g#IZ~}qJLY 3zX#tֶhQʬ. ҰE(5y%*H@!&PڔuvHS9tBKQrVh<$*qd؞qVZ͙/}LnX= '3%!-J Qk8zu[3p ElcٮDnԵ?ەoumX#cJo9}`!JDi#1AHgˢc"@ v8Pa(ن-evḆo֯ؑ[a_.Q#,ނsn ^U;wlj ߼cͽG٩U_ ߭enCm%ǽTb 9޲mX|N G;q;D;/9RL 7Vģ[#%r[\N %jG15*f` UjI>&{CPw418_?';K?~f2mBX쟦`fL魲=!ob幩I݇0|(k D"@@^pJ Br8~]co@ bΊ1 C)q2v-ŞFϙn%u 21Ce_8I'^|x$-.-fƼtPrf?vglx)V7; "@ !Wêw->p\ymlwa>2%pQ,Y=Өk/ԭTy@۸t u0uLQ%J2U㮿Ŷ U}ཞ=͂} ^1U8ۄq8Or($ }LB cAwPqdKO|sIO -^3P #6Ni>¦ 8ۿ_{z!| >܍~Q1윒!h uƔ-}5Pκ 3rnFcT }֎SU8 ewt)¾O ?=D+.lplfGL kqs&N߾q |q6Z>xXtxt@ߣXX)!1dl]ٺVw%^ҋyH66WǑ1k] gNjbxgvtOjHװdΘ3Lh"~% 3_q|ڬ}ؖ~l@/h] Ճ6HȂ"@S}+><̿D-DG 0.$nFB^s;:I޲p] ;wg #W`<|cL-߀lJxvPz#݈*f#D{p J_"<%b睕ECåGA+H>}_!0%CBpH( G^I$^FfҊŀ*]G|K hwfuee=M־7$Gc/ Fk;ؒfam<)@Of`=|)@6hzwv(woFbZ d̿;n}+bԪ a0[I{x՞-eH!r,E5H/8;/[9y'W>\xfc4iJ=7 nچ8"?;iz0v.Jӎ9wjBJMv?T$N M@]Ü߱`Edڼ føBEӡFm~o 9+[:K@.l?şrdyq9`!+% h*1 N7G9LGИ9 rDsUz\/gaZu} KfO!Xl 9-0*xIef!9O5 W|X#Mc@;kX0GdX6G@I>NT]φĺs֞$5h_S^1%'`Pmr=TQ`e+#__(C8;'+&$c>@Kە2tcss5k,߿h xuzJu8[4)w!oVswӬf"m9,};Uχ63"zM`޶Bz6\5.?dJr*m9h0>ZpA?M~($N MzJ)a#•Ÿ=q3P]]ğeEdPX>!3Cl_#)-ЃG^}k{ُƹ,=ߑh}?-+UI xS~?[$^籨7Zf{}^}I-:o}wZJ2)e#9r1qyXΒ7r]MM+0lqHމ!CWyd quGn &9^y#_aC+[%8vWj4г~x?7JGFjB5'}-3)hE<}0xh5 Cb`6 Cb򌂅 [JL2uPz O~9#W [zdi2g<s D<YM ʧ8"@z+<"0F[.4h`{,uo (2ج6|AO]c{nP$3֭ A~ԈAO5k+^o,eJ F]K+aZ\q+n~;k $ݔg/1u<^rDyt'D"NyjO&'ʼn<*Ǽ^Qe{myzzʒAokʹzI>[P.gKc =B_]ZuE򽲝p/Н̟͘KsL*ri^SЙ`Jr,Cc&RB_"ɂy/aSwL+uI&s&~O8~rtq"a عU HNI1 *+) 2ZX^oUE8(!ɺx6Y< lBZsftκGvqm_=(|gKʧ&D DNOblr( 72^Gg]ͦ0/sKaG2aGB%QNcn.?&{S 㚬PSoLRH@BPh,Fw,'!<ݦpg< wzIhb`HdϞf,ZCe31<- Z {bc햓X}<*-ŧV9Y#+,39dΘt ÚS.`қr䀏AN୎}My3N|g`ԉ+*ygή^ A>?L]P{DHpǏ8dz{ [yMRɮEͬK;ʧ D~owLLl&/]JBT g _bHR6&ґz(~>#;@p5z\d@+1|(f3yqU_:} %eO=dRCp;1YP n؀ 9UXynM?b dϹ\)Ǽ^ ̰_{*GXpN1\o|vOX8G._^$ÖAA>Ww!p! F DL"@;UL:$5N#B«RuufXIJu3}0{^ˉ!Kz~@&Ik6;/9sg_bCZ-]C&Dx1! *!,OXdWq 7 -UB\#B96FcQQvwlxgJ 7ϰe*ACnqp|򐀾DK}Ǽecoy A&9? %k'oq"͙CdW,wF)oYIAk[YjaT{ %E.~I)@hLsh)b!K"@cf8rV;D$?6 Q)_+]c0l@' tIu$CxKNn|{&Yաd%E9%gH .SyKHb!]wo'#U2sX lZ#~Iߥ&5&q~2OvX_nsˁM #^}!r |K/?}vzJ?ԖjzNHn=)nʑ?Wua|Rϥ\Ғc!/X},X)9}B=NF6)q MJkc;ՃLȆ"@򔀼YHvh㥜39JzY ]v^İ5/*֌e-t*3d˽] W$Dx'OÙ1/bSSOV߾+֯@psl?jM@ģzylz9t .bT6[Lstܕ˒)d({XjVeEuB# My_6Pɶ?5zv,(uW/k0)s] TZ̷=( F57]f$"2 ]8F:7=guhz,;Փ`hȇz/ř'_"ʰ2L"}@<ۻPM'зV-{Re`^㒎g{'.ʧN@"'D"1QN|"Qsg29iwbJߝ3bO:Xqj;Dɭ[{ו޼jɱпHf&gk'N=T`&Y;좌,azei-c KˍXg{ЎZ5h sVۉm ^'G;wo'h?}9wU>N L>6|c/yQ ĝt=|GUM>HZv7b@}wO| XMD^yYCg9W;RdqY# b&WkY6|қ9^ϛWMG:~Z%/CnyeÊK+bs'B 9$ Y.f)Ӳr7DS̚ƻptlRҁn,H=/QY< [e^_}Ҡiebٛ$mu|w!,6ﻮ՟VS @ L@jwp?dg*w$U!nSOV%TǙB+Wq9?wntc`ʚ(+&xKX$H!ޙ /_ÉÇ7^vFѣ#_85SYh(f amŖ/u>BxtE7q$iǰo>>JtKnkڵˠCf5u<(Z.tAHo.ͳi$#D~yUKg9RMkqdO,-c ?@r6>(.쿗p DzMP+TVhJq+'Mm3]^[;NDn[ZTX0mB~[~'B[OPR,Xf-jr&% ;J{釴ݫjbޒX@4k󋗰7#7 ֽ$ ̓Č헼^mO)r 6t=%߷HL# l^ g,O=o3Z]:+M9C-{2|nݰ(N S*b&mD#$.M>(C|!Pk  W.5Ww) =}BzOo,w3﮵sW>ÁG^|xq՜WΥRrUΓ5GT0K^r仒.Fiᯧ0fG15/9WDRyYC9o+ZBwK [hX ?iLmxS;J 9vB)>AxzH<-( lINPaV6o~r(Y,HOߞޯ~t&ࣵ1{EQ`xgZS;/%/^m0*;>Aek*FYHԘ!(yZҲ}ˢN+fJ7˸u ScH®o؃b׍jrEmH3ru{qZZM1:u%ΛQm/v,hR`>p֟6)SqJܾ@{/b[ iK4Mgd*'Hb- vC`-3S݊bO~Ֆ7cIl4Ƭ?eLmZMO]r>{3xK04pxx4 8{Ypost{WMxB-)J(@ &22R^=P28:#4tܸtl CT; VEc2ɸsG_،4ۓ7DƧ:B1h.oU/(fJ՗bv~+Z֎@%N?wx~SCc=z$DNX^pxbOl]SXL\\~gXDYgvh//9﮽ 22J5fKS8tKvoNFPz=kꐙz {`޷y#$(yzJ &Ǣ~2(򖐿 Wo1fO|l˒0\/!a~f?t[}fP=>L wŷQ$D"@pRa-E"@ D"@ D"@xNx$9D"@ D"@ D"H"A D"@ D"@ IC D"@ D"@ ^A^(D"@ D"@ D")y$9D"@ D"@ D"H"A D"@ D"@ IC D"@ D"@ ^A@H"@ D"@(Ѥi)eHLL,4"@:^xꐕ$u:y&+5WptlXÃeۮP3*eAhRq%ܷ kN8/9NH(xyQ<;mlWC㯅-)DG L}:5܎/}и'|@7fgbi(;ޚlV^1%!DՈl/_e|?/%R=l͆ D8"4:r9A,H; Wsja7mSzڃN_1XQ~&aEԼՈ~q.c-稶c`v׬l|OMW}KpW-lqFj!nOFC%G]:"PH p)9S^k*E vg{ϱEXej4R.BKl e7M8_t,cJpZ h[ D'Õnk)> Cu\I:s[[rk- DujbU -W5; -ڲHp)z5F#k,GNĦ)P20>,/v %^r2]2N_Em)|KF9kڲ%ǥ;plGȲL8]>Ay*ZÖKd!DUA3uƴ c+W,Хo{h*fJw v|)Y:H'"jxyQ#li7fbXg $e}b$D"@ ;߆ޘm"#,Vk#7s\ګf4ktfJost&!7 9Z Ξ_W ];.|X丛{n^ĹpEHѠbh\,k/O%^<;Wp& NN)(Y)Z4#~h3x}3[Tf3L  '!?cL tp6&_(_:=rtuƒ1PrD$v”/&;!?hKM=Ȏ>$AG,W!+*"65el2Spkyls3%9$ D"4߽fm-B-%Ndo1s55Vnj m//HIGꑈCm?&~0b' i)۴C^|x>M ^M{싷f ̼c3 GupIⓄݎWbʊ߭zX#tֶhQʬ ҰE(5y%*H@!&oGFZWrF&U(TPPBV4wjxQ]K|(k D"@@^pJ , Y751f6 v:?YC0'cׂYjM@\R'p:^-3jo^|xɑ彡iwMu؝/9Ά؝buC:n^ױsoyɱ DS^dGCX~Z޵zVjMѻc(SwŒ:sO4 u+ggd0ȼs.nŇ<v v^מF(na%W:}*?j)k6ay?N<b@ʡ%;AT&;ߜ@;)-[U3P #6NqV SgI;"@S/@ʽleύ[Uyqv<~g{*V)ZRͻD3nf^|xᖰ\eUJoiڅcۜ_|K(#"pQl;pj%GU8Y"P( *)z irdl]=^I"5uF9pۼ2{c)>| >܍~Q1윒!h uƔ-vLi e n} ۆQ=X("2lwhfۿfmog?J9g0] $@P% b *M R|"|(E AC \ٻ۽$g5gf~{3L;bVl| УDtmEуʗ]BhAxɱ.YLX)eܵ|][ы-ӷǢܺb;caT2-J]3i_R\sggz ;o"Wxz Y"@@.pH}x/ov4%j **q3q Ž ˧$oJWdGkNZn ˦d$cϘə8EO]7j+lOX`CCaG/9v '"@ NCG?ϔa j9֙ Ḇ%]CrXR(dY/ȗu)5~ሞR0zt1)lh/kYBBe \q"Ef[ܵ7P&8g3,ݛ)Y =Fw~2rX+>q6X;;# m>v*_~d %ǚlGE'kzrHgª'yAX1_ %zx[N z͕z^~:v-D&xa !eP"^/vB^tyUEta(ͲR؝zД="@@4y1fL|n}f4L }B*6*qQ/Dؗ< `9HwlENӝ1H%ǁL%enDƆ8iR0÷.+_TD tw@N"%ǚlG횢Ep!Re-xrO=xOIͿˏY ( h6LNӶӸrWcq7j;ҟ`f'թM:e5 $iwIН"@u s^WzbfNq'2ˤ*$o'pWEݡZe(b>ƒ-RCjɛ)#Vdx駣Q#W9%CKQ/J@KQA9f8Qzs#D<}r=t;& kkǛ.ϼ\5yEpc,m\,IBVBisXs2daJ Ja\|އ<2LѨw3J4Ǹo&O9/5 U1;ո;b+X]+@B˅G~S4ԇg0n ;XVQ.CR H#A8ԯtD~!o {̗"eex;UϾ_ oW i-5Em ;D68 /ˎvx.HX-7&j5VD12H|\FʩHD"\ywyc•Ÿ4|株l4Y:HH`Ք&b,5Qm\"Ƣ=P]+vL^rX ~G[HiՀh%( DpS.A˅@'y _ Wp= qu k' Dj=N%_y\qC M^rDyU`Ȱ7n ö-<=âgݰS0+[|Q#5>9wh י{ʆ1BoѦTi8wf1`rj D  {vSׯ{>moO8](<ߙ՗Vv=!_AծǞqV մFz9>vFJ2V7T^rܯgzݶ mcm,I0t6jlG(Q݉x=ͼ'?]>7oz ژ&/ԭ, f'Hʼn|zQUm[ya&ϔ~Ҳo*u5A0mhr?hprFU3Gq#MQ|aP+LD^B*_"ɂqw?h&/9M؉8vnUӤzSVVRpI ;/$2ذ#FY٪H동KqR (]9̘k^ЄMHިaFi{oGuFa$_f TNu"@y6ϟGb<.zAZォXnȚA5X l w,Sҳzp@l6~XX ;)vdO8/9p O~Oi&!jy[Ra|}\8x1 $ O ܾ?j]*̼cQIq{q~')k55V7,*tu,M+Ya&rA# ;Y;HzPNJ\w{pK&]["u{ǎs1*̕; ĩ83+\+dPHDSNK5ٮiU W86j WX]{ʆ;۶DΜq6١uk*N?~(7;]e JuKwʩ@."@60x'DĞk*}dMx6j _'D8ͳ!jbdHB-k!,7v &ecZqz7/9v]fw@ZQ)E=vJ6/9NEJ(0r{G-MJ[2d^qYXB':cGwcvIOذe XݫHM mغq}͘F})(@IDATp.DC3m]#,o\0U|g3~+LPZɓ{prqp:MU%u BU9TDDnnyɱ{:jBPJ4lx4 -l]"CzMExp~w̨WYQ_޽nOrjDN"@cRo4#cεpDHH>VzZwzFiúx>&1N&h޾>\}Zu_ w#/r>'^FO>_jј1za%džxl_mS!a-cD1<faJ37M26 1=PT FOXgXqˣZh=q.)33׬ j,mn͛Ύ>@2x+Qn8eqF_dU SPgZoY\B*_ν;Dn>xɱIwiBb;*OQ'B.mPwœP?mRʢ٧=hiNk SXȒ"@rhZ.#1O~=mgeoer_dG@1;7z|>dndOwϋ2ŷ %+L9Kϼ,"@@^~̕x>}>2R[눭=e^kQ&t=|hKpVxI]#&ݗ s~׍q${䧐rQ5^傗*_^m IZyɱ&]*)9T }B59Fvyq MJӢ;ՃLȆ"@rigFpvh3}MéҬ^VƏ@>1b}õx11l"w^rGyxl׼3QL$C{ߟ=."&B:j千 5$duAlnkބ)Гo'xF)Vk~[2[)FVEg>hm/ &vk8K| g 91b̩_πvٸѽ3vgY;n7?1(O\.x s*ʗ9ɂ̫?KNA'Z{7 keg= F [q ,-PYWph?~PݳTYPKõK݁ʩ  DLQwWa}q|ߏ8l'$,#:igaJWߟe=p_0cnr6_Y!BRd?%>Hp^K-˖s؁-J~f^?/9g"@ ǧqqBWAnw2muRMlVby̲gVYp!T{ .P^傗|Q@%Ǎ9ڊ m0=Q:JQ8쬬 ~n2eQ}s?XbW[>P;Ob7Q95ND"vRnH:ٰX,a>|E1MoѬT$qh"#ml39|>naǗO ejo}_ݮ|l !''T, |mVc-.o?ALk"uvuYzR+8K~Q'^rl&(tx}y}^IF&7w Θp(̤?~,5n(A\!ISa[5L [>nj]n8;{F&<ˆc:!s8jG9‚ӕr!ϵ+r|IW'&OHqP[Xf2QJ2nHf} ׭luRx._7sC+ݬr*; Df|[,a֖p(u$=ʸOR鍴Vz߬0T L[z2\w=P&yv0eu bsg!FO]!q1J8SW"Q@ll.psTV7ֲYB\~uZ˳=ȑ!>^rY|E5q$լ83ļі $ oQ Oaq%*.{7݅gP8jmB~Θ)!uԎeJpE/9I7),w$1-o{%ƈ^-)E –nѳ^ٝU>>}':9d5|f,M8p|R<3ۣ*+u!_ʘ}Ǯ[p{֫<ߵq"@C2*l&e:# tPfO>eQ@tbwΜ(W@ʼ*,*u_\L7A79iu ^|x1Oc2 '7u2%Bg]1cyrfkN`yL<^i\-ƎzؚTq+3èn4478R.X{vD/k׎W"ū| c{g#< پԳ"6Nl>5uK7-M5787Kuh ޓ7ϊw*Fd D| 8A"]ڈX%BN&i0[DŰ-\J(_*&C |jH-Yr{MD_|>#*!2 f\b|)vM]Ѣfʗ, oohU*x  pc %'QS"P P*2H D @[kD"@ D"@ D"@/ /$"@ D"@ D"@܂)5P" D"@ D"@ DR|"Ir D"@ D"@ D--^%"@ D"@ D"@x 7/$"@ D"@ D"@܂)5P" D"@ D"@ DR|"Ir D"@ D"@ D-""@ D"@ *7,991'"@ nG+#ڼ/VCY_233ÿƆx/]Ѹ-<_ A(Xxp+ w5/9ď=z0g/7e UW{{0'8Ip;;37@2ސwYYzF >1|ʗ婾DOs'z&D"@Pk[u҄b~̈́V''VV-|z"M[[5T>Nڠ֪bC8ߎy=_9c^r:m ‚QR4  cUL3o퟾%l7n`=^rK'["@ )Z_Y]T]Áfcg{+YI^@@ՅPƩ䔀"je#Cj?)35x 4Wʩ;xɽ[D ULgogXH[Ili@|J< D<"[s"u~DZXU*uɔ٤Z!U[ iѠF̔c* aߪ.Ze(͟_FY5nK/9d"ɽk|<.^ TU/lKFaa&x1̳CIu磓jl/ձy3⊊075lo)LM-G2"@ pHgv御nlAEM/9gߟQ6ޝ{KȁBH!!~P[y߽#=oa;E=oPR;el2x[n[!h"oQRnտ̇=W4u*gghґׯ^ƸC%p(ES{sCX=q/GlK1Qlq>qHr!F*_"I7XNݶ?(iTR\ئ@T(.T9HO p6D4SDC=?~G&!\ZFB8;R}dzCeM B.QLW^re,AmfMq,KNv; CW m4 VriJoa'9Ჵۢ 6(Dwmf,>X)u6KBw"\}{#wD4;dZ6`ݬΘS<]e-uS#n9*_,2[p\XJi|W`܁~WC=/cWQp&΀G9 mbzjAz ZNݵ?(uj )r<7w AT1S\u 1zt ە},{+ʭ+.3&N/ۢ[9ϛlŻ>Gv_а&r- D2߇WÜ]7w !Q6F\Ž ˧$oJWdCּz˦d<'cϘə8dGDO]7j+lOX`CCaG /9v '"@ 'AT&4Gch۲&vCkYZ!]'l&>t/A^t)Uq֬P@K:"JpDL@)zv#F;e1t 6򻖥/$6ْR"?{yd(Lʳ͔b X?[{X+>q6X;;# m>v*_^S!rAAQɚ\5ҙ@@IZJkW6<+,䷈-Wѻ0y\魏˯2ZA\$-^EBCTȾ꠬]vCaUj%]X2J{v'74AOD"7$MrgſY,f5?Aƃ|rY4 ;'+5C߀BeJ\h| f-XN4ҝ)AQtgr7?ҁxxq *S/luFY.ϒaxx~g*K f[\^rt"@fy=Td60IũC?391Ehx,^o{=o?l FK&kazlKӟEf~DwbƑ&x_e3w/+wCɀ<~^xH+Ero9C$Br0+KmcC4)@LWo]V<|@'ܱ?n-`\WH.R=o[>y' 7>?.?FPd}f++2:MۮR(O]  ި=L6W炙T6,Z մ*VݽzP$Aw"@k5y _97뉙:a0z,C}i21EcȍCjɛ&i#Vdx駣Q#W#f2#_*Bs 5xp(G;x:Jag@Joa ވ > Alu%Euʤ^FQBisXs2daJ Ja\|އ<2LѨw3J4Ǹo&O9/5 U1;ո;b+X]+@B@vondSo?)ǿccSWV3T r7U + (BxȡAECv*[)f41(3/cŻޑb}jxSc[|g%3au9&=$ҖVmdB"k$_>nBʩ z D<$6ʻ#KߑbwguqsPU6,UZ$E cjJ1mǤ~d =DE{0ߵW0i8@+D+]Q#.śҪKQ xDqvKأ{t*F͹Uٶ(BsE;YuK;LdJmxU[Hx P6(R,yM ., r|\݃ʅO> ^GD[X;i Uq*B*_ܱ?:<CǸq0,f'myg&=3ؔA-be2;J">p]{јG.k|>r3x bV{MwA pfcx@="@@ʥeScZA\xi%357Z ^+`C>AծG,(>ι؈5}ۍBL=enh送3n=ֶD0ۣ![T Cn}0zzh,yѝ;|GӸG""zg1SfI>mq"_faUU |f8(0c*N<ʹulJFP.L>/mi+̣BDvoP#aݪJ噣֋zo;g^XBi~B̞r|$&Y+h&)}Qrk!s9zfD +8Zy7ВcRn`_rڥd6;H¿QUC]4|tfGPW7 ҌsA(S#Z2"@@ߢx$c0W45;.QLL7IE50}klkVmRBBJo!K放7@9"=sF%,˔;XcMSv1xdL6 n/5@WqcH"@=L1En;Oz¥÷.K[Jv + 'b| Jwe[niZ?+C>qda'uBq)lxTfv59_$aFű#y\i s%ca%v/o^)1{y+bSqppH3kB59T$O *qEE޼5gbr|툕;UYlmKK`C+_wuʅS6 D*ST?NxA.]TN%d"D%owB@-uBvL ۦtC w$SޘHãy DX,Ur-DƮҡ,{L!N%NR]v:k6l.HK35EN%ǩH3 @~J}-m|7Qqs}Cc;T2][ݍ%]>cÖ-b۸q+WWuF1R\0)4X}I$gλ*GX-iNG//gV,ӡB@v/^?gq _~ q;}r!F*_"ɧNA['.lV2ݿ# H [=opPgeA}i=û3*%cxVcw[JD yK@mc=&惐}ܵBӆu|L b_:Mм}?|꾨"eF_V吥}Oz"}R8Ԣ1c`K |,7>;F/BZ8/9L!pc=Ԗ{hx4(}]nHQw/ 8ecjM`(*#'3z8BBtQ-4$LkEQD~=9?} ^."t>^ 6Gf31Px8iulȣ\!_HCALki<0UХ *Kx'M\%V>@Lsu] rj Y"@@.4ùN_5;#1O~=mgeoer_dG@1;7z|LO*=Kv,S|PRΔ3#D #)&/*ʃ_Q4Ex^e:];=e^kQ&t=|hKshؤ.GyS8Q]` Zf\W%ʗpg} eQbUR8 ^ }B59Fvy%BzZmuiL ՃHȂ"@r4+wqXz8;4R5ٔ=@>GF ~L 'Q/9˟p2ЕWߟ9sfb/f@;!)2$8%lgeK}ƙL lšg^r<"r{luDMy!^<1i@: 'f 3V] Jl?Z:8K>$Ag2^EQe vQz#Kf͊řsL&Mr>jYL Wڽ.g/9Yeip?讌ăܕRHQG6g&2P9y DNȔP[L"5X |hG`EA;Hhsfm!-ʾ_3cV"Ofk,[yI4KC"c[DX$'zGO'2ӚH]wV t0Wq| OL9"PPl}_pMFYy7nf^;,R&:ߌ,5n(A\!!Sa[5L [>n]n8;{F&q"J{+^,}-椽k-M`J9T$ 9'4KQGWz^:GUT|-k%eVp"m\i"X3p%7$b nu^:Ch$ܕ+ݬrCC"@@>O&BLҳY[*JImh7m2STz#U2|QrVH@mP-\pg._(<;:f}`63(C4Cnc8p E*;|^YL]殯n8 eądKtyd#O_NHMVN4-<k޵0}LaϢ~~sg&"G[K7\,5F-<ŕuܸvAQ1jJ I8c.3%\Jy{gI˚OŊ#V0K^rw&_* @_ok˜&` 6k^rDANDR9/Vy ~^ {}g E؏؟\?9-^P@*Vf֓{%xMls.Gb:˯L"=$cw"2od \Xpalٺigg{{ \/S[9ͳ`Qղ"uruˣVkhR<3zw,rdɉ5h=|?f q*4I={Oꔫx*hɂ"@黲DLFɜlզ=; Y-i#9lY{mKyBy3qSG^&vk'M0q=#%G44_)Ob4(4_;goďk}J !=0쾐fMϷǶSq;>롴~6փ#rT`CxoǪM0>Ζ Z( p+@vi{gWS36n\%n_|d[ 㖆O0Uʀ0mőra39Td W9͍ /=AalL{Gq#òy/Em4M|jq醴f{zFY YTN- D!Exx4+"v k#bJ : w_ğ}l%GúsU*Q 7<=b#Ͷd5}P0wȬ[%0CE0/YЪT~7.?8;&,yHmD-Vp ]Xnq$LņDSis&VbNwa5|\\R>>HKKGB{ɬڧ1*TQ-=R|?<}~ &a!ҎͷgrUP>TEμ Vw:B)g[{iGksgv)OT"@\|) D"@# WXO`L~ٞaM2/9dx Pz"@ D b!D"@@r=N^r\O I N)GD"@pv7A D"@ D"@ D.H# !D"@ܑc"yqGF&"| D"P8 Ӫ ' D"@jFL"K> %'QS"P P*2H D @[kD"@ D"@ D"@/ /$"@ D"@ D"@܂)5P" D"@ D"@ DR|"Ir D"@ D"@ D--^%"@ D"@ D"@x 7/$"@ D"@ D"@܂)5P" D"@ D"@ DR|"Ir D"@ D"@ D-""@ D"@ *7,991'"@ nG+#ڼ/VCY_233ÿƆx/]Ѹ-<_ A(Xxp+ w5/9%\mƠ~mQ+,%1A3ɽ0۪;/9V;kf[L~>:D[iyp [Ň.Xɖ+y\ hh}_1=zv*h't|\(_.Sw೎euSV/6Py@)K"L2Bjl8 ]`ӝMWg+x/R\-_<3ȭ|LGYZ;P8FC 1u94\?.«LyTφ! *( +Ƭ(I|5XMSļcqEFMX:[74/b+[Fd D"@&ϼxyɾd<><6JS3w1 !ܨV#qXk USPO1wgv\`K+E*nJƭbO c31t|ˆ* }l^r\ɋ\5ѥ4*R]Z7¾?7:E7,`@IDAT/>a#kV1`zܚ2kP.h;h~N|X0S;q{E;/9RBN御nAEM/9gߟluC׾w،(h\}=wDRƭLdoE}muh),5Mз([ )Na_rrv:ʳ3OYHyW/bcܡXc+W=+qxrf35۱*w\*ʗH޳/Y@vdڟ˭ɓO< ہ ѫSR)ѶaޫG?K8k)!l"pH/d5+:QHcZ1arY(+̋/926Ц^-x}r6'لyz{L\=}=[,<4P;_#9%"_6 fuƌ#XݢX>ez:R/ƸLrdMe[r8Z\;Yoael**0V9[m2Xl\C}vA.!rt#簶kٗ, Τj R oDZ0k(cK8n!F/#Z֡ (Xh@n8oY xj}J\Ŷ\sgݷAÎ@BD"R|^a%j **q3q%Ѷ\KJUN7mp+W%e{^|xq9C`-"l'#隌ݫ>c!K#ivOuxWOIM#;Q yƭɉQhl۷Z ,&A8+ndt/|^6pa׎bK fʿ"_OQ$di=3|aF?ɁxNx ]e3E{]@-SP(/2"j%77)_d(Lʳ͔b X?[kX+>q6X)ʌ4۩|("2,~WyBş[#v e`X1K_YYO~ةD\YqkJo}t^~:v-D&i-¾&feP"^/vB^tyUEta(ͲR؝zД="@@4y1fL|n}f4L i,U1TQ z!¾DX'~=lENӝ1H@T^댲L7"\%\,;]PUAy eحq KNCO WA1v>Wfv-SPd6+SY߇~s&kazlK1~#(.v,Ų#MV-#5fJ4$_8W@뇒x& VR9cx/3(-!>Bb|/D+-[Th16Ide˓ʗp\dה'tnuԛZԨa EEVO6O9TkePJ}Vc.ë]6=o[>y' 7>?.?FPd}f++2:MۮR(O] '+FyG?HujhpYMka%I;xݝA@ yH9 FWzbfNq'2ˤ*; UQwhVϢdFQx'SFԅK0z$CXyuय़VD\,^rȌ~Q\ 1|u(G; F={p|>%: | gfaJ ޺5Uzp}#Dl |F74 yyaLioܑ[9Z2 ,|?EplL}xʊ~&*9[ưAae_AB9Tx:N+BVVn0Yf.f41(3/cŻޑb}jxS]ZHk.-mK!w@i]v }k)a5VN*$vA0?TNEt'D&;mƒ W /fJoDzp`@ui=#a2yq:b1VV֣6Fn]7#Urw@2"@lȕzwl8ei{0F,`;s]L-c;~ժN-Dk:AEZM9R<3 ^G*-48dr!C/+H7ʅBjWf)leOL5'})hpz0(m"kZ.Dʡ%,8wGr@}uqƨ{.~O[z# M؉8vnUӤx):;Moe%ı" {92ka'e߀ҕÌIv)8x_M؄ftƺGvqi_~ofNAEiƹHT!D| !rxY%=c *b `bw(UE Al>Vjd9ͤ}>dD=sM_&dJX ;޼ G;A 1mFcWQ8S"^2_o.0/9)! "@uA1V/9fL;X_<6*m5R0%xAS4UNlC(w"44*T]w{JlW-Zc絘w+U' MǿwXƒ=ˌ-eME5-&QIHDT/T ,mdGY3,ws=s˙qg|O{ys{}M#˛0i?B2b&>O9"789_ H"nZ*3ؼ+P2Z2-dh>D~W}fO #OzņEPU~ _ ue"WOݐȌR ZH:x!h"j/77z%NjA{vn;[.Ɇo t nw/z+R&iF9wlȂ7(F1zހ^푏`]ZZ1&|z<+CV|FiʑwW5\`1Ew㱗f@ǯ=+\ɍ'OSۓָ7ƚ*|!G/d5_(Ú[$;r"^|y+1K9F.Krk|U)U1eәumimY[мY/԰7.v ø0Pd9ҥs?$@$@N t Rҥ)܋w9*'!j>JixvhѤ!nOH@kР9o,VRw*raبL>uRy֥2#{K'Q(Oު2@B/9;HnR37UvalƵϴox8nr)3/3g-Y_lz )F=0B:tvB wZϱ?8E=0:@r^\rMХ-*N>'L߱LJ)|!47_>-[,t$ ae8#U| $$?q_kTWm._CK1N@2f['՛ O/9ţdƄbc"M$@7 ,c i OEՋp7|4ǟ7c^4t]⹞ʐhӼFL;\TupݍqK8F_kR&c+_%K ss<8TFv$Elo),hJWG\8&4jv#wPQ-1Wv=WrЕ ]HHreQ&`qK'3^N%NǕ4_1o  YzbAېy'Y+ǡafT;FwohߤKM<[IYr~7FaɎxu8uuj T3ɼw-_lz*c:|7/6= s2Z 34m+-RTo2oxRvΩ8q9bivO$@$iJЎTJ?TZVGU%i~f7)<'Ox MWfh~Yx}Exop\y;y#{ڣsYOx _>!'teU,|*v}'%]$z7nβ>?)˿-%Ksܼ&ף̙XE$|ۇ8bZS;5%73dqʠtJ󟩣'|nu#jNlX< 5W l7]|$*$9r#κu0B`0I^!b*~>wӋ4Szh4bo~!`գJZB^EOݢ57wt H3-gQfG?eu׍թb>` @.>ɮLߩ !> ivQ*ݰfe;4]1ydVU렾0(w~ =P2v1emV `r!4v1T:w-y7uWWNHesh%..1x^9GO/9,4]IpĽaaJL8oK7tmuB݅0@jcN$؅IWݠtqw.?RIK Q[78YŨ]~PkƃP|#tzǀ)m7fM\/F\F#ae8}Ytv 1JE4喻xc)aH.æKpq>vNGx8i u(G`N/i 蕿Rot}CK/i FdUH)ߤh7wŵ1cF-Xwu}D_>/RڵQvcqz4n$fmHvJE]P5;HcjtڰIfDk6-62/ 0i85EhNR^Y]Gѭ\Yf3+]#d(ʠ0b8+eJ|mK:|IK^KHUP%P?2^nԋ^rj-]Z9&+mr"O ˝#nG专OL.K= nf$-sz% z }u-I#f|xNa1qo:8\'bTV 1en%\cj.kGyGT@ҟ&E24T2- S/7ᬃ>ꪝ7 aҒ;W;R:Meu1{EG uKݕ<'ÄbKyE-!}ba1QF<6(YJԄPr934{ܤEh=x!.^4d/ąx;q恲.xSt  E> r9;{ چ^ؑsb U)A|;9 7zDno14}< _\-ϔ{Fh 'lT]Gj4]nF&$K:MyZ/1.=|O|=P/9k$@^y=A=cgo&3\=U{EvI;Nf%9qf8t]i~ǂ1r'&+]PyJzVc|UͲ@a% J2B6%]=P!VsK9_厔 owqz\؍y 0jy3+ ZͲ OĄ;6Ǡ| g{khĻZJ8|JY4_.f.Tݎ_S 8eS.(߮;:4PhQD,0Sq!\ >\IԵnV%F$d##vـ_I=+T( é2p" yrzRbvEUQX1DFb2!BzO_W{(mƪ'xI$@Э́#Cǡ}(,hүE%-h{^^]=;oQQHM;bKbȳtW/9ɂwǚ!(m/Kgu,Q7@_7 :TbrCNn Dj!ШF)eTd]=oFέ73pX\$rQ8wh;zq@mر o& @Ww oki-M{^r|ӚH"us=oHHHn1%   $`n| z ^J G+=SHHHB ߡ$ .h# "KN(2N$p 0'IHHH P`ΟcHHHn")02 @525o!|O+?b&HHHB7 @%HHHHHHHHHH"N"I9$@$@$@$@$@$@$@$@$@$@!Ax THHHHHHHHHH@/4|ErHHHHHHHHHHB ! ^h֋$ C1P           I!          4|c$@$@$@$@$@$@$@$@$@$@z[/C$@$@$@$@$@$@$@$@$@$h@%HHHHHHHHHH"@÷^$)HHHHHHHHHH $J EoHR @H;$          Ћ z          @wH<*A$@$@$@$@$@$@$@$@$@$"I9$@$@$@$@$@$@$@$@$@$@!Ax THHHHHHHHHH@/4|ErHHHHHHHHHHB ! ^h֋$ C1P           I!          4|c$@$@$@$@$@$@$@$@$@$@z[/C$@$@$@$@$@$@$@$@$@$h@%HHHHHHHHHH"@÷^$)HHHHHHHHHH $J EoHR @H;$          Ћ z          @wH<*A$@$@$@$@$@$@$@$@$@$"I9$@$@$@$@$@$@$@$@$@$@!Ax THHHHHHHHHH@/4|ErHHHHHHHHHHB ! ^h֋$ C1P           I!          4|c$@$@$@$@$@$@$@$@$@$@z[/C$@$@$@$@$@$@$@$@$@$h@%HHHHHHHHHH"@÷^$)HHHHHHHHHH $J EoHR @H0T#f%`&5/jMdrr2Kprw  @7|Bg^XD[`v Ӆ nz1/}@?@wbH3!3DZb}%*[{-^C⛵Gk\2(VPUtle>D/9>GKkr  1mCmj0_}5Rᕆg:{/}ϡWy'ʸӷDM^r^5Ñj6Gh2Er~wa)+>U fbݟjqnJ@q뛓@0a;ǁhV[x 'QޜnKw](e͐o5eYqGP?_p8I’;ʸI1\9z j/ETq_b*H7R9YFe ]xk;ۦt^Ͻ1&  3Y4xD=ЀUMJFWܛSRenь.(-ÚRwb/ 1, bHs)yMn~~ҳH9}\nPaDhdw!av2;`ϠnxFuw2׃ }XGdtܛ]i#+ł}eB3wf(Z0gӥ>j!el tn_0X_qReNi{6"9qw[ܛtmF<,Nӵ~yc$m6\*Rťj$@$@$@dknvZ s;Y8-W}x{s6_Xi CwfNa%X].*D]ƚ)ee$)Mt$ی O!5D~=Fk6:̑ks+ @@:ǛmVIF^|;U5Cq(f$>In}|9E KN@{IK8~xGhoz/_(dwYMs^_9PM[?T|.ús1jzom}JW.Ljj4G[g`Ÿk9SX]T_K8y>y ߟb宍C26@:͖.mcH'q0F:&1{3g/|1AlX7e\aL$ř}7nDE:z>epfsoB_)UgĂ5}7|!G|2O_~,xEɣbrRmyII!+٭XEF, zS18F8: scסXiɧ9Q0 ^-CTt]wn7mY}} \#,+ vC~Ęp4]ߎ8.*hҵ!ӌ eF=Ć`*㣴9U8fh[~#b@(³:cŮ⛗LK37؜Hw9)>'HC>83Jg`H:bah]v>SzqK]_}M#+,}q&~jկ4H/# ~ZJҚ♊ Xq_>Ev&aV)X'#P9zjKŞ|Mȸ3n8LX_qm'm5y)/eN| 09W|l׳rm;>\<5l0.+%-f_TKy\YN!YvbjDp}Mj?yU.ǔխm}v#l_':a~0y=x 7l5ݳٴ>+^_=B/|шDe>}彗n5I{b \2zϏpo3vӱfA|*wc9")u߻Gve''@ĕ=OzFvA~f&c("ښY~^"X3EAQr'4yeX  @j6_V58˽֪R˾hfߘ/3" UM*Q\ECD 5-q#JK(% E8SAhÉ)cE˜(ˊ,瘮jˋfc'lƣC FW@B>bJv6K z֑*SFoUuXSk: .ЬHJw kwH9׃Fz?w`T8 h(QNY ]U7@rкYUe(ybj1ҲX@*+4%Oz('Hkt*fl[!eYp!HWcj oԸ-ĆlGVLzL؂lʭ91wy)gl,S:,+Pa #sDtm_J&_8{ew7.`Y{\0ĜI=ECtWƃÆ#Po[v|1`۷+G`WvOɈUA~1f)1ȩ`$~=yc4ּx-i/77(e^ԝnn=.Sum?K~QbKrW=<L4`$%q(%M͓pjX`g,YHHt1#(N=XjyHH w(-7.F'"By)ԈJ BEP %ڍ mdUB>BE#P]!~ev-Yn+ 36Mgͩ8q0GhXR4 !I0ztځ5a&-wBVN߰#p^,Jg!\>+,aqiS WFƧϓc<&4"jv|t]!T6T*%wl&ȯ?Foe̫'q\:" OQYCהs)C G6zK~)k;@}Cjg 6Ws ^7t(],b[F_ohp:qי TNі0,Hx "MWna%E˴WaV}s0wrP/9~,r2z"=zYcP,6ĖA1I[8׮RҕA B:djTIZ_-SD[Mby?UGɏ@ˉc|3JU^*nkt;P`Q,Y4cг~`꡾?GguٚˆkQ,aU|ʉ_YU?̘flQ @nڲnҴjFX新!vφ*X3Ҵt{]9uS*0~]Ǻw,)HbЎUPihp ]ccS QЭ$EIϾD$lh 8<'+~֞.:HZ̽ҦBg<#sX#rŏ$|0D*EhKqDLiĵ|Zvmु5X =Aݚ)AфHFkbΥ"Sh;+@&ȝx*frmRIX ?gfwerqQac$%eȤT!Pҹ+(wfl'֔ۊgAި.bfwcДZA;^GM˹^_|/KKƩ:byI37Qʠb{H6a[N|NiF>׻S@ YOoj(k"<%+c ,Zªyx[Y۩|9 VZtԨ_,]S ^ "&"~Ym*~s j\ C-TX\]rOԛWpe1[{[asĽr_'= ] oʸ \jLV*f ~\!}@IDATzM&OcDɯ1>PZtEp ,qbmKicOhcۀvƚN||V[@=&TIue퐌OwC1|1C_~kAX锣),G:܏xf(\QF_YZЎKZ7ya8͒OWD!9v:uEGНSbKczA@ &F6n|F[<%kJvrHƕQ6 6"kc vQ^9EJ0dȺD[5nYy[\TC6L[w}fbc evԠV%;!@qĂv꽖~xjB _h4B[Ctj~J/DD\\@cwI_DS:jJIp/$za87 G$ݠm ßĜ`ZVX].rozO8)ޑcY[3ZJˍ힂cPlaѦ1l߷Wa>?$@$@7l)p]ŹovsѲk8$ gգH,Z6'J9ϭ[}˹ ^4B1? 0 sgWex[sv6}}Q|twoav}6jscM>ckt5ՏxM1z p51 Fo\]ik*=uz)b_aIrGi})q[°`i{ݥxx@nx샵>FnM=I6jnj4G=mq]-q[\ H))ތhVOؓ.Iux`$:M?8~ypGF(˒9G3'Ɔl#)6{,óowׁ:z6/ 5y.fHJ(fAg_$C>8ϐy=?Q زpC͸oN>*6e{:6pO3HD[}`,7X9_2I~߰/#!xeiPE 4fIZN-xӚ7֐9OuߕryET9cVCUQɺԉMم? fP)uuɩzkLٸ>=q8Y )k5dGhӚz&DEIѠu` 4".#2#ZҘTHHFZlbׯj7!oC&uN؏lA3kAj),CZm5Pu=ߠ%c1͍;;z.=}n<kw0}tNlո#|ĵ]5aZ6:0XyzWi|\94z8{ ĈOFeNQӯ^r$aIXgl0e1ZM>b+TO'ggXZDWDSĮS0,œg(?}DSs UTa1qߥ\5zO}i86Jґ]7{M4šS4._/aRHf{&ft?Mtv `+ 5?ZKIJ4\/O52źS_{/z5gon7X4m!sն 0or̪YTGcR"F#aT{eX=@^r2nzņ1V, sXA>nu YIX2ڛ*i~!:#{?/Mz*MzjDfb1i %g*lGUF"YjOn[ScJ;5 :IB#X9_2z]SR9|d,x I1ހ^푏`]YEc7˗`FNmb%eozwރ`=s%]* 5heQK˯r"͇:U Ď隸)_; ?-4 H 4ǝ{㍅!ѰR=t:{ۿ9R,&NeZ/Rݜ :=T9_W텴1+:d\f<:|'&cѱ2`F8 Nϴ7*އ.iٯɧnsUST]țS`܋K1[&TK% AT݇P>|[̧nБHH (57IR=6FnB)NmV.<(c}׻$`ʋ}@_sY`N>*-%FpV('4b {Y sO@Gy#l)e)lWG4%}Vo옏#5֭=^r|c/ F@V>NIו+p@: 6`EU'-rwcܒ&%%!IZx#nz2Y7Ô]ckY-WsƇr3>;Azar<'wu~pی.!u^mBq7jn"PmBʱܑukH< 0Rfؾ]XdK sl{=d9 }HHrW÷q8:Zߺ;M,i:?h:OJi4Ttwxr3GEF<"$׫[?Q輣(Sǰ-g]sԳ(8~ʘNeZfƽS0iNwoc sCa\HWkC? Gy4XS>`zɑ5'6 ؛LO~.O:(j6oo:*yѯ[k!v_~k=W;uf47789{Zd/.sN:!.Z6 꽚.r3;E gLhv[9),yp3(Q/ϰ?S)>y˧IR^$Ξgx=ۧ;gUMj(^~~[췞L^0mVO/0zC/  d[3)Gd*;'l"\3Q :FvHSO:WvJj1ibw0)Q3bn&qӨ"<=Gg9wcWW5:ϗ6LE?atO~ 砆\j){FYq|ܽ s !{(n) U!.*? dƍʒO׷nX/9>)6Qᚌڧ[ W9û~6`>zTUU Z/9.Iuks@"79 -~~FrzmB2˒xޥrb~Gz $ˆDkVOq C'$pX %!Wg_W*O)*sq~TS:OOBRwl{`&Fq?pg.HH/V'pxP3a*=kq_L꧟;nga+zN#+2G'N@Yd0Z=k8ѨPSǎ^T-ޛ;K8 ML>FPv\)<M|;x}v>?lp(gN|>ݿ~uev!cqJLfkGSub;$ν$1|M]Sy=J)Qg^l{˂0}&CZkF%'RUԬZ{>ž0]lWJ:`b0cYrSAB|Di;L(ZF1ZtÚnFc$R/@% Cb:#ae8}YGWGi胑c)a)æKpq>vNGx8i u(fG`NS,޽ [ž"&5)=ns73^ټ~ΣcУbRsW3s&_2kArO_<+vojc͒x oNtCJΔ&el&[.܅b;v"q.N ХsS fqz4nocr="y<:%]zOlDZ)&F1 Z슀[zϻ[fVxޥ4jG$@$ -Z84q.%]"37wZ\S< +5$ ;z#U.Uޟ/~j|~@w9FꨂIFwvZY} kZwVb4cBA(V /zD=7\߽/r4heo)QOUUw6ǣAx(i*? ]+) Uk %7>oZql'VʟPR|]aɵtQPs2sq&H &~(E ɞ.Q|Pw*p3#ixi-)(g[FzIo}<闳 1enoǸe_:cSJux /u{ fQG-!ʘ{8_֮٭\͈r(TB:rt]5'~՟DOosQ#|Ȑn[MW_ǤGNyb:8\'b/S>fBtx4_V@u.0`A[8.4,a(Q5-C@!XQq`B8@YG|!>?S @$v_~ ?%!fZ1'OUrp7b3MOiyG=cW08zfw (;*SR0K_j)U-#[19=^=UT#V/vl%I5d*dsv>]0c♳oL|32RDu Kj(l+llrw{8wЬDπtz1U݄W27+zJ#Ŧr{|[ .x8pr#H HL^YQ z ;rNlN˅vr&ntmu3z*<ނ4Wdzڃo'G3Ӷ1|+(=Evw,#g$/Dcilx:[0Qi ޫg~l)JݹܖkW59N+:bJ\Ɨ|־K|!tmNW]>{3>pVM zU?g#x-O[k <*<>I=ڧBSiYruvKb@$@$r qqqʰԋo4zwo%')+(T( bYRy%xME@ ]ǮaYlvw&]mC1=C$8]`u6ze?Wz݁+W]I2 ;_oa_&Ÿ$/ 8RL +s"\ڎ_=5+HV]/Uq5Ct\毦dw^1thRD(HS _Y^{7`|P+XѢ22˜'ao è#oKҴkqgbٳ(\-IHgbߑ5C\[Q<"/Kgut i.cPVZe, [H]Ŀ{6f]S꽅$z>{s~jP&bc m Mkdtfkbȫgu5FWS=lW[Cdd,B<=/~]SIRTK$@$K6|^9MV樝K 8@S1*i{9qF@>˛ C@mfvZc֗K\ C7毛3$@$@$@$pcɍ-Gc5#,-f[~{I9ޒ2 !C`)HH Xq^rׄH `ϔ)"   P!$zgb݆Y~8}X_$\4#bUTQ ekJsGE"}6ԌHHHHHHHHH;-}d2t,hTRA0z[*HH kHYTrW¥h    ܴKLڼfz|̫8pxؙy܃|$@$pHIOY=3Ӏ̔k8e0DB/9EΛH `#   !pnn߬=^ %c Aῃ;1oO`79ܴ '        U fbݟي8{=z GٜmX) пhjf8p+OސA|g^Ƿ}{GPVs{kƅ%ҭ^r QxF,"_K#ƊcK{t%ȏrs?{Dgy45ͭ^^r*IK@|jatUxRV1=xa/,3p`ތKeVrp/9_8ngS 6H]+wJ'"l]GXgI&  pOjf@V2EQ(* )^kl#S2jZ\ caR5 Ͽs5ҝ`5KZ_0gpwmFoIfx;m{zKy/*Z\iFo[y"zw^p*6Mk<'^FR# f{w.ơ##8q.Ū5ǝ-90Z\_w;D/9 Mz~7#F.;-Q9VY̜ˉ(3bfꎲŒk;8{JmZV.%eĶƻ4w*?z|7ieƍF1WG#ΎjckM#ܔcesQ^' /d>C`nho9HHHg>`%J.})_bʆ(.[N˴] O8ی%#0qnA|i z̥ xQa)%'n7̙d9~ \Lh5;{'T% Zz%QBHn^ u*ĝm'kױZU)ޤ' xI$ H24FXV+Z:I׾vm XS(HN"Pxʼ@ };ԏީAl؞WuZd)^M6mA )d0aavZ3,'Fq!i&F\ \$@$@$@ 4B[4FkcaУl<.ۂ7߂55RK#Ws4Fo)O .ֺJS%G؈Nw/1zb_;QFf-4^rtI"*^yз-P¨Q~*.$@ߍqOVK-%VENZ.W(_K|ja/&*U!ZUϾ5S~ӤEj_rT M:_4kpCbx @NPjbH2qa-Q3ů~Ý *VW4q{.\L`͑NA2baX{rS"=ы^rMG.wc3~RaA5jyz#J/A#T=p޼- /72^ͯRwРWy"pVv*g`Ÿ&]C4/_ㇰ|ր7y>n^g;*m<$FޛuJٽ+]:ʲ;/daIo"K*i(fq凣n_T|9s↶bWRK8{"l;'I&OHH@>7|= )׶KConzW GEh>_#5mˢevQDs%Ge#(D~|o@k%'u}ڽ*u߻G\v=K[t$ȗ|n<4XU>v,q7JT/9rjm|߫:²`O"!)VU:6!cuOoѪJz>f\R]W1De܌P{CMcJwA>|;T0TUNˊ+!z lM'4p]k#2Rocɧ> 0PYCJFŀ P6y{06/fnD&-i!(>M⧩Q$Ӏ}xE/,0}4(*gB^ȕ.X  @mئ"E]X,xVaKOE](l$p%nErp^zKNpn11ccd_ϑzQOյm*JxPx/QЋH KIr[cFU%GGYDqDEǸVA{»mO-PR@FS.(U X+ܷ (r (@ PFBKG|I6I6iII˻Z;;owvv޹ r"3- a #NZe=Ew;(<:fpq\0;/97 @h|[a-α VG¿X?DD7cJL"=U v[(IPh/O kqt|4ELքMe0Ҫ~eC^@7]9PXbcl8j7^ Vֽ_#nf##ABø_P=dY0cTFS}rs|5N. Jj0BBD"$Kr>&"U]>Fk8w|5 %+G+-Ky9GS lGpaZa܂CˉˁѤTiR\ҁpx86w,oi/%<{s:4ǿzۙ" ڈx4o6&K=}D?F:jW0u*'w.k*OyXE hֿFMkĦ*P-*]ϼK3KGHJA,H9s /Dn`J౐PC4 R7=l_2I}b^9h>%Hć++!_D ΀vZFT}&FH#ST^n7cϴDZ7:+2.EE"ٮ ߃JdFoy \ >%n!-¦WuJ}o墆~l H]y0+#j.s9-kq$Iƭz7*EK M@œ߱`Cd0UW F nV#ӫe̊[uU~E ̶?:Т.l3t4 /a+ _A\h\EU!|UmQG:"P v# ݬ ڵcg z2T5K5qKg,m,Ua#j B#~wc1kidw-ScPh`*$ƼY{C2ɋXIqݑ=W%UwLP/9MΞO<-l _}^1Ф$aP?j XYE-BCK+7!x?j>%}dFEu<>+yoJ̜~VjOBl=)6X+wdXu.R_Zṗg&ô%l[wY#{1?>97HBZ{`D]tb_~Ak($"@s܌&l9WSXӗ&a&j>~ >5{W]v$VLl& ,͌*f{ O߉dRcT y^g遗:9RFS~\׫K89"P( Pҡe囸8EfzJS:^)/1^<̨-|޾7wv}Fo! ȂӠlciNpTnr X Β7:rߍ|mϡyHCVEf'gܰ] Yc)g S=V"@_Or"V/{u@z0HLڊ5եU3EV G?^0æI /o2z n^U pL{bme %a((fVZ^Ht961zDL2oO7A+ hJxu,0xiBO{C/ ׶>~>'Ż]RvpfytBR5X6zuYӷV0m;>0ǫ#~Ģ/bDG۞Coʾ5twM9$l vb@PIߡ+>@ iUk;ѷk<><+[LIVƘ{ÿμ7- sCPJWliM4%0uXV eV1hՍA7LqmZe 'ݭ+VDʧ&C Lρ[' ]}dž< Küdw\-zud2/ v}C9IA/0-<#n<Z cK;=ڗ{eSAxDkA\L k$>qmKwfEC%V1}´xtǫ<#=u@II+"inCɛލ?3ܰNAFU(5ʣa8ؤ9uyh`:+_hxsmf+72E |!::dwM'Sm!Lz]0NEXҳc[t(N7+d # SqɓR]&kPh,Qhh]^>yE=͵R}ʧ&C H{z|m;GFoql,9Gٷ.k(ll' |Isy="qA!.Kǥ""@ $ Kc89Y,my򔗎e=gx9l{l'k54OhQ wL=Ŵ5#C5+_(Pwħy ^:ܾxs; +멨N}5w xa/{å" ilmiQXg!/L଼"/Ƒ:g-V[qؐAF5Q¨tqVHȁ"@g8o;`ʧyD"/ 14t5Nj2{ٱc´1R2N*if{*/3gNøמs6]99J}gB-Φ˖{܀6&c3YOcy1/%mr#Dpq](KzV/\wRZF`װ8\v]wS0/k Zfl?/+LΘK9h*E \Mڅh'|HRy=N|Io~/xXoqЩ5Vc+8Yw{[^`[0uՎ_]R$<ұ :A@#뽡Hڬ-0{V_+^‚±R ~ WyK*n:rqkf >~nHsnO\=}ZHJJJroa:/&z]d|/ipGdAۯ>PUQUCKm@IDATF<JG-/J儽(XW6ecYkI µڦqO,1g9-lOKTUsV^?,/ʧz4 D<$7#5` @fNEP" [Mtjd YjJVNxjDF}fPu\:s'Ο[>(4r94l7Uҭ21y5^ Gao[ -7u:>. \C=MmPy.0Ń/AK\MWt3q7A)Ǿ):n+:6moƛtouEݕ0jHttCKNw>dԮvBš m]_eJy `T~jb KӂhIL~)0XzJUaP`) ]wFraT,u8gZ|Rُ; gPE+ f;/ʧ=_"@x2|X %e=#Z!͜52v`vPj MStV=zJҨiLl3i:37|>\ȋ/;Q5r,]:JurS#|*V\lQy(;. FEg᯷s"y8 Zry   ;Ix]]Y&0XڹӺ)e3UґEϮAU؟ڝ?۔OJ5T fRw!CiB~< Rv8+/{P2'GBuČ^UMQ 3qTz[޿xNHxߎʺOP9JW@ ,dZLt DawLi'#Xq^)/4x-1l1-pKb8{#ݬ_)̫bl MeT`x]'N0]xc率M cIoӲULf; l8C)^m|jBA;D""##1Nxi}DUX ܺro ܨuUF0 ?|2|V-ͶQ}ل.X{1kt$Zr >NETFBɒN^=?}s(,E"ztH@!&FnK (Eq.Ӱ `]S^:v#v~d=alS7gR(8Z$%KBfQZJ0O @ Y0x,$7@HXnݏ>[R-*T.&D (D"@p`- ;Y?;mkұ!._"@ D ? g` D"@Nq?&@ _R D"--wA D"@ D"@ \ F!D"@e"yx##x(=;@"@ Dp`.T"@  hYzU9* >د /K@'@bJ  D"@-n"A D"@ D"@ T'H D"@ D"@ Dx2|{mH"@ D"@ D"@/dEt D"@ D"@ D++nE"@ D"@ D"@x 7/C D"@ D"@ ^A ^q(D"@ D"@ D"y$"@ D"@ D"@ E"@ D"@@a$-)KIIArrraL%"@(ㅧjl޹}D?]#ռ#Z ^rX|WXky D;T:;OFa~ :,GA j"C;+k4WibO<;GP>\c^:Gux uD04|)^}OyIy(;z[Lv:W{~= o8/eur%D0Uɍ5_ f9zpOKjGJ=mKY}T0a_S̉v#>S<^/y M@Yzj ͽi9!'ӟ^ U?`~Z*1 D"L^IxEA 1&0|PkX$BAUmR*ZCWcL.DVS:9a(@[%TirNCN{%}?sm )P/xu{h*6 Rp22n_:hG+7X|C[ IlyX~-"@ 6|F,fmahgj U%U|v>kvcF\ dFq(g4U1p=\׸I>,t8$L84^[i*TjjclMc^߹fYұuXcxvr^ gq\ZVLPFobBo&^:ĝ"P p{op(O$9Ra6|;t/Rooy^<7>T2y"@ Daނ 5؊FoCx0k -K -H[5;W:Xk -@vH{L=2Ë/.S\˿|+LB1cǿl8jЗF%QLe'NY+Pe;Rh>Jұ"P xjyjIUogkx!4*&DQko B,oţu)_xs)]@f\)u5CZ0#q6ӦFG`bzN=XEVN 9I`vˢg%D"@lp]c-Uh[Is|$lX}Ogk$?8-xWi1.0 Tkx͋/ lQ]U}F>rCZ*bsǼk.#766G_E_^:tD@a&+fuX|_.;0xҍi$2%P9fGEo.t5-RëgaŇw駻1LJi51hxF7d{y  WS%lt+>%}8fVLc˭}[<%z٥`iڽT40 D#{ v?>`95;9}q6aa\4_l70 hfU>/>t%,ѡ-x}p4'y\/#N\:~[7<)Q'G"@ %ΗxX7=ٿ2z7|{>8|r}>; GŰuJ:"XְɌq`nziN^:rqӿEL(cc2v~Ƽ0)x]֤9څW]3Kkc8%T6ӿW,{ foO7A0o=='d.hqɌsfSZ;+>_J [S\[/[2=7WU/|He,B v_Ct!bZY~ɛ=ϑwmrTf"9u9Vt]ngIG-NnQ9h΃ D H Jp8Lo%~pjlKVVZB*GS lGpa^jZrmL8MJ.X A{aC|_}~-:㥄gOx4Pg7Y/v9tcGGDfJxyi3#="_>æ)S0]ƁT4A_5bSHt.g^%뇙Z#$ڠUF 9o"70%CBXH(J!GIw)|a!<=.S wrس} $K}ucK*Zvu R1,RЯ8^>oaJan93-ѺVlJ VF*k%_>s{W-3kāQzcg#-[4*N٢wR\#Y'ޑamu˜TVm?W]NڨhIqMA"@@~P0w,Xx~ALkCU@(_ROchz> &9MVS.*ң#pJCKѯ _U*>h*dQG:"P zآHyjZ;k>5`~lAK'G#~Vk5{KgYN#xljC=ēfUMe1iqUR{ '|=.WMΞO<-l _}^1Ф$aP?j XY ByPrIk貆bl4P:`ԩA,73 NzRlVF>\|1^-ĵs/MiK⼷0FiKq\b뇛?FH~ D oa(rد౦/aMLԐ& ~5{W]v$VLl&?{ &6Xyxᅳ}08~,ƌxlOOz7/>f9i`kӺX@~ֺO}5V5Ab|-aQ$:4,Vq4UNh^ء|j D  (׼<bL[X=]k u"KZ/!h=htk}}Cvjt¿wrP\8J ް±gvLU͌ޓ[I!/g´뛰d# FJG \?Չw`X#r3⟱XxzK@'vyjKDl =M<̷_aUMa-W93uN?? Ռx iطeչ~-n.&ވ),zdy Kxz"]tXs*'jF/9ZL52UBNcbա%, LäNT|a8 'h!)CѦٖ-ϸu̦v&0k0\a"!hO#U"v\ ]^o CZp,_o@C(Tz OX"@xm>D3n bMc|oQPx"^p>lp=P >o^fB"F q@̴zҋ0vdNypgLcuyT9%4(#ԨW'–gZMULȁBK WS%\t؜Ɵe4q iCO<6joZוM"_bTHe tЊS |Nج հVBP70~i5fC!Gȉww|$RwaƒL薆=>esyҽ|!麧CK"Y\h.MI:=2 ;ZVi ^Q&Oa4-<f y&Q(EJꀪػ}zKI][޷TY|w`|$B nl!w:U%*^vخ0|.&BJZƉ߱AK?̙0 r6]99Jg!KrKfgeK=n@SOwe3dšc^:F(78pFpLxk7{M&^m2\d a#aw=-\S -ZjF!pp8_x9uy ^:V(Y!y\prIh_m'T1%3pL' an34͗jG\0 :c&}VX>*?XdW gQ\~xǏy@4OD"@\' Y0_-[0KFaw}Ve*R`YB;ի˥;L/lzul]8tdo`KϢ]y=v9ǖWҕ[4$8[[QKG)Ws?Fk ^F¨~MD yXMD^S^:;&)e5T{_UԈ+3y Q;aۍ;:ڦ?s i!,*)SkD[y 9$a~om yZѡ%'SڲRYG Eǵퟚ[JqsGGWOߐ\,=;!w p+z|Gv*4|\ R |!g9|ty۾;Bwt(IzvG=SϦ[KӂhIL~)0XzJUaP`) ]wFraT,u8gZ|Rُ; fB .GXyvl0+ܹeU~Q>5"@x2|X %e=Y!͜52v`vPj MStV=X/7@ʼ-j Θ^u&:^g܁ yc'S\G_Nn~xcOŊ#^#/%}gҥA(,VcnZ$AK8/8ұ"Dpa*1,Oy7H^Lh&/}ׯAU؟rxwXgGoS>i꺎*ռ_PET+ ;H3a o'wb=> 1u|l=qWp=_u_$=yz'?:ZT걪t Bģhj贔[G=?8 퇬:;0U[v k?eޥ<֦^2S>BKD"lwߕEBa>4Jvnּs&[hCVJڸRw6ΌE_mX M}  Ga+y3;IT-avRv KG5Դrli{K2k뉿Y7퍣~lm!/cB>(,x7xtG -i姼j?1l1-p+^hM’{qFYRWc`tUv/x>{C.WӲ2LjMDVfh&NecfL; kѡD3n<)X84J-xOZ?ҟy/7#6KZ=֩<Ū8a"Zx4]4ͽպYpd|RKԄv D!PEFFJc!Q=LT%A+gi0[ɍQZe . P'ۉ'~ilKKM肊OG!WS,[ۛѺn*, 4 cw=¢o\t,GDb B D%/~t܏ )GWủ""@ Dx 2|{˝x"@ D"@ D"@d悑D Do$kH^:ȈD6_PD"@(% g(UD"@#D -+Z^U H*KDž"P P*H D @[zmH"@ D"@ D"@/4 /C D"@ D"@ ^A ^q(D"@ D"@ D"y$"@ D"@ D"@ d@ D"@ D"@ D^͋$"@ D"@ D"@W ÷W D"@ D"@ D"@o^$I"@ D"@ D"@+bA D"@ D"P jy`}RRR\SIi"D"u ;xᩚ(Avv6w.`/3.`E5ODXPd,ܽ~vq5/,;tZQ(W6ӢNǵWpjK:pt<"P(z.QQ֗#"$$ $<啿x̫®a[r=^Twb+T<1#D2OSxST*yNW26 Sw5,7_U|??'S9lɆ D#t.72Clxpd9:Zm;Я/2Z3w&[``:m_i0Q;GP>\c^:Gux uD04|)^}OyIy(;z[Lv:W{~= o8/eur%D>]Fo>Esm:kugn%Ng ]h)ޙ_QplSy m&oŧ]raJþXW~x_^ػ pے]vi BS30f!n~5Tc"@ D@XƃbM`5x -ұHp) y7:N[;LƖ]Qю20a^rE:D]#\өg07P7F Ru1`:֬y8y;sQ-3zOOPt%yX+  !r0f&6z ůXU+c&<嘿x궏bOaY=\KOEzǀ,x_ޜb̷2z?})VAAy,mx[:qA(D"@Æܨ # -,Z GUO[ҵVBS1T9ϸp]@okrFcN]/P‡!ॣD/8{2nPa[4U}6OOy =ґk:1<;w셳8y.LCj-dz(W{aq6A!r?a\r2rp6'_ P?4He6W廙/;q*L] g;R本h>Jұ"P pz,3fF:5ƭմT+j0y(xOQ?H4zgxh؞ Q![t,v>FumcVy퓰x5j=5 /Ra};]kwхw$y8 $*01W#"@ 3CG۳ Xa̖0/6,>+䥰Ur_׬XftA9 UhIEAphs*!Ip/o_YD@"Z.U^IƮN-з([׏aʟ]rWi>hP[$Իr,'u)>"~婽ścLSrSw<3,;ط`)eNe-Xp7_CK$]t|<O}gL.?ƎXdwy(# @^C e `:|nK÷᳾'ۻwT [$#e иfz7Lfh'/yJ5QƸIe"ya!R(*O__CUX eXw)ӡBaOƃaրYl*߿+3çMqϷ~ܨ)%D'ԁśoZyk"h51M5>wgw,Ɓyy[~r|+#_mЎ~=o:x2Cl؍x},ޒW#f. vvY7&u׈Yte{лРmi6Tf")mvwf\V_߇%s@IDATxɟg"<@IOBں%GS-;ǺS,RЯ]s{W-3kāQzcg#-[4*N٢wR\OT: ks5.j.rZFEH[7Ey/f8"@򑀲9# T^V]1t1xYT#ӫ~J*b?aE Bl}]hQ6#D`=/=:?"z x80 ;}U4PM^eP;Fo:iGC^@QVe(;>`֓ '_C]:cI\opgG#廣Za#j jw8~6Fv25ǬOb̛Uך<:O7 I^ĊN4*kerWtn? Qa(#$'c\w$۝=yb[b II 6;=0/Сȝ~x_xKR݃пYnfSi?+sg!uy,{;Q:_ iW 3ya𭻬q<\ZۡNyn-=0.:r ^? a5?OEK MxJ;)a=DŽ- [k$D SSwߕ AmGbŤf2>譂h6@nxmN%뗺:OpZHt|c؏zuiԀiIvx8>UTвrR_fa"3=SVe\:]fFmǺ0.Ѯ-#Yp-_]v, MU;!5K!Yֹ\@{ ]"= I7 wY q9 ϸa Q.y#_aСe}^ëL7/;I?cƌA|l<6'M{'pN32)܎' lmel+[WC0jYS:5?uŴR5R|[T16 B}[= x/nMS'6v("@G_^MѠA4nXD[E?`?`C㋅Nt=_ @Sp`OXGxtxs :r@V3iT53zOjowF3aZ\y;o̞ص$.ܖz/͕1u/Q~x8>T9y)O9?ՠ1?zoI Y1׬n廕"\yubX䙩p:[2@Iþ-CwWiO0M3b?`cON/]vٻMʆ_'1?I+GUO DTq,SeM7w/zu2iغUIHɐMmjM8'KTI rd˿aTEBX-Q;tS.K15~WD !gZdꑡ] ڿanRs$S=""@|5|['9|:EqzXI.v/\_d@|SA,iRˣ$Dn^UǤcѝ~]#3m+ h&(CaG1bGBc/ αyL'bʔx{pox z^?oG#T«fY>0K$H;D<*\}/2Uyymx*cxK|[2k[~\)beJu^! =/ى2u]Jhڿ&.ŕ>3@-~=QW_"m;e-[g5K݅KN+HaϲO9/$et(I$}kKN#P:-Fmoz+$058~bmN`0E6U.LxI7&[me+HL9ʧڈ Daxow\! Jbt3oI=PWu&d?&2~6  *\Jh4jue#v7/;Qu3zwtWU%EwJSg"@ w߇P M@PAR^@)*HyQ"A)DIB fn]L%ZV,RR+%$'ljtڸ.[m<&JS=h˄l D _ x;ax)Y|оTiV/ᇏ9| ] H,b8}R%/v oT˽]W$Dx'Un{iHYu+I܋H\鰢G^(QCIoߎrWί'c@6h8YuܷZ}= ˗J,|D}d-Nx]s\N&w˅utʡeMp=U=ѯ)Z{C 6=%U|Yc;tVXS/|.I{àA>uG1ceKy^?*e-y(KOj+Ο!Vo{#8@B^޺iGytXٙ >n_TNmM!D#l*(m -ia/!:ƁS@9T'5]r5W}k HW9fԳ?h?jkK˗|>O%J#^%/9j]ɗC#A&鑼j -:m]%AD@%S=7iݫHßIϲ"6L̶]]ɤCPh5. ܱT5(Rw"Cm"g]zPdw{C 9vRuIOP80j H '9Bɠl{o~Lr|Y,OR<= Uv}#T=q oS߱oj E8^rIR[=-!^rykSl^zޗ0SB>(*x~yփ>?36n#ʩ  D$ 8@1\FR3@Kgph:y0{ɍh]c5lpq ?|2om︯lFRFtD\ދoLE"W鿰)_V;d]dT4}`';# D /RIqh\Jo_߿ØyJxyFℇ?`]Ks ߜ ժL@22nؓ,+Lm%B27nP75T6BoӰ&r"lQ~G;;Юym-^o,_Yאw3~B {5?WBB!W D*o7F-K*g/Vg(mTz"@ \z) D"@#TX {&h׭#xq.<\|=\7"@ σ%D"@@~0<^rG} CQS }09{UyQ%mwСv_g̑hixQNDQ= 5|Ջڥuk43ۮCs+e.D!mL~Ұ( &0{ 淁Z="@ Ī! &Kph)U֖łuգ־U1n;'wBe1xxsv<$KRKfoރe5@lYRx CL8_>Ayb)CKd!DȇaӮ"#f_-+W}ȔBmm \wc$\xS0'"*K+?]Tx'Kn?isJ/~O;%jϴnKpiޏDǩ&oNXm$~_|rulnKĽⒿ/cc4S[7rV0[G2"@ NpJ{FDE^Tz;ǻۮ9r3/Q8x %ɫF"nv)AAˆ35tcC^r$4iz5Woү2=?0"ͼ/:`3x$/ʰ>)mH:taf]k:O1yvY(5%*H@&{e:Y(/b'1f f]潝NUCY';n=[-B8 ok~?{~ì4<T5m_$꧄oŒ!K)CMSaLm=n,_,D($$T:2أD"@N)+Wy)[qI&\-"^ߋޒ+ D&;f~[cm{N'+["HSsLd5zlt#>t8HvHC _"/>HP¸ݍi &~ΦgsO!7ػ]W^rF@DAy}s‡C"pMAKЧiQO0p~w1n{ϭ>я\)?ܹz,O4U+3O2YȺs.na\xmЉf貏MWByzBq -FbqQ\I\;O8-r!F*_"zw\Ti+MnjfNjkTD'-_rVx[^DIAfm3{;?*ܑ@"@C)#~wv:e})_JO[ז8Zy US<ZuaJ/̼-cyn;mkނYnsGxљQwP ǎg'U#/9ɒ"Iaybˢ(%@C|ҿ3}u7`՝t7+•~6^-yQo/}j'ڛXvN`J*s_B8S. ])N3~ato}_>)r ,@QH̯/!Vk;ؒaM<)@|˲X%x؆?>l)D0#~S̮lF mߧx^J V땳 _?[9y'W>B&hV@WH˷~ethc&$›g0x"ln9-룲y%IƍF;Et'D&a.T#ULi CoL IazJ:>łڤ2Ε#Υl~  ]uWG{gļ%g^r\Ȏy(J):hrqWY#فқed&D#PحQNfeծ=37b'zEbX4LYIא5(f NL[?Oȷ6 ju7lcX<#Xw mbXT6+FJ=߬WTfl0E2cY\{sGrZtǀjwTg*<4FgEak_5I1y 'eZbͷ@`jZ$<Mk,+=z+u ,}y ! 4[**fbs9.M]jfGf?;q ha;Qbl|9_-R{zӶ% {i0? 6cUrk4@;CPڍ$4r*; D4Ye'>lp尻~f=*~:HO󶬈 zj`!f0S|#lFRz[x6wՓ#;|L^r\X `#Jy74{?/UJwK$ DSP.c[:z6p\Glx +,Y|,{6& FƸdr,Sc&RKB*_"s~fv|4ժ+  ٌٌxvnfNNpS%FR@1Le"!SHR.M+#MH޼yF{doDdWL{0g TN"@x Tm?1 `iKc0|؊sT v>l ( 72ZGg]*4-sKaG2aGb%QN}gQaqx^lSɊ a@I xXx=Ba-J]ݎMYܰ4[l,Fw : ݦ&JςAyHFoe;q8N'պJ;!k:39_} W ūyw+~Z:*\nۇlO/f$9 O;;1m)iؽs{GV Ygr|$ G)xz۸oÿ XE?Q_1&@Du7cNR~M낢S4Ht,!ۜ`d.G7U.l5InH]vx'79KHHS]D" 3jOʅ›ۇ'~2Rq{'?}6gtg9%ў 12OPI+>F V+Ч`|2'X!.S~r"a!(Slz%b[/7Y9Gݢj>Oo%Ÿt= i%~"S 9 D ȽX5:aD.VW-vl"#Dh~vFDٻtL~/1#^>yӦ1#ۻcO>W{6 嵯*VIף%) ^L!S"']܂]TYc#$QbET4 , + aaFJ[=đ8[7wh/o7"Na7"rҥ{Ӟn ^% 6x`3URU`V E*}(j*R*=q>DVA>cڲ V<ʅ *_V?Na~to6~1wpxe~E[Gxg;Jy>Hf68|##KV"#<4O@hTHKN^t=h,PJsbx7E#{ϰV抋ΗաyP:2;ן)2^8]^rJf 3ё9L7OS)LZ5<ʅZ}W@jO-dW[</9T\P^g:D*V-qk-N~=*[~''%-_lXE>eKs=T/|~nG% ` g_6bvmGfW8xuF9XbeL}@ޅ'o-6&Ge4?2w'.*N@"/D"1Qi_STųsFall 9kI;IwQ0}y"+$\WNXt[vLJ{\Wzc'~@֒ip_8KS'"@v"[{v$e+%*Rٌ=Ǎ%0s^>*Ĵ ?E9EfK9hE;فy(yQ$ddq;řsLh\mb ^™,>!بY^V鯜ry8g*px˗ BbJ٠A>*b4 :YGVmw yez;^Q˧-$H5>+We^?~;q)ŕ}\;vBNTNDD D}vEي"5zT)27|}9P(7hUVnxۃvrkV0gUl?SzrdyZ?2N,C!%9a5PF}4b ί_Ņt4.AوP^PsNz$C4 ]Éػ'G/@;|qѮb.peWNT76YB\ARyGAx>n<=M4XW6y#WfhР,O 6ᯃp0gQ<2]:ǢesmE; EmD̼!O î0wv\ ]{ }K뻑KE;^w;4r 1i\;jR~]E%U=c(gH.Ő|._ Ceϭ$Y))JfڇjzLOa{R.D/%I7j?oGԮ Cu~&UjN=!t1VVx7h?o+7M3_> }iM?؊XOiRvj،Nv!9UML,O8{f[|ޏۣk8W߳e׎V{2|nݰN Sﲕb&mBK"$|P`fOPTtbwhN^!U* ^5z>Q.ۧ5v=|8ȋ/9jvr._rtN'k>()zaw%_:@t᯽Y@IDAT0fG1wQ ECݾtML^EbO}Nz aJgx$$!(,, XGmSp_,oʨ@SI^i0E%{}(*ϻ~We *_$ ϓggaaKե,#V Y<'3I¼``նTJc0(OfP?Ƿ<Ƽ*yx Ya[Xxs=(ƏpW;Vw.(⩜ڠ% "@(@N y3LŮ+SoIPԒvnO7uvti`߂ah2G,t q,Ns|q(wuǭzv4oK2O|mJK_;F׷wJ EObϧej 7cq\ Ƭu]-b^ݕgn-;3/O͠yEabڽ8s-b_-bhT Ux okEJ 잖!ťbiKG>ɳfӲa.IraF9TybWoR5lwMlNd 牍g^q c/_x}c,S_k8nZLqim]7rqYTNm D MxxƩ"5BD(MKgph:y0{ɍh]c5lpq ?|2om︯lFRFtD\ދoLE"W鿰)_V;d]dT4}`';# D%-1tR.[cW0fzDyGIbا?̈́]Ks ߜ ժL@22nؓ,+Lm%B27nP75T6BoӰ&r",=Iڇy"AS*uR T-Ih׼6^7փYאw3~"ө̱vٖ%q T 3+3;=OT#CD"O \O D"@Ra-:y_C?  |2!"@ D$"@ K`<8x<%$=ToJ9"D"@ ŷ"@ D"@ D"@B\0"@ D :&odDi" PD"@(#f(WD"@CD -+z_MHvw%Ǎ)(|2H D @[z@ D"@ D"@ D^h^$I D"@ D"@ DxR|{@ D"@ D"@ D^H͋$!D"@ D"@ D @o(D"@ D"@ D")y$9D"@ D"@ D"H?%"@ D"@ D"@x 7/$"@ D"@ D"@+RA D"@ D"P Et1gHNN.<"@:^ux.ꐝ m$yœx]TҌ5WSpt,XÃeۮQ/ʗ hq%ܷkw: ^r<"P$xP+=22ϳqЦyJۉ!ik|]N tLyr4܆|$hv, rx}ST)e[þIn'HϔUkV/_e0&w˚ ="@pD@npA-7|0hpR4Oͫ_mE:-tn]Ǫ6 >hQZpi:#Xu9[ط֨Ҧj웇t\/KpW-lC௵>gGNKt%D=UJ/9EglA ?ޟ^S0Qxس}}fuuL- ޏ°xahxm"^ ?`~H~%3 D"NQMɠMajRkmY, YD]=jWEIQqr'Tv ;cNr/9J.sO |-g`˚snjgJo L@^rl%  E!Wzx);1|xbǘvYBJ:NGޮ,E:'$#i9R#ysmo^JDPP `9{;KRdo )cI4 D"CiwnD`65L#,Vk!7|ګf4ޫ'wfJo3r; ӷ<feA[go1Tg>,r^r͇po^ĹpEHӠJSh^D<}^ ƴ) KbÞ3+[ AZ5P:-:48C>(|y ̓$qGȗ,WD!H/.26FR*SpkFs RKH"@ DiN){܈ؗLTz;ǻۮ9r3/Q8x %ɫF"nv)AAˆ91 &nS%? %4\7bWb͞Byf?j CpⓂ?~ۆ+0yVRv=:s<U:.9"!DsQNݮ=ώJ}kJ"@ D 8AIm"}Ox:vVLn(#yN3ٓ9N ! 1v^߼#1C v7š6;^rϱ?? ܼcv]yɱ9"P =t>u'{ϭ>я\)?ܹzL0?iֵ7"Vbgd2ٙȺs.6q@^ONp4U qh ԛu^o>P¾yul\/W׎mIJN\y*ʗHUNty;P!N[ʥ^DIAfm3{K\r %C <8|ۏ“$u9pֵaa|??1aJT@.1L]酛^re,AmmM[z1mN/:#N qQl;j%GU8Y"P$ =t>u{鱕Kd[>]xwD,;d08US;cfCyQpd]n}Lۊ{PD*ev֨򝕁`#/ޯN_4A' AQ\5@Huf CX+ؙ.ᬬG D|X JoSt~Aftם!-X}/ZawrˋAKD C@$L|R,`j+SdݾsGv9Y=YiY*GWUTBq/5kyi;M׷/h '–ngc-]5~>d80CwSg8kټX˵#WM7)ٖ"r?3=3K㹷b,V-#VJ >c#70CBhH( C^&{g]1Ps`S>uz W U&'Rw}($f X`K:Z5\V`+_TD tϓ@~̐كx^J vexr65bvuSz+'dq=T ( i֏N74c8#uBǒoޞwd:c\V̝m Ӳ>*WdܸnzP$Aw"@(hN/<[V0tߏTƭ4h#X,xoMj_ <m#¥l~  ]uWG{gļ%g^r\Ȏy(J):hrqW 'Jor3i^yҫVi:kApnaƺSn;'Ǣy*:^1ZS!fa ,#9-c@;k0F9aw9Þ0jm,x'zEbX4|S~ov O!qDˊi&.uފ?D+Ke_ABCd9QBUvf9Y;~vʩfÒwXXZrHKcZHk./mK4Iq &1fG  hv\lfBj7 FʩHD"P\w6•ŸGĪ騣MeEdS#l| 1YG'aJo Dzks_=yL;1.%=wO#B^[^5t̼H@F#GM/9j20WPz^߉{Q-2 ~Z!Frj+e#9rɽI۝eT) fW&zX)LTj|a¥Fa!w?%R=h|)T3 B[MxVPҼ*'~ *8"@ @* hҤ?bQreCVvM_V @q`/Xgpͳ{ R#R=O-x)Ƿ`wE*/9i7~5Vn؂-6wgv;`xs kaG(Q݉x=>X%Z.0>l;ӓT}[㩜8q}g3ߟLbA\&hD 45+hg6/_{ȸglJmA\0nkdul5&c6$O˅(S9TDa9ͯz]lF||<;jR3NNpS%p7np\{921Le"!́) qD xZ Ių`_peD iݛ1#uL품l1\iof3sʩC @*瓜>"l:m`,ѴδVl@L9EGMJXXfۛ] ,=S6Sena)HV;Q 8)7ݘ5nF7z۳Mkr\Pa@I xXSZs^r޳tR4븣%R(<շpU!P w{⧥30,6zmDa&-J>ǿûכGpÖլo;;1m), |bʳr!KL/d1/ Dy +\^)F$Y8 x i'"(Gy޼8HNǷLmŬ-7Kyq)RL@!CS#ӻ&7+픘LyOx=)f 2hQS=|>%z8֢ɓG3.FEV4BXTͥ}6gYeE@ri#T/ Qz?=R2 :Ŧ' kxiBغDykw:+G([AmswImח!9-P62ܸdOr*!' D0Q| 2Y(uF#BVEe6͢x\_dd4h-]NKUY:&pחRI'u3o4f$v ך~-u!ƓK1S"@@|]Oy?64sMMPۢ];79?~M{]g2x* h\?㡚,aJ+[skWIa#<- \\1C˵.rB=@xxI*$-mI'C.mQټw#%l irhYgPSU,dI L@ sDΊw3ⓜdhZ72k/2;"`#H%HKN^t=P|Pj[yq+r D(3)sx9␮wxg 3ё9L7^S)LZ57 gXrκay(>JpK'g#Vp8CuZmu1QA[&dC J K9 ̧JzY ?|H`WPEbqGĩ*yc%ǎ|wZ5J'%D; ЙWzx)_ml(!]Boߎ宜_OƀnmДq% oլo϶gLnYu+I܋H\<'n]9TIzsASgA /¡7@h(s["QEO?}I׍a-P1}'59w Ƅ㒻orj.5ۏB4/BN <xPﴯW*9\6Xlv$I>} Ƽ#o|儏Ey_[=w yɱ|ٓcm?kS X{qꙗ"#ODx5ZdމWzxɱH?iBfAK, 8(: Qtgq;řs@ pdg;q^ʭz=z|zWK#",ް>* |˗ l"{Nԃ SS;F[VOnN[PHj|V~ ꓳw S+݁v쨅<扈<"@nۑ8,RWLAs`K}Ve*R`IA;k+E[Nol{*6͟)=92|<~b ;sݡ| !ǝ|Vy2 $^;c|ͧ];[M.%X>ΟvF xɱr D=UrK"?~pM5 GUX- ?a7Mai-+#(-__ r=d(=z5~㴬*3ؠЌ Y^b`wbX;vIP9T$̫2z]JkʳߠA> b㛍A@aӊ,D؛"u:J]'YʩVz D&xz91gdmt;1FZ#2tXNNX ԩQBWq9$;K7|P6qv0e=W aA3$ '+app"I 7hܹ?z \S3ͦbWPdkxq9#K KWLsq7ARz &cDih ޼BT34hP 'bCH8(.c2\e`ҹ6b^rНOW}+=oI6T -[[jDq Dx sCn)pK||+~T >RdWKWQgFrf0!O î0wv\ ]{ K ﻑSD+ Aw&6V޹w d eҦP2$G^M)kBo(KxV˫B,EAB!FvIcaܙ?Ͻs{{.\Ը~;[(un ,@`ʅ6aҒ>eSYA̞h?s_MvTV yb[?XW)UҚ,q%MWՃ2q`a)uv'p'/~/.* 5^ fdGބZi\nS~//q%,>xIjorcB'wv5YDx a)K|eB&H^<X˒% : R5f$&y6W]M;Oo/ \˗3$ /yv#b+ N=6Tu,7Eܪ)/6i);} txuB WD8 9ǰ3aЌbNwp4_:ᦈg9uCK   |$y&'pk=^3;w"mhjI,IH@vNa뜡h|oޜLI?`ozn>5|qh@u)Vfdj[B",9 4_.}y|e;%A$PXʪOeG|Rz̠E䙻t Iw׋Zʯ? u}~ֿ9XGr~%ňy+mo(]23^.d;m *.32FL_ʅk=/rXȅ]TV=*RYU@+G(HG'BƘO_gφ3صl4Zyn2[vniB?SC;~YN(xC$@$P |H@BnxٝHZKB9>}<%''u׺JI0FG#ݳ6I>'UGÐN@"Wzg\8]pO\4#`1)G}z!KKh$ _ȨO}zsoČ_eٵ 18Q #<UEͣ8IF_:|♃D,Irw KrZb[h\G!K# @ NB(d]P\б\ACIWfm֫`Wӱ:iOATSoc ʑ>TNe<վ)7 }6X5~1 [/+Ҝ/rM$@$@$p0[MtN|?|:7j͂>E33p: 9O&S ,G.#us/v W*+_ࣗ.A|\3"K\_ mۡAJ(mk: 難uem1곩P,_G͔axm!dїN[ BJ 0k(-iӶ̉}%D$N`n5W\q>r<!5ͣ,=7h_f.ouՆ߁ޱnV4=xQ/,-YR\2iAȈ[>մnW_iXJ# ] ,݉T?~;JHHH@X7D5kR*_W" Zt@գc-c`y;X U(.VjA~';9w> `h#%G+ӯ{~dރe%@GY˒WxΩZ{"by(S>/.Pwɴ!({WrMeZ)R#jP1hOtzLŷ?щV7cDJg6f]vGTI`8vd/A)"Sm?r)`rZ&8s8SoJ^la4%:ayO$@$@$OgwNB/L5m#,h6#q*s/M[ZBvf2mdž=p-&rc :CGB sYr4^=G'q.̀]u+D$Ϩ)Yrɔ1\<cc8y6 kݭ8Z\vם%KN0yaX 뗀ECg%8-yGύ,> nwc[*lH;e5>42p޽{t%ct ( zճݪTWP{1N;G9v jqʗ¥{0oww VN.=̓kB5d^O5[qƙ@^";E4 cbCj*Yc+CNn\ b 8]Zz<Μ$ͬ^8w%t[.+N2~07En<󥴰xm* %*׮]ġ_fKig$@$pIYH /s"^|׽8~N{jVi:Ri+?hHGiCPV hS˦V^=y<۝eɱ n55"=>{+KpZ J~-Yfb-6_ŗڢ["Y453V]:== u+"r"`<O?~O.){j lC:cjCeѦ߄Pú1o]CuxNHޗrؠv$Sv,[&-˨im*g 2B8evñǁJo186wėuz%L>}:ᓧ:bq3F}59 |y%DG;4MYf{Rr.(㰹Х!*W3c/vK믲]GnA$;u dfcv()VE{-{e̵a)1w;4\z -HHBL'K_]ڶ-E/T*lNV~zs(5rno';78IlȒt| l'c߻2Ys&,9?.3m*cRwcf /dH #Ҝ{qsK!tn|CV8ìwŧgW2(fYa*U|F)ʉGQhc4Vuw؅Y]43_HwljA8}, 7`l@Q~ M6r͗=-]D$}Q -5/4ulb4_A%KQ9{K|wg,]7Q==3u3=+fq,9VaHF&Pf_i^Q(?MeG]ي=b:/{t!M=~#Ԫ 6RD_KOX1x0w NdQ\kBwRgS4˖epSlr"ԕT>d{\( .e}'A[JA*E951k<г}o=~~cbLG#G*h{teȃM]p/T\w8P'ή8:ʷM1ۅMy}RDZˊm&)u3ܻ: L}e 9,_z{ rlN io֪󒙙iփ@M1(>y^;U>= گVZ'sr۶%s`bm0,M(8@IDAT*m5EkG;KwVs0H=5S; 7) B̴Ql_1Xf8:6- EU{K?A3ĬC䁆<3c=Ǔ :[,9~Gl`Q#q'^j@%!7$@7@o+V=ٯ!Ö!1Ayj\!Pj+3|a;(avo}kއb6GǣM7*]MvWnXNp@$@$[!J'aÆhҤ?s¶?YGɵfm꡵> I7u_KI7wvшwASzNYݥjJ1#Ut컹hZ]B;+a~;^P.X˓%.$@7@*nD9e*$vo(ӣ>kjnE Ttt5`[=[Q.[m5s R.Xg+v5lw7G_vf;18mP|x,45+T]F71c2.36/f]Uiܷ)֍zQgrc m"[.2e'yQ9AnXf8j3R3RNq%鬤jWQ4Cu*ReحT# ': JUA 1!]urgt!Gv 4l5\#(R3GS+"C$@$PU횿$,:?-C M`dm{%|7b!IV"JǨ`լ8WSene)0;[,x'7/ѣ`SxyvQJs*3m#YryC$p{hn6;Vt <0]'tmOqf{ӞP4s&yH)~ٛmz'ݺ`8KYe"989?^bUqWs:|d Mlqo)627@?ÿ;wR ZnַP@u={-%aNXsХoӰw+j$aRI^?waTNC bqDCO|׵#6o'45p}6h).7g۫\:%3eow\1۷hII]qc9U(" (vʰxGtgԥ@ITiM1w!6:YDSvLdǨ&^4#ک(JZ{,9^6cEubf_e+Rz&n=bWqLzŔh%˗cy[-jx,]ju_kL}7cK9GɢΈrDH:=uFkD$ [ڂm{Q55{)ɿʡwW{ŁLK'uϺ5f~e$_-iTyQȒ A$d~sjJP)OYW TVW(KF9vUnʞ]8,SVz ƘyhX9dȉJnMge:QTDñ}| \N9o ^%Q{O86ʴX؇HӶY}ԴmHIՇ1P%*ל?c۲e %FrX{v; ipH/dUHbQ^\tMHT;n~mRʣ;!.w9\b% @ G@:#HjָUsFF}ZX2`KMN>d(nLéΨ oW,9yCw @A<4;L3~ PO7e^Ɠm|s;A嬒C 1l~fߖkCd!.( A"Dw}<Сfu;>tr1vVA[=|*6r!K˗}\"˜G9<+e$c+%$>OhƁmĈ*:hH3Ʒڶ́v)ݙІHH MCš]ΨsOy/x}2tĐ/;\ۯDOjϿ$q9yy%ǟ8H < =Il!|w%3`̲RͶ XĩXjױq$Sj~{%qk c tЏ8E"nrR"8Ōo[ān8(Adnnv["׳^m AWBnHLPf*; 9'Zm|x߽tu닄!8} 6Љ?F/"ᗮrs5ar%p+AEn*Cke:rg_o$"I?Yr?Xt:c@f@2>={fʤS=L-2ppb9 M^Qz z}-={{XuRvv3>*-4-sqW^w+;~$:$= f+e|yj߯nN[˒>I?RbTf/yf*[#ާh\VE9IZmo󩃾og8%.x^ րO 8.C-K[Ĭ Va甍mlSUhq w䇹r!;p*SZDaLA苧ч` 3ڔ{K{:0*!˗u9-Ncլ)>/?047^I 9+MPg`<%ѫ,9z[y}6zֶ)x6zk6.7oFiG$P8 {,1u'btt e6#Š*>v1>zNo5Qz^c/-Tk է`4ߒzoa2*鐺If2*x_UkІw#םrMqReMȃ \hF˗d߇*o _fW]YEYE>*aFbw:O uǖc ҲŬo:}Pʽ s[}E8~vXNK$@$P)^y^}~%Z2LL(v$-5bʶ}*hd1 g.ͮVujÝBqQ;z@;uQ, +X ͋cjgNlټ{NC8|qxݭ}ln65ĕb+s >J|(Wj{}7ER [߰ "X Neɱh*6jˣ f5}đtktw.?)%Ǟ ~  <}  J9Z.VcSkuK(@r:}_Ǐ?Fsz^Јp \z* v2&Dۙ{ko(6.~UpoV엲gVZ\mo{+~YU]+~Y_k/ΣHxW4,gW]5aH綖k LЦ;9,_Zǽr~Sx,3סU$1oGnŐ'S,]clMϫiF$S hUC g64OMgV{}tuVwآMO;kdS+D\8ViHH .W*Jkfb'ƕp6]Uʰ;Ɓ)<*UڝįzUTx5yj5zqqMaڿ?CO#KNy-_rLTN&h>/a",eѓOL(z„ɋG`CkYrDA' BJ Ci?(M>M.St9xN1\r[+Nh fBQ&{y^.L[%1[UG,E‹r՛X1 ~] e&a5"bPaK đ>x#aney{˅sLz0*o*8yv#b+ N=6Tu,7Eܪ)/6Y >:ٹk!Z^bH+"|nc0hƏ_;Vt 8/pSij @>iJ yٹq8hCSKza9_OJD<˘u [ E{fJ{ӣ 'w;1ࣈ%G4K-5Q,9<)kվż/bx=,9y>H P}]d9Vg._qu5ۗ0+t]?Z@5ܵ=zb0 |(y.݂gҝ^K5Tzo;b]0zt,wa^b[X;tK7,=EX57zhwMZfëم;m *.2Ubn|)N<|^[(7&SjȌtx#61wbXM2deѲ;#)ꖦ9)eڲϫ߸nXNݐЂHH  5NqBnxٝHZKB9>}<%''u׺JI0FG#ݳ6I>'UGÐN@"Wzg\8D]pO\4#`1)G}z!KKh$ 3~e2ļg'D(8KlNVy6ʒ$Y]z6G|(ŜT.gb$y), i!o%q(!xI?\3%KF;Bj\(fkz2 yj|~'\<+gN[zԝ,9~%ދj_A(+"::2ceqL J@P\J펨{`8v`Kt#}:۫1"Sm?r SNC].UzEKU)W gpSz_=Z`*^EۙlVf KuH) \g|V|$dm]6",ϲMa3(cל+k'6)ɋ!q󣘳a *#涎8QQw FYrMnhCNNs[c۞oCF̼9]@F%GJcطk!-EخG4BؚəEo}bQj $KK h$(d}eڵŝvwv:OkWl(|zD( ~חFO(@c{5м0 (dP xp $΂vVAqKl (=} IPρ 63>$l9Iz&E%pfJ_6`˅]xrX$%YNCY.j -x"sUR6]nB kq$ټ?0aBJH$@$@>IYH /s§8[rӧ>y#&ﮘ{+׽=#mԷ^!$ײW-#g} K3ߡYǗ*փnHhA$@$b>).x[%'$d=m[E^ƩU؜.U(526o';78IlȒt| ;FNƾ7l*Ws&,9?.3G=Rwcf /dH YC?DGE}ޖ/Eסε慦boQ[gh p~Hu GY2(fYa*U|8ʉGQhc4Ƀ,9vcDj"[,ŸA8qWL?xanբs˄Ȍ)>C(7 ;<:%DQzr^?m ,) m&V|\wa-*#Ҝts+_EQDk`ek(mYjtƢOl3ҕ)ge]]t&Lѯ{buIXst<)-QEr^U|/rthJ^ٹGa-9QTȪFewr3HH򇀪IΟjZٍo#)>V''+-րPBeJ\i|4KmۓT_*K1_Ebj0H%LJ%O3 ݈rE߁[(`:8WA0C$p#=K/{-+bj<I =RQ;˩qJ*# ~S"Ԫ 6RĞ;=b43K>ܥ/8Z%GIz ]H=t{ENX.[7MQȉPWRmzU}lWr{Zf0'>ؒm`uRIfF|E|ف+Pþcէ-LxkJ;j{kT\-it;]>LJo };~9zk7b*_X>:Xi2ԱnsVwj~{%GSk̘f ݲ$8w.z  $@$@H@_Ü G[V0dWxqoˏpቪC+-H|;漰.r7vnm~8<& C>$:c< ھ0oY)V@J17}Z(y(%{ a rb'obLi~ޱ=y+/^`0фh&D]܋yS^ef$V*̒wgkYkڸeyY5㘟^ܑЯ%ٵB ﳗN^5o6IaKMi-+p: **2uQ.`2|9^].,C0<+NcmYe>.C߬U3ufFzYNFb55ƬyQhV4wpKkZIkGDϱmۖlqm0,M(nV^f}`XRjGrj'_  &}Dd T\WӿbpsDZx$qtm>M[׋"20L'1Ĩo VH X>u.mðΝ,9~Gl`Q#q'^j@%!7$@7Y`䬛0EOmŐ>`wt4 0!=JoE\ڷϵy2|ں1ɐSZor>uXqʹ}՘ԟ0hDm?";nNcѨH2}=Ǽ` [tؑ圏{9/[#zP=w@LP5GǣM7*]MvWnXNp@$@${!J'aÆhҤ?s¶?YGɵfm꡵> I7u_KIn,1ވwASzNYݥjJ1#UthZ]B;+a~;^P.X˓%.$@7Y`(}e]W*t®f ~$8H{eUӵ]x`"[Y::V]mrN(Fo3Fvf dA'#۫fl^0ͺҸo;R2}3~qʱk6`˅]frX$Wbw7PIDul-N ͫfqnffg̗Ҭv/Ig%s>jWQ4Cu*ReحT# ':uGbBZ6#Ctlh)kFQ%Vg+,VDHH5IX4u Z4UśלYo}sQ+bj*t8qQNBh@0T̤_RaBwؑݏޯ,9zN2^=o1 쁮6E`! 11V8%!7$@7 YàYs%8ճ219v~߽ ͌m;UY\W*:əzH>O'chbS+o&9k{5L{P'KirH?WpB)89,_*pQ.FbxNJfv:MY̶N`1d:uW~OA}{>d]JCճUlܲ;Um0hII]qc9U(" (vʰxGtgԆ PU;yS +tŝ/ j1=zb21jk4+bD;PER~%kbtܿv"yl.Q̬^K,9~EJ$@ Y` :ǪEZ^c읚V@f&ի3~_d,̷3y+GYqT3\IX އ/~9!z%/8;c{kQRmvkVu@`˅]|rX$`Ű"+Ds,YsUj]\KҥV+g ]N(ɗIq{:0l]by7uVfh sG\~{oW1O@QM&KN^t1|7[C'um~`sTwAؗyѭ͎vKdzY߫ a7?7(+gYY,@˅+@|Obe 'ULge Хm;̈S1Ԯc:Y"wjCke<7~7+^nNgbwY^ (RT [>گ^}ճG) <"}XN}D/$@$@Ao_KTo5լe }Tz?[4isNMUѢEnq`xC.TV%Ǎ ˗pȳ\ɛ[W*Jl?eez`c8aյ {K_/ux珟S2h+}a1u<=SΥZUg~ipX(ŵUٻIN< @T2O֯L"sa5/ ʩ_vrj֊vj3ߋ9bjk~Ǜwn$L@S&3sg^H׭>z=kg鉴5zk.7oI @# {(K3Hug\Ӱ}PC_jFU'2w' l3R "9؂=j_4=[ J$r{#k> U{U/aLЦ79,_ZH9-"tOj=-h**,2\ kn6+ӱHY\'l9&[(A< Cb_QmKrjEHH )ϫԯksJũIKXcJ1#9dgW:5NP8q(=E\`ʺٓH}CűT3ؿk'lމ='!MѸs_T67֚YJ\ Ky >J|(Wj{}7ER [߰ "X Neɱh*6jˣ f5}đtktw.?8,9vy%(d}eYW8ȳRjW.,^GʍhXީLvaքubZȬexk$;E+ߊʱjEw-4#R2:\<5N߃c'pϿr"+ƣ^*4".>^ ٕi EN(ګ+Hs0B`|iI^F9URXEBi&YD;*u+<1ܭRda,+eSvglW^N7"9ՌJM@ϬN8i>|2nچ?ڣcҿ[^?<* Mq[rj_  \((WtU=(`\WkjwW}WRR`FvMA6Ih'; ^#K:/g9&D}rg\E4z'_&K@_=aΌ#0`!,9^ @!% {e֌a5|#bPaxa;ߵI7!6v9xN1\r[+Nh fB[!>FWN'ڦ7 .˙d(M2ʩ5}A%Gd[izDlidž*hV}ʭlّРhn.\"i9 f;pcEaK',nhiA$@$)[^"5gFxqsfvE;+Ԓn!lD$N Jp{YuPa7oNn7=prN >8Yr4IC䲔]+Yeq3|e>_ =%' @a! {(KuWkNܐ~&F_@{̡_Of/rL[`-8|&i_/k)bƓ R/o'#}vh{Ck|1ef8ج;m ar#-1zKp gE˗*Q.űBC]Gf#=zM&о1nٳ! v-}qv#)ꖦ9)eڲϫmЎ_S ސ C||)жjv'VARc`cǺ2u0OIhA]nAR`2шvGg4{IFuD0l^/'%YlS&*.XL&D)aQsCȒ<IH <[hwWm+,V2 yj|y&IFg$>x7MYv-.A{v2NT2118#;dUg,9NEѥgs׬2,8n@rx v~?K`gmdB^զ ZeA WA>*^.ab˒J8WV{3GփAc`   $2 F@V$޺۳%M; ܈Xnħ< @(# zWȒ|J( L#   T|˓`:HH?@IDATHHHHHHHH[ F !  G%'1M$PX 0~   (# g+   @Zf"lv)%KNQ3 z,_3$@$@$@$xeX<&HHHHHHHHHH@nu"$ *10$@$@$@$@$@$@$@$@$@$@P-$ *10$@$@$@$@$@$@$@$@$@$@P-$ *10$@$@$@$@$@$@$@$@$@$@P-$ *10$@$@$@$@$@$@$@$@$@$@P-$ *10$@$@$@$@$@$@$@$@$@$@P-$ *10$@$@$@$@$@$@$@$@$@$@P-$ w'QMB8B0GQDD" P xQ[(*"*U$ xpddgv7N`Ѱs>gvvgy@@@@$(     ]$$     #H|;b7P@@@@@H|%I@@@@@Gvn     v K8      @!@@@@@ m$q@@@@@!@B     %@.I     8Bķ#v@@@@@Kķ]A@@@@poG      `o$     ߎ @@@@@.vI@@@@@$(     ]$$     #H|;b7P@@@@@H|%I@@@@@Gvn     v K8      @!@@@@@ m$q@@@@@!@B     %@.I     8Bķ#v@@@@@Kķ]A@@@@poG      `o$     ߎ @@@@@.vI@@@@@Ɏ(@.}ZvcoJq:Z'W50 @@@@Gvnp^!FLa]TTT6љ٦F3֧85DF4UTZ88;ی  pgBMWFU+ţZgdԎ3վIř7է/V3S69o_xV-o& Upk~evn^%KWFFvqc֖ B#͝BRDһt=i-iZ^]4#@MD@iERA𢻻X  8Xķ)\2U\KRR"^WA鴤TOsۋڶ YTMQiCқKL^tp=]ME^V.ez+TNe'sưkMv]le^ՌzuFˬ &ZHlL)g降5e\ݱ YH5H;;jоcS@@j@o6 MҲt\=UwZޡq푂)Bj$Z9 nyStB&{weK$#5g5=vmlw^~[kCֳ~sq;3h.t[~I:{_5uH8zI|Q: 5OS@@ՖE-15%j}J@U]q!cPeIoi{OE.rZ*lCSO'qQv)  p _p{%M7tU=̿U`3Kaئ%[eY6saO9xNiԖ[n|wW|lԻy֯+^1g+Jٓ]q*y/[7Rv[ Pۚv~n{ܐ,yG1M*yXE#u;Trg9UZqJgڵ6A`nmdT\1\?k33Þc `΅\fgYaD @@wE==[OR͊\7ZK9Fam77iu̓v, Lwy[>;(#¬cߧ뚇XI=qlPku5 sidK\>Vq=QYQuŒ]EE*,,4i`Pm_XSקBGOx۰"Ar.6_bbW8pcygbr7n"^@_ik7̌IkzG4iѼ"cիma߯Ф)S(Rӓ4-KٱnG$y?j7OaAcԼɿ%uʬڮ8c|~t.堿zv\5KӮ;使uɲ'/W;z暋4k䅚l=I.r~^ )aŝwgOXK;pޮ^1Xdxf@ Pzb&&pv5-+$,]r.9mJ084g}y=#_0Gyz4]٠Nޤû뇜oC ul̻9.hG+_Vҭ&TL;ejvlx9pM3M֥Nso54Txzs{A3ui)ӿgې4{Q+=DY){>diz/"bAW՚;{(X1wI.yWFn\V4vێZq8,I:@@h=-I"?} c44![GmMիVͭ=կ=J:A)=bKs74~VmZ_R.uJBXIvN+lK k;|U6LDO>7\.!+xEMo\E땱55l'tdEuHqoG]+?,yOnx6(Jnۄf/1^+ˮ8>f4 lhԸ#>>2ٚodߥO֥q1(P?=_D;.h}aH ߦz6r y^Nn06nvGt'+KKVӖkķq4mb[.ӻ63?%953c'kڮus^;C-Ϸٮӊ4>uDžڎ8_1v@@/CYݤ^meeߜ%oi==j˺M([TYؐ&Szρġޝ~6o_[e:6ƅޥ3%~Xi;k$z\&/+  @@E22ͼ(IՔjx{W8+v۸չyh]`<3֭?)';j`Uӣu \5zv*8 {*ߣnbp&jF-s%F,ժn"%t^@Szw$]Ud5מһG|F>ǾWE"{2!]cxe H,Ο_Zz]hduۨyȚ_^5WS+:k'{>Д6s,Aiz("@@ \$ݞO5cQ+yA9maoFڵ[n .?uru gke`JUԲ=X-wP,J//rj$ra8V.]rWị04jwGoG lXqX]ڲ)lD`>z\5X #{c5GuZ< np-ZKz译g0pquevY6NrvK  )pH[pUixLW?v{%Z d ˧(/)#t s7WT+P5C[Hgk{5wԺaڭKprj;wa胞W @Uvĩ4o+\`Z%hբihMw 8FS;8.lGx  @u T/%uP%3|ŏRf˒!g;,!a|lW<; 9~Be{ulБ\Y۔_lwZozh\D?  8hҒ%]恒^~@{%/>]ͭ-տ9OcBj_ݻ/f#:M;T̚_;^__]6kժw\>5fylg5aD p|%K  8CR:=.$[Eo V^}Þ27>MP4X`CWnWi4ʺ6hgAqxe |Dku[JbY{oV|O .~6unnVN]?Wת̉ʺN:^Vm k-= 0w8vW\Up|EJ2 _ l?*zo%=J-T7Sշ&WtWr8u z[!Kgެ/We>J~/%UsPj]=C\u5FWkFOV컍l}2ͺ;yr2oQ4C)&rmW2j~=9A 6:22pqa]Dž]qlWF  G@ȯoƂДG6xDL^E<熇ˆvL)Wt-^ldq??[cLɯoh|2g>cMoj%qn ShXzkݓ9t*Z"M ͹"9TnϘ򆶄ϗ -=DUUpi@P ,zaǺ'ޜBṓuUw˾.u5=>G_m-jaѾBRRU&ozϼJc}k^q)< Nt'҅%3Q:穥X]/KJMQckĺ{:$NWhVOD5S fJfX5#@/hG&R: t4kO38^0:&z~yڴq6|U۶:[o_޸l &RS0hjLߞg9ROWfb.y))ggw5j.^A~J.砨sBj7lQ_?5YlIk_`LSXPCC"0g ֫wn{ 'tuY f{MVFc_?k\lzמ;YR3E_X$Sn55k;QEP+;>;.xՏ` d@@ \BY␇ԅ/}^7WA l+ Qa;ެJYzFa6.;iaۧZ)-)KW<6-h`G}OҸZYy+jσ|+ʺ=?4H75 Bh!Q`} 6[}T΅wUXr{Ƿ+Uuz-j諝{O}펲+a 8_NQ~90~\N!ߞ|)QNH΃boD9MdJAZpq[<.#šJ ސsܰW丈d؊ 7c@@ \>9:-SRrծ-f -Y_V;÷?YG70j]ںs-4a7gد *lawY3^=Ѳ)Ή:Yw0\zvVyڹ/MRͭ_kWj{3b p t:z]=24iۦϕ=Aͯq"|7LKq1ӴYZvjו>Eukv ̐{+1jXk>]jm9vּ3EW} [!p@,YtQƵUD@HPF%`q@8BV^wZK\-l(⍀  p0*K`u   xfW7_5rQ  #H|;b7P@@@@@H|%I@p]+(rN  @ SkI  Pr 1*rIe^ fjWl   n@!@@@@@$     ߎ @@@@@.vI@@@@@$(     ]$$     #H|;b7P@@@@@H|%I@@@@@Gvn     v K8      @!@@@@@ m$q@@@@@!@B     %@.I     8Bķ#v@@@@@Kķ]A@@@@poG      `o$     ߎ @@@@@.vI@@@@@$(     ]$$     #H|;b7P@@@@@H|%I@@@@@Gvn     v K8      @!@@@@@ m$q@@@@@!@B     %@.I     8Bķ#v@@@@@Kķ]A@@@@poG      `o$     ߎ @@@@@.vI@@@@@$(     ]$$     #H|;b7P@@@@@H|%I@@@@@Gvn     v K8      @!@@@@@ m$q@@@@@!@B     %@.I     8B !ާ5(k._jQөwo%:nB@@@@*6I+ V׎U7+gk$\(KSٽo X;E<矪6Mj) **Raf-w&椕zFT׌"\}>|r  @M?ljzf KvmF?)5u. ?6LO5= UWNդ(%es\<#K  $ 5-Z){l*jAZ5tVs)ڵ63̏GF{n%Q'؛&U=%ݙ\`]R)sҔ499Mޟo7ѕy$Ms-}i\zcD 8.]1͛s8A]y:%  3QVr +4nHzIk^WMӢF oEUy^}b53sVkUֶkgBY%I*.Ҿ`X"qa<]&}`v{Ҥ?N6\Gpe{|}:nMY5Ww,nm6,iJL E^Xʾ]W詜Mv}cɮҮ8_͘w~9^gm.O{=:8Fe%*˜_MyK5 ٴZ6LV "p =EzgcN9Nlض?fvH7Pb   (7m]ۙ?Rs?]Q1iojx=\RQjmeQFUB&;Cm-]Z|e&];\'C͙i]ۮ8r;/y;| !UѴe?aG"o^%j\X{~/^S|:?+5R.-ƖV yI:{_5uH/FBQ  T@*OzK?,Lz{.qViX'_qMW㲭GZ!`@SZHwG&S'Z8_`9Z8*kMjMV.|^+Na$5G y{O\t<&?/+  ϵcF߻1LQj{M~|B5(dw){a̸2ijZ?U79jN6M/i`-0C*MMR OwoѺ]E=Oթwo54[Zr)γ'[WTz-^vnmްJ9-oI#UHN,TEwԶy;;z;^1iqI% {sQ&]kso֟=uZQB;.Z/95zB/1mTjO{豋ʔ8'UZƭ@j{=Ҵnҙa>͛Tx@wD~+ `*ŸƁjXr>hz*0{2K8<;In: մoT >[f}yK>lӮ8[7+wu㣞?Ƣ߹߯Ф)SgRnqQ4cK_Q,;OrKk.Ҭjڳ'uV+9U{5w2._U=,  Pqߞ ZÚe޲=ޥ,JMS&|5$7ŵZ 7m}}E o_+>1>8*: o[4{pTH'^^]&fU4+n_\2^i>YOGzӕ&Om5jt_0_C./( Cv;۹]&>?hP2WJ6kjp'[%CMղz2 rq{mK}Y~ a[jf%"gzzVpj؈+~;8@'hƪSg~XvLԢYI=hnN* kPuA+9A}u C:)G t/Pnv5q |syM3ڶ~5yg+¶ĺKbJB'?NHNcHN9C3\iΧ|UE)qwVC޾vpܻ8jwAa/  BSRufjZ\ݲL(P6nq[E'SZ$d7]Gg>tqxl[x׸T` ?G6U mdu&uyMX5yIz֟E|^jUno^sؚ=Hm/Umn͔KɫFN|]A.RjpkyO&l+ڸ}[yGRW T;ſϤHϠ̀?CjUd39_ߨM\jִjuCn @g|Tw~HK?s_k[|{3z=y:]3"zQYwR__.\R}FzJy˛LeQtM=~&n@jHh3.դ3.ؕ'4x_|Oqg6xU8yqZsz>J=rWO`&{y۝l.;v'~is՛ ꟩Ǵzwqle4{Q*=DYy?$?/h^wyrnn(͎5%^?Zsg+s]9uֻqRZa%|; dv ! &맧@_qjxmMիV=կ=JnNЪvwx-D[Sw^CǖiP mߐķc}x?/٘n Iz[뱒 M⫅Sظ& mnk6/r|/2g_]VRڗX\E땱55ʭo&_o=)Wǔo醱o(M;hoka0Z)35 fߛwwSuipLD*,djbKI&KĹM]Z\’.>yLI濷ɊQ"Sú^u޺'jԌ֒ )59Oj{M;ea@)Km'׮.YM[v6JB#Nr9V*> FS4w|)X ռv4[ P_tF~Wuez"m9Jlꡖl iErxXEq\Xc'1@@ ;)e<򿥣==jﺊM˲ Y tms}6IMd]cC^?~ gnP2:B|޸`һti۬z v?4,뿍.%mI%{䚶hM`}Q fD-gkla&n;NoMy^QTI`=r;ag:C'f6 ˦q?=3jnuWVEeyYqzbͦ;Ꜭ{VKF; SJ8fH3q]Xz.P+Mmپ<8 oR'g|=_cmuc_^n_U-ogyWG9sXr˽n]w#}]K+&们hyVB$z\&/+  @@LБ >/l5OE諹+b㿙%]ޒjx{W8+v^չyߴ@<1̤F:u+O?=Su.)EOց HݿFNZeP ,Po]g9]qb ۻ`yES i ͵1k1&ο?NFv~La2d>~X!S@IDATf"^_m7]nt άZV?u8ި ˵C{Bj7>cy#痖^':o6eo9 Z ٕWJ6מһG}V@{>Д6G^`ՇWĎ6$+(I  @@ ʥs`~weSVaq{>ՌIG-R}^'ڵ=ӿpajݼG3uru gk̙oaGف#+]&ir\+_nD+H]U_~ZLF`;~uZch7 a}e Zfֳ@s׼W ǜ/8V3[gn+ ou7Pjj74Kc @i04zKm]{ *;]+&oP:2U'¿Dp|%yE@UOeZnqqˇ ׮l39g}aaYx]'?iqۡ}#*K%Ȧݮx{ԇME=lxUK:lMA@H0s#vz_GMYSniu+~5SohȩjW rTKѪELM (yFS;8.,;p|@@(Nژ+wc:5FϦiA ]Iŧ+Uqğ LS&bqaW  (ƷGf[pNi G ͿS޹_PZ tBbqcnn tQr{LA=9x 6QE OӨ^5| Jڠcqx p2{uo+\‚ZZvo%7V!B޹E+'Ů/tUcDe]OvyԪR9Z>-:˵V3@n}Y'8?3#Pïh?jkQ {P|1]_Q:穥^ڄIRj_|[-S!`OR8GzR&jܬeS5{ŒTDmupN۵o$5tN9x5 i|Q&Y_識\#s3ӏuY/Հ_Z5XWz4hmqPVW=||ϛ>V3?,}Lor%8^0mRA~yڴq6|U۶:[o_2lmj-y px]Q o s*KYzο}qoPޞ>Ib>?{Nvl׃|+ʆ{iTojѸC ,ػ?8}vűV*[⽿|?˹%jg'ovGٕ0@)/sŽ;ߧӰEg]q?;?a4~HzV'|}oo3eUfDEX_q_%:}{c c-xG TsU3 xy>?Wv,Pp@{CΡs ⟣XqQHH2  p 233M*u>l^[y{T^~ eOPW.vzNvT\{kfY6CK/]c*=9Yxv 5EUϞyҷk3M1ͧ)/o6~]9_ ]]qBc<3dpwZs?~5L–aFϟKum>OPwAmqJ:Ի# Dt;J F! MX[Mij-qU +NhL@';@@`T(@@+uS[̵' ,u|6@f Lx@@@@@+@z6@8v=&Ү8qYa((  pX Ti&RX@8Rr 1*rIe^ fjWl   n@!@@@@@$     ߎ @@@@@.vI@@@@@$(     ]$$     #H|;b7P@@@@@H|%I@@@@@Gvn     v K8      @!@@@@@ m$q@@@@@!@B     %@.I     8Bķ#v@@@@@Kķ]A@@@@poG      `o$     ߎ @@@@@.vI@@@@@$(     ]$$     #H|;b7P@@@@@H|%I@@@@@Gvn     v K8      @!@@@@@ m$q@@@@@!@B     %@.I     8Bķ#v@@@@@Kķ]A@@@@poG      `o$     ߎ @@@@@.vI@@@@@$(     ]$$     #H|;b7P@@@@@H|%I@@@@@Gvn     v K8      @!@@@@@ m$q@@@@@!@B     %@.I     8Bw޽{u9ޜ%Yyg*AC   .|oͿ'4SզI-TXY ǝ9iQ Hg^Nu(2!s4B_iX4,٦F3֧5u{4oN1ޡwSSN[yŘ:mN5>{_S Ʃ_g6Pk7Zte85aM8Jj6,ʦ! *-k')?JVsrk4VnC9—t^,^[jn%t69Iok=:oOwMjX5o}#ui/]۫6MWcZYu71m 6հ>8\7mNјދ4@qf^j_P^w|z)gL%rW7ϬP+'iԾnq/X=ǰř7ks R͚;zHԋo5aE#wHy!VV  kָҊ_նu!Zj3OIzع];x\/]uf~Ǖ_Fryez"N~ Ue%}^}b53O$ o ;{J~x/Md[^XNh6ʓREEJdU,k@QNUӐ |ﯟ@6e48I H2FOz+{SԶZSKzm[M?yxl;@@7oS2ս}]ʾ]Wrignov}cɮSծ8_͘w~9^gmӻ^/.FT^`Ыi&q$7]QIO;O74e%}nذY{M[y%%+=aaiۿ\^w6蔳F juݚjX.L wiߠ~DDžif̺ʹߪdX9euN`_MyK5 oڤZvPˆɪ\61ڣfBӻS[6qr$<,md@@@6g5Lb OkMK̈#spZeLһv%}93Qɮ8r;/y;| %.Z90nyS<rvYI`P8ރ6Ymv&?Kꮱd!Jך6Pěabһ2q"z~y7f> E_mJ8KG[%`\6!  ֯ȼ>OuJ Ӯ{[=_9}7WdzVC3㮟k/gN)ɵ^J+PI3-]մ~\w6oX[XDhv^:ɹtltY٠e3jZd%s:ꨣ|_6]=h==o.fO8MnkKZ Kfjφ|7O{h ޓP0FH JKڵ6eq|~ޭՂ*~ێAZ%!;ןgXgVZ#@Q497 V(;7޼oG8+*Uꦇ:㮣((Jun--u6GmѢ}?{MMOwgg3 $Y!юEoș="zID9>(8`j-^7+?$ sf6e e#㞜&Z?Ž{-T6r"@ DE {oӝsQ' ҼREDWBa;{C}t]q8\(_Ѧ>-gMP .muZm+SIhRNg2e6Sq<-X φ. m21 On^ P7q৥0c9;ZGUA3[x7lJ,vV?2lǿbLb8ZpRMHȬ}o&^8jEx1` b„M7mbl(ˣu#Lyl5XYEZK\5+z~{n^weQ=йT@!-ӮARfl9sڶ@j"6:|*pӒpiFw:>mǴbӉY ~g~$Y1STaς0pe+>C"iR8\٘@{9jz)T5Z3av6w>ig^`eֈ:s~WX=zqXDVxT5zRoW!19" bl`wɪ4_v5< D"@%·~#r`W0qS#M1V(~S-MPA)BD>2s*G l2aWOaO4K'? q!gǵ/Gj#:\ FfI~<.Pd'[0 l2{$9]ꇿ~ppj g&M\2UYe6$Ao_5C~ e`_3U U3 J[2#>Qs[ otTE܃O-wNk AZwI|Rf<ջ +Beܦ:3$z KofubLxZ^;v@tZ|4fET~Ap։x 0v! 8iCi\xҎܴZcs!L|wncq10kVjFA0y;:Vb.L.e)'# Pn9>.n}wL_ƒȮa[w)>|$|Pɲaoø`5U{(^=^r?V&ch#l2Ok)xj;A* D"@@kʱ"X!Q0(PZl0ʼn1>cA*#5IO|ȟ??ry#9tL_BB눢VWM%/c)AQ(bڿ4*?w Jr-Qu$D/`֑r)φ<%9MC@|ݞaLː !:~4n Fe!K['V ۋ9GŠ}1L'b[@s%Z=za /뢰ʦB[hP{aο\Y!m݃8.wMz:}K_yݤa_m#z'!YriPP!c|>S9ߛ><﹙GC.^?gcgKG^!g%޿?~͔]%S* |˜nNaۛiY j~6/3g^l{2fjG_PVYnI^/$0~+Br'lah3{(im7{P /"@ D de5)d*wK ܺ'|TĒصn87>fPQj&-E#,1աKx,C!|ۆ?Na´U:3hb<5\蒃X2c[$Y&Ź3Ma@{^GZGaU̓i=)zcCAGJ,zٌn]&J-%?yEGvUI8u:vGrfIvvEoZ>A~'huxh#D"@e-kn# FѢ6Qӷ ,|R!Tb|!xMĀw#ߌ+ h9k$xن~C$[H=ܱG/i2Md*3ztSގjk| ݙn"?Zw bYr$53-,L|C)6[%\نX1OGCR<.eDuF ŌDC^6f*wLlK%N֎@wD]a=/P1'Y7lmkkndDr@ v.w^)iYeǸxկ,5{.XOc;ƩMއ;7`I?ytDx+ߘ~ۤTNyG%_KoeMo-k|S볘Exg*=BqiX3 6C3: qBL;|0F^ENW(UvCK>A 'hqT D"@2M@^t(زi,z[3HLv6ku/СNݎ%qrdzLq8m fwGByŨ[9\op ¤qf-@X02LeWwUx=m@ywD]>Mܿ5d}_.xc"s=cۓBnM]52 G0n&ua8 -Znᜬ㭐;+Rf=̲߫,/w${0 f,vLy 2wK. D"@Mk]|7Y:wmݹtG̙<KIL%`9̩,͊} ^MX }W~鼸wfr EڭwZQ{P Z/eM D"yOT9?)3*VI1xK)ˑVyǴwx_™b Ϳ,U4:bUk*޿92/?MXD6N~OUE q΢Сvg|u[Gcpxl6rXE.dg5z.S"{iʠTPuxp|b%PL!!cl[JA}[ V`eM>dw"?9gE\ݛ(ep?@`@o: U6چwX@:FtTtFȨKy]Nq~(9B;l)yK)E6v0Q³tlv&Ǣs&_epE֎Qli\dw;&?' ڔ;JHB霙i0-TF7X"Yz-GVI߯"Sn5R: tr6鰐*>ڽ5R:"D"@,':;3GM)26u~6(>unEKokJa0ˣ]Z'wʥDaSL;9|rE(JW M5Ax;o"wWZ7l,^6[jR2sNϑR$rҮv%1~D' `풂ұ+ǿ>*GVI/y7I~Ryy3q(on7sɊ _B"@ DC@V pJ ӚX>@ݗה)LcvڏCdqЬ/^+6-=9+ ;d6uGf'٭reg(^Rӫ#wd ,N/>6 º ⡰g2L:u21;? QQ30wc䤀?qSʎ +a'oڙbFE)V313&,H5ċ/Vhc#,OӧO;*L%?/+۬qE˝Qj\mqNl4Z79Zt3ֹ u-L̞+wܼ2;VFě3Fnd'7if7٦`D"@ w tTC% _9T*bQӈeN faByPz,܂@=+F*|dgj?e<)vq~g?&֧FԐ3(,_[0z>Y"4'JMG`H Z {1ݻy6~hذ: {eO~Umx6ě5}<+ HZ:A̚Hmnhכnrx~z.xZ<,ތlAJqәԝVu>byYL">R5GFx|.ͩ-p9[7>.-W9>ʫv2r= ~lϠda.#F#pn¸}h<'jZMxx1sa |JtԆZPR:|W;&(BEhx]_}`^Kɞ^-B:O! 2ơE\W 1~yyn RSyr \<:O{>e@77H)_z'#Tlߡ&D_>:cС#8uӄbϢ\h%b (gWK+~ QXk'1,~1ڞS0ՙFe_e24B>џĪm1j*ջFۈVʘ-F{3N_d7"\\٢r.^»x*רdye¯- 혘'-}>~Hfm\~s}ԩgtҙhLۥq[B cߚ'x]'U5NQ߳ }\y*ڝX{socsѩy>e;8je E?tOkV_Z!D"@u&IdDT?3w1CyG.XP,Yy ^.T珐-?gO(UPZ 5,2W1S;+P_4" yyj3W@Q0TtKPx<-2:K#N (P6>ϝy o|$HtlȭQdN·Xe4m_Y{.rjΰTZu@i cb ^'t|W5sgPBu|(9k6j[gmSe!\gywWry;_p-ͮ(CmѠ >AIÓ.y|KCQ ^@='uTRÖȡMUQ-x.īzڎh/(PX'iP1~c0`lCц*!1bfs3^C.s94W'rR}vWe06HFFC'2ʚG]U^GU[FW۝y -p`Z A D"aR;$ړdo#W&p;'e8x1'! 7O30f8?~DS~I1\LH|Q0}̙ )Gʡƕ=E.H^*^c:mnLkP^n-5tmӰZ|κU냦5B !_ј%nCn&l߈Gpq,$-1{m=LmSBcx6/t7bYf$̟33B:Sn^9'cCv;Mv!li1vYNsGsct:wK2-5+KK?l9>g{ݿ`Ϭ |q$ Ý ۹kzwYJ4ZqZ LI"@ D·' D"@ D"@ D d"@ݭ:@IDAT D"@ D"@r4s"@ D"@ D"@@F Qb"@ D"@ D"@r4s"@ D"@ D"@@F Qb"@ D"@ D"@r4s"@ D"@ D"@@F Qb"@ D"@ D"@r4s"@ D"@ D"@@F Qb"@ D"@ D"@r4s"@ D"@ D"@@F Qb"@ D"@ D"@r4s"@ D"@ D"@@F QbdzPl]"@ D"@ D<} O_/Qa|GGD M#Q5 +?@ϹJ9$ɴ0cx2t[Gv>9$s@hfxJ*^ r_ o_Ɖ?ņջв(Z$<Θ[7o]WƐ+vx!8ac{njB6N++hgwt5a "@ D"@L@zX)lu'#ɤc2`Δ ~쉉ҍTarE,Y_߀R_*K%5\U}$?uQś I]w}IzrB=j scjBW̏_V W[>7O'"j "Azm$m0-$Ab´ñ]x׀EУ7{gf0|'nNA{@#>i!(# o\Ú]d"@ D"!P0? w.8<PAP }:UP^)~._y[#z(EbE^ؔͯido{^1SY;V\; t^@M FLR (&.C`*]Q߻gU;M~Q\pڌ4cQkw[1"wƕ˹7O}zRh6Nj'58r#e)%S-':M D"@op<̆EL4j6ڑWV#&G gu:A1&2 pvI4~ɩHMk7 N$@8ux/n\+?oyf EeKf9Jsb-i,^b-A3^p&-I0oi*?选vP&>K/w.0)f,FW=.72ۈD[/v6A{+GB"@ D"@J ߘx4 6[ᕲ%?}7jv9*}_" F!U?`dUYK@۷pA|hx>jz6xjrķHeaY):WbS3 ɮx%PhSrWBf}GUXn(aߔ/ETa ,ƈ#,ZlْpwSz~6*Te;o0;0 |mWy7 5Vpۡt|49˲+ސ%:j/˧E D"@ Qⷵ㮈i| پG9JhzdS򇻓وUQ$tɈǵ#/طIxuR\D̲Mfq6,i K`yIf,?q68O=S~@|<3EpdbOW4Q,?RZB (P𼟰`q{9=w`l Ny{#Һ;j.|$/zUM3?a4x+S`DΞ֘#᣾DM uiqs>fVLqZ2.hi.DOwE)HJ{<C}t]1 M#<=M1s".櫿-C\6e!,bbRolCv㝆OX]K,qR/F Sރrɉmbl(W,XDFe+}]Hg@SlQBڈ@&Dr(Nfrdg1d:]s Mx]=hm9hb5ŭHfxU?2;e-7M> }Dwq/ФĊN{ep2q*=>&Ys:lY /'qt7- G D"@ O7M"yyrbNG1tYh^ֱkfYR)%D!>Wuq΅hTlo.Jne,"%O)gER.XTB 1Gf}aTi?{K~'g%Q՟_oT&o_5Q}!;]nBdqf/33);J&zQP+qciGmӢxs><yYHoN*tA+"(H &!-5r_9 oɨGegpDaa6e|P>?60){Ց>=Mv<8ߗ@.%Yf%m&dv̍/$<+GGw Y!v=Qr93_|[t|/>Onl=X'ŒϣCӍu"ۼl1 D"@ Dw %wW V-uj oB'Ŕr6dsvKa ¤yEIoED8 6̈6jV$43A3- #`[~4 ʢwʙe$|[Y|Kfʭ7&#A&uWҡzQpSx+'.A _Y%kQjхU"?3hlLt^rpKl &b6x:߫Rc 6Er'x #̔k{QzJk):)yr6C8P%7 D"@ DN@w):$5lgxSas2ZmNƾmeA֥^C!f:8O\qnOTT_Do_bvНQ]gdCY!_Q#sB(A ћG^FTaP]KGfskY >zp8_Vo W!F,??7`!ǸxfVHc7`;f_:xu㶬Y싪" 2lԻM hQ̏,Qy' s:a{Bt?u[?[HYXWYWw@)geCgXnKc0bi X 7e6)@o6smbx6ϊ#^fjxo| KN|-ѩ;fߤ} 21\d8k|jguCGȼ[W GeWv6y`?84HK~cو@5*YA qD+Wg,R-@&F57g·`_5N; XL>#&;6r[,X =hcNY-˛&Ȣ5+ijo8CY~ \>߰\^[fz5+ K³v6n&VJj7/+K:̡(zyD'oϓ|\D@M';U32"qk^5hS=FCel1u"TWmE;z YW-K/5z![.Elm[GE؏}ΊRʧ)‹o _}χTɍዅ]Qґ|&_oW^D"@ D"@Ȱb\aNQۦ7X{CkqM|]'F% v¡l%Cg)Qhsћ'*jiVS:UT!Bl^~fVe@E+bF]َ9OIev WO= j/켲'۔z_?2s;SصE2?םW;.@d>vIr =Y:]Zm:Ar7̮c}bv-ףe5@:_)(0bdnޖḪKEGh93;#K⭉}1!. .Ǜ!KcuO>݂#kw #j6(ךbkbHtS:}k UQ *Ӽ sXj,{fɓ"@ D"@n|=_#{`x$Eo\)$ ƛlU,\ 13؄e_ ߂6R&zGXJƬyA f0 <4{<6N2c(nGt@Ĕo=R}dÈ6EtF=_+Z#Wǯӿ©?fVTlMeKw*3sk? YЪ\!%iyt6fh#ILX_7âf |ztWQ%{$8b{:c@V \{?mKSa<،I ^bе{}-,λJW 3Mw:$v аv̇gk% NuNŮkY;\Q\#D"@ D"ao06f#뙨%rSays\J󅯗 ,8:? F`VV 8 t[|j1"-.^`ߗHSLi ޛ7jAQ+pzzh;"273`V?ZǦrWcix `YhB@:ykiUF,0KY3LD ~lHq3:hދdk?gI 3 h6i g%ncXXQC}P`3-jYT)ky~]= ZJ`K{ %쎃&(N ~?Wbns'&>r@x_C0}gL!HtL D"@ D@A@Wxr_ŪeJ3*܃=*L(NX^'o"9]S/| (c+0x LPG|OTELĉ!tS;[^g`1{HGf'mjp~f5#s$rV?ZGk~_ gz>6$I/: }ʚrbϕr+^Ts Mn"q!hѨ75`mEok/I3p@BR.';?xYS<'i/!?q)qM8 {ȣyvbz#Ѵڃ ,-/|7[2Fm0-Tf,7X¶"@ D"@SѺS س|_h`EKR"̞͋-S[col cR3.W8.'$H'R eР|,a͏߃dh7TfKWf !y|ՊVGh N&M2bPhTV~ftvVːa1 qrϑ'Њ=!qncHm;'mM~,֟7琷\yp S6 gfx)p]$P_<6tΤ3[c[3>yq_ CXD%or"@ D"@pI/b[Op,ƙ?6BkZ`l2Υ˦K:%f}Z9\fAWrjq6{ P)܅ Ҩl?4Xq6sN(^3::3&,xEYTYI37 OO]s2TZtgcS/`èU| Lcl1͕eh"*FQҵ8>yӮ#í [)De&p\lܹ[`rhKeU~߻Νva#E`CDnuN%KA D"@ Dy$u*eN(lrisו$r K\Ǟ#>3^2^t_< [.\-9Y^G  5J*"f~lzrUv`z176*Xj7R~ĬtY|8T:ђG8iB>9>`qlb,Jc2a\|}EEiN*Z8cXy u?1᷿-аa3t4 {R\D@&V rKI8k(#m'/?B NQu 5]r;wP/a7/Q%AcәxSΣ?2bnG<sItс4/񲴰%3V̜3)O"@ D"@[-10f-ɾt{Џ p#,Ah6L|9ʧGmv؁80 @a*ZE'++=ܵ@L~1fy5nٲޑhˇxj8o=>:S7M,,ʅVB)f_[⚀Υ}ϧ8O",2o JnEHJ st*3[lKyCaW._ū7p]xAP,+xݿEGE)CiYU~,wմn9GGYi4Y&m\E=UiV9V\WrX| QzcyCf3Se *Zf\y Qf3rfġ)fxy9567jdxuPJTYuo᣸x:yWG:aQYh}9N禈VlV^0zF?-}a]![;~K3fj;|Vx'yEi+r5؟]h6(;8mFh1V[)j!rl$ W :zP?j- }Q+٧|':2r+~&Mw-VMWVۋ>LtQU8lʄ ׶UQ2q~OTKZ-,U*\2SKq,‘WbTbZK; wa Ju|(9k6j[1G]){gq핯D;X̤֍0 OP؊uu ׎E_5jn[,yʠy$'ߑ7tt ]ʈ撏ґOq9$Es"tr}բ"0szGLOy2 D"@ DI-5dÚ}&K2lrjXh&s3 lqx@!Qr6Eyq\>/LIqP%n15r7e2mv19-,%?{ 7O30\1J>wFYq%QG037+zph"n< $:թ$c^Ę*5RH#L%gOwFfeѳtloL~}űkZ?)fo7._3AD7^ds&6Á8(t׏ví?Din_8'f8֍𙻝f3kvwn? Yz~4]wE(.~>xwp734;E;-|Hw#&G D"@  yxY61Dxv6d#4שg$^ $3p2kh~]Tóc[=ÍlwwҮӮ`S,~ Gc_.f]z)[y̌%X9?EbN^rR-9kQ9-u^~1nQK 펹[ټ<ff`$vkO_ES޼_66#żuqX<2NG(2 D"@ D"@ D4$|;B!D"@ D"@ D>L D"@ D"@ 9 9P~ D"@ D"@ D#$|{""@ D"@ D"@@N#@wN#"@ D"@ D"@ D"@ D"@ D""@ D"@ D"@<"@·G(2 D"@ D"@ D4$|;B!D"@ D"@ D>L D"@ D"@ 9 9P~ D"@ D"@ D#$|{""@ D"@ D"@@N#`i~kw*k~tv^$fl O(C;']2^`1|ؿ^D( D"@ D"/!9[?FH2T2 Й0/=1X*LN>UtNӟ7?)\.%Ss}s !5Y}>5z}FbstD D"@ Dk /|3@z炯bA */Emi,NA-;=R*l&|Xy[#z xz Δ7Oň@y=zw.w@Ϫ~,mMWuߜǡm&K D"@ DL-K* Fٟ,K'WV#{+Rl` N"(fv,d8a@h8~U ;Vao]Ӳj9U~t@ D"@ D; %|+vS^bWǨh|2eaT~t>(:Ht?*$D+;n[J##| 7WG$D"@ D"@2,|yUeqUHuLD"@@O.yg| ;):BP@."@ D"@ D_I }n뷏"q4,,-̞;CC N}8}JmE8VE|>%'#>>.ƎX o+Ayn]!E,PvPT d|2OSG6 ti>x.ԟArr<~zVEtK]|p>nhPU=s(<&YdTx+t6\5f)( wZM0$D"@ D"@8̈́o^KR¢iQ٦>seѦ>DyӥZlŽozEi9lz +!t`3w ~Z: 36ާ_tBT4b3goRxkrLDp ,.wĀk ^t6u&$8aN25:֯|M7iH bٵxfc1oc+y ʒ? ΍OwE)HJ{]s1;8L߮:0/b a}x{3#fO}W!gvk=]ZZJXz~2|/,΍7؄C" s GeZΜ&J[vj"6f[M)"@ D"@ D@I@9+3.'jKXfмc5PRK21݉]B|ԯ6ȭ;QMua(c-yJ9-"r]-蛍XT,Y3 J[2#>=*'.Lg|2|C8>t2_8()DGz_ gfR\w3av͚V?\(XLܸE9 qp/xj#WGʹ^>(" ;*GeK-Ȅ{6eŐ?*4YFqU( - g"0wM1U:e=b"ֽKL.‰R[y2aqI69#V-3s׎=جO]3Wq'Q8NvIb>>u;ϰ/gUľ]y(~ϴ`!! y?e~!sW/VClV,_m$ĝ?118wYҼh<^Nuv߯ݼI]M9&lY^l9@gض5GC cԄCbGA4{Gfo4m tL D"@ DtM)7ebBşʲ)>zn#,l}۔a~K4S b²4&DEvLv_;cNu|WGFaȆLCeֈr)DoT&s{QJZb ŌDjF?",.[m8_ lEՖpebml&#AoSgp&Jc ;abNdAgB ^ ^9m:GtD EԦc-$&EO&]uH{8{BO*ߤ\׶WDr*$RvҞz: ܶugǥ%,vRa6U ,m[\;*R:q'F;opd>0&l؀/5Lcc]e~quxokO~~ugFdy:I?{_j왆V P(@ P<*rɯJtzqo3l6o\r >૙E˲̕ wz@y>b̒"\w/Q0q[c2V}VC:NÏUVˤrBeu k/%ePk+.AN^"&axq.8[C_[xnw[ XHMuJ6C{~8OJ&g׮E ;>pQϡ˳P/TL|"mGIT#ҡ&-U\MQ :w\F 'K_tc驙bپ (@ P(@ \%#N%‚ё"mi[GVty zg֮B`𵓇5ɥeY@)_aoSE{ET]-)r~Et$yi?Dˏ <+*zQ#[!b4TkheQ%ĝN_ ;Va-k'2Bed:ʼnbU)@dD^>u*V+RȻ^e>#e:D~qux9OO o?xF| eä!O y~ VXq(@ P(@CwU\ | HNs kE0ӜrÉȟpsZS9|eL^)#Z Y ݇ D^EYG2oZohWiMF aSѱQ0JXgʣaqtSNoD t=Iwy'0 V4}[wd0QsqOГZsA܃q{r 4H~G`) P(@ P pɗr29d؝7~KEJb^ǒ)6>X(l6,< SǔnMP/b6>Mie}G^Bۨu0EoU8/qsE[~`\~=7Lٷ 5O)Ґd_y )bPXy26jbԖR8 甎&_8)zݧ:g~u`ϗ;9x|ϻ|?V%7mZR\ߙ$͛㏯(@ P(k/[eR*(hDu73fC{{Ŧiwe`uBHaz5hM7/ϻη8ֶU~^2][zꢭq6bÐ1]΅q#Mlz)VNq_<ИЭOi"g:1@nx:|+'@nq IEϝXLjs]-"SNz)s(@ P(@ QBTJ{"P^@]fnjOѲ/IDAT魳zڋrp٠ƀCUkz2Ky8m Fג"܁^~h9T1/GK|e^Ġ":bn9T3Ϥ空6OhK5ĄZ#/5" ymGά~Wm{EtE nҙ`vMȐ^]*E0Ny]סW#*j&ƿ񂝣" W`$KO-Mʥ+4JÑm,q(@ P(@S@`rOjF/Dz&q19A܄D@zCs{%i7L:}q>xs6ԋz%kNr"l> 6ZaעYwCmD݄Mm !'GӲwjc5]d\YCpÒKo+j)WEiKm1X@ٖ{R(hr}ˠW k2[5PIĪU)ڬC:%M)|p]EoGVw]<_l-ۢs9zp%+qVj į$?ɪ^f5|bZ(@ P(@ PxC206jl &7?}9T.-fV i#Q#2ſ^/}'8+:(Reˢlq>aEl"7#PrKPVWv~G<H#3.ڰ?4G||'T !5렪ȯm*C*qZp1oY%p`{_y?vMpvɢ(@ P(@ r4ﭖz@*r;*е~=6 tmmv3 ]z;zeί B?yBus>l7n,R^d iH mQi֢ Onb*w"7#pAuѦk]ER^suvtRSlj5y "qC7ZvyF;KwcB+<CE~f7Ǽ|(@ P(@ P!p*|ߒZZ3% [v61i1 |)o\xd "nqu"Ӵ]fuT*Hr-\9w1K69Ih0E TJ1~\"x>MZ EƁEb]u 26c]f͗1K q` 6Ǒ Cu@ WJUaͧxZN$RNcɰ\MCZMKX65-pW=uY/> Bsβzr1p)P ֭9BitiHxM|(@ P(@ hz t$(@ P(@ PNB(@ P(@ P(@ <Lup'(@ P(@ P`I((@ P(@ P(@C<(@ P(@ P(NB(@ P(@ P(@ < |?牭(@ P(@ P(@'v(@ P(@ P(@ P`8Ol%(@ P(@ P(@ 8)P,F P(@ P(@ PhgZ_`+ճ*dz~3;b d6":Q:6OK<νOPU û՛׭ Z{r;៼Po(yFG"ܶz P(@ P=-\6FXthҥ!-5ag ,^H^vZZ T *:Z^60\ (-KZ ow麿/F DD}k"@-mNv"İy(Vxv@GVDw_u2N\k|Iszxu/!R(@ P@o*QޅkEP8$+TEg_[18ʐƗ^5O>XK( 4i ǢWN/(/|{F.E~|_y%'ĭNכׂ=mV6}EHGS ސrhs3dnƠ7C‚NliVH(@ P(@%ԿZ\fjq_acJUo?pe} bCI(@ZN` z7Mm+Ţ5{ya{c5jg J{F֤]ÉB>7}Qٴ$zP(@ P Soe2M4A3SO}液=V8㼀ԟy(ഀAoTUQL316NGۡ;YPڣV!KIJ7:"b@'̎jZA'Y>x*tbz̶(@ P(@ P Z+0VݟVCZYs(O 4jsW |X/ l)o(-jT J{ %Qt\?#4w~6_xFR[ۢp|lP-(@ P(@ Ts|;gB Gв1_o@hv&wͦM(s8PA8{2|JOGrr2.ۏmqy''ZRntQSƝsY)|NMSU+=ɸr,og?VZ(Q5\!MDOX_Om 5%'|]7Agu-޹zKVrG=qESvl Vl;'nDgZ7"{*`Cm̝MA~G}mݍ J{UGqS yMl,ΦuS"﷾pVoX;Hypq=z; (@ P(@ PA --@i1@xe"7qq>k?/ TXJ!v|Khr)yʠ{sxυXc.1S{O ͋~ׇ_A>+4ȿ_TQ~5mUG:/wa8" */T76UzRR "f}렴4ׇӑr~ƊHc$k4b(V'抋>)@WX}y.a^gcHP\1%1 bo5pr|ڷ2?jvioyV∈X=Ë6tW=rbMסTgΗUKw\'F^Ce+Y G@1̔C1RMOu2mwmEon^,H{> J'F(m~{Z~Y# Xdq@m)@ P(@ PQ-STNu"x9cn?kPUK,ǰ6>ZҼʻ+G]|LjSƩ5\,`qU0 =*֫Kɮ㱫r4LD~@LA}`9&o(>[܇9`L_݃WG3/zpKNVUcp]Q].܁~ s-n[?x{KB(Y9|~]ǩg[mϕT'ZEDa[$(Z2PEOIMbz*6%E- g!p#ɖf]w\=KQ,l zWTmm ? ZK9I{峞$2|8<.ֶ2Ҡn^,HQߓG.R|?WeL7mӒ=,^m/Xq(@ P "NҠ]5 y=o\pW}\ė-l& VzN߇{K~7oʻD[-^3mKꉼ]SXV{vgD:Uaہ?3 ɚ'P28JbYh:ij> rZ^B֙# 8z"n$^ۛq{/cli(HMsno>UcMD~ٻ~,5{yv6Pkfoc悖w3;[޵J@dl(&UOV s2WickEfP!o) Y@u'CL}[]Ce7,\(Hl gea'}LngCS!| rY(@ P(`{rPqL97e*wRV o ńdƢ'4^?=m ǝQԐdK33-( |Wv7x.Pf܈>~h K= 7Jtwܐ'UMoOnZ&Ux즋7톈jw=n^o)Ké svaYesIxRpl7+'z:+Gzw}i<#|e5^_ofu_~3eM~^Z_n<|抴:6eFwy( F%:t2RmZV܋ۊTT}2߳V9kKm0 +VP_~+O74mŀlޙUŐk֯[ʹxq9*>Y>Jq$2 q}|>yq,泚_#7" y)]:?)| D=+JW9>|J ~.A֩~Uzt8/V _eSZ̐Dn a }:m?eu[[ìXMQJ9SzGlP@1ߘRR:1c7$o di{qvYAkϖqP5`:;(땖4Y?_ʽ^9 a ?_ P(@ Pp9T`̚BPbUThr {Jn>#A\DMʉbG׋y;\+ dB ^ ^9m-GtD EԦc-$&EO&]uH{8{BO*ߤ\׶WDr*$RvҞz: ܶugǥ%,vRa6U1pD^tکUW2ԉ;1Ag~#]%UwĬ1pf=f~}e*SCϝ/{{$^s <#0b I*Y|cUVlϗ6] K:csƛj hUɣďvX(@ P(O ypj^[gČ8ۄWϽj~l,se3Mlz@+Ie}*už%EaS7,(t8-mױB>!*eR9U?jV2X9ZpY zuZ70pAVjfzĮa]Dཱྀmri}PRx0Cg^IL|"mGIF:t(IKU2WhST,{==z2QCwIRx~*?tXdzjXomfʊ8]c>?M_S *K2KXc(3oם19Nz/ P(@ P@p9m02/T.,)жAou=mIGǠwf(_;y^\Z DQ:XWZOQmr(ZnWsMhwEUEot8*>}2DL]㰍XZYlTw q'S~+a*7@eS1J)ZKI6S(VeM޾OJS 2O`"ՊUY3YOw]<_ُ@lķPX6L'Xiu?N {j\^' Z{lCt~qDrWL 0WcrYb(@ P(@ (ߩWq)5 9 %Ls '"zX*ƠX(l6,< SǔnMP/b6>Mie}G^Bۨu0Eo 0z:-4KusGt\QA7ƣg֨_"$:)WhCtϨ< SmG S4$׻cB:~޺ G}{Z03B9Ij^zs:_.=_ayO#*1i"%l iw[Akc0yHZe52yxrxtD3݉(@ P(P\浗2EJHK s^4n:r G!=|bӴNX20Yώދ:NOq!E0`&ϛ][uk[K}?/tXU[juz^W[ܐ1]΅qCm̯''mߥ@X9}tCcB>Ƿm#%A/9E'@[!}JAo;0jU*WXd.h1>#̷|G=eQ^mO<+[nڴWNS(@ P) GYkT)}Ϝ=[(PY,3t8Oo;/0^u0,*_ ?'Xi[0QLCˡ4mH| ly\.fEtĤr0_s/O岑F~"@[!&2ML v zt>~Z- 9-:m{L(z+aw {kB,Gꊏ݈rzw]>_=? 91p"P'v|&ROȶYrpzt{rjuA l `KO-}Rx?$Q̧Tyxs6ԋz%kNr"l> 6ZaעYw4- Xyi7'BN惧e/"ޱ驭k2?%$ f Zwb}m%.VR̋BmSEf F",x.=9rd2lnLa )ajbGU6됤NI}j,\W*6~:]ס'Gk~=ۣe˶j@\I l\oZ{,O׶~?{|6W^gO']mF`+Ɣ&үLzE;Cr[&Ar>;3=s!(@ P(@ PƮ[9[ @-pE_KKAc٠Bm)cihxԺƈ&L c_?%JTٲ(`|oX%I{R(c%(q;#ǾMU,y#-~L96쏃1I:UBH:*kʦоs!\==v+eZ Fw:bLC69cm* Kx2]. n}dznsqiշC,gmRJal!֦W1T|Z&6v=WBLkɿFs_x`܎{Wzj|cu8_ Wց\rw\:_&J]䀧7n|^S -wY'V.h'K?.]-׳ړ}Ώf6RyU9阽G'F3(%棭CD]cƶw!TF!߇ [גt+=q۬օ0vL̍>H1"WvazŌ^0͗D?p-kQԿ͋]{75Fuh{.mTJ 7LBs\[Q:]#D! K]tw\<_cw3,-W%Ӏ:N<ڽg$Gp-av%=;Mqd@n3H6~5,R%iYՃ[9 s%(@ P(@ Uhhj.|f ]v&cϚ>Ts-B"iy%}Ej8mvS+Z7@P-•sGdYm?렪M D(mVe&yiX \T(ڔ|4/b92k}_]x[m9f:jWP k>.sr&j:i,63~~HKs kwB=i_wc^>_5ӉP/TI0w,''Q5 kݚ#4?+JGms7׳;㊓.l <7w[ٮlβ(@ P(@B 69(R^SHwՓCS(@ P(@ PSN;0\0i@NwBY(@ P(@ PߏiAQ(@ P(@ Px|~|=܍]X(@ P(@ P:(.LdA'R`)r}R%=GZ(@ P(@ P(hppG<((@ P(@ P(@ P$T'(@ P(@ P(@ P`:< P(@ P(@ P`(@ P(@ P(@ .bnDB~[/$ KĢh/Xƀh5LQq1r̈P?(4ҡhTX0VhMFPOy]3%E{0u`͈`җy)L!}XDB9S?#!f9XQh . uƶ'3߁# R A잼8 4 ([>8ȓwro(X *u#[X0gs)5pz8?$X, k?@Z" Bc?o'Y-?_^ xpPkm9u㄁2N Zb3iC1N??|L7]]OO$\ɉm.`Mvu®2om6DQUP믭4Hajдd">!L5p(.DPS93yv_|' 'Z;09)u< e1bC40  Z N Tpp{<C07F{09AAqDQD4=Bw B‘8$D9EN"KMy 'd]ʢj:h&h -C+hz>B 061=s0mR;Xa$G8e8/ۈہ;5p}nOx;/>ŗO3F  r]m ;uׄ"(DT$]4b21xxK%~gacg`fgcRrK/+'  kk:k!1s$.ɈI"eHgIIHlllllnll:F~ d r y|QR%a蕇V(  (:**fkGTUU\T*꯺GCuQM[-Fڐ:/ tMkM͚Bk=vަݪ93+[;ǫ窷CSoId O[تFF4FT #&&4JWR!'LVE6hf0o40`q̷:`Z:ܺzFf [AvtvS^9*82[P'{NϜe/;.]\]/\uWwt8ܳsK+ū՛{IYK"a_U "'gV[.Y=F{M;kƬH  :4OsUf˃}!!!FEcaFaEaF{'"L"J#&#-"D~v^񉩋e /0A1!7a8 $q8$$5'sW|֔uz!+-.+]!=/},:zzL̬̑ fnD6ol$)gf5Y[Զmݒ"gs6[kss Ulmޝ?o1?$nZAi;w\Pn{Lqe봷Z_$NVi>Ҿ}ee? Eg=lzlŊG"<>jsRP1cs#[[noskpݺVYN+w \w={M]]7vt7=}ܣpk&7,=r~pk!ǟ/}!ʗu:WG,G^yzMMқќcc'5ƯLXO[n}\?ldkwj3җB_θμmn6z?:~K'Η-\hYtX|@cЖlѰ0T@knȳc@"hkR12.o@ :(pfsL Y`Ҩ"uRղd@+*4תVmSW7=%5yMƍMLcJ̯YLXXmw5?vsusps}6> x}}y%VˮQ PYLSrAQ/c=OM`QʔcǿTKָrvYϺf/(64nj*Xuuj쯇ȾYz@t;wj˝nGiw_lGE7>uY{/yjěW-oƼNԾy~bGO*>|~%ukׁșoMw13}8o^nyaax1ee){i kB%w/\DE=/v F~b/7v"{&';eh>*K–"+u0́0}rV6(*+U5yus /DU:WuM Wl2kvܼUuQ|{KG'gːk[{GgWwj5|lKkGoӍCDB5G2}7ߕp+%brsʹuGR #3;fm(It3oi e.{:grnȻfG]W ~gj/XDq_tYs8~UTU䱆 V5'q:zsc.nhj|v߬~)e+`V9غ&vۤ=#VܷNxpT3ԓЧCC>?&^!.͌xɦ'>}ʛJ4U㙝̿}YCG9"KGa 3D}.B@ ;F't >Ded-' ){g9#x,xf JE*E W$yPy$H-,!%'7-?YD1YGY_OeVOzFy{\/P_?yjј^QffO7YH[tZYZXlXmQs)N;]X\nntq^) e^>>}+Xݶ&?2`~mS`bR;<}"646L5KxcDFA|Ե1&1 W2g3LIRBiRjӳ2|kdR2'6tl<)w3=rL6!{2gpܺmGdڙ+p={=R\UrԾ e+9xSű#GDYO=QS=xrʪv3? 7~[륺\W2N\_sVm睴;m={R3\%~ëȘD忢r+^ɟXx[i UeRImz6IgHezV275jg^`۲تv;~w\8\Ed5=<\}}|cRsV)]{9~K:[LEXzQ*щ1bś$lKINI9KJͫ.glb۪vJ-ݳy/GqE%{o>qtk5V|*oΩ^i|2r5-v@Ճmo=a{s{Ga-o%b8<_JW&> koCBE'#~a1c2ª͚:B2$ՓyisMm.O¹s4:eKdFDDɢ7WlsDm(\'.!%3%'$_2CaҢ Vգ0қ425kjfegYuبƸ٤7 uMm] i';cg>ݾ/>\X+H )uB C햣<S;xl3-)p <]7n"S6L kpe(,m-S_|`zTi3*E^nXjzزʆk7ZW;w7tt?j|\4qD693_/s/%p'IE":p G6!ey y8j Ov6I &ya.w ^ 1ř<V$:͖Y|Nidb8%5]ɓk'>m"7DWTmOK$iR)2ٲer;W*%e-<M~a=TUF1ƷM̲-:ll[8;9uutpifU@ݠ`-PȻN1lW1ꓥRJR6Y|{YN[zr>?ĊQ;pF|766656\io$:*j{y{] ݋=K{_0(f>ə;w3ߒff?|?'gÜ|Ղ3Ia dsxbiiZ_giirii &3Ltó.?LXc pHYs  iTXtXML:com.adobe.xmp 1280 800 r@IDATx fWUc% R*LC[ +WJVVzrPT+%seLaL&3~sgsw} ҧ YY]Azt\mE<ZH륒[./Y 60Aȡ];ܲT-}_VWze{L6< >E5Vt)hYZ+vX\Aw>MRzqM-=Йʸ*;;; 5UYIWVm/iJ\T*@8 eJhG*#}rysZO0K圼?vkflXl\[zuUayY}j}mMnnF_g\t\/_1S<qSrwJZ[Gfa,~ uBB}{`=]eĀ6g9ծݞsrv!5u/mi*n{/~5Mw͔m 54~3 b ʚjy@66Oܻ 1;:嶻c>''p Oyy K]qP=Jw6XJM::RAɜ]*|\t7,vwYn7B R h4Me]:iUͭֈ}ڢMk\/ר;Q3Ν4+뻚{!eMVV6:0oT/y+$_Xnu7|ߋMBb|OryWȭ]ʞigΞG*7չ"|w&;3,R41x0o]VNjԶrx3i¦UZYoJْsgNə3~ ȇ_&*/n])[m)5?"1w8cP?Sd5zW[?;g(ƻ:A "ˋڲ"u$ibK|/霻5]Uq[ͮDŽ$ԕMvqcGo)ܯQ݊A r2iQؚkG3@fv)oQ\m '<Gs XE~Z9Fb3݌l 7)=2h՞Кx J8@'c1Xv= $$Rk\`fkkK6הv.`&[Qh˩r]BGj I\5\w-!!C~9´/kOPORIy߼ӹs:y5ހe~[/ڋ|eQm=XxS𐮨;'dz.AVKj_1aLQӲX;sS`( >iIA3G{gO6vL mZ v"N,7hCE7QT%e87ϴDP 'qV K^6la=?ZZ MtQ-]d4#?fSe10{U^6F G!6 b(9zb .26yuѪ*C=!m\ sϑU詘 b7b2߅#ǎ L;Z+Zck;Bk8^l_YmՒ߮؎LڿjS2`^{iu+$j=#1b/q] Y"0yyl?珫R^"|-TpJb8LPT) TI@.º[@a`J+.($?hE0 BmPSXUȃy5ԊI~Y=@+\ٖ elHd?ǣ́'Ȱe*-3;rxoc(Ґ&HǼ~}P0~| 0VE=D5:c< DALG*]z1 Ueíz8co1R|ߖv VP`vhQK *CB`r`Ȣ+P 1 (M00(qA2`UIzPܳQ\?)`Y d]ɑ#C喣ܗ{LAN,&zi4zAdX7@p ^6e'^Ƨ25_-[OE)ܣ=\eusF(؍rnhCt4#vਮP.j?KxJS,o!WdE( Ыw͍?&rdpTۆmb=FU >Nh/mbZOvzw(i~w#ciomM?oTۄM%RJX'|/nq N^GohXMyV~&r;ȁ_+׉}afozShԽ3 VVz%rWc7>9éRLFloUVq,XY:d.+N=%Ȱ4 ӭQDN^|րM=M̹JIl!®3V * uu; *鬜=R5un9z|>rJ>c,t"rt`.׫oV~TΎ(]|Mb,"ș3g9-u_@inwv:q-¦ll\8R 9_K1ŷ.skҝ){y/sSR4514V7]o` BϣeuBaM܆aOФ3x@J9=c⇛W;r ĒwOʉ!ѳ3 nS-,sNb(۟p̊,3tCYlCnghgt!*^G9UnPv% Ud=ͱ{05f TiG5vP,wKHYg{r0neZz𽘫Rx{uEëjgjii6m/n+i<OGL/c(7#<׮='%z:bĖ)E:'DLdwPz秶suBL7Cr 9q1"cm/5TэcgHK U:^#sz2&<;~ /?.2e?ǫ}Q<sXh}}]k'5p୭ɘSb [~Q`D˩VJ }\]BGR%oW8Ux&5 "1=o7Dޣa~zO7REWCR.x(<%xg^ԒT0^GG>7 ^w᪼+rdbxi"篆Gu|x9ĞKCHpC_\Ľ^ λ G,޶,{G 5H^^#?cѷ6< _-&K{Õ7͘4x}ؽ>O*}k発kkJc@ۧZhȢ'|Z1mE]~MOiNaFLOytx$ iψLIip[]pX~[mˡvQ3ِvUTOYGTuBpnÑɌZ@>^:0拪hzan$%? JJ*D)9d |[GI4aǑտ(r"}LaۥK`5L0J0q@\tXLś67HYjI}$bwRҍc,^[ 0p\ I-?VqUJ p2Ӌ1|tx;>P~j8jN f3'u &00!yV3*g^þHoo}ܘteqH}ͤ*zj~N'f!`P$,/~p#=݆&gLd7UΪH;.Vy[/5pE4 ȜN<\sM#HA -UZꊧl xX)$t饗}W`Օy٧;D~:0#m-^+vdhN Km䶡\yK|4*?JptCݾJ 'USaJ`:J}eJ-.{E>rYpI %:ŊMܫ>A{XaBC启~ J0̥O~92[n1ߙ@v{`&R[Pbܰ~aKkh6%k]5q4+Sm0K~bln)ZȲ܃gNf==Xv7Aš -,;t!c̦V_wp Ꮥ)$dWf7za#`zX"FPc57ƗʑQyv'[l#LM {y޳li˘q#(uO3KOzpH n3vйsCh8 C'3;%vvͺ}H \۱aAzy*o^· o?$ʂ3ˁ&em5O]#@S`K. #'SNɱp[ +8!PMD4{4./ ӆkT%pE :8*@:(,NJ "•3ќ:V2zǼʞzX;׌1g0*Ezm/?Vi>5O7>z5lj֙S̨ul4(ֵ57@>strz5 ! m|:mƾrCJ?*j1/ Hш/# !X_/[C|Ee1a@n1͎ew(H~+8"U@ǹVw5Tҍ,їqR-y`g>= pxJ3yN9Y9C땉>k>n֍x3=Y83x$,1ݺn>W*ؖ.ِA0LWF4`%zK 6.!s?:lnfp(U겙i7mf_yu7oOq|ir``=1fj7rQ]^Xz=7)mxE8"=MO\TAʴ &fl0(0ɈxDL'1 nQِ~;9.Ǐw_5vc WEt4;uӖJ>ò/i"Piz 3IۆcnqXF/d@Y(NG.Czz" >=0sM\аPOF]Cr_cBNiؿ1NE~UyOִ>>+ʡ])]ozߤݔ J&kNUyW7Nfȟn8oiхz5AF8EKLU&r\UǨٓ-#X[ñy0u 1ā±O>/{ pc2eQ }]`Je?tߔ t\&ct跃Ir{f(7ND95"u;yKw4yet|v'n~<+%̄czgƆLL7ǹ :Qy=XXZ/_??rkw̛}V*X-y_b@X,N`sg1G/TM>)X2[o35*с9. r;in}}kv,5"?}<_^q||7;]WȑXff~@@[#uIQ5}|< *8z"؉xpꭣ Bn-B-|آmH驺qGiL v|GtSWڱ&; oWv)GUx U`AE)SnQ>!ENFwJ;p4bX-31P֭6Z,,–y!L]$G6P,HNW9 !Ϝp=NTq~|i=XRM'/o@&R:"Qm9JM'L|FoLx&e-̆W+R,V,QB'GJ~ dY*`Нҹw˂3?̵5?q2q|[ [~ū|lk!?}(Y ?y ծDȃ>4A_W|F*Y=tceGy ǂ;YX;Tzʽ&qwX/䰬{f^( Up6=H)_zlӿ'C[}u8Z";\I{ANdPD@)ki+D[vYu0 I )8we,wO~ytDp%eGr摳qP<& 1Ys\ 4 ҁMxI'F3?ݼB˸bz7Se3#xI| xt ҡh9db3&p VΛkG4ֱ8/f2G_a':i8>ZoeƸYL)Arͨ[mCW qWeˏ>OL bE}7£pV왉i샖gȿ6 {ҏ},ߤo>ioqxdc7>I5vHRڸ퐿k=`.SL5 eq~W6ۜ_+TF~dH@ xxʺAܞN9:`=p˟-uK[-_ɑ#"ܢ'&aB>MKh+1dNDz80B5f G%(D1ZkynϜ9HY ˱ νagHM}g#q,7q)xOT٪ u옝R$32?6F~nȽ!WYK5zh܋ꊾּP 6Vº]s{n| @"Q~݃%LiXя WY(ZbW`!S]΁lR:A[XoDm,m')ʄ}@"*A^OehQx[.(4#yI\F#FL pj^ƦILNYeu qa7V^k}JԽ5Jӗf`Xت%e2Ñ\47ڔl~ ,;1Ü2X{U,eLa=5:*vQg@ӕ\'(\d;jt3|38XXMmP++%mQw?+xwQ}h0iFiҩ?Rgx,\m ;xgeL7&uoE-ur6Pa@fʇ6up|̉B|)QϹ7eRN=᫓Yc@ѶM$nZ,N6?! Áu Y/ ̺os,-יؖNF UVr}_Ue`;ehH  .5yX{FR70[?xUqY[pܵ_֛ʧOmwIʘpT؅"O%LYg 1X9i]nZF)-:wOmL:-~7M=!{}2A=>}ٿGaBw 3uJ:w4N0 ]g=}x*zFG%\J?^{+r:.z]Krxn^ J= 86n蘮-SC3iq噇OnJrZή~A6_u}JѥoU`0znr@ &2Аg)pLVS0l&' A;Wˤzs#gQ^%Թ]'He)Eqڝ@n 8[X&?wÅؔ$e8?J7q#:0g0wXEf^ʁ,!7.N+W<:54r"c]Ӫ8MвF]UG5Y%wd&"`!= ٸ@`KC:-?IݻfX`Х6LuBע3Tc /0lv8w?Vqu}$NYj#AWV.)QJr |jHbW4(D i,nZe.rbt6-^C 1i9EF*2rQɒk+MNO#h:,Yxȃ:<\:\L^M_1Rs &)x?0)u_p/6~ 6|+s@QFbk /5#LXXbo]7_#eX2_ ,~ZUn%%Wwۮ֮z="ZD8i)*e!b".xk6"s{Nn"}W^@;l寎~=g uе[]q9qRz{ ?o@q=<Z"5ۨoܲpU pW+A)[t}mlo&C]kkEE@x=^ qӢ{mbz"ȞUr@,a~ǒ,7Rly+5O"?U [@Vͳ'0{֐Y\y9ΜdwjjyDSu2tc݋8A8;h[Rp?Yxº3?ݞ9gڿwͭ:s0oq_lmˈg[ 6j \,Ja%e i=,//}廏Dw*u54BDJNOx.,ta|$Dr&1'2Z^f E)pwf1iW"5Liu~Z#_t[[z=y;a]frnkXniY|Y?s|hYe F`biם#G^%W]B)׮|[E^$ ]sʻo| w$3V8s{2!]Mpטz6bQ=p *䰎8l~N-Cߝ/ٶeq4ZVbFaTeHT?&8OuO+SBXSWXjdnW9aw4uSke0({>xA &%e+ [rV1-ʼn=ofgrW7MzhE륉<>q"Stӳd4ɰhlm#O86t7rR1rf}T.pS;:uh Sre MV ̥*p(>sKb XfA&o=^DPq$ @ca,3O>Cz5dIp>TY/YgXPhn ){Kp>T%:rD!A6 ʛ1}oIʾ/' MgGZOM>}z(wg v 뤛4\?2I= 8bl{iz ήoYۛpMS.dsBanyvSv6J|ݭ %K&<"*HUYayCx7 BaM)jhi:w lV`y[!Q,+6H4p%e M1f.frh{s_勲UKS'b8,O]k`)cpdq|?[B[*cy=~;cO]wDzNN/LwAUaJW"EZigC7v q´BcRt,7"iK[Z@~eҵMV{WRshf1`L,3'|rbTf7nxHߏ:kDo;r'升 {XP ?*(3C|7|+Wח.zr?$WJO|]ZƱ&_w_ƱO'BU[FMr $KbanKKP# a9a 1O{&Dqڪt.#@ 0L]$SIY '%k-njhGx4g  P=:S%k-P&P[+= ~H.GmbZkAr u`q TXd/9ͯ sh-G|E#LXW$ZhC``jjpN0V{SSзiQMm|&xoQgUb6 YtB n%VG;+@L+e0G='WAR 𶜢OMO2/ʁ Mï0̈6Y9ufd#rJ:2?nQvC|0I@xCq|pԁ Tr`(7)d` 4#9`4ry&nlʚn]_m+Rp[ïi|$d:;韹u9~, d]$勞2ۅ 1xiVxgب xTĕ6j ?.{FJ49 V5"{/ t.DX6 螢p*E˓*2\rD2/h-IK^hӖ3p=+-ֻwk݋^0>BiP7Xjإze+(|ZIC^FóTUM܋ `L˕[YaX1sI؉tLn`1}?V6pg iP Y_A -8 ( "ZP5LFq^e_ %2pUŹZFS ZC=o{8m:_ 7I,_/Mm<0IZ& pls{XLoa;}L6Ҩ ?ݴ|Wnp*~'<%G4SYm](UeL P-[mnG|mTåxzHwD:B4oEe5EnlxF,el[%ir u[&ܵy\@IDAT' rU0%EӞWE&qy4z_,>!ᓞnCejX&cͳj̨ϗPF6p+:4ܞu < >b4{ο- #@jر!{,g{Ê06t|nrꌶ#Eu/77#oExb:@(alʊ Qۘ.{hH'ۿ{[-]OI+^;6d(7,5xU"}k1ŝ+_)~2kL6@A^ɑ#COiK!nJ%i'g//^p frY2i5'2EԢ;Wts>H&8,-Yf ڏ? Rnn^94bum ^g/_qvc88>v2%WDX,YRD i]s{n|yQyo+QA=:HL I_xCxJ&|CU皴X,XIbqs(bc5Em>zSTs?塿rSbʯ5.|j]+JREI0>i>c*_9f"7V^W  h ѧq=0Jɨ)8 R _H W=XvF4%8/!`4nxlglyu0iP;w;f4gZLGoo{G}_Ng̃Rto[U\$팁aƪPG %MN ;-z p-MX]EA7cy[wC:3(Fc&|ba$Aѡ ooPd:((NXHvRoVW[wF%1#~nA2+(W0RpͿ '~0FҔ'MMӧEND% 6" 9 eL}ޝEYN]REA0^:|]*fF03 ks2r.܅K49dy4}ZT`𶾚bvjx=M"%^"K*:%l 3 b){RO˝S]`@_O^olMhNC0xy؃3&81nW#h4.65cu9BGI/s ij^ʍe~ïʟsLKNr}LMS_1jYpׄN^RO'\8h>GXlISp+z7[٭J_*]#΋1'w@?~8M\hJ~kJ@K}2h'y#>h+8$ Xyq !W꣠ t_,X6},)'ܼ}:lu_: @}bRbKpX$WyzLԕt8| ۱vuS:rβJ} KgcVo@ar\ҨPHׇ~"x/ 'iJ~0}RE{_=ȩ/^/kufTs6׵Ֆ[.8cd,m\mB ZQo>9{--mhVg&C8u§D7ĸwEp+"㴛٘ EWV w-NՙIBDk#={#C sGC3Mv__Fýa,#gy&лҞ :9 0 ӄ,+l!тv{{KN>&Qv͗{M}f/ܳ ax;5)'5s oqYy{G|+D~5"?{ -) ^uJqX(q-me.]sN ]@& m[@{K"Et̻eY~  ,PA mI ^}Yi~%p˵mE&CSO߲F393u0CE$"eO  BXRe:Gsk G{=`U qYD[x=Jj]*ERbuc+-"[z 6z,̗\ tTV4}``}J3+Wj]$gi^RBu[XZBb1R6X:۩50[1: GO@_QNxM^T 2Zs1c}+/(U}I]:~B|E }ȿ8phFus `# ˃;Ab<QEuqbif䘹)7, ŭv$l MuGj!Օ}u9uzY~deOg4Wsmoc{}jx^v#%׾%(",#N:.;ox|cr]r-D yBDc |y7֤ZFS=Umm*7+4y]j^Φ~+_R߅5gkgꃕV{FH;?s`XN孚嶻gıOT\eOJ#aS7}=V~ٜ+ʡ]!gO#)zm?'[/yiBu/#QB\b0 0Py]j0VQ&4μR.44S,M4aͩvOǴ$:b㳠%:7mtJж=:X6 _76/wR|!P%_H"kP`WJy X}}+v2 O~7ZG9Ux}*?4S Cb}n᣷P5F M_/[ݰ ve *HQe(l c=*5\ d'lGpW-ZRMH'Þ(苒(}1XCw9,%|USTE;B@kZ.Skȸ&y3n῔xb[s+,y0E| -Pw z{gﰴqY'*>$en|x ΄ø[vM41X{XTp[Fݣqa=fi280˨h <Xf9걖'?G˂"jKYs?Q/մ(;J[x4X{1O4E V@(~Θ}T*K 蛊x`d,#<wSG ;#YԬm#-Wt*RX0 agڄ Q~GQVVYӀJe* f\D|}^ a+;bO^6FRv$/3iKh>9p=QO?|SP/H)Tj'[iyR轰 4./`,>^c=Ui  !6>Y@D,M|ߺRE}Que4숑̝6,qjN|1s^vG6EPYE.+mS/&|o癰_8!9-nw罺Rx傳(U=ml[BT"^2!Wޖ ^n5g廾U]++x~#&8AP+ۣ,9i 1Uut1 ~o`/*4*B9Fd;^蟹;y,YOfM@ܦ[_ni@D>(;n[1Ry\87b_cN)Xp:bik3#`~5߾*ٞcQ.έ"yaLK۷3}xJm &/OkL%[7:# VO  [AHW4ޱfvEсœI뮰2|'s#/ S2gf=`ia:uvvuj>m)*CmW\VM\@&hS7 r+剗p'&]Ld 57ƗZզ GTXPʼHO_,Ya,Ff%$8j ?+z;?" bHoa{"M]{gDžu@C/ΰ3WɵZc<Ɩ[ۏp _{dΔ"HhZ2MW#@> FtWP2-/*)Ptc0;opS1S0(g,n憴mJjD=ٔ<84ca,w_6hS0\ #,R\iCSǿH8-Y%|f}|hTB.Goi!9>! m"KH]{1m$;8R𐮯wc'M_XY>Z[r&+Lq[#äih/EQ9V#ujNe^wŌlj~QuFBK:T'&ğa6i IsTUo%1%/:a2gCc1q}>{ⳞOVW`DYg,)| >5zG:_?YuRy F8r,__/uü-]Ojoel5(ȦSSuU\ܳtCB1[eچ"gϜwlвW.ٍ gj jSr{*,[X>n۝J`,77t 99v/[ =AΔn FBwHb'",3y0wY)Bf F\/OFCilo@3Ԡ$yH B <( +>/QW-9fY9qFB2f4/}Ge,_?EDzzIEW\)w\vP;<P^/l)㯻pU}a s@bzv%[gw&̥vU)O5ҟήrłCw7TqjGDX'9-.S?K Ven%fh}\?G[Q@v/@ЀOÀ-ͶAqs\QS8ZC'~]à`NBUw$ 𡮽88Vs?>2' ws_m6 k)O^% U&]MP\P!R<Ƕ>ᠴ8IlGT3]#5=J3z|9!Tx]ڐ~U}ΑI3Q:ⱐ?6]Uoqc薽%mt++ڂ゠boHv֖ͧnQӺw=2KW\d=, $͍wW #BCbuKsoxq&|'p pt7oV: V ;F5z6Uh'ky2n>7VElnm69fz)C::_-U/>B[#WX"5ιwDտsR+.M5C&a}9af_kS?ƕٙkzg*A8bn' #nUx^=:sX*Ac'Ғt-7}ZHnJ>F·Z'<.̃#cˣ,~KyQJ^E65բ)LjϤV&n`O ΋'Gp-[`i6G(3o:ySo)^퓰oçxJ?&f3,6o;c}KO˭-j?,ç ~\? 9[;^KR:ͳi >D|Skil0#z{?Mr/Ie棅${ӛ=gaQ'Wm>X<[$iiʐۂbs] _:z"}+_'&ݗw[~#wYI$G [wl`bWedCN|$_`=>9SBAHF>jU۶,Wݸ{1t!B71 ,fKyf{@c"]qj?KxɆڍwIzȋf/|@v_e;mmmp?"j^L})+<[ʘj@SCT, i$O <ȞbUq#~5R#yJ>z oKE[oL˗qPh9@29>1 hڻ {sFC,N"TO? F.@^1}moxC$mXZ[A1Fha,RIvXu5gԂw㣊~Xy(j%Ƃ.u"Y\xfY8XoS:Y9_8k,g1_hn ,Srgty oCغAiΎBX]_8y۽68FՀ>brC9`}g!e%&Sq=@lo<% ߵKOy*]%rMWɥI~e+%[B>[_ܜj>{Jc!sr_]\].ϖG x{9F=@w=n+<^K/ V;r:Zz)7bgOpl+H,k eA JHr5E1}vn9)'>qysF X&3$VTLg+"; ,r.zrC)yȃpԎ_AA-{䩖ϔ&[[4h^bZ 栳u48GrbϪ>d|eA<7J3\Oc?]!ȣ~=M-z_eߗkh $brOYki <|<X?ϔ:s—1ǹ"gw4Xz-y}PQAcn]}B?8 +wx qފ)<abvm|\9kyċƿ$/)Wf53ʖ%^$\zbO5 H :#>3>|{D},~șDc)g,o| >D>W[=%9J U8.C|}?-y/ڶn@c!đbY^Cٟap5b ߛhK5?&΍`1 3=:˻ߪͦBφuzcMibgco0ыny_FGtofsgo#?rZ{G9|㱰JWJtՋ,֏Օ"Qg@20!pCA3W$(Rb{h 'oZ#%i a֫)_AeggC㗭oSR-aE٦nq)Oy05LT>``%"RW4B Lt,  h^\.re01ƃίx+GSK9!HCN3gӯkYHX ߯gWWE^|#72¿uC\2|eە#ؘ .0W*Jװi:h#-`! `&5 e_ֹ_bڋrsJ9nͭVI,# %B:::ҌfOޯ.@o.+,1ߘ$pUKe~+'N%"6<~wy)ivE!cMъnCcr7yuP8[2fi)lm]o_"ȴx-^++\:_I\^B[:^_z~ { пa*L\_m(j`c :Gq­sx^EX:$8H!ʽ壌4~)Gu٥! %?zxq)3Wt%`s@e ? {S>sB;&G2&E,`!me.1q'ӽ_jLs-]qT5S7,ò/%kyAe>u¿l=l+ąۭD ~,Q^ o6vr0!?ᅮT<~G?lWr =JR|%C *X]Hx#β|ysliK1,!]+z&^%ßL?.rA>92+3rXMyg)1r , [lUԋ@AA?O )RR;q5 >|nuCW: qu6^xg7|ŵV>p&"M%$DXeQ}ppgؐ m x-H3ܛHيx`Pd̖௺CNl}odM+@Vu9I3KEdK<9rD[h_¾( 1v ָc='hPr!/-¾[vm }ǽ テ2}}@W$0@S"p4^[`os oxifܡ ^ BLkϮ;k. U ',_o'^Gex(y)`ö6aH5%c nx.+&7>g|jϋ|u2x{V|rCfwwywXע <X&|sapUK{t/re뛸P™|Fz<YWVհ׀q7ä4d`uEv_nIkzFgbɰy5tN 1+d(h5pЗUy׿V~?~v?Sچ,^:}vױycJfMÅUc> F.{`e ¾Іob䲗:AZBʳM;Z=K_:g(uՍ]DgB4+ޑ2e~ `b8Cu䟺ؙBфZ=aRY3\GoorJ4V @οBG4O_};&7@64ҝr=.{"T o^ H2~㄀`٫;LhSC:I{ }+={<g),g_3CȴwTyR 2rp{⮊tsܯP R>ZJ޻[k~{֞={}3fugϞc-cTЎl'sNރ3.wl$/sU6dg_n?ұ #Jc1$ۄ;kׂ:6oiȟ`)Qֵy#wx6& "PKD8^خDZ?Jt8ճy&҈F c_!^3WRh0$P_T7¢ &Sl}|s;غ 9juoL9(f, *9ṁg}]dtdZ8|Z0U?pv*u暾:-Y' (O1!`]e(;(XRVQv0gO>Tx{<,.!u߮ !G=38EW4 l3"l$ix|vL4m_/4RߵR;B<]XAUɇ8 '%+`&tS-LN qJ1J Uy*{_[f56I=6eVv96ީM'Sm^~co/? M*0.682>*#;iN_78M㗌g?E};;\~͕pqh5xh|r yi]W/WOG&#8M6^/lцqalFTmg'EE::~z/Ro7{QӻGяbY9wr#>nR͌#\q]CZ{>;H>UWrNj֩՟tsKJG^iE(ܯe3:`9i¸s\O"S`ȜF?e<ʐlapC㭺S}G@?$9k4*Fqh^ h0[ p?8χp|`wS=.o ;n ;0:L_,.nv_it2%uE-vxI?f"'uG%mZ/:~\{W$ߕKDڐa" }NB4܂c3߫J z/ ze_Qed'`KŌ݆@"E.NSr!;@ ;, %1o1) V lע_AhƄqĬ:-!Q2~/[%I9T>"ooU'ڵ?/Y %9#t[?}e_rq|:#S!!p_~GdWAm_-DN=IXp.G/x1~U, kfoNؒYDEN6 TTHߘy@}Pݭhls;ߑ3)%L .!3>&A*EK_{+`Lȫ=CkoV6&Id|yzcڏu=$Ja|ɬH"uO=%o*O[o(S]_w |,~3+Ftt:+?FvNb79_щȴFV8t~z'E[3!;~v NOeFx*}]vmŷX@ g KBL5Hh\T51cE1!$裌6p^0+u)6E̗ThdaH[RQXo!ف잮'n'(C Q0AE=Km9K~0z׭!\{u' +|$ d0NfZ.l'.Iɟ%j}9SVߌ[&k@sQ:{u ?l)x*_3t4~|-w ok|ZȴC؟G:|q gu'RⱷklonbxzHZTQ4 2yo:7G8"ZIXK 35̜؟m9 .X}UI HWYQ/A`QiSJmkXz_%W)h`$ jϰ@ O{¯/nz}=.u1dE |_ }wk|$Ɍw  $VIDATU- e$ 88gd| /8}аp&ØV?}$>8ym1e(~$?wÞ}xVVi;P-hyvz>/z pw1*+lPBۤ6'x]V|\4_l]X~ +~?BB%BIq`'ў5X ڰ?v:$W֔EZ3$,'e$<% G5ya#a)āG"UjYv♩Ӧk[>?۬6a}!Iz4;c7i /3bbk/|L]S㸔%m.J׸y#Pkql8D3 ||KQ}Z!@4H_' &le'i*roz#p eӺ> -u#5ޒI&KߧLn ̦r; x#CadƗaF{]7I~{^')Н1WyȚ9nY(@ Aa"uXBߚ=ms}o19*\e'IN1⹸,䦉4ĶyIN{1pU9'vYI.լ~]l7uQ5L P>k묇GBNɑ5!ɳ* LgYhM$)բ.u#t|CJ8 vf'1Zlu.cX!mmACֽL*',/|K`9o~{2rϭu,]ƒ_ L{%Ay6'3/ɓB?‡#e"zu~<|8ug": gI}IMKRpZ7#O: PADOx%:񝸻q*1ÌcQ;nĮ<`q<.Z.t&4 6'?1<M|+KesYbSHҞ$AsuCQc^d(pেmt/AEF%-&=SЪOUΉ֘ЅޘA"b: v^0A6HL}0:[B\DB"ITr6#QLg̻7iBZ,ҩBxR\'O}"~CwEH>ٓ;7½-O߸&]Va*LjM O952Iz]79+MyIXE܂|'oT[xZ%|THuP1ѵVFgE1}9b >Pwlm ^)(5(75V1EF^W^}=̠UlS?cTG/3~™{9j{&K‡\Su?$)Aߐ5/ѣ& Zx?.ϯYcYC-EyK:o0C}(P0q]V,aRvKHO$6`"a4'`rtxR.;%WiKUU<4Pû"t.|6#x"Ǔh:^I:uiO۳IZLoY[BNJWehxrp؋x5'v}4Sr^]b~LX۱F\cMΗG:deh Ljq+a .oS i׽{*As^cVU2myw(mH??`{@\@{^K%p3,VT‘!v4=Hz#AԨJX;RGY ̮`\v{x$mHX6'||{ak'ֵ"/vJpKL25i !޿ơf*`T.Ic3;<:f2R7YJ{ITM"NM|O:I]u8[^yqE闧Eq҈~p24DԐD[\M`G>}o3-41~chEdcb&3FjE?=HOԠt֍zw9q1i2/ r]@YǍ~{;#pQm(H+@*-k莬-qH}f .(]!3}Aݻ*ta_v=şC.XhlżW{%4Z'?nI X Vysa*Il&ƞ[Ě=饈5I/ppek.y7^5 Sc4Y]`!;,Q(qֶBN҇!^\W6u6[B7I!Й pӃ7;NIE2MU %5`\O!D#5_xe[TL<є뤫8 !F4Il-zSt&m=|^E}bJg˛Yݪ 2Ƌ?/.s,%{m:"77sL0+r!#e=^$4v2>B3T\xE]BD[ OGS$N$qr4sVqm+VO2K{E.]KU 1Lmڔ"5eB a x{{;9g_v_79o"6Ԑl^uĖ*ML9mM^lxZ9!5f;J$mE6%<E^wXSz;,^b 08J q^Vb/qg ?pnz5 .|uBjb|\>xt9TpJ B5yZLʐ/o(4u)BOt0ʮQJs\Eg VA+aLW; oUfLj!GHo-1҇c>,V 1)%z QѿC+~\>o{U?ŏcN㕷)SzBnxƷǑv~B`-ِ@rlIx?<3y|B!BJskZtX  rȯrRY>mg"B!ګfn6` $U_ƘR +6TFU_B!֎C5k-Ed/"` ERPRJic(7r3iv)].B]Hth aAć)2w-1X |ZJ)_!B/MSSӵ/-nW.k3sHߝϦ=ٴ79)(nW+E`D#>>X$v1&H)RjRgB!BfkNPJ] Dyߝכv|.ۚ͆yy ֚~ KȞ H'O)R2IB!nNk PP\j?Nۡndh6Dp=:dCf%Eqڀ.LԍIڈ@`PJ=kQ !B֨^1&ZGkt1Ekڷ|v+K Mb\$FDW陌zfrJW.я))p4RY,Bql3`6Ƅ0r-]^X<$ Cm/Q\iP \7f 3"Bc V*VJy!B֯րsu"O~VO-YÁ&{{t"$5Zc&<^iu t3sq̚t Q'Uk}zUB!mN1fkZk+߬g߰WGaIlܓGiy˯ t6g_\rݤÃy\9\k=CkbB!mBZYEP *$yWM`ˍ!=;\ҲrIi@st̜n8V\ysKW!A O)uot$B!Zj1fR=;s3&ӯStttH#zvjwXrGޙ94bVmͮW[97i”fڡB!ʀ"gyyIG~](C3 D4".9Lz}>eghXo^#6a{fq-D%wc\ԉZ[c!B489w c#hXoOIy*Mĭ5b6'3N9@WFBdzGp =ؼw;&=nVi^zwZXB!y7yOAzإbChN1O&.,rN&=u‚ܼzlinPZ~B!3Rl)u&c,{ [kU;Lh$ЯS )qzlRRA+EJRڔҲrH9jZ)zG2K,fdtܰ#0_ g !BVCX{h=Y5O(wN\WRaenܐɒ EHJXwcp8t#%) 7sbBߗN.fFJ磁B!hkxQY9 cSHc4#{&pC95qO>r뷺B!rcrֱJ᳜@‚܌zJ%'r3wG1ۻC_;m}c `!Bfxg0k ?_Vy/~Bo,B!\jjmuiG U؞s\9|hH=gcݮ\~ޱwqwI}6Tm_Ckiʰ#fzg%7B!%ƘZ~}DAqYڀ.Iټw?=#ZgG-W||n{}?g/q|FH=#xډ3u!5/l.{t$+){zF`yq6ox^e!BF0\l]c&z /֛oBxPoߥ7z%[O,­5\v W?"Xu_-]۫7u6vNʩ0%JqѰ<})DqՕ>Kå/|ʻ?eX!n^f /y"f-XQYY); Ot uSXĂ2x,Mjќ!Bc6zL~Q)>Qwk wdFN7+W|MYZRvG|qt[LaA 4MP_TJ23I{[ AB!D 3 ~)Ӳc'f>- '[@C!B4vRʯcyǖG,B!Qc"֯3B!Zhsj˟Ѭ B!uf!BSc&1oOZ!%2}VcJdiQ?.gZ۲ !Bbc&qϙ|VY)st_Tb{?!BZYԸf_ܾaZ)֔˄фth¥u+Bq,>i_ `]:1k=Vq)ĆcYr=!B֬>i!&XkW]{9VmXrG}ƘrB!DkdC&s;=5P%`Zd-4(E7ax-ۋMSlۥw(B!ZPZ?iM wgk/Q VoZKNóaA3r=1 !BVMAn-PCf}Ƙma߸t+7W]P Q ijOkZ;Z{kDP]=wD疥疤%٢c#L=Ɔ+sZcJRO!5k5sk!c ƘY1A34nՕ`VpOxPg %{3nhx<1h1 !BWg쥵ZWk5 {=N@IdhV>Ymg996)]|\7N߅@0lzA)a !B?W쥔>f`{Ğ{|6g,ټod.+2w rIUFJG|ccVJ^B!5(`RJY`9Z{=0s60w;Dq{eWY9l؝dBv?X=&!2.1I(tbPX%DֺƘ5b"rT-B!h_~KjT\R,1&N)53j.qX),boA1WTBAq%ey()1c-n p9_nMdp Q!WtH Qć:NccZ.K.km< bH RJ[k JbTR{+!Z' 0oLmepZ=@_km_EAPm)vۀMJMJuT&IfIQJ^[gIzhoQJ ֦^@1&I.B)u1Ǔɡt`R*H`Z{] <=al$IQѮR7RԽVYkcD WTNW|qf'r]Z뜊t!ZcPk{ZKk䍻1/;c1& c 7 Z{۪AnǐwE+,JxRŢP]@@aDHlX!t q tcL V)XjE6 `\3]kQݻw߻e\soY ncYkkCXCX eVkr,ZkONNWJCqq{He<6'cos SPJ!"1􈏠O(%D30!1!<:#J%R'_jG^i[.ڲe6` Y K]BwWQO)gZ 7r 0K+fMvzs̨H 8W) eiSh=l؝GiwZv/b"Vmͮv̭5܁=; =OyGkosIYo+p׀ZB4cL1E%O‡r´=6# z7XtkR3sX1LƘtcuZdR8u;lu;صG .FR3sHaފ$Eqڀ.LԍIFF^\eby^)5W)OOC'%NˑYf`I־>^Ek_=Sqϙ0ֲ8u/McG<7DC*-Zw^q#\JUm-`Y4ZEZk};ߨ5;Eze~l ɌS#*$`էYwB6D7ĸGm`{ݕJk [%cp! MΌڣUus!N>E^-(]kDŽca i-ZkN[l8b)ątt k&0x̑){ x'pwh&oe3痦q$D6NmQ!ڊ̋qY^Iy86]Y=9X$l68ף=3<'DZ}c&1oOڊ ʑ lf$^,rWAFSU0 gA m6;5%2C+k\\;z ڌK)g1wYdp;N􈏨{Y-^ OK_^Ug} IDATgJ.[ϨK~ qM^I)pX~E^j髮~;Vu!کw0NL[KK*fbHxrpqs6#9x SP Efc+@V .9?nWy5;xډoFWӰZBcNsnU{z0|E BT}xhnH e΢UIAk9hI*gUfM:IKzS{ rxq\rSphݮ\~3o~y[N!S1%%kjrb՛OkBcVs-/YYFk`;k8߽[[sI{6=ٲ7b%exː5˭ֺ_㟭W*><7QPHOgȟ<\.xn1v痦U0ozCCNQѫ-^3Y V-ǩfԓ*Z5 !!` ڏ,[X-]Ƈ[ޗ_Tʼ+*9z#u_ς妐`*xZE*7I5/Z``;bE*UK Lұ]V,+Tq,k>}7q```uZ!`39L3Fc$`dbBjW3s?? =VfnXHG0oʍTc#*ϽO#ǘy ͒Yg_pN?±s/5WN?h8ߥ r[G>Y V43RqϙX+wak=w]b(nR Uۦ(\+n?GL'BT7oRubr0{{>Vfў= Z|[}Usˁn͛MFۧ~$}n8~?ln jڷLeyIyU/s@> \Ibl>K [T2SZz/YgWq8(2lQJ-PXw?ȔALTVǚNL`Y){8Ncn$T{+xŸg>]g ey>gG,+o ވjJo !j${4F];^47j<.1Z+R i_fq4_m}ε*OII)IMMy|?@{{lR<㯮_R{pN/,u;{ņ}A􌯹3YO}kջٳg3{R`·TQny1\3j109;{\<}3O"\=Z;KkԆF,y+Ϣ\>\v ACt?~=>Nd٦,~ܾ2+8kFN`|L9XZΌ/e^7sSX))hi&r^ L\EZVnswffA%{cjC7yt 綉CA5>lszyؙB!=Rg+E0W-єn|Ih`@Kut gэS ZBn}c9I⢓zq = ;2_),bFg5뮅BߨZEK1RJMZ6%`Mbl$^Y-o rLVmͮwO;ZZZj 4.1cZ_޶>Ve ;rNfDV^"%>1a$EDTH ..MP_TJ23I{[ Am[ARWZ _) 0S m3/cx5>+FcX_y)8;3'301cL\Tn'Wq{ZR/Z1#X hgo<5N;nNi'~景2}@^6H=+F#4͟Z)丈ەTLV̻jc$b٩:C)%IBh0nh%` }D^"yќ?Y[llK6dr~sW JZL߅qfR6'ƘB!? ĕ#P ņ'sW),(iC{x,)*/J?Xʼ%]xq(nZND5Z񃒨K;e-B90 q"춳ٺ/7doΡ5\EŮs1|Z1I7uq)]ý8BŖ}H{AwX㬅>򖝼mƒꓞo} ]ٺ3=#Xt㙄`yUkG5.f)-;5Rԟc dt&95up>r)x0 QyE%8\:G.Ukewq߶=S1 Cz`>i*"C0|F)%Q[ Zx<BG]7f BW͌SSd(Ƙ.5-p;&J\hd <8eџ]Pt eSal$x g IG7#ťܹ+O|WBݼwtZ)5M)UNQ)n3x?g%iN_n1Z[=!ڈÙ]&@*\)  88H qUX |[qi@}Zq^^wqA=@~=@>\w~HvA:< p:u%J?/Ù1SJMRJYRcbogk/Q VoZ boڈ@er?U7q@GNzG$N{gr0 '3}&bE;V.y6P| bq-::8!8HشΌ=p@/{6j$ dC&s:vqRMV[0g*,\)5ee3%~睈&ZRJ`+"(_?_j;$w.1ale '<(NPmԟK8/:o*/H`5 8 wQL+NuƘQю8xH>0ozn0Z.gDoɺduZc^4̊ gz礡|vkrvl.aAn$}9#Z)<soZ=mG8 pz^Osqϭ ,ĩrxA} H©ѐK7q?V]wYz}T1Z{/0{"g.lo:{+Su]lXP0lVJ=zA)qFH,ۍ. '{qfv8Ppf i YZ`~Eֺ8 q8S8/X0Γv)'q G~Wqq4ހ`"aByJ?-yjc̥4(lclͷ[YK{5b޽#'މYy(RJZ* 07~>"/ ֦q>=$IZkRcJq>QlҕR @Rj 0wgӱֆk4mcLZT=^Z]yfawo?s ٵ  V$D56ENQ$?!W?Ƙ5b"rH4PƘ$`v+C5k-Ed/"` ERPRJic(7rv)].B]Hth aAć)2w-1'R-Kv\oٲţ)f[kk1SZƘ*C5˞"@1yE%QXRFqrcK+\Zv"<(bB CD"Ѫ?KƘ8堾Z/.vOχ|BZkVP_xiurYC|6Ϧl)dOA1wZ):D5&Eh&2 1W1GJdr+qu(V[k{)Z2th}V RR묵c5~>MtR2\R!ߝכv|.ۚ͆y"έ5܁=; ѕ=ORm+Jeܮɛ/;&7F*rc8T6jpŗ YS\UAAN.TAUZC ~!1ťjq6>JNf\lRvIQa6 Suc6"8л:C)RjR*eF-Z83y+_o8>!oBFךF^? iIZ;r{eњmz Mb\$FDW陌~)!n&t Kx*Z? T3 d[%oBFךF^?: i `rg?h[N,yI-@>\I^Ӡ"Cn@fh;D*jTJ=jHmѺ)Sэsx7q51 !y_ky|su"O~VO-YÁ&{{t"$5Zc&<^iu t3sq̚t Q'Uk}ZM/ˁC,O#Zχj}!17[kZ^f==Ehrd? KHemB՘78k) (O9H4/V JoY@TH ɕCzv>ie咖t0ޙ9:q>75K߃g#CpRfH_']_e@`95téR ,/qB$_CGƘYJ_B4oϘLNG4R!AّR׫ayg0r3Y5^m5VJXtM SڛO6kdg_?Xd&s1@{ˊޭx]8fq`NɻY5{ G`2&pp0gqN/V? p;moV'U>'o@tg @!Ͼa:-psEV#mij/dErNQtgUnz}".9Lz}>eghXo^#6a{fq-D%wc\ԉZ[cK|3: 'wo98YM8<@4 'RqozU܆6No Oĩq?VOr(%c'NS8;b*ߎdNh} Zq{N{6QnMs}Iχ*֪e}Uɱ\47[!b6'3N9@WFBdzGp =ؼw;&=nVi^zwZXǢ5˯X`NS.N@898nLg&#Яoqq89Ok'':g|$,v^o}8y/{uV ˷>.-7-$!2k8ؔT 883h-W|Ǿ|gF QU lodž8cEyXp"]֗ۖ84S g; '܏s&!4MQY9 ۻsB%TAA@(EaUE) 6VY XnU溺ղmuuuER4)s;$&z^w#wkBGt- _1~r bϼt~]|g~sBO~z;MK`>O<>uH#8OC% L5r+n_DDsU!e55TzF%zZR|J.7&hQ/ vK:dƛ!d{ܾ湎5>Wt=&-[Q!y>#'>딲- ފm 2hu|h? )HAY Zb/""a~su"MI >ȨCಓ[w?w/]oxLY-}6snY:4E w9)bY $;$]d{{90,K95WȹI4O<ɝnOӐd$#|;~q: {)yZqڷ:R/ /_w.h/mf),Z]7{>>7ԮA#ZTs o&|ķ?LQr%>84PWT9*+kS?K-9!? )notݜy|:H}xP5J tm7yyߗӔ Sb͏;~nzflOAw\:dBJRZl òHyTO<\~R 69pd;hy֢to07ŕ`jH& ;:2Î= ;KNfG٪I!'g~+z\{.{_DZGFˣwEz-""kť/ax-O^}{w<ş䦇cz˻+ =-%p?dD y/__09tͪ<7 4[4ONubYDDDD**z=};w{.M, ;v{LIoQ]t*#gwexN?)]1b_M: ,(KOn1ˇ_XЇ9s<)5 .ٴ}7;zwdx _𧩟u_OŸ.<ocة|*54WdKykO'Hǣ^4y) e#SBH +B4g?$g)s jme?18~K,G'R+dpgHj0Aߺ{}^li#czdu?d/RKWԜ.F*Ewؗ?Fbȑ#Zʅwjsj,D#I\ ޴ϒZ:U֪-V9;~M+7zFO e-<|b6éаVR1ugpyVtf;=&$׉};hi (>N o0O;Vkm[_fةl۽ԮvF,۴5nŪ8o%c.ɨx][4DZGѳMٹw?3f/.ZDDD$_Cv\9. qsr3o%Z=HO'[}@:ֳ7.91Wp-I=O5?~iI%x}+es ֚\m/}P vәmշpaRs[y;[}5[6䒓spXKr]{ו_ȡf=iӦVXQ6Ksp1S 0ι1wc';R$u^g/<\΢5h Ych^&գh~xM]SZVrZi}YYYlٵwaqK6la-|j7mS()%ιG+V@9.  *9Xk KJn\sZRu悀a9wucOC)X8}ι7MrUu=ogk_,/WMsלEH#"""RAs1Y/X4׋͞Z;; j;^v۲k/LJf ҵő """4ιݟō/ǿ>:n ڼHи^/IU4\;`^ V̼!֝~h|EDD$)ιAjmnNGݭ) dʂUIGcI0C#0A1іi 7mKi """R Vxzʺ-;#NiMj`OZ>tiۄ>m+Yߺ+y/HX= u'e AƘt.I @[""R3k1>{ޒƹ5=FNiMxL7LjZ0+F1J Gծ^hΌ=IsusG]Xl1xt=Vo^m"+ߖP6_',%װsWX>t=h*WdayUW,|/ʾs]X|miEqZJul#ee$#Q*Is rb[5{)ik֢Z6%mUv`.ZNqs(U. ;xY {iii_FTp kmϾąN)TO&k߄Q%= ?:%:=)}/Vo**(rjxzE59e'0cns}]{3Xѝxt=9peYko?"""u΍2e?,bsk_re GԬʣ?~ԭe^zk~) =h(*pZs26AiR9]EѾaLOPr ƘQIDD8 {Z5P ⢱8mc~8z=k [v噏]{R/V} CaoLL#wEY M+g9WAwκ`賳̄ϖEѷ2iP<~yF97:--$""Rιι׬7åO筯W1qJ&[I:<>]I)$ ɔi.ܚ+=4s*="~GlV;Z˻#jV寃N岓[?_"""ښ Na٦)i{aۊN,H ˩RRww !nAmQVkq)w_# ?dVU?l续;r+J5Ѹn&ԫIhyD-6˱ ꐞf897b1זsUs[kjoe9|_gs1 Xm)ť,)r`̙ׯsApй57:zFtn ظ}q=ؽ/=rY. Ҭ!RUHFtTˠn*ԯQ5`ɳιι' --MuDiӦqFWcǎl޼˗ӹsgƎˉ'aO6uT6lHv>m4zz!훨<5oޜٳgSnBs64k}_3ػb~,$0;~ 1 AZ9wF jVKoPZJ`5-U KUfQլY7xK/%KvZV I]v]+B.] 3fk-(,x?R,]Tιӝs㭵mcpmrpƭd27Ҹnuzf²l1Ykc^ޛv1挴cUA ;pAC 1ۃ 3A 3~cx܀ Nϯg<--̴{.VX K/| 0 βe2d<[f=_lgq͛7gС,^{エoߞ2|zy5j^vӧO?cǎeE}GӦM8p }Yz7s=G۶miٲ%SN=Aecʕ 80K/塇Clْ7| og'<}μ"8n-km_'tX7qJڏ~M՛J+ɰ\5D%!$"ꎔ&))ݻ_۷K/}ݗkvYv-ƍJ*=y/~ MwK/tq.\Hϙ>}:z+3fH:0}ttBnݲٳ']v-Zw?-tl—_~ɸq[ywǢE{իm/]={{aɒ% ߏŲe`Μ9_{O>]xt9Sn^zFq-0{lN9b(sιc/Տ ]ng7kR˲$r uSEn`r[5L eMY$ݻ7o;wZKfG>x`vĉp :!Cp}qaDڂHkgg㹽gnC]*rʕx86<p8%|S-ҽehzW¹yGXr%]tQիW/?#{!xu~sN֮][r+N?pnݚx_8͛<}<ϫVÐv5ϫs=_O>ɔ)S8q"wqo'|r~ RT4ˋ??ύ崑#}j_lڴ+V0kq4ɕ( Ry\}|7L8B_~}>hΝI'oM,ܙ3grW0{l8 V_SNeȐ!̞=MҢEm=;X|9m۶="#/ 2^zѷo_=\j֬ɀ8q"}A̘1CY{!NG ٙn{4紹kûZk^ָۀ+V1kHQ`)4ܹsi֬YvMmaGp ̟?'u30Wb7I8vcε*veϽ[iW:FGcF&~~? O %h>6/ @2ϖ!% 336m*uQd۪^z!jذA3hV2}||XQ\UT""Qvee9P<>|Lb8.NHO&ޡ@ .Ҋ/> cU7"RI:5D%!wx>nJd 0 8+أ`>lK<~i ~ޯa/m<H ˁ9'u!3 ###nz?D׾ O9 ~a}6.I I4-ܟ/Y Xtz'[Y@{ s5o{s5mtbs~kt|~ ؙuHCڨQF1bd;Qw@D-ZC58NKK{0>II5̀@sk`˱ԳYBn. DO6&/<?\Ei.`~uhCJn`Q>zS%=peQIJV&T^ŗiF_°;| lb@U]ް8 禲Y |s_jVsk--7Z%<|/uH z5>Dk:~8|lsv5:fvc^*ڈu;MIDATHըNurkT97Z3cL D\_M|= Wĝ-~yPgyqW4xum%pFxp85p2QǤuHU/ ou3q}`dut$ Ok.Ǘa< |~Ə}_/|3~x#=7^7|9k*(ܯCDDD*UҙxSƍ}c iiicvKNWlInI IP;~~ T6q+}[奨m4{(Gc{"""RTIOc°spN뮥#Fj)6:ֺYUcr`X.$V*IzIHԱ9 >k0c̸$eFEDD׳Mc,_wƘ"U3 }Һ4;"=+$%G; ؓy*CěPr%sOYkmѢEԽUCu7tRLO\ OEDDVZιW),O ޲a8m1^df㜛aZ-EDD*}^Y{Z;?꾔we֢_\9ѿK:+{Jn^1-L """@LxǚBsccvGݏ`֢8ԯ߿lupZ c+l """cA`I#s>p1'h^c`ywG ZǢ(0Tڍ=Q1HUwsƘaNjf:'""""Ip^jym*0HѿC3-Ƙ1廉MfIpés;cL/k""e(ι;29,կO#""""R\7ɩQAD0HQ݂-ꎔ@q$oS=*yyB6Hp9 pY)߄?/""1(HQF,< L:: tkt`rxq@SW 3BSU9p3XOVGg^G:<ɇAHQ -pnܾ:K.ŇѺ/fxazQ1 9%u@a5k@ RDb9#τ[Y@yU鼶Ň˦qm/H7kl1i㣀 ^ a9~C EN/A؂K8''= ?Zh{ ^prF!k|%gWI=^cf]} #o{ 1E7,.fZRgN;"R4 "=prm/. ^L= ~no ŏÏ ]LI=^cZo|=|> ؄1#/_ז{Z90Qer+90b#QL4z9 8"HQ`ZǶӁ×A,[~/7h z$>> 0>k7"L2yfp`9 |}9=IYK&?8#cg&{+k HYDJBea!g#ù5%EA^cIN)F~Kσ(~6DŽ$%uu Wg59/UqfiYEz1?`L `+ֱH,""P2/0' %V?kI͸g{{/!fiZuӟd'~t7D7FKanŏ>om̘{e8gOș ͧ~?:MYDDDD g};n fBYϦ6K#,""""=p97M΍+;&~yrX]q>@70?,DĮ[{gKG_$"jEDJ3ǁWGxC rnoYK. ,Flis8pElbCҲWnr&7AD?fy="POV'}M{YKlf%+,-"rgpLec,X4""X~KDD,";7ziyZD)H>%=oTP9ȡMYD$ ?펈DLYD$2_!""6f<<~uGDD$r )LD0ܑ5Ƙd~۴iM+V LQAkݘ6EDDDD9*++kyPdeeMBs iY ru Mkm}?OJ kUgpT-RH,R8zA0Z׫4v*v+ݦEʕXi+ԫvCu@DDQw@Dr8. `6sܜ/Ԃ7nCfF:,ًK"""eH᜻9筵˟ԮәmyIOĮT'"" A`swc40OwʧUQuJ"""eH cꂀ'Ԯ_Z܍kNkC)E"✫{Z;`,?1׾X^jׯ^9EDD fAP9ۖ]{0f2-^Wjׯ]5Wo8-$니' "9gV̼!֝~h|_)OEJYc" /|ӶH"""RiZ9lQX)FEJv#Is=qN_a~ML#""""" hY$b[5{)ik֢Z6%m,2o59 """),RTTn9H`2e/EDDJHL/DDDJH4kZF0D^_^amm;ȡ٨; abn=i')3DHaު![5LI[e֢)iEDDD$jRh(0H5zRjSSAYDDDDJDMɔi.(+YYDDDDJ\2ee"/ """"Rf-ZhgŰ "@QwADD*0EQ)0HZQ[z =wGDDʡ eP`LV ޯ|3wRLO\#""XY E,-ҽej&uO4buH97sv[#z´5fZM-W/ԂAz9v1fo)vODD*\fZ6{nҔ_`̶QW?0ƸRH,r3Fי;zw 4wMDDLP`9UwsƘaǣHYbDk{k@eICIE)ma9 `1|c̻QGDDʴO )SCY$"ea^JwƘ^yQEDDʼ)b)SKYAD1ZkG ) %uv ~iӦVXQ&LMfXcңs>p²Hvf {/pkm+V| (kSH)n7F|})~#0XCAYDDDDq/ \ mwJdt~4 ˠfIΩQw *aI@YDDDD$fHVw펎Ch>M?s3נ"'+jq3{4l7E뮈HP`^`?~cA(;U߄?)B&"'wjws3XEDDJT$̀s+Ձ'ULsܹѸ}O%FW YR!1p3~@p_ϰބ|4/at`2> _o^}sx\#&&rLհ3wyFܾ{)W.""RL R <Go焏S3+/ܒmcE\.w:gǵ:| _|@x;w?3/Kb_=7뀎G +>Z&yaUx?v~M^m T#E`>A;રOr`H(0KE>ŏwAr.> _-LjysvFOG[tnLn@:p8ܯ7|ɫ/|>x%^`3da' |T4G1hq΍{"pP%lg|ڈ—jM$^ת;5b_RJ&hl>Z|!ܗ{#sn2MnA2~yRܾfekܶe,fm.d7p<4|,|9~|1Aq F@W(B|H2\% _;|eҦ? _V28A{wmKͫG7}0f!ْ[Gc󞈈$M#R|,V ak u>m-(—cP6~/+w39>ԍOć+ćZ,E*HF&>d~ Eu$\b+L6LUdޛ@s|B'""(ѓ0zR19zVG~޷(cdW T4>\g8RF2R="',Áޛ )*I@YDDDDff9$:h@>=jEDDD$%j~FNjR7f/;x,sEDDD$%lH Anӈ"3# c̅mɞ,"""")1kZf-Z[ %Z=HO8^1j^fIYF2s~3rE]1s ۾T(:sGApƘ()0HQZ܍kNkscYk/V꜈H^';49Z{1ⶩ,""""Fc7Ƽ6EDDD$%VQZsczYkEswesY_FEDDD cLzw.5Ɯj]EDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDʾ`9pv1W6R!0 mwDDDDXr`;ШNێ\10mwDDDDY \a@ ~QӁF`P G-qmMVCw*`&9׀ @ŀ ux"""""y (La9|8>&|p>Pҩo@`Axj}|~o BW~\ @N] | ^/Ja[DDDD$i~8s:!|\2XIFK +3 &|> 2Mx_:>0Mk9#9DDDDD?[?G|x̀qۖ=__? >$ùŗllGĵ17E JBEDDD 9xF Y`w6Y? P?|P'l(+[>hwLy'{4nv=e䌺o[n[pܹ57^$8 onovbn=[ [W qm~/Xz#Kx97 y{ۯŗB . ,qm&>3~V  lܷ saRMy ?$<|I(8?Z^S[S)0&z@{rn+Ȼ"""""""""""""""""""""""""""""""""""""""""""""""""""""""r)dU}DIENDB`fastnetmon-1.1.4/docs/sFLOW.md000066400000000000000000000001011343111404700160650ustar00rootroot00000000000000Page was moved to our [site](https://fastnetmon.com/docs/sflow/) fastnetmon-1.1.4/src/000077500000000000000000000000001343111404700144605ustar00rootroot00000000000000fastnetmon-1.1.4/src/.clang-format000066400000000000000000000027271343111404700170430ustar00rootroot00000000000000--- BreakBeforeBraces: Attach AccessModifierOffset: 0 AlignEscapedNewlinesLeft: true AlignTrailingComments: false AllowAllParametersOfDeclarationOnNextLine: false AllowShortFunctionsOnASingleLine: false AllowShortIfStatementsOnASingleLine: true AllowShortLoopsOnASingleLine: false AlwaysBreakBeforeMultilineStrings: false AlwaysBreakTemplateDeclarations: false BinPackParameters: false BreakBeforeBinaryOperators: false BreakBeforeTernaryOperators: false BreakConstructorInitializersBeforeComma: false ColumnLimit: 100 CommentPragmas: '' ConstructorInitializerAllOnOneLineOrOnePerLine: false ConstructorInitializerIndentWidth: 0 ContinuationIndentWidth: 0 Cpp11BracedListStyle: false DerivePointerBinding: false PointerAlignment: Left IndentCaseLabels: false IndentFunctionDeclarationAfterType: false IndentWidth: 4 Language: Cpp MaxEmptyLinesToKeep: 2 NamespaceIndentation: None ObjCSpaceAfterProperty: true ObjCSpaceBeforeProtocolList: true PenaltyBreakBeforeFirstCallParameter: 100 PenaltyBreakComment: 100 PenaltyBreakFirstLessLess: 0 PenaltyBreakString: 100 PenaltyExcessCharacter: 1 PenaltyReturnTypeOnItsOwnLine: 20 SpaceBeforeAssignmentOperators: true SpaceBeforeParens: ControlStatements SpaceInEmptyParentheses: false SpacesBeforeTrailingComments: 1 SpacesInAngles: false SpacesInCStyleCastParentheses: false SpacesInContainerLiterals: false SpacesInParentheses: false Standard: Cpp03 TabWidth: 4 UseTab: Never # Sopported only from clang 3.7 # AlignConsecutiveAssignments: false ... fastnetmon-1.1.4/src/CMakeLists.txt000066400000000000000000000654161343111404700172340ustar00rootroot00000000000000cmake_minimum_required (VERSION 2.8) # cmake versions: # Debian 6 - 2.8.2 # Debian 7 - 2.8.9 # CentOS 6 - 2.8.12 # set(ENABLE_GOBGP_SUPPORT "yes") # We should set compiler before project() call if (ENABLE_BUILD_IN_CPP_11_CUSTOM_ENVIRONMENT) # We use custom compiler too set(CMAKE_C_COMPILER "/opt/gcc520/bin/gcc") set(CMAKE_CXX_COMPILER "/opt/gcc520/bin/g++") endif() project(FastNetMon) # Unfortunately, Debian Squeeze haven't support for this feature # It added in 2.8.5 release: http://www.cmake.org/cmake/help/v2.8.5/cmake.html # Get convinient paths for all system folders: http://www.cmake.org/gitweb?p=cmake.git;a=commitdiff;h=a262fe09 # include(GNUInstallDirs) include(CheckCXXCompilerFlag) include(CheckLibraryExists) # Enable it and fix all warnigns! # add_definitions ("-Wall") set (FASTNETMON_VERSION_MAJOR 1) set (FASTNETMON_VERSION_MINOR 1) set(CMAKE_INSTALL_SYSTEMD_SERVICEDIR "/lib/systemd/system" CACHE PATH "Location for systemd service files") if (ENABLE_GOBGP_SUPPORT) # We could not compile gRPC without C++ 11 set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -std=c++11") set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -std=c++11") endif() # cmake -DENABLE_BUILD_IN_CPP_11_CUSTOM_ENVIRONMENT=ON .. if (ENABLE_BUILD_IN_CPP_11_CUSTOM_ENVIRONMENT) set(BOOST_INCLUDEDIR "/opt/boost_1_58_0") set(BOOST_LIBRARYDIR "/opt/boost_1_58_0/stage/lib/") # It's really nice part of this custom build process :) set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -std=c++11") set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -std=c++11") # Disable warning from Boost when compiling with gcc 5.2 set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -Wno-deprecated-declarations") set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_RPATH};/opt/gcc520/lib64;/opt/boost_1_58_0/stage/lib") endif() # Specify full RPATH for build tree SET(CMAKE_SKIP_BUILD_RPATH FALSE) # Create builds in current folder with install RPATH SET(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) # /opt/libgobgp_1_0_0/lib should be mentioned here explicitly!!!! We link it in runtime SET(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_RPATH};/opt/libhiredis_0_13/lib;/opt/log4cpp1.1.1/lib;/opt/luajit_2.0.4/lib;/opt/ndpi/lib;/opt/pf_ring_6.0.3/lib;/opt/json-c-0.12/lib;/opt/mongo_c_driver_1_1_9/lib;/opt/libgobgp_1_0_0/lib;/opt/grpc_0_11_1_7a94236d698477636dd06282f12f706cad527029/lib;/opt/protobuf_3.0.0_alpha4/lib") message(STATUS "C++ compilation flags: ${CMAKE_CXX_FLAGS_RELEASE}") set(HIREDIS_CUSTOM_INSTALL_PATH "/opt/libhiredis_0_13") set(LOG4CPP_CUSTOM_INSTALL_PATH "/opt/log4cpp1.1.1") set(JSONC_CUSTOM_INSTALL_PATH "/opt/json-c-0.12") set(PFRING_CUSTOM_INSTALL_PATH "/opt/pf_ring_6.0.3") set(LIBPCAP_CUSTOM_INSTALL_PATH "/opt/libpcap_1.7.4") set(MONGO_C_CUSTOM_INSTALL_PATH "/opt/mongo_c_driver_1_1_9") set(FASTNETMON_PROFILER OFF) set(FASTNETMON_PROFILE_FLAGS "-g -pg") if (NOT CMAKE_BUILD_TYPE) message(STATUS "Setting build type to Release as none was specified.") set(CMAKE_BUILD_TYPE Release) endif() if (FASTNETMON_PROFILER) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${FASTNETMON_PROFILE_FLAGS}") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${FASTNETMON_PROFILE_FLAGS}") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${FASTNETMON_PROFILE_FLAGS}") endif() execute_process(COMMAND sh -c ". /etc/os-release; echo $ID" OUTPUT_VARIABLE OS_ID ERROR_QUIET) ### Executables definition # Main tool add_executable(fastnetmon fastnetmon.cpp) # Get last commit hash execute_process(COMMAND git rev-list HEAD COMMAND head -n 1 OUTPUT_VARIABLE GIT_LAST_COMMIT_HASH OUTPUT_STRIP_TRAILING_WHITESPACE) set(FASTNETMON_APPLICATION_VERSION "1.1.3 master git-${GIT_LAST_COMMIT_HASH}") configure_file(fast_platform.h.template "${PROJECT_SOURCE_DIR}/fast_platform.h") # Use new Memory Model Aware Atomic Operations # You could enable it using: cmake .. -DUSE_NEW_ATOMIC_BUILTINS=ON if (USE_NEW_ATOMIC_BUILTINS) message(STATUS "Will use new memory model aware atomic builtins") add_definitions(-DUSE_NEW_ATOMIC_BUILTINS) endif() CHECK_CXX_SOURCE_COMPILES(" #include int main() { uint64_t x = 1; __atomic_add_fetch(&x, 0, __ATOMIC_RELAXED); return x; } " HAVE__ATOMIC_ADD_FETCH) if (NOT HAVE__ATOMIC_ADD_FETCH) check_library_exists(atomic __atomic_add_fetch_8 "" HAVE_LIBATOMIC) if (HAVE_LIBATOMIC) target_link_libraries(fastnetmon atomic) endif() endif() # With this flag we can disable PF_RING build via console: cmake .. -DDISABLE_PF_RING_SUPPORT=ON if (NOT DISABLE_PF_RING_SUPPORT) if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux") message(STATUS "You are running Linux and we can build PF_RING support") set (ENABLE_PFRING_SUPPORT ON) else() message(WARNING "You are running not an Linux and we can't build PF_RING support") endif() endif() option(ENABLE_NETMAP_SUPPORT "Enable Netmap support" ON) if (ENABLE_NETMAP_SUPPORT) message(STATUS "We will build Netmap support for you") add_definitions(-DNETMAP_PLUGIN) endif() if (ENABLE_PFRING_SUPPORT) # Set path to manually compiled PF_RING set(PFRING_INCLUDE_DIRS "${PFRING_CUSTOM_INSTALL_PATH}/include") find_library(PFRING_LIBRARIES NAMES pfring PATHS "${PFRING_CUSTOM_INSTALL_PATH}/lib" NO_DEFAULT_PATH) if (NOT PFRING_LIBRARIES) message(FATAL_ERROR "Could not find PF_RING") endif() link_directories("${PFRING_CUSTOM_INSTALL_PATH}/lib") add_definitions(-DPF_RING) if (EXISTS "${PFRING_CUSTOM_INSTALL_PATH}/include/pfring_zc.h" OR EXISTS "/usr/local/include/pfring_zc.h") message(STATUS "We found PF_RING ZC headers and will build PF_RING ZC support") # Enable ZC support add_definitions(-DPF_RING_ZC) else() message(WARNING "We can't find PF_RING ZC header pfring_zc.h in folder /opt/pf_ring/include. Will not compile ZC support") endif() include_directories(${PFRING_INCLUDE_DIRS}) message(STATUS "We have enabled PF_RING's hardware filtering option") endif() # Our LPM library add_library(patricia STATIC libpatricia/patricia.c) add_library(fastnetmon_pcap_format STATIC fastnetmon_pcap_format.cpp) # Our tools library add_library(fast_library STATIC fast_library.cpp) # Our parser add_library(unified_parser STATIC unified_parser.cpp) target_link_libraries(unified_parser fastnetmon_packet_parser) # Our ipfix database library add_library(ipfix_rfc STATIC ipfix_rfc.cpp) # Our packet parser add_library(fastnetmon_packet_parser STATIC fastnetmon_packet_parser.c) # -DENABLE_SNABBSWITCH_SUPPORT=ON .. # Please also comment out line: set(ENABLE_LUA_SUPPORT yes) if you want SnabbSwitch support if (ENABLE_SNABBSWITCH_SUPPORT) add_definitions(-DSNABB_SWITCH) add_library(snabbswitch_plugin STATIC snabbswitch_plugin/snabbswitch_collector.cpp) link_directories(/usr/src/snabbswitch/src) target_link_libraries(snabbswitch_plugin snabb) endif() if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux") execute_process(COMMAND uname -r OUTPUT_VARIABLE LINUX_KERNEL_VERSION OUTPUT_STRIP_TRAILING_WHITESPACE) # Extract major version number from Linux Kernel Version string(REGEX MATCH "^[0-9]+\\.[0-9]+" KERNEL_VERSION_MATCHES ${LINUX_KERNEL_VERSION}) # For tests # set(KERNEL_VERSION_MATCHES "2.6.32") # We need 3.7 or more recent kernel here because older kernels are buggy # VERSION_GREATER comparator available from cmake 2.6.2 if (${KERNEL_VERSION_MATCHES} VERSION_GREATER "3.7") message(STATUS "You are running Linux with enough recent kernel and we can build AF_PACKET support") set (ENABLE_AFPACKET_SUPPORT ON) else() message(STATUS "You are running old Linux kernel and we can't build AF_PACKET support") endif() endif() # -DENABLE_AFPACKET_SUPPORT=ON .. if (ENABLE_AFPACKET_SUPPORT) add_definitions(-DFASTNETMON_ENABLE_AFPACKET) add_library(afpacket_plugin STATIC afpacket_plugin/afpacket_collector.cpp) target_link_libraries(afpacket_plugin unified_parser) endif() # sFLOW plugin add_library(sflow_plugin STATIC sflow_plugin/sflow_collector.cpp) # netflow plugin add_library(netflow_plugin STATIC netflow_plugin/netflow_collector.cpp) target_link_libraries(netflow_plugin ipfix_rfc) option(ENABLE_DPI_SUPPORT "Enable Deep Packet Inspection support" ON) if (ENABLE_DPI_SUPPORT) message(STATUS "We will enable nDPI support") add_library(fast_dpi STATIC fast_dpi.cpp) set(NDPI_INCLUDE_DIRS "/opt/ndpi/include/libndpi-1.7.1") find_library(NDPI_LIBRARIES NAMES ndpi PATHS "/opt/ndpi/lib" NO_DEFAULT_PATH) if (NOT NDPI_LIBRARIES) message(FATAL_ERROR "Could not find nDPI library") endif() link_directories("/opt/ndpi/lib") include_directories(${NDPI_INCLUDE_DIRS}) add_definitions(-DENABLE_DPI) target_link_libraries(fast_dpi ${NDPI_LIBRARIES}) endif() # We do not enable it by default, it's testing feature # If you want it please build with: # cmake -DENABLE_LUA_SUPPORT=ON .. option(ENABLE_LUA_SUPPORT "Enable Lua support" ON) if (ENABLE_LUA_SUPPORT) message(STATUS "We will enable LuaJIT support") add_definitions(-DENABLE_LUA_HOOKS) set(LUAJIT_CUSTOM_INSTALL_PATH "/opt/luajit_2.0.4") link_directories("${LUAJIT_CUSTOM_INSTALL_PATH}/lib") include_directories("${LUAJIT_CUSTOM_INSTALL_PATH}/include") find_library(LUAJIT_LIBRARY_PATH NAMES luajit-5.1 PATHS "${LUAJIT_CUSTOM_INSTALL_PATH}/lib" NO_DEFAULT_PATH) if (NOT LUAJIT_LIBRARY_PATH) message(FATAL_ERROR "Could not find luajit library") endif() target_link_libraries(netflow_plugin ${LUAJIT_LIBRARY_PATH}) target_link_libraries(sflow_plugin ${LUAJIT_LIBRARY_PATH}) target_link_libraries(fast_library ${LUAJIT_LIBRARY_PATH}) endif() # pcap plugin add_library(pcap_plugin STATIC pcap_plugin/pcap_collector.cpp) target_link_libraries(pcap_plugin pcap) find_package(Threads) if (ENABLE_PFRING_SUPPORT) add_library(pfring_plugin STATIC pfring_plugin/pfring_collector.cpp) target_link_libraries(pfring_plugin ${PFRING_LIBRARIES}) target_link_libraries(pfring_plugin numa) target_link_libraries(pfring_plugin ${CMAKE_THREAD_LIBS_INIT}) # Add action for hardware filetring add_library(pfring_hardware_filter_action STATIC actions/pfring_hardware_filter_action.cpp) endif() if (ENABLE_GOBGP_SUPPORT) set(GOBGP_CUSTOM_INSTALL_PATH "/opt/libgobgp_1_0_0") set(GRPC_CUSTOM_INSTALL_PATH "/opt/grpc_0_11_1_7a94236d698477636dd06282f12f706cad527029") set(PROTOCOL_BUFFERS_CUSTOM_INSTALL_PATH "/opt/protobuf_3.0.0_alpha4") add_definitions(-DENABLE_GOBGP) add_library(gobgp_action STATIC actions/gobgp_action.cpp) find_path(GOBGP_INCLUDES_FOLDER NAMES libgobgp.h PATHS "${GOBGP_CUSTOM_INSTALL_PATH}/include" NO_DEFAULT_PATH) find_library(GOBGP_LIBRARY_PATH NAMES gobgp PATHS "${GOBGP_CUSTOM_INSTALL_PATH}/lib" NO_DEFAULT_PATH) if (GOBGP_INCLUDES_FOLDER AND GOBGP_LIBRARY_PATH) message(STATUS "We found libgobgp and will link it: ${GOBGP_INCLUDES_FOLDER} ${GOBGP_LIBRARY_PATH}") ### We do not link with it in compilation time because it broke daemonization code and pope ### So we use runtime dynamic linking ### target_link_libraries(gobgp_action ${GOBGP_LIBRARY_PATH}) target_link_libraries(gobgp_action dl) include_directories(${GOBGP_INCLUDES_FOLDER}) else() message(FATAL_ERROR "Could not find libgobgp") endif() find_path(GRPC_INCLUDES_FOLDER NAMES grpc/grpc.h PATHS "${GRPC_CUSTOM_INSTALL_PATH}/include" NO_DEFAULT_PATH) find_library(GRPC_LIBRARY_GRPC_PATH NAMES grpc PATHS "${GRPC_CUSTOM_INSTALL_PATH}/lib" NO_DEFAULT_PATH) find_library(GRPC_LIBRARY_GPR_PATH NAMES gpr PATHS "${GRPC_CUSTOM_INSTALL_PATH}/lib" NO_DEFAULT_PATH) find_library(GRPC_LIBRARY_GRPC_CPP_UNSECURE_PATH NAMES grpc++_unsecure PATHS "${GRPC_CUSTOM_INSTALL_PATH}/lib" NO_DEFAULT_PATH) if (GRPC_INCLUDES_FOLDER AND GRPC_LIBRARY_GRPC_PATH AND GRPC_LIBRARY_GPR_PATH AND GRPC_LIBRARY_GRPC_CPP_UNSECURE_PATH) include_directories(${GRPC_INCLUDES_FOLDER}) target_link_libraries(gobgp_action ${GRPC_LIBRARY_GRPC_PATH}) target_link_libraries(gobgp_action ${GRPC_LIBRARY_GPR_PATH}) target_link_libraries(gobgp_action ${GRPC_LIBRARY_GRPC_CPP_UNSECURE_PATH}) else() message(FATAL_ERROR "Could not find gRPC library") endif() find_path(PROTOCOL_BUFFERS_INCLUDE_FOLDER NAMES "google/protobuf/stubs/common.h" PATHS "${PROTOCOL_BUFFERS_CUSTOM_INSTALL_PATH}/include") find_library(PROTOCOL_BUFFERS_LIBRARY_PATH NAMES protobuf PATHS "${PROTOCOL_BUFFERS_CUSTOM_INSTALL_PATH}/lib") if (PROTOCOL_BUFFERS_INCLUDE_FOLDER AND PROTOCOL_BUFFERS_LIBRARY_PATH) include_directories(${PROTOCOL_BUFFERS_INCLUDE_FOLDER}) target_link_libraries(gobgp_action ${PROTOCOL_BUFFERS_LIBRARY_PATH}) else() message(FATAL_ERROR "Could not find protocol buffers") endif() # message(STATUS "grpc: ${GRPC_INCLUDES_FOLDER} ${GRPC_LIBRARY_GRPC_PATH} ${GRPC_LIBRARY_GPR_PATH}") # message(STATUS ${PROJECT_BINARY_DIR}) find_program(PROTOC_BINARY protoc PATHS "${PROTOCOL_BUFFERS_CUSTOM_INSTALL_PATH}/bin" NO_DEFAULT_PATH) if (PROTOC_BINARY) message(STATUS "Found protoc protobuf compiler: ${PROTOC_BINARY}") else() message(FATAL_ERROR "Can't find protoc compiler") endif() set(GRPC_CPP_PLUGIN "${GRPC_CUSTOM_INSTALL_PATH}/bin/grpc_cpp_plugin") execute_process(COMMAND ${PROTOC_BINARY} -I ${PROJECT_BINARY_DIR}/../actions --grpc_out=${PROJECT_BINARY_DIR}/../actions --plugin=protoc-gen-grpc=${GRPC_CPP_PLUGIN} ${PROJECT_BINARY_DIR}/../actions/gobgp_api_client.proto ERROR_VARIABLE PROTOC_STDERR RESULT_VARIABLE PROTOC_RETURN_CODE OUTPUT_STRIP_TRAILING_WHITESPACE) message(STATUS "Protoc return code: ${PROTOC_RETURN_CODE} std err: ${PROTOC_STDERR}") execute_process(COMMAND ${PROTOC_BINARY} -I ${PROJECT_BINARY_DIR}/../actions --cpp_out=${PROJECT_BINARY_DIR}/../actions ${PROJECT_BINARY_DIR}/../actions/gobgp_api_client.proto ERROR_VARIABLE PROTOC_STDERR RESULT_VARIABLE PROTOC_RETURN_CODE OUTPUT_STRIP_TRAILING_WHITESPACE) message(STATUS "Protoc return code: ${PROTOC_RETURN_CODE} std err: ${PROTOC_STDERR}") # Build gRPC and protocol bufffers libraries and link they to gobgp_action add_library(gobgp_api_client_pb_cc STATIC actions/gobgp_api_client.pb.cc) add_library(gobgp_api_client_grpc_pb_cc STATIC actions/gobgp_api_client.grpc.pb.cc) target_link_libraries(gobgp_action gobgp_api_client_pb_cc) target_link_libraries(gobgp_action gobgp_api_client_grpc_pb_cc) # FastNetMon API add_definitions(-DFASTNETMON_API) execute_process(COMMAND ${PROTOC_BINARY} -I ${PROJECT_BINARY_DIR}/.. --grpc_out=${PROJECT_BINARY_DIR}/.. --plugin=protoc-gen-grpc=${GRPC_CPP_PLUGIN} ${PROJECT_BINARY_DIR}/../fastnetmon.proto ERROR_VARIABLE PROTOC_STDERR RESULT_VARIABLE PROTOC_RETURN_CODE OUTPUT_STRIP_TRAILING_WHITESPACE) message(STATUS "Protoc return code: ${PROTOC_RETURN_CODE} std err: ${PROTOC_STDERR}") execute_process(COMMAND ${PROTOC_BINARY} -I ${PROJECT_BINARY_DIR}/.. --cpp_out=${PROJECT_BINARY_DIR}/.. ${PROJECT_BINARY_DIR}/../fastnetmon.proto ERROR_VARIABLE PROTOC_STDERR RESULT_VARIABLE PROTOC_RETURN_CODE OUTPUT_STRIP_TRAILING_WHITESPACE) message(STATUS "Protoc return code: ${PROTOC_RETURN_CODE} std err: ${PROTOC_STDERR}") add_library(fastnetmon_grpc_pb_cc STATIC fastnetmon.grpc.pb.cc) add_library(fastnetmon_pb_cc STATIC fastnetmon.pb.cc) add_executable(fastnetmon_api_client fastnetmon_api_client.cpp) target_link_libraries(fastnetmon_api_client ${GRPC_LIBRARY_GPR_PATH}) target_link_libraries(fastnetmon_api_client ${GRPC_LIBRARY_GRPC_CPP_UNSECURE_PATH}) target_link_libraries(fastnetmon_api_client ${GRPC_LIBRARY_GRPC_PATH}) target_link_libraries(fastnetmon_api_client fastnetmon_grpc_pb_cc) target_link_libraries(fastnetmon_api_client fastnetmon_pb_cc) target_link_libraries(fastnetmon_api_client ${PROTOCOL_BUFFERS_LIBRARY_PATH}) target_link_libraries(fastnetmon ${GRPC_LIBRARY_GPR_PATH}) target_link_libraries(fastnetmon ${GRPC_LIBRARY_GRPC_CPP_UNSECURE_PATH}) target_link_libraries(fastnetmon ${GRPC_LIBRARY_GRPC_PATH}) target_link_libraries(fastnetmon fastnetmon_grpc_pb_cc) target_link_libraries(fastnetmon fastnetmon_pb_cc) target_link_libraries(fastnetmon ${PROTOCOL_BUFFERS_LIBRARY_PATH}) endif() # example plugin add_library(example_plugin STATIC example_plugin/example_collector.cpp) if (ENABLE_NETMAP_SUPPORT) # Netmap plugin set(NETMAP_INCLUDE_DIRS "netmap_plugin/netmap_includes") include_directories(${NETMAP_INCLUDE_DIRS}) add_library(netmap_plugin STATIC netmap_plugin/netmap_collector.cpp) target_link_libraries(netmap_plugin fastnetmon_packet_parser) endif() # Client tool add_executable(fastnetmon_client fastnetmon_client.cpp) # Find boost: http://www.cmake.org/cmake/help/v3.0/module/FindBoost.html # Enable detailed errors set(Boost_DETAILED_FAILURE_MSG ON) find_package(Boost COMPONENTS thread regex program_options system REQUIRED) if(Boost_FOUND) include_directories(${Boost_INCLUDE_DIRS}) target_link_libraries(fastnetmon ${Boost_LIBRARIES}) target_link_libraries(fast_library ${Boost_LIBRARIES}) endif() target_link_libraries(fast_library patricia) target_link_libraries(fast_library fastnetmon_pcap_format) # Try to find ncurses librreary find_package(Curses REQUIRED) if(CURSES_FOUND) include_directories(${CURSES_INCLUDE_DIRS}) target_link_libraries(fastnetmon_client ${CURSES_LIBRARIES}) endif() ### Move this code to cmake module # Try to find hiredis in a specific folder find_path(HIREDIS_INCLUDES_FOLDER NAMES hiredis/hiredis.h PATHS "${HIREDIS_CUSTOM_INSTALL_PATH}/include" NO_DEFAULT_PATH) # Try to find hiredis library path find_library(HIREDIS_LIBRARY_PATH NAMES hiredis PATHS "${HIREDIS_CUSTOM_INSTALL_PATH}/lib" NO_DEFAULT_PATH) if (HIREDIS_INCLUDES_FOLDER AND HIREDIS_LIBRARY_PATH) message(STATUS "We found hiredis library and will build Redis support ${HIREDIS_INCLUDES_FOLDER} ${HIREDIS_LIBRARY_PATH}") add_definitions(-DREDIS) include_directories(${HIREDIS_INCLUDES_FOLDER}) target_link_libraries (fastnetmon ${HIREDIS_LIBRARY_PATH}) else() message(STATUS "We can't find hiredis library and will disable Redis support") endif() ### Find mongo-c find_path(MONGOC_INCLUDES_FOLDER NAMES libmongoc-1.0/mongoc.h PATHS "${MONGO_C_CUSTOM_INSTALL_PATH}/include" NO_DEFAULT_PATH) find_library(MONGOC_LIBRARY_PATH NAMES mongoc-1.0 PATHS "${MONGO_C_CUSTOM_INSTALL_PATH}/lib" NO_DEFAULT_PATH) ### find bson find_path(BSON_INCLUDES_FOLDER NAMES libbson-1.0/bson.h PATHS "${MONGO_C_CUSTOM_INSTALL_PATH}/include" NO_DEFAULT_PATH) find_library(BSON_LIBRARY_PATH NAMES bson-1.0 PATHS "${MONGO_C_CUSTOM_INSTALL_PATH}/lib" NO_DEFAULT_PATH) if (MONGOC_INCLUDES_FOLDER AND MONGOC_LIBRARY_PATH AND BSON_INCLUDES_FOLDER AND BSON_LIBRARY_PATH) message(STATUS "We found mongo-c library ${MONGOC_INCLUDES_FOLDER} ${MONGOC_LIBRARY_PATH} ${BSON_INCLUDES_FOLDER} ${BSON_LIBRARY_PATH}") add_definitions(-DMONGO) # We add suffix name because cmake could not detect it correctly... include_directories("${MONGOC_INCLUDES_FOLDER}/libmongoc-1.0") include_directories("${BSON_INCLUDES_FOLDER}/libbson-1.0") target_link_libraries(fastnetmon ${MONGOC_LIBRARY_PATH} ${BSON_LIBRARY_PATH}) else() message(WARNING "We can't find Mongo C library") endif() ### Look for libpcap #find_path(LIBPCAP_INCLUDES_FOLDER NAMES pcap.h PATHS "${LIBPCAP_CUSTOM_INSTALL_PATH}/include" NO_DEFAULT_PATH) #find_library(LIBPCAP_LIBRARY_PATH NAMES pcap PATHS "${LIBPCAP_CUSTOM_INSTALL_PATH}/lib" NO_DEFAULT_PATH) #if (LIBPCAP_INCLUDES_FOLDER AND LIBPCAP_LIBRARY_PATH) # message(STATUS "We found pcap library ${LIBPCAP_LIBRARY_PATH}") # include_directories(${LIBPCAP_INCLUDES_FOLDER}) #else() # message(FATAL_ERROR "We can't find pcap library") #endif() ### Look for log4cpp # Try to find log4cpp includes path find_path(LOG4CPP_INCLUDES_FOLDER NAMES log4cpp/Appender.hh PATHS "${LOG4CPP_CUSTOM_INSTALL_PATH}/include" NO_DEFAULT_PATH) # Try to find log4cpp library path find_library(LOG4CPP_LIBRARY_PATH NAMES log4cpp PATHS "${LOG4CPP_CUSTOM_INSTALL_PATH}/lib" NO_DEFAULT_PATH) if (LOG4CPP_INCLUDES_FOLDER AND LOG4CPP_LIBRARY_PATH) include_directories(${LOG4CPP_INCLUDES_FOLDER}) message(STATUS "We have found log4cpp and will build project") else() message(FATAL_ERROR "We can't find log4cpp. We can't build project") endif() ### Look for jsonc find_path(JSONC_INCLUDES_FOLDER NAMES json-c/json.h PATHS "${JSONC_CUSTOM_INSTALL_PATH}/include" NO_DEFAULT_PATH) find_library(JSONC_LIBRARY_PATH NAMES json-c PATHS "${JSONC_CUSTOM_INSTALL_PATH}/lib" NO_DEFAULT_PATH) if (JSONC_INCLUDES_FOLDER AND JSONC_LIBRARY_PATH) include_directories(${JSONC_INCLUDES_FOLDER}) message(STATUS "We have found json-c library correctly: ${JSONC_LIBRARY_PATH}") else() message(FATAL_ERROR "We can't find json-c library! Can't build project") endif() target_link_libraries(fast_library ${JSONC_LIBRARY_PATH}) if (ENABLE_DPI_SUPPORT) target_link_libraries(fastnetmon fast_dpi) endif() target_link_libraries(fastnetmon ${LOG4CPP_LIBRARY_PATH}) target_link_libraries(fastnetmon ${CMAKE_THREAD_LIBS_INIT}) if (ENABLE_LUA_SUPPORT) target_link_libraries(fast_library ${LUAJIT_LIBRARY_PATH}) target_link_libraries(fastnetmon ${LUAJIT_LIBRARY_PATH}) endif() # Our libs target_link_libraries(fastnetmon patricia) target_link_libraries(fastnetmon fastnetmon_pcap_format) target_link_libraries(fastnetmon ipfix_rfc) # Link to our functions target_link_libraries(fastnetmon fast_library) # link to our unified parser target_link_libraries(fastnetmon unified_parser) if (ENABLE_PFRING_SUPPORT) target_link_libraries(fastnetmon pfring_plugin) # Link hardware filter too target_link_libraries(fastnetmon pfring_hardware_filter_action) endif() if (ENABLE_GOBGP_SUPPORT) target_link_libraries(fastnetmon gobgp_action) endif() if (ENABLE_SNABBSWITCH_SUPPORT) target_link_libraries(fastnetmon snabbswitch_plugin) endif() if (ENABLE_AFPACKET_SUPPORT) target_link_libraries(fastnetmon afpacket_plugin) endif() target_link_libraries(fastnetmon sflow_plugin netflow_plugin pcap_plugin example_plugin) if (ENABLE_NETMAP_SUPPORT) target_link_libraries(fastnetmon netmap_plugin) endif() # cmake .. -DBUILD_PLUGIN_RUNNER=ON if (BUILD_PLUGIN_RUNNER) add_executable(fastnetmon_plugin_runner plugin_runner.cpp) if (ENABLE_SNABBSWITCH_SUPPORT) target_link_libraries(fastnetmon_plugin_runner snabbswitch_plugin) endif() if (ENABLE_AFPACKET_SUPPORT) target_link_libraries(fastnetmon_plugin_runner afpacket_plugin) endif() target_link_libraries(fastnetmon_plugin_runner ${CMAKE_THREAD_LIBS_INIT}) target_link_libraries(fastnetmon_plugin_runner patricia) target_link_libraries(fastnetmon_plugin_runner fastnetmon_pcap_format) target_link_libraries(fastnetmon_plugin_runner ${LOG4CPP_LIBRARY_PATH}) target_link_libraries(fastnetmon_plugin_runner fast_library) # Add all plugins target_link_libraries(fastnetmon_plugin_runner sflow_plugin netflow_plugin pcap_plugin example_plugin) if (ENABLE_NETMAP_SUPPORT) target_link_libraries(fastnetmon_plugin_runner netmap_plugin) endif() if (ENABLE_PFRING_SUPPORT) target_link_libraries(fastnetmon_plugin_runner ${PFRING_LIBRARIES}) target_link_libraries(fastnetmon_plugin_runner pfring_plugin) endif() endif() # cmake .. -DBUILD_PCAP_READER=ON if (BUILD_PCAP_READER) add_executable(fastnetmon_pcap_reader pcap_reader.cpp) target_link_libraries(fastnetmon_pcap_reader fastnetmon_packet_parser) target_link_libraries(fastnetmon_pcap_reader patricia) target_link_libraries(fastnetmon_pcap_reader fastnetmon_pcap_format) target_link_libraries(fastnetmon_pcap_reader fast_library) target_link_libraries(fastnetmon_pcap_reader ${LOG4CPP_LIBRARY_PATH}) target_link_libraries(fastnetmon_pcap_reader netflow_plugin) target_link_libraries(fastnetmon_pcap_reader sflow_plugin) if (ENABLE_NETMAP_SUPPORT) target_link_libraries(fastnetmon_pcap_reader netmap_plugin) endif() if (ENABLE_DPI_SUPPORT) target_link_libraries(fastnetmon_pcap_reader fast_dpi) endif() endif() # cmake -DBUILD_TESTS=ON .. if (BUILD_TESTS) add_executable(fastnetmon_tests fastnetmon_tests.cpp) target_link_libraries(fastnetmon_tests fast_library) target_link_libraries(fastnetmon_tests ${CMAKE_THREAD_LIBS_INIT}) target_link_libraries(fastnetmon_tests ${Boost_LIBRARIES}) target_link_libraries(fastnetmon_tests ${LOG4CPP_LIBRARY_PATH}) set(GOOGLE_TEST_INCLUDE_DIRS /opt/gtest/include) set(GOOGLE_TEST_LIBRARIES /opt/gtest/lib/libgtest.a /opt/gtest/lib/libgtest_main.a) # Compiled Google Library include_directories(${GOOGLE_TEST_INCLUDE_DIRS}) target_link_libraries(fastnetmon_tests ${GOOGLE_TEST_LIBRARIES}) endif() if (${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD" OR ${CMAKE_SYSTEM_NAME} STREQUAL "DragonFly") set(CMAKE_INSTALL_BINDIR "bin") set(CMAKE_INSTALL_SBINDIR "bin") set(CMAKE_INSTALL_SYSCONFDIR "etc") else() set(CMAKE_INSTALL_BINDIR "/usr/bin") set(CMAKE_INSTALL_SBINDIR "/usr/sbin") set(CMAKE_INSTALL_SYSCONFDIR "/etc") endif() install(TARGETS fastnetmon DESTINATION "${CMAKE_INSTALL_SBINDIR}") install(TARGETS fastnetmon_client DESTINATION "${CMAKE_INSTALL_BINDIR}") install(FILES fastnetmon.conf DESTINATION "${CMAKE_INSTALL_SYSCONFDIR}") # Install blank files for networks list and whitelist install(FILES networks_list DESTINATION "${CMAKE_INSTALL_SYSCONFDIR}") install(FILES networks_whitelist DESTINATION "${CMAKE_INSTALL_SYSCONFDIR}") # man pages install(FILES man/fastnetmon.1 DESTINATION /usr/share/man/man1) install(FILES man/fastnetmon_client.1 DESTINATION /usr/share/man/man1) # service files configure_file(fastnetmon.service.in "${CMAKE_CURRENT_BINARY_DIR}/fastnetmon.service" @ONLY) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/fastnetmon.service" DESTINATION ${CMAKE_INSTALL_SYSTEMD_SERVICEDIR}) if (${OS_ID} MATCHES debian|ubuntu) install(FILES fastnetmon_init_script_debian_6_7 DESTINATION ${CMAKE_INSTALL_SYSCONFDIR}/init.d RENAME fastnetmon) endif() # Configure cpack package builder # Run it with: cd build; cpack -G DEB .. set(CPACK_PACKAGE_NAME "fastnetmon") set(CPACK_PACKAGE_VENDOR "vps2fast.com") set(CPACK_PACKAGE_CONTACT "pavel.odintsov@gmail.com") set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "FastNetMon - very fast DDoS analyzer with sflow/netflow/mirror support") set(CPACK_PACKAGE_VERSION "1.1.2") set(CPACK_PACKAGE_VERSION_MAJOR "1") set(CPACK_PACKAGE_VERSION_MINOR "1") set(CPACK_PACKAGE_VERSION_PATCH "2") set(CPACK_DEBIAN_PACKAGE_DEPENDS "") # set(CPACK_PACKAGE_INSTALL_DIRECTORY "CPack Component Example") # Specify config for deb package # http://www.cmake.org/Wiki/CMake:CPackPackageGenerators#DEB_.28UNIX_only.29 set(CPACK_DEBIAN_PACKAGE_DEPENDS "libboost-thread-dev, libboost-system-dev, libboost-regex-dev, libpcap-dev, libnuma-dev, liblog4cpp5-dev") # This must always be last! include(CPack) fastnetmon-1.1.4/src/Dockerfile000066400000000000000000000015241343111404700164540ustar00rootroot00000000000000FROM ubuntu MAINTAINER robertoberto RUN apt-get update && apt-get install -y \ bison \ build-essential \ cmake \ flex \ g++ \ gcc \ git \ libboost-all-dev \ libgeoip-dev \ libgpm-dev \ libhiredis-dev \ liblog4cpp5-dev \ libncurses5-dev \ libnuma-dev \ libpcap-dev \ linux-headers-$(uname -r) \ make \ mongodb-dev \ python-pip \ socat \ vim RUN pip install exabgp RUN cd /usr/src; git clone https://github.com/pavel-odintsov/fastnetmon.git #COPY exabgp_blackhole.conf /etc/exabgp_blackhole.conf RUN cd /usr/src/fastnetmon/src; mkdir build; cd build; cmake .. -DDISABLE_PF_RING_SUPPORT=ON; make RUN cp /usr/src/fastnetmon/src/fastnetmon.conf /etc/ RUN cp /usr/src/fastnetmon/src/build/fastnetmon /usr/bin/ RUN cp /usr/src/fastnetmon/src/build/fastnetmon_client /usr/bin/ RUN touch /etc/networks_list fastnetmon-1.1.4/src/FreeBSD_port/000077500000000000000000000000001343111404700167365ustar00rootroot00000000000000fastnetmon-1.1.4/src/FreeBSD_port/Makefile000066400000000000000000000023141343111404700203760ustar00rootroot00000000000000# $FreeBSD$ PORTNAME= fastnetmon PORTVERSION= 1.1.2 DISTVERSIONPREFIX= v CATEGORIES= net-mgmt MAINTAINER= pavel.odintsov@gmail.com COMMENT= Very fast DDoS analyzer with sFlow/NetFLow/IPFIX/SPAN/mirror support LICENSE= GPLv2 LICENSE_FILE= ${WRKSRC}/LICENSE LIB_DEPENDS= liblog4cpp.so:${PORTSDIR}/devel/log4cpp \ libboost_regex.so:${PORTSDIR}/devel/boost-libs USE_GITHUB= yes GH_ACCOUNT= pavel-odintsov # TODO: enable this after updating /usr/ports/{UIDs,GIDs} #USERS= fastnetmon #GROUPS= fastnetmon USES= cmake CMAKE_SOURCE_PATH=${WRKSRC}/src USE_RC_SUBR= fastnetmon PORTDOCS= * OPTIONS_DEFINE= DOCS post-patch: @${REINPLACE_CMD} -e 's|/usr/local|${LOCALBASE}|' ${WRKSRC}/src/CMakeLists.txt @${REINPLACE_CMD} -e 's|/usr/local|${PREFIX}|; \ s|/var/run|&/fastnetmon|g; s|/var/log|&/fastnetmon|g; \ s|"/etc/|"${PREFIX}/etc/|g; s|/root/fastnetmon|${DATADIR}|g' \ ${WRKSRC}/src/fastnetmon.conf ${WRKSRC}/src/fastnetmon.cpp post-install: ${MV} ${STAGEDIR}${PREFIX}/etc/${PORTNAME}.conf \ ${STAGEDIR}${PREFIX}/etc/${PORTNAME}.conf.sample cd ${WRKSRC} && ${COPYTREE_SHARE} "README.md docs" ${STAGEDIR}${DOCSDIR} ${MKDIR} ${STAGEDIR}/var/run/fastnetmon ${STAGEDIR}/var/log/fastnetmon .include fastnetmon-1.1.4/src/FreeBSD_port/distinfo000066400000000000000000000002651343111404700205030ustar00rootroot00000000000000SHA256 (FastVPSEestiOu-fastnetmon-v1.1.2_GH0.tar.gz) = f2c554aa402e608b9837132b17da79b49f1b998c17934344779ddc9a397261b4 SIZE (FastVPSEestiOu-fastnetmon-v1.1.2_GH0.tar.gz) = 6072730 fastnetmon-1.1.4/src/FreeBSD_port/files/000077500000000000000000000000001343111404700200405ustar00rootroot00000000000000fastnetmon-1.1.4/src/FreeBSD_port/files/fastnetmon.in000066400000000000000000000010451343111404700225460ustar00rootroot00000000000000#!/bin/sh # PROVIDE: fastnetmon # REQUIRE: NETWORKING SERVERS LOGIN # BEFORE: securelevel # KEYWORD: shutdown # Add the following line to /etc/rc.conf to enable `fastnetmon': # # fastnetmon_enable="YES" # . /etc/rc.subr name="fastnetmon" rcvar="${name}_enable" command="%%PREFIX%%/bin/fastnetmon" pidfile="/var/run/fastnetmon/$name.pid" load_rc_config "$name" : ${fastnetmon_enable:="NO"} # TODO: enable this after updating /usr/ports/{UIDs,GIDs} #: ${fastnetmon_user:="fastnetmon"} : ${fastnetmon_flags:="--daemonize"} run_rc_command "$1" fastnetmon-1.1.4/src/FreeBSD_port/files/patch-src_CMakeLists.txt000066400000000000000000000030011343111404700245360ustar00rootroot00000000000000--- src/CMakeLists.txt.orig 2015-06-02 16:43:16 UTC +++ src/CMakeLists.txt @@ -14,8 +14,8 @@ set (Tutorial_VERSION_MAJOR 1) set (Tutorial_VERSION_MINOR 1) # It's pretty safe and provide big speedup for our packet processor and patricia code -set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O2 ") -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O2") +set(CMAKE_C_FLAGS_RELEASE "-O2") +set(CMAKE_CXX_FLAGS_RELEASE "-O2") set(FASTNETMON_PROFILER OFF) @@ -91,11 +91,13 @@ target_link_libraries(netflow_plugin ipf add_library(pcap_plugin STATIC pcap_plugin/pcap_collector.cpp) target_link_libraries(pcap_plugin pcap) +find_package(Threads) + if (ENABLE_PFRING_SUPPORT) add_library(pfring_plugin STATIC pfring_plugin/pfring_collector.cpp) target_link_libraries(pfring_plugin ${PFRING_LIBRARIES}) target_link_libraries(pfring_plugin numa) - target_link_libraries(pfring_plugin pthread) + target_link_libraries(pfring_plugin ${CMAKE_THREAD_LIBS_INIT}) endif() # example plugin @@ -169,7 +171,7 @@ endif() target_link_libraries(fastnetmon ${LOG4CPP_LIBRARY_PATH}) -target_link_libraries(fastnetmon pthread) +target_link_libraries(fastnetmon ${CMAKE_THREAD_LIBS_INIT}) # Our libs target_link_libraries(fastnetmon patricia) @@ -217,6 +219,8 @@ endif() install(TARGETS fastnetmon DESTINATION bin) install(TARGETS fastnetmon_client DESTINATION bin) +install(FILES fastnetmon.conf DESTINATION etc) + # Configure cpack package builder # Run it with: cd build; cpack -G DEB .. set(CPACK_PACKAGE_NAME "fastnetmon") fastnetmon-1.1.4/src/FreeBSD_port/pkg-descr000066400000000000000000000002641343111404700205420ustar00rootroot00000000000000FastNetMon - A high performance DoS/DDoS load analyzer built on top of multiple packet capture engines (NetFlow, IPFIX, sFLOW, netmap, PF_RING, PCAP). WWW: https://fastnetmon.com fastnetmon-1.1.4/src/FreeBSD_port/pkg-plist000066400000000000000000000001721343111404700205730ustar00rootroot00000000000000bin/fastnetmon bin/fastnetmon_client @sample etc/fastnetmon.conf.sample @dir /var/run/fastnetmon @dir /var/log/fastnetmon fastnetmon-1.1.4/src/a10_plugin/000077500000000000000000000000001343111404700164175ustar00rootroot00000000000000fastnetmon-1.1.4/src/a10_plugin/.gitignore000066400000000000000000000000421343111404700204030ustar00rootroot00000000000000*.pyc *.python *.egg *.egg-info/ fastnetmon-1.1.4/src/a10_plugin/README.md000066400000000000000000000101161343111404700176750ustar00rootroot00000000000000# A10 Networks Thunder TPS Appliance AXAPIv3 integration for FastNetMon ## Prerequisites: 1. A10 Thunder TPS with AXAPIv3. More information on AXAPIv3: https://www.a10networks.com/resources/glossary/axapi-custom-management. 2. Network topology is Asymmetric Reactive with BGP as the routing protocol. A10 Thunder TPS peers with the upstream router. 3. TPS contains base config under /fastnetmon/src/a10_plugin/configs/tps_base_config_vX.txt for base glid, zone-template, and ddos protection rate-interval, etc. ## Overview: 1. This script connect to A10 Thunder TPS Appliance via AXAPIv3 to create Protected Object. 2. The traffic is on-ramped by announcing a BGP route towards upstream router(s) upon FastNetMon ban detection. 3. The BGP route is withdrawn upon unban instruction from FastNetMon. 4. [Important] Please note that the script works in conjunction with the tps_base_config_v[xx].txt and tps_zone_config_v[xx].txt files. For example, the script assumes the 'bgp advertised' command is configured under 'ddos dst zone' to advertise the BGP route. Please consult with www.a10networks.com for the latest commands and configuration guides. 4.1 As a matter of reference, the tps_base_config and tps_zone_config configuration files were provided in .txt format under configs/ folder as well as in JSon format under json_configs/ folder. The assumption is they were pre-configured prior to FastNetMon ban/unban actions. 5. Log of the script is keep under /var/log/fastnetmon-notify.log. ## Configuration Steps: 1. If this is a brand new TPS with no prior 'ddos dst zone' config, do a quick dummy zone config and remove it: ``` TH3030S-1(config)#ddos dst zone dummy TH3030S-1(config-ddos zone)#exit TH3030S-1(config)#no ddos dst zone dummy TH3030S-1(config)#end TH3030S-1# ``` 2. Configure the fastnetmon_a10_xx.py script as the executed script under /etc/fastnetmon.conf, i.e. notify_script_path=/fastnetmon_a10_v0.3.py. 3. Please note that we have various versions of ban actions depending on your topology, such as integration of aGalaxy. 4. Alternatively place all files in a directory that is reachable by FastNetMon and indicate it as the executed script in /etc/fastnetmon.conf. 5. Make sure both Python scripts are executable, i.e. "chmod +x a10.py fastnetmon_a10_v0.3.py" ## Please modify the following in the fastnetmon_a10_v[xx].py script 1. A10 Thunder TPS mitigator IP. 2. Username and password for your A10 Device. Please follow your own password vault or other security schema. Author: Eric Chou ericc@a10networks.com, Rich Groves rgroves@a10networks.com Feedback and Feature Requests are Appreciated and Welcomed. Example Usage: - Ban action: ``` a10-ubuntu3:~/fastnetmon/src/a10_plugin$ sudo python fastnetmon_a10_v0.3.py "10.10.10.10" "outgoing" "111111" "ban" TH4435-1#show ddos dst zone all-entries Legend (Rate/Limit): 'U'nlimited, 'E'xceeded, '-' Not applicable Legend (State) : 'W'hitelisted, 'B'lacklisted, 'P'ermitted, black'H'oled, 'I'dle, 'L'earning, 'M'onitoring, '-' Regular mode Zone Name / Zone Service Info | [State]| Curr Conn| Conn Rate| Pkt Rate | kBit Rate|Frag Pkt R|Sources # |Age |LockU | | Limit| Limit| Limit| Limit| Limit| Limit|#min| Time ----------------------------------------------------------------------------------------------------------------------------------- 10.10.10.10_zone [M] U U U U U 1S 0 - U U U U U Displayed Entries: 1 Displayed Services: 0 TH4435#sh ip bgp neighbors advertised-routes ``` - Unban action: a10-ubuntu3:~/fastnetmon/src/a10_plugin$ sudo python fastnetmon_a10_v0.3.py "10.10.10.10" "outgoing" "111111" "unban" ``` TH4435-1#sh ip bgp neighbors advertised-routes TH4435-1# ``` ## Notes 1. In a10.py, SSL ssl._create_unverified_context() was used. Please see PEP476 for details. fastnetmon-1.1.4/src/a10_plugin/a10.py000077500000000000000000000030701343111404700173550ustar00rootroot00000000000000 # # v0.2 # ericc@a10networks.com # import json, urllib2, ssl def axapi_auth(host, username, password): base_uri = 'https://'+host auth_payload = {"credentials": {"username": username, "password": password}} r = axapi_action(base_uri + '/axapi/v3/auth', payload=auth_payload) signature = json.loads(r)['authresponse']['signature'] return base_uri, signature def axapi_action(uri, payload='', signature='', method='POST'): # PEP476 2.7.9+ / 3.4.3+ cert check new_context = ssl._create_unverified_context() try: if method == 'POST': req = urllib2.Request(uri) req.add_header('content-type', 'application/json') if signature: req.add_header('Authorization', 'A10 {0}'.format(signature)) response = urllib2.urlopen(req, json.dumps(payload), context=new_context) elif method == 'GET': req = urllib2.Request(uri) req.add_header('content-type', 'application/json') if signature: req.add_header('Authorization', 'A10 {0}'.format(signature)) response = urllib2.urlopen(req, context=new_context) elif method == 'DELETE': req = urllib2.Request(uri) req.add_header('content-type', 'application/json') req.get_method = lambda: 'DELETE' if signature: req.add_header('Authorization', 'A10 {0}'.format(signature)) response = urllib2.urlopen(req, context=new_context) return response.read() except Exception as e: raise fastnetmon-1.1.4/src/a10_plugin/change_log.txt000066400000000000000000000006441343111404700212520ustar00rootroot00000000000000Change Logs: [8/12/2016] - removed configs/dns_test_server.txt - added configs/tps_base_config_v1.txt and configs/tps_zone_config_v1.txt - modified README file to reflect the dependencies for items under configs/ folder. - created change_log.txt - modify json_configs/ddos_dst_zone.py to match json_configs/tps_zone_config_json_v1.txt - Took out BGP network advertisement, use 'bgp advertise' under dst zone instead fastnetmon-1.1.4/src/a10_plugin/configs/000077500000000000000000000000001343111404700200475ustar00rootroot00000000000000fastnetmon-1.1.4/src/a10_plugin/configs/README.md000066400000000000000000000022641343111404700213320ustar00rootroot00000000000000# A10 Networks Thunder TPS Appliance Configs ## Base Config v1 Functionality 1. Assumes TPS receives inbound traffic only (from the Internet to the protected service) 2. Rate Limiters (GLID) for 10Gbps, 1Gbps, and 100Mbps provided for use 3. Basic TCP and UDP templates provided (SYN-auth, UDP-auth, and low src port filter) 4. BGP configuration for auto mitigation announcements (ddos-advertise route map) 5. Base sFlow export configuration 6. All events logged in CEF format ## Basic Zone Config v1 Functionality 1. Filters L2, L3, L4 packet anomalies (consult A10 documentation for specifics) 2. Drops ICMPv4, ICMPv6, and all fragments 3. Performs TCP SYN Auth for TCP dest ports 21,22,25,53,80,110,143,443,587,993,995,5060,5061 4. Filters well-known UDP src ports 5. Performs UDP Auth for UDP dest port 53 6. Blocks all other traffic 7. Creates an "incident" in the TPS GUI when seeing any packets to these dest ports ## These are just examples. Current plug-in does not receive rate info from FNM but future revisions will Authors: Eric Chou ericc@a10networks.com, Rich Groves rgroves@a10networks.com Feedback and feature requests are appreciated and welcomed. fastnetmon-1.1.4/src/a10_plugin/configs/tps_base_config_v1.txt000066400000000000000000000027501343111404700243470ustar00rootroot00000000000000system anomaly log system attack log system ddos-attack log ! hostname A10TPS-Fastnetmon ! interface management ip address x.x.x.x x.x.x.x ip control-apps-use-mgmt-port ip default-gateway x.x.x.x enable ! interface ethernet 1 name Inbound enable ! interface ethernet 2 name Outbound ! ! glid 1 description "10gbps rate limiter" bit-rate-limit 10000000 ! glid 2 description "1gbps rate limiter" bit-rate-limit 1000000 ! glid 3 description "100mbps rate limiter" bit-rate-limit 100000 ! ddos protection enable ddos protection rate-interval 1sec ! ddos resource-tracking cpu enable ! ddos zone-template logging cef-logger log-format-cef enable-action-logging ! ddos zone-template tcp tcp-protect1 syn-authentication send-rst syn-authentication pass-action authenticate-src syn-authentication fail-action drop ! ddos zone-template udp udp-protect1 spoof-detect timeout 5 spoof-detect min-delay 2 spoof-detect pass-action authenticate-src spoof-detect fail-action drop known-resp-src-port action drop ! logging syslog information ! logging host x.x.x.x use-mgmt-port ! router bgp x bgp log-neighbor-changes bgp router-id x.x.x.x neighbor x.x.x.x remote-as x neighbor x.x.x.x description upstream neighbor x.x.x.x route-map ddos-advertise out ! route-map ddos-advertise permit 1 ! sflow setting max-header 128 sflow setting packet-sampling-rate 1000 ! sflow collector ip x.x.x.x 6343 use-mgmt-port ! sflow agent address x.x.x.x ! sflow sampling ethernet 1 ! end fastnetmon-1.1.4/src/a10_plugin/configs/tps_zone_config_v1.txt000066400000000000000000000063621343111404700244130ustar00rootroot00000000000000ddos dst zone xxxxxxx ip x.x.x.x operational-mode monitor bgp advertised zone-template logging cef-logger log enable periodic ip-proto tcp drop-frag-pkt ip-proto udp drop-frag-pkt ip-proto icmp-v4 deny detection-enable ip-proto icmp-v6 deny detection-enable port 20 tcp detection-enable level 0 zone-escalation-score 10 indicator pkt-rate score 20 zone-threshold 1 level 1 zone-template tcp tcp-protect1 port 21 tcp detection-enable level 0 zone-escalation-score 10 indicator pkt-rate score 20 zone-threshold 1 level 1 zone-template tcp tcp-protect1 port 22 tcp detection-enable level 0 zone-escalation-score 10 indicator pkt-rate score 20 zone-threshold 1 level 1 zone-template tcp tcp-protect1 port 25 tcp detection-enable level 0 zone-escalation-score 10 indicator pkt-rate score 20 zone-threshold 1 level 1 zone-template tcp tcp-protect1 port 53 tcp detection-enable level 0 zone-escalation-score 10 indicator pkt-rate score 20 zone-threshold 1 level 1 zone-template tcp tcp-protect1 port 53 udp detection-enable level 0 zone-escalation-score 10 indicator pkt-rate score 20 zone-threshold 1 level 1 zone-template udp udp-protect1 port 80 tcp detection-enable level 0 zone-escalation-score 10 indicator pkt-rate score 20 zone-threshold 1 level 1 zone-template tcp tcp-protect1 port 110 tcp detection-enable level 0 zone-escalation-score 10 indicator pkt-rate score 20 zone-threshold 1 level 1 zone-template tcp tcp-protect1 port 143 tcp detection-enable level 0 zone-escalation-score 10 indicator pkt-rate score 20 zone-threshold 1 level 1 zone-template tcp tcp-protect1 port 443 tcp detection-enable level 0 zone-escalation-score 10 indicator pkt-rate score 20 zone-threshold 1 level 1 zone-template tcp tcp-protect1 port 587 tcp detection-enable level 0 zone-escalation-score 10 indicator pkt-rate score 20 zone-threshold 1 level 1 zone-template tcp tcp-protect1 port 993 tcp detection-enable level 0 zone-escalation-score 10 indicator pkt-rate score 20 zone-threshold 1 level 1 zone-template tcp tcp-protect1 port 995 tcp detection-enable level 0 zone-escalation-score 10 indicator pkt-rate score 20 zone-threshold 1 level 1 zone-template tcp tcp-protect1 port 5060 tcp detection-enable level 0 zone-escalation-score 10 indicator pkt-rate score 20 zone-threshold 1 level 1 zone-template tcp tcp-protect1 port 5061 tcp detection-enable level 0 zone-escalation-score 10 indicator pkt-rate score 20 zone-threshold 1 level 1 zone-template tcp tcp-protect1 port other tcp detection-enable deny port other udp detection-enable deny fastnetmon-1.1.4/src/a10_plugin/fastnetmon_a10_v0.3.py000077500000000000000000000047511343111404700223700ustar00rootroot00000000000000#!/usr/bin/python # # Eric Chou (ericc@a10networks.com) # import sys from sys import stdin import optparse import logging, json from a10 import axapi_auth, axapi_action from json_configs.logoff import logoff_path from json_configs.write_memory import write_mem_path from json_configs.ddos_dst_zone import ddos_dst_zone_path, ddos_dst_zone LOG_FILE = "/var/log/fastnetmon-notify.log" logger = logging.getLogger("DaemonLog") logger.setLevel(logging.INFO) formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s") handler = logging.FileHandler(LOG_FILE) handler.setFormatter(formatter) logger.addHandler(handler) client_ip_as_string=sys.argv[1] data_direction=sys.argv[2] pps_as_string=int(sys.argv[3]) action=sys.argv[4] logger.info(" - " . join(sys.argv)) # A10 Mitigator Information mitigator_ip = "192.168.199.150" zone_name = client_ip_as_string + "_zone" ip_addr = client_ip_as_string mitigator_base_url, signature = axapi_auth(mitigator_ip, "admin", "a10") if action == "unban": try: r = axapi_action(mitigator_base_url+ddos_dst_zone_path+zone_name, method="DELETE", signature=signature) except Exception as e: logger.info("Zone not removed in unban, may not exist. Result: " + str(e)) # Commit config axapi_action(mitigator_base_url+write_mem_path, signature=signature) # Logoff axapi_action(mitigator_base_url+logoff_path, signature=signature) sys.exit(0) elif action == "ban" or action == "attack_details": r = axapi_action(mitigator_base_url+ddos_dst_zone_path, method='GET', signature=signature) try: if zone_name in [i['zone-name'] for i in json.loads(r)['zone-list']]: r = axapi_action(mitigator_base_url+ddos_dst_zone_path+zone_name, method="DELETE", signature=signature) logger.info(str(r)) except Exception as e: logger.info("No Zone detected or something went wrong. Erorr: " + str(e)) # A10 Mitigation On Ramp zone_name = client_ip_as_string + "_zone" ip_addr = client_ip_as_string returned_body = ddos_dst_zone(zone_name, ip_addr) try: r = axapi_action(mitigator_base_url+ddos_dst_zone_path, signature=signature, payload=returned_body) except Exception as e: logger.info("zone not created: " + str(e)) # Commit changes axapi_action(mitigator_base_url+write_mem_path, signature=signature) # Log off axapi_action(mitigator_base_url+logoff_path, signature=signature) sys.exit(0) else: sys.exit(0) fastnetmon-1.1.4/src/a10_plugin/json_configs/000077500000000000000000000000001343111404700211005ustar00rootroot00000000000000fastnetmon-1.1.4/src/a10_plugin/json_configs/__init__.py000066400000000000000000000000001343111404700231770ustar00rootroot00000000000000fastnetmon-1.1.4/src/a10_plugin/json_configs/bgp.py000066400000000000000000000005271343111404700222260ustar00rootroot00000000000000bgp_advertisement_path = '/axapi/v3/router/bgp/' def bgp_advertisement(ip_addr): route_advertisement = { "bgp": { "network": { "ip-cidr-list": [ { "network-ipv4-cidr":ip_addr+"/32", } ] }, } } return route_advertisement fastnetmon-1.1.4/src/a10_plugin/json_configs/ddos_dst_zone.py000066400000000000000000000252441343111404700243170ustar00rootroot00000000000000 ddos_dst_zone_path = '/axapi/v3/ddos/dst/zone/' def ddos_dst_zone(zone_name, ip_addr): ddos_dst_zone_payload = { "zone-list": [ { "zone-name":zone_name, "ip": [ { "ip-addr": ip_addr, } ], "operational-mode":"monitor", "advertised-enable":1, "zone-template": { "logging":"cef-logger" }, "log-enable":1, "log-periodic":1, "ip-proto": { "proto-tcp-udp-list": [ { "protocol":"tcp", "drop-frag-pkt":1, }, { "protocol":"udp", "drop-frag-pkt":1, } ], "proto-name-list": [ { "protocol":"icmp-v4", "deny":1, "detection-enable":1, }, { "protocol":"icmp-v6", "deny":1, "detection-enable":1, } ] }, "port": { "zone-service-list": [ { "port-num":20, "protocol":"tcp", "detection-enable":1, "level-list": [ { "level-num":"0", "zone-escalation-score":10, "indicator-list": [ { "type":"pkt-rate", "score":20, "zone-threshold-num":1, } ] }, { "level-num":"1", "zone-template": { "tcp":"tcp-protect1" }, } ] }, { "port-num":21, "protocol":"tcp", "detection-enable":1, "level-list": [ { "level-num":"0", "zone-escalation-score":10, "indicator-list": [ { "type":"pkt-rate", "score":20, "zone-threshold-num":1, } ] }, { "level-num":"1", "zone-template": { "tcp":"tcp-protect1" }, } ] }, { "port-num":22, "protocol":"tcp", "detection-enable":1, "level-list": [ { "level-num":"0", "zone-escalation-score":10, "indicator-list": [ { "type":"pkt-rate", "score":20, "zone-threshold-num":1, } ] }, { "level-num":"1", "zone-template": { "tcp":"tcp-protect1" }, } ] }, { "port-num":25, "protocol":"tcp", "detection-enable":1, "level-list": [ { "level-num":"0", "zone-escalation-score":10, "indicator-list": [ { "type":"pkt-rate", "score":20, "zone-threshold-num":1, } ] }, { "level-num":"1", "zone-template": { "tcp":"tcp-protect1" }, } ] }, { "port-num":53, "protocol":"tcp", "detection-enable":1, "level-list": [ { "level-num":"0", "zone-escalation-score":10, "indicator-list": [ { "type":"pkt-rate", "score":20, "zone-threshold-num":1, } ] }, { "level-num":"1", "zone-template": { "tcp":"tcp-protect1" }, } ] }, { "port-num":53, "protocol":"udp", "detection-enable":1, "level-list": [ { "level-num":"0", "zone-escalation-score":10, "indicator-list": [ { "type":"pkt-rate", "score":20, "zone-threshold-num":1, } ] }, { "level-num":"1", "zone-template": { "udp":"udp-protect1" }, } ] }, { "port-num":80, "protocol":"tcp", "detection-enable":1, "level-list": [ { "level-num":"0", "zone-escalation-score":10, "indicator-list": [ { "type":"pkt-rate", "score":20, "zone-threshold-num":1, } ] }, { "level-num":"1", "zone-template": { "tcp":"tcp-protect1" }, } ] }, { "port-num":110, "protocol":"tcp", "detection-enable":1, "level-list": [ { "level-num":"0", "zone-escalation-score":10, "indicator-list": [ { "type":"pkt-rate", "score":20, "zone-threshold-num":1, } ] }, { "level-num":"1", "zone-template": { "tcp":"tcp-protect1" }, } ] }, { "port-num":143, "protocol":"tcp", "detection-enable":1, "level-list": [ { "level-num":"0", "zone-escalation-score":10, "indicator-list": [ { "type":"pkt-rate", "score":20, "zone-threshold-num":1, } ] }, { "level-num":"1", "zone-template": { "tcp":"tcp-protect1" }, } ] }, { "port-num":443, "protocol":"tcp", "detection-enable":1, "level-list": [ { "level-num":"0", "zone-escalation-score":10, "indicator-list": [ { "type":"pkt-rate", "score":20, "zone-threshold-num":1, } ] }, { "level-num":"1", "zone-template": { "tcp":"tcp-protect1" }, } ] }, { "port-num":587, "protocol":"tcp", "detection-enable":1, "level-list": [ { "level-num":"0", "zone-escalation-score":10, "indicator-list": [ { "type":"pkt-rate", "score":20, "zone-threshold-num":1, } ] }, { "level-num":"1", "zone-template": { "tcp":"tcp-protect1" }, } ] }, { "port-num":993, "protocol":"tcp", "detection-enable":1, "level-list": [ { "level-num":"0", "zone-escalation-score":10, "indicator-list": [ { "type":"pkt-rate", "score":20, "zone-threshold-num":1, } ] }, { "level-num":"1", "zone-template": { "tcp":"tcp-protect1" }, } ] }, { "port-num":995, "protocol":"tcp", "detection-enable":1, "level-list": [ { "level-num":"0", "zone-escalation-score":10, "indicator-list": [ { "type":"pkt-rate", "score":20, "zone-threshold-num":1, } ] }, { "level-num":"1", "zone-template": { "tcp":"tcp-protect1" }, } ] }, { "port-num":5060, "protocol":"tcp", "detection-enable":1, "level-list": [ { "level-num":"0", "zone-escalation-score":10, "indicator-list": [ { "type":"pkt-rate", "score":20, "zone-threshold-num":1, } ] }, { "level-num":"1", "zone-template": { "tcp":"tcp-protect1" }, } ] }, { "port-num":5061, "protocol":"tcp", "detection-enable":1, "level-list": [ { "level-num":"0", "zone-escalation-score":10, "indicator-list": [ { "type":"pkt-rate", "score":20, "zone-threshold-num":1, } ] }, { "level-num":"1", "zone-template": { "tcp":"tcp-protect1" }, } ] } ], "zone-service-other-list": [ { "port-other":"other", "protocol":"tcp", "detection-enable":1, "deny":1, }, { "port-other":"other", "protocol":"udp", "detection-enable":1, "deny":1, } ] } } ] } return ddos_dst_zone_payload fastnetmon-1.1.4/src/a10_plugin/json_configs/ddos_dst_zone_backup.py000066400000000000000000000021171343111404700256360ustar00rootroot00000000000000 ddos_dst_zone_path = '/axapi/v3/ddos/dst/zone/' def ddos_dst_zone(zone_name, ip_addr): port_num = 53 port_protocol = 'udp' ddos_dst_zone_payload = { "zone-list": [ { "zone-name":zone_name, "ip": [ { "ip-addr":ip_addr } ], "operational-mode":"monitor", "port": { "zone-service-list": [ { "port-num":port_num, "protocol":port_protocol, "level-list": [ { "level-num":"0", "zone-escalation-score":1, "indicator-list": [ { "type":"pkt-rate", "score":50, "zone-threshold-num":1, } ], }, { "level-num":"1", } ], } ], }, } ] } return ddos_dst_zone_payload fastnetmon-1.1.4/src/a10_plugin/json_configs/logoff.py000066400000000000000000000000431343111404700227230ustar00rootroot00000000000000 logoff_path = '/axapi/v3/logoff' fastnetmon-1.1.4/src/a10_plugin/json_configs/tps_base_config_json_v1.txt000066400000000000000000000101761343111404700264320ustar00rootroot00000000000000a10-url:/axapi/v3/admin { "admin-list": [ { "user":"admin", "password": { "encrypted-in-module":"sCyT4priW1OZSg3m1RiAf0bOyZ0Odnf1rQRp+BHohemGp1YhW+V1NjwQjLjV2wDn", } } ] } a10-url:/axapi/v3/multi-config { "multi-config": { "enable":1, } } a10-url:/axapi/v3/monitor { "monitor": { "buffer-usage":91750, } } a10-url:/axapi/v3/system { "system": { "anomaly-log":1, "attack":1, "attack-log":1, "ddos-attack":1, "ddos-log":1, } } a10-url:/axapi/v3/hostname { "hostname": { "value":"tps-fastnetmon", } } a10-url:/axapi/v3/interface/management { "management": { "ip": { "ipv4-address”:”x.x.x.x", "ipv4-netmask”:”x.x.x.x", "control-apps-use-mgmt-port":1, "default-gateway”:”x.x.x.x" }, "action":"enable", } } a10-url:/axapi/v3/interface/ethernet { "ethernet-list": [ { "ifnum":1, "name":"Inbound", "action":"enable", }, { "ifnum":2, "name":"Outbound", } ] } a10-url:/axapi/v3/glid { "glid-list": [ { "name":"1", "description":"10gbps rate limiter", "bit-rate-limit":10000000, }, { "name":"2", "description":"1gbps rate limiter", "bit-rate-limit":1000000, }, { "name":"3", "description":"100mbps rate limiter", "bit-rate-limit":100000, } ] } a10-url:/axapi/v3/ddos/protection { "protection": { "toggle":"enable", "rate-interval":"1sec", } } a10-url:/axapi/v3/ddos/resource-tracking/cpu { "cpu": { "enable":1, } } a10-url:/axapi/v3/ddos/zone-template/logging { "logging-list": [ { "logging-tmpl-name":"cef-logger", "log-format-cef":1, "enable-action-logging":1, } ] } a10-url:/axapi/v3/ddos/zone-template/tcp { "tcp-list": [ { "name":"tcp-protect1", "syn-authentication": { "syn-auth-type":"send-rst", "syn-auth-pass-action":"authenticate-src", "syn-auth-fail-action":"drop" }, } ] } a10-url:/axapi/v3/ddos/zone-template/udp { "udp-list": [ { "name":"udp-protect1", "spoof-detect-retry-timeout":5, "spoof-detect-min-delay":2, "spoof-detect-pass-action":"authenticate-src", "spoof-detect-fail-action":"drop", "known-resp-src-port-cfg": { "known-resp-src-port":1, "known-resp-src-port-action":"drop" }, } ] } a10-url:/axapi/v3/ddos/src/default { "default-list": [ { "default-address-type":"ip", }, { "default-address-type":"ipv6", } ] } a10-url:/axapi/v3/ddos/dst/default { "default-list": [ { "default-address-type":"ip", }, { "default-address-type":"ipv6", } ] } a10-url:/axapi/v3/logging/syslog { "syslog": { "syslog-levelname":"information", } } a10-url:/axapi/v3/logging/host/ipv4addr { "ipv4addr-list": [ { "host-ipv4”:”x.x.x.x", "use-mgmt-port":1, "tcp":0, } ] } a10-url:/axapi/v3/router/bgp { "bgp-list": [ { "as-number”:x, "bgp": { "log-neighbor-changes":1, "router-id”:”x.x.x.x" }, "neighbor": { "ipv4-neighbor-list": [ { "neighbor-ipv4”:”x.x.x.x", "nbr-remote-as":1, "description":"upstream", "neighbor-route-map-lists": [ { "nbr-route-map":"ddos-advertise", "nbr-rmap-direction":"out" } ], } ] } } ] } a10-url:/axapi/v3/route-map { "route-map-list": [ { "tag":"ddos-advertise", "action":"permit", "sequence":1, } ] } a10-url:/axapi/v3/sflow/setting { "setting": { "max-header":128, "packet-sampling-rate":1000, } } a10-url:/axapi/v3/sflow/collector/ip { "ip-list": [ { "addr”:”x.x.x.x", "port":6343, "use-mgmt-port":1, } ] } a10-url:/axapi/v3/sflow/agent/address { "address": { "ip”:”x.x.x.x", } } a10-url:/axapi/v3/sflow/sampling { "sampling": { "eth-list": [ { "eth-start":1, "eth-end":1 } ], } } fastnetmon-1.1.4/src/a10_plugin/json_configs/tps_zone_config_json_v1.txt000066400000000000000000000250551343111404700264750ustar00rootroot00000000000000a10-url:/axapi/v3/ddos/dst/zone { "zone-list": [ { "zone-name”:"xxxx", "ip": [ { "ip-addr”:”x.x.x.x" } ], "operational-mode":"monitor", "advertised-enable":1, "zone-template": { "logging":"cef-logger" }, "log-enable":1, "log-periodic":1, "ip-proto": { "proto-tcp-udp-list": [ { "protocol":"tcp", "drop-frag-pkt":1, }, { "protocol":"udp", "drop-frag-pkt":1, } ], "proto-name-list": [ { "protocol":"icmp-v4", "deny":1, "detection-enable":1, }, { "protocol":"icmp-v6", "deny":1, "detection-enable":1, } ] }, "port": { "zone-service-list": [ { "port-num":20, "protocol":"tcp", "detection-enable":1, "level-list": [ { "level-num":"0", "zone-escalation-score":10, "indicator-list": [ { "type":"pkt-rate", "score":20, "zone-threshold-num":1, } ] }, { "level-num":"1", "zone-template": { "tcp":"tcp-protect1" }, } ] }, { "port-num":21, "protocol":"tcp", "detection-enable":1, "level-list": [ { "level-num":"0", "zone-escalation-score":10, "indicator-list": [ { "type":"pkt-rate", "score":20, "zone-threshold-num":1, } ] }, { "level-num":"1", "zone-template": { "tcp":"tcp-protect1" }, } ] }, { "port-num":22, "protocol":"tcp", "detection-enable":1, "level-list": [ { "level-num":"0", "zone-escalation-score":10, "indicator-list": [ { "type":"pkt-rate", "score":20, "zone-threshold-num":1, } ] }, { "level-num":"1", "zone-template": { "tcp":"tcp-protect1" }, } ] }, { "port-num":25, "protocol":"tcp", "detection-enable":1, "level-list": [ { "level-num":"0", "zone-escalation-score":10, "indicator-list": [ { "type":"pkt-rate", "score":20, "zone-threshold-num":1, } ] }, { "level-num":"1", "zone-template": { "tcp":"tcp-protect1" }, } ] }, { "port-num":53, "protocol":"tcp", "detection-enable":1, "level-list": [ { "level-num":"0", "zone-escalation-score":10, "indicator-list": [ { "type":"pkt-rate", "score":20, "zone-threshold-num":1, } ] }, { "level-num":"1", "zone-template": { "tcp":"tcp-protect1" }, } ] }, { "port-num":53, "protocol":"udp", "detection-enable":1, "level-list": [ { "level-num":"0", "zone-escalation-score":10, "indicator-list": [ { "type":"pkt-rate", "score":20, "zone-threshold-num":1, } ] }, { "level-num":"1", "zone-template": { "udp":"udp-protect1" }, } ] }, { "port-num":80, "protocol":"tcp", "detection-enable":1, "level-list": [ { "level-num":"0", "zone-escalation-score":10, "indicator-list": [ { "type":"pkt-rate", "score":20, "zone-threshold-num":1, } ] }, { "level-num":"1", "zone-template": { "tcp":"tcp-protect1" }, } ] }, { "port-num":110, "protocol":"tcp", "detection-enable":1, "level-list": [ { "level-num":"0", "zone-escalation-score":10, "indicator-list": [ { "type":"pkt-rate", "score":20, "zone-threshold-num":1, } ] }, { "level-num":"1", "zone-template": { "tcp":"tcp-protect1" }, } ] }, { "port-num":143, "protocol":"tcp", "detection-enable":1, "level-list": [ { "level-num":"0", "zone-escalation-score":10, "indicator-list": [ { "type":"pkt-rate", "score":20, "zone-threshold-num":1, } ] }, { "level-num":"1", "zone-template": { "tcp":"tcp-protect1" }, } ] }, { "port-num":443, "protocol":"tcp", "detection-enable":1, "level-list": [ { "level-num":"0", "zone-escalation-score":10, "indicator-list": [ { "type":"pkt-rate", "score":20, "zone-threshold-num":1, } ] }, { "level-num":"1", "zone-template": { "tcp":"tcp-protect1" }, } ] }, { "port-num":587, "protocol":"tcp", "detection-enable":1, "level-list": [ { "level-num":"0", "zone-escalation-score":10, "indicator-list": [ { "type":"pkt-rate", "score":20, "zone-threshold-num":1, } ] }, { "level-num":"1", "zone-template": { "tcp":"tcp-protect1" }, } ] }, { "port-num":993, "protocol":"tcp", "detection-enable":1, "level-list": [ { "level-num":"0", "zone-escalation-score":10, "indicator-list": [ { "type":"pkt-rate", "score":20, "zone-threshold-num":1, } ] }, { "level-num":"1", "zone-template": { "tcp":"tcp-protect1" }, } ] }, { "port-num":995, "protocol":"tcp", "detection-enable":1, "level-list": [ { "level-num":"0", "zone-escalation-score":10, "indicator-list": [ { "type":"pkt-rate", "score":20, "zone-threshold-num":1, } ] }, { "level-num":"1", "zone-template": { "tcp":"tcp-protect1" }, } ] }, { "port-num":5060, "protocol":"tcp", "detection-enable":1, "level-list": [ { "level-num":"0", "zone-escalation-score":10, "indicator-list": [ { "type":"pkt-rate", "score":20, "zone-threshold-num":1, } ] }, { "level-num":"1", "zone-template": { "tcp":"tcp-protect1" }, } ] }, { "port-num":5061, "protocol":"tcp", "detection-enable":1, "level-list": [ { "level-num":"0", "zone-escalation-score":10, "indicator-list": [ { "type":"pkt-rate", "score":20, "zone-threshold-num":1, } ] }, { "level-num":"1", "zone-template": { "tcp":"tcp-protect1" }, } ] } ], "zone-service-other-list": [ { "port-other":"other", "protocol":"tcp", "detection-enable":1, "deny":1, }, { "port-other":"other", "protocol":"udp", "detection-enable":1, "deny":1, } ] } } ] } fastnetmon-1.1.4/src/a10_plugin/json_configs/write_memory.py000066400000000000000000000000521343111404700241710ustar00rootroot00000000000000write_mem_path = '/axapi/v3/write/memory' fastnetmon-1.1.4/src/a10_plugin/tests/000077500000000000000000000000001343111404700175615ustar00rootroot00000000000000fastnetmon-1.1.4/src/a10_plugin/tests/README.md000066400000000000000000000022211343111404700210350ustar00rootroot00000000000000## Sample Test Output ``` echou@a10-ubuntu3:~/fastnetmon/src/a10_plugin/tests$ python helperTests.py Testing GET { "version": { "oper" : { "hw-platform":"TH4435 TPS", "copyright":"Copyright 2007-2014 by A10 Networks, Inc.", "sw-version":"3.2.1 build 175 (May-17-2016,16:57)", "plat-features":"", "boot-from":"HD_PRIMARY", "serial-number":"", "current-time":"Jul-27-2016, 09:46", "up-time":"70 days, 22 hours, 44 minutes" }, "a10-url":"/axapi/v3/version/oper" } } Testing POST { "hostname": { "value":"TH4435", "uuid":"", "a10-url":"/axapi/v3/hostname" } } .Testing axapi_auth ('base url: ', 'https://192.168.199.152', 'Signature: ', u'0855fef4da06d7beb89b27e7d2d042') . ---------------------------------------------------------------------- Ran 2 tests in 0.092s OK echou@a10-ubuntu3:~/fastnetmon/src/a10_plugin/tests$ ``` fastnetmon-1.1.4/src/a10_plugin/tests/__init__.py000066400000000000000000000000001343111404700216600ustar00rootroot00000000000000fastnetmon-1.1.4/src/a10_plugin/tests/helperTests.py000066400000000000000000000025141343111404700224370ustar00rootroot00000000000000import unittest,sys sys.path.append('../') from a10 import axapi_auth, axapi_action a10_tps = "192.168.199.152" username = "admin" password = "a10" hostname = "TH4435" class Test_Auth(unittest.TestCase): def testAssertTrue(self): print("Testing axapi_auth") try: mitigator_base_url, signature = axapi_auth(a10_tps, username, password) print("base url: ", mitigator_base_url, "Signature: ", signature) axapi_action(mitigator_base_url+"/axapi/v3/logoff") except Exception as e: self.fail("Not authenticated") class Test_API_Actions(unittest.TestCase): def testAssertTrue(self): try: print("Testing GET") mitigator_base_url, signature = axapi_auth(a10_tps, username, password) r = axapi_action(mitigator_base_url+"/axapi/v3/version/oper", method='GET', signature=signature) print(str(r)) print("Testing POST") hostname_payload = {"hostname": {"value": hostname}} r = axapi_action(mitigator_base_url+"/axapi/v3/hostname", payload=hostname_payload, signature=signature) print(str(r)) axapi_action(mitigator_base_url+"/axapi/v3/logoff") except Exception as e: self.fail("Failed") if __name__ == "__main__": unittest.main() fastnetmon-1.1.4/src/actions/000077500000000000000000000000001343111404700161205ustar00rootroot00000000000000fastnetmon-1.1.4/src/actions/gobgp_action.cpp000066400000000000000000000326071343111404700212670ustar00rootroot00000000000000#include "gobgp_action.h" #include "../fastnetmon_actions.h" #include "../fastnetmon_types.h" #include extern "C" { // Gobgp library #include "libgobgp.h" } #include #include #include #include #include #include "gobgp_api_client.grpc.pb.h" using grpc::Channel; using grpc::ClientContext; using grpc::Status; using gobgpapi::GobgpApi; // Create function pointers typedef path* (*serialize_path_dynamic_t)(int p0, char* p1); typedef char* (*decode_path_dynamic_t)(path* p0); serialize_path_dynamic_t serialize_path_dynamic = NULL; decode_path_dynamic_t decode_path_dynamic = NULL; class GrpcClient { public: GrpcClient(std::shared_ptr channel) : stub_(GobgpApi::NewStub(channel)) {} void GetAllActiveAnnounces(unsigned int route_family) { ClientContext context; gobgpapi::Table table; table.set_family(route_family); // We could specify certain neighbor here table.set_name(""); table.set_type(gobgpapi::Resource::GLOBAL); gobgpapi::Table response_table; auto status = stub_->GetRib(&context, table, &response_table); if (!status.ok()) { // error_message logger << log4cpp::Priority::INFO << "Problem with RPC: " << status.error_code() << " message " << status.error_message(); //std::cout << "Problem with RPC: " << status.error_code() << " message " << status.error_message() << std::endl; return; } else { // std::cout << "RPC working well" << std::endl; } std::cout << "List of announced prefixes for route family: " << route_family << std::endl << std::endl; for (auto current_destination : response_table.destinations()) { logger << log4cpp::Priority::INFO << "Prefix: " << current_destination.prefix(); //std::cout << "Prefix: " << current_destination.prefix() << std::endl; //std::cout << "Paths size: " << current_destination.paths_size() << std::endl; gobgpapi::Path my_path = current_destination.paths(0); // std::cout << "Pattrs size: " << my_path.pattrs_size() << std::endl; buf my_nlri; my_nlri.value = (char*)my_path.nlri().c_str(); my_nlri.len = my_path.nlri().size(); path_t gobgp_lib_path; gobgp_lib_path.nlri = my_nlri; // Not used in library code! gobgp_lib_path.path_attributes_cap = 0; gobgp_lib_path.path_attributes_len = my_path.pattrs_size(); buf* my_path_attributes[ my_path.pattrs_size() ]; for (int i = 0; i < my_path.pattrs_size(); i++) { my_path_attributes[i] = (buf*)malloc(sizeof(buf)); my_path_attributes[i]->len = my_path.pattrs(i).size(); my_path_attributes[i]->value = (char*)my_path.pattrs(i).c_str(); } gobgp_lib_path.path_attributes = my_path_attributes; logger << log4cpp::Priority::INFO << "NLRI: " << decode_path_dynamic(&gobgp_lib_path); //std::cout << "NLRI: " << decode_path(&gobgp_lib_path) << std::endl; } } void AnnounceFlowSpecPrefix(bool withdraw) { const gobgpapi::ModPathArguments current_mod_path_arguments; unsigned int AFI_IP = 1; unsigned int SAFI_FLOW_SPEC_UNICAST = 133; unsigned int ipv4_flow_spec_route_family = AFI_IP<<16 | SAFI_FLOW_SPEC_UNICAST; gobgpapi::Path* current_path = new gobgpapi::Path; current_path->set_is_withdraw(withdraw); /* buf: char *value; int len; path: buf nlri; buf** path_attributes; int path_attributes_len; int path_attributes_cap; */ path* path_c_struct = serialize_path_dynamic(ipv4_flow_spec_route_family, (char*)"match destination 10.0.0.0/24 protocol tcp source 20.0.0.0/24 then redirect 10:10"); // printf("Decoded NLRI output: %s, length %d raw string length: %d\n", decode_path(path_c_struct), path_c_struct->nlri.len, strlen(path_c_struct->nlri.value)); for (int path_attribute_number = 0; path_attribute_number < path_c_struct->path_attributes_len; path_attribute_number++) { current_path->add_pattrs(path_c_struct->path_attributes[path_attribute_number]->value, path_c_struct->path_attributes[path_attribute_number]->len); } current_path->set_nlri(path_c_struct->nlri.value, path_c_struct->nlri.len); gobgpapi::ModPathsArguments request; request.set_resource(gobgpapi::Resource::GLOBAL); google::protobuf::RepeatedPtrField< ::gobgpapi::Path >* current_path_list = request.mutable_paths(); current_path_list->AddAllocated(current_path); request.set_name(""); ClientContext context; gobgpapi::Error return_error; // result is a std::unique_ptr > auto send_stream = stub_->ModPaths(&context, &return_error); bool write_result = send_stream->Write(request); if (!write_result) { logger << log4cpp::Priority::INFO << "Write to API failed\n"; //std::cout << "Write to API failed\n"; } // Finish all writes send_stream->WritesDone(); auto status = send_stream->Finish(); if (status.ok()) { //std::cout << "modpath executed correctly" << std::cout; } else { logger << log4cpp::Priority::INFO << "modpath failed with code: " << status.error_code() << " message " << status.error_message(); //std::cout << "modpath failed with code: " << status.error_code() // << " message " << status.error_message() << std::endl; } } void AnnounceUnicastPrefix(std::string announced_prefix, std::string announced_prefix_nexthop, bool is_withdrawal) { const gobgpapi::ModPathArguments current_mod_path_arguments; unsigned int AFI_IP = 1; unsigned int SAFI_UNICAST = 1; unsigned int ipv4_unicast_route_family = AFI_IP<<16 | SAFI_UNICAST; gobgpapi::Path* current_path = new gobgpapi::Path; // current_path->set_is_withdraw(withdraw); if (is_withdrawal) { current_path->set_is_withdraw(true); } /* buf: char *value; int len; path: buf nlri; buf** path_attributes; int path_attributes_len; int path_attributes_cap; */ std::string announce_line = announced_prefix + " nexthop " + announced_prefix_nexthop; // 10.10.20.33/22 nexthop 10.10.1.99/32 path* path_c_struct = serialize_path_dynamic(ipv4_unicast_route_family, (char*)announce_line.c_str()); if (path_c_struct == NULL) { logger << log4cpp::Priority::ERROR << "Could not generate path\n"; return; } // printf("Decoded NLRI output: %s, length %d raw string length: %d\n", decode_path(path_c_struct), path_c_struct->nlri.len, strlen(path_c_struct->nlri.value)); for (int path_attribute_number = 0; path_attribute_number < path_c_struct->path_attributes_len; path_attribute_number++) { current_path->add_pattrs(path_c_struct->path_attributes[path_attribute_number]->value, path_c_struct->path_attributes[path_attribute_number]->len); } current_path->set_nlri(path_c_struct->nlri.value, path_c_struct->nlri.len); gobgpapi::ModPathsArguments request; request.set_resource(gobgpapi::Resource::GLOBAL); google::protobuf::RepeatedPtrField< ::gobgpapi::Path >* current_path_list = request.mutable_paths(); current_path_list->AddAllocated(current_path); request.set_name(""); ClientContext context; gobgpapi::Error return_error; // result is a std::unique_ptr > auto send_stream = stub_->ModPaths(&context, &return_error); bool write_result = send_stream->Write(request); if (!write_result) { //std::cout << "Write to API failed\n"; logger << log4cpp::Priority::ERROR << "Write to API failed\n"; return; } // Finish all writes send_stream->WritesDone(); auto status = send_stream->Finish(); if (status.ok()) { //std::cout << "modpath executed correctly" << std::cout; } else { //std::cout << "modpath failed with code: " << status.error_code() // << " message " << status.error_message() << std::endl; logger << log4cpp::Priority::ERROR << "modpath failed with code: " << status.error_code() << " message " << status.error_message(); return; } } std::string GetNeighbor(std::string neighbor_ip) { gobgpapi::Arguments request; request.set_family(4); request.set_name(neighbor_ip); ClientContext context; gobgpapi::Peer peer; grpc::Status status = stub_->GetNeighbor(&context, request, &peer); if (status.ok()) { gobgpapi::PeerConf peer_conf = peer.conf(); gobgpapi::PeerState peer_info = peer.info(); std::stringstream buffer; buffer << "Peer AS: " << peer_conf.peer_as() << "\n" << "Peer router id: " << peer_conf.id() << "\n" << "Peer flops: " << peer_info.flops() << "\n" << "BGP state: " << peer_info.bgp_state(); return buffer.str(); } else { return "Something wrong"; } } private: std::unique_ptr stub_; }; GrpcClient* gobgp_client = NULL; std::string gobgp_nexthop = "0.0.0.0"; bool gobgp_announce_whole_subnet = false; bool gobgp_announce_host = false; void gobgp_action_init() { logger << log4cpp::Priority::INFO << "GoBGP action module loaded"; gobgp_client = new GrpcClient(grpc::CreateChannel("localhost:50051", grpc::InsecureCredentials())); // GrpcClient gobgp_client(grpc::CreateChannel("localhost:50051", grpc::InsecureCredentials())); if (configuration_map.count("gobgp_next_hop")) { gobgp_nexthop = configuration_map["gobgp_next_hop"]; } if (configuration_map.count("gobgp_announce_host")) { gobgp_announce_host = configuration_map["gobgp_announce_host"] == "on"; } if (configuration_map.count("gobgp_announce_whole_subnet")) { gobgp_announce_whole_subnet = configuration_map["gobgp_announce_whole_subnet"] == "on"; } // According to this bug report: https://github.com/golang/go/issues/12873 // We have significant issues with popen, daemonization and Go's runtime // We use non absoulte path here and linker will find it fir us void* gobgdp_library_handle = dlopen("libgobgp.so", RTLD_NOW); if (gobgdp_library_handle == NULL) { logger << log4cpp::Priority::ERROR << "Could not load gobgp binary library: " << dlerror(); exit(1); } dlerror(); /* Clear any existing error */ /* According to the ISO C standard, casting between function pointers and 'void *', as done above, produces undefined results. POSIX.1-2003 and POSIX.1-2008 accepted this state of affairs and proposed the following workaround: */ serialize_path_dynamic = (serialize_path_dynamic_t)dlsym(gobgdp_library_handle, "serialize_path"); if (serialize_path_dynamic == NULL) { logger << log4cpp::Priority::ERROR << "Could not load function serialize_path from the dynamic library"; exit(1); } decode_path_dynamic = (decode_path_dynamic_t)dlsym(gobgdp_library_handle, "decode_path"); if (decode_path_dynamic == NULL) { logger << log4cpp::Priority::ERROR << "Could not load function decode_path from the dynamic library"; exit(1); } } void gobgp_action_shutdown() { delete gobgp_client; } void gobgp_ban_manage(std::string action, std::string ip_as_string, attack_details current_attack) { bool is_withdrawal = false; if (action == "ban") { is_withdrawal = false; } else { is_withdrawal = true; } if (gobgp_announce_whole_subnet) { std::string subnet_as_string_with_mask = convert_subnet_to_string(current_attack.customer_network); gobgp_client->AnnounceUnicastPrefix(subnet_as_string_with_mask, gobgp_nexthop, is_withdrawal); } if (gobgp_announce_host) { std::string ip_as_string_with_mask = ip_as_string + "/32"; gobgp_client->AnnounceUnicastPrefix(ip_as_string_with_mask, gobgp_nexthop, is_withdrawal); } } fastnetmon-1.1.4/src/actions/gobgp_action.h000066400000000000000000000004071343111404700207250ustar00rootroot00000000000000#ifndef GOBGP_ACTION_H #define GOBGP_ACTION_H #include #include "../fastnetmon_types.h" void gobgp_action_init(); void gobgp_action_shutdown(); void gobgp_ban_manage(std::string action, std::string ip_as_string, attack_details current_attack); #endif fastnetmon-1.1.4/src/actions/gobgp_api_client.proto000066400000000000000000000310301343111404700224670ustar00rootroot00000000000000// Copyright (C) 2015-2017 Nippon Telegraph and Telephone Corporation. // // Permission is hereby granted, free of charge, to any person // obtaining a copy of this software and associated documentation files // (the "Software"), to deal in the Software without restriction, // including without limitation the rights to use, copy, modify, merge, // publish, distribute, sublicense, and/or sell copies of the Software, // and to permit persons to whom the Software is furnished to do so, // subject to the following conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. syntax = "proto3"; package gobgpapi; // Interface exported by the server. service GobgpApi { rpc GetGlobalConfig(Arguments) returns (Global) {} rpc ModGlobalConfig(ModGlobalConfigArguments) returns (Error) {} rpc GetNeighbors(Arguments) returns (stream Peer) {} rpc GetNeighbor(Arguments) returns (Peer) {} rpc ModNeighbor(ModNeighborArguments) returns(Error) {} rpc GetRib(Table) returns (Table) {} rpc Reset(Arguments) returns (Error) {} rpc SoftReset(Arguments) returns (Error) {} rpc SoftResetIn(Arguments) returns (Error) {} rpc SoftResetOut(Arguments) returns (Error) {} rpc Shutdown(Arguments) returns (Error) {} rpc Enable(Arguments) returns (Error) {} rpc Disable(Arguments) returns (Error) {} rpc ModPath(ModPathArguments) returns (ModPathResponse) {} rpc ModPaths(stream ModPathsArguments) returns (Error) {} rpc MonitorRib(Table) returns (stream Destination) {} rpc MonitorBestChanged(Arguments) returns (stream Destination) {} rpc MonitorPeerState(Arguments) returns (stream Peer) {} rpc MonitorROAValidation(Arguments) returns (stream ROAResult) {} rpc GetMrt(MrtArguments) returns (stream MrtMessage) {} rpc ModMrt(ModMrtArguments) returns (Error) {} rpc ModBmp(ModBmpArguments) returns (Error) {} rpc GetRPKI(Arguments) returns (stream RPKI) {} rpc ModRPKI(ModRpkiArguments) returns (Error) {} rpc GetROA(Arguments) returns (stream ROA) {} rpc GetVrfs(Arguments) returns (stream Vrf) {} rpc ModVrf(ModVrfArguments) returns (Error) {} rpc GetDefinedSet(DefinedSet) returns (DefinedSet) {} rpc GetDefinedSets(DefinedSet) returns (stream DefinedSet) {} rpc ModDefinedSet(ModDefinedSetArguments) returns (Error) {} rpc GetStatement(Statement) returns (Statement) {} rpc GetStatements(Statement) returns (stream Statement) {} rpc ModStatement(ModStatementArguments) returns (Error) {} rpc GetPolicy(Policy) returns (Policy) {} rpc GetPolicies(Policy) returns (stream Policy) {} rpc ModPolicy(ModPolicyArguments) returns (Error) {} rpc GetPolicyAssignment(PolicyAssignment) returns (PolicyAssignment) {} rpc ModPolicyAssignment(ModPolicyAssignmentArguments) returns (Error) {} } message Error { enum ErrorCode { SUCCESS = 0; FAIL = 1; } ErrorCode code = 1; string msg = 2; } message Arguments { Resource resource = 1; uint32 family = 2; string name = 3; } message ModPathArguments { Operation operation = 1; Resource resource = 2; string name = 3; Path path = 4; // uuid field can be only used when operation is DEL bytes uuid = 5; // family field is only used when operation is DEL_ALL uint32 family = 6; } message ModPathResponse { bytes uuid = 1; } message ModPathsArguments { Resource resource = 1; string name = 2; repeated Path paths = 3; } message ModNeighborArguments { Operation operation = 1; Peer peer = 2; } message MrtArguments { Resource resource = 1; uint32 family = 2; uint64 interval = 3; string neighbor_address = 4; } message ModMrtArguments { Operation operation = 1; int32 dump_type = 2; string filename = 3; uint64 interval = 4; } message ModBmpArguments { Operation operation = 1; string address = 2; uint32 port = 3; enum MonitoringPolicy { PRE = 0; POST = 1; BOTH = 2; } MonitoringPolicy type = 4; } message ModRpkiArguments { Operation operation = 1; string address = 2; uint32 port = 3; } message ModVrfArguments { Operation operation = 1; Vrf vrf = 2; } message ModDefinedSetArguments { Operation operation = 1; DefinedSet set = 2; } message ModStatementArguments { Operation operation = 1; Statement statement = 2; } message ModPolicyArguments { Operation operation = 1; Policy policy = 2; // if this flag is set, gobgpd won't define new statements // but refer existing statements using statement's names in this arguments. // this flag only works with Operation_ADD bool refer_existing_statements = 3; // if this flag is set, gobgpd won't delete any statements // even if some statements get not used by any policy by this operation. // this flag means nothing if it is used with Operation_ADD bool preserve_statements = 4; } message ModPolicyAssignmentArguments { Operation operation = 1; PolicyAssignment assignment = 2; } message ModGlobalConfigArguments { Operation operation = 1; Global global = 2; } enum Resource { GLOBAL = 0; LOCAL = 1; ADJ_IN = 2; ADJ_OUT = 3; VRF = 4; } enum Operation { ADD = 0; DEL = 1; DEL_ALL = 2; REPLACE = 3; ENABLE = 4; DISABLE = 5; RESET = 6; SOFTRESET = 7; } message Path { bytes nlri = 1; repeated bytes pattrs = 2; int64 age = 3; bool best = 4; bool is_withdraw = 5; int32 validation = 6; bool no_implicit_withdraw = 7; uint32 family = 8; uint32 source_asn = 9; string source_id = 10; bool filtered = 11; bool stale = 12; bool is_from_external = 13; string neighbor_ip = 14; } message Destination { string prefix = 1; repeated Path paths = 2; bool longer_prefixes = 3; } message Table { Resource type = 1; string name = 2; uint32 family = 3; repeated Destination destinations = 4; bool post_policy = 5; } message Peer { repeated uint32 families = 2; ApplyPolicy apply_policy = 3; PeerConf conf = 5; EbgpMultihop ebgp_multihop = 6; RouteReflector route_reflector = 10; PeerState info = 11; Timers timers = 12; Transport transport = 13; RouteServer route_server = 15; } message ApplyPolicy { PolicyAssignment in_policy = 1; PolicyAssignment export_policy = 2; PolicyAssignment import_policy = 3; } message PeerConf { string auth_password = 1; string description = 2; uint32 local_as = 3; string neighbor_address = 4; uint32 peer_as = 5; string peer_group = 6; uint32 peer_type = 7; uint32 remove_private_as = 8; bool route_flap_damping = 9; uint32 send_community = 10; repeated bytes remote_cap = 11; repeated bytes local_cap = 12; string id = 13; } message EbgpMultihop { bool enabled = 1; uint32 multihop_ttl = 2; } message RouteReflector { bool route_reflector_client = 1; uint32 route_reflector_cluster_id = 2; } message PeerState { string auth_password = 1; string description = 2; uint32 local_as = 3; Messages messages = 4; string neighbor_address = 5; uint32 peer_as = 6; string peer_group = 7; uint32 peer_type = 8; Queues queues = 9; uint32 remove_private_as = 10; bool route_flap_damping = 11; uint32 send_community = 12; uint32 session_state = 13; repeated string supported_capabilities = 14; string bgp_state = 15; string admin_state = 16; uint32 received = 17; uint32 accepted = 18; uint32 advertised = 19; uint32 out_q = 20; uint32 flops = 21; } message Messages { Message received = 1; Message sent = 2; } message Message { uint64 NOTIFICATION = 1; uint64 UPDATE = 2; uint64 OPEN = 3; uint64 KEEPALIVE = 4; uint64 REFRESH = 5; uint64 DISCARDED = 6; uint64 TOTAL = 7; } message Queues { uint32 input = 1; uint32 output = 2; } message Timers { TimersConfig config =1; TimersState state = 2; } message TimersConfig{ uint64 connect_retry = 1; uint64 hold_time = 2; uint64 keepalive_interval = 3; uint64 minimum_advertisement_interval = 4; } message TimersState{ uint64 connect_retry = 1; uint64 hold_time = 2; uint64 keepalive_interval = 3; uint64 minimum_advertisement_interval = 4; uint64 negotiated_hold_time = 5; uint64 uptime = 6; uint64 downtime = 7; } message Transport { string local_address = 1; uint32 local_port = 2; bool mtu_discovery = 3; bool passive_mode = 4; string remote_address = 5; uint32 remote_port = 6; uint32 tcp_mss = 7; } message RouteServer { bool route_server_client = 1; } message Prefix { string ip_prefix = 1; uint32 mask_length_min = 2; uint32 mask_length_max = 3; } enum DefinedType { PREFIX = 0; NEIGHBOR = 1; TAG = 2; AS_PATH = 3; COMMUNITY = 4; EXT_COMMUNITY = 5; } message DefinedSet { DefinedType type = 1; string name = 2; repeated string list = 3; repeated Prefix prefixes = 4; } enum MatchType { ANY = 0; ALL = 1; INVERT = 2; } message MatchSet { MatchType type = 1; string name = 2; } enum AsPathLengthType { EQ = 0; GE = 1; LE = 2; } message AsPathLength { AsPathLengthType type = 1; uint32 length = 2; } message Conditions { MatchSet prefix_set = 1; MatchSet neighbor_set = 2; AsPathLength as_path_length = 3; MatchSet as_path_set = 4; MatchSet community_set = 5; MatchSet ext_community_set = 6; int32 rpki_result = 7; } enum RouteAction { NONE = 0; ACCEPT = 1; REJECT = 2; } enum CommunityActionType { COMMUNITY_ADD = 0; COMMUNITY_REMOVE = 1; COMMUNITY_REPLACE = 2; } message CommunityAction { CommunityActionType type = 1; repeated string communities = 2; } enum MedActionType { MED_MOD = 0; MED_REPLACE = 1; } message MedAction { MedActionType type = 1; int64 value = 2; } message AsPrependAction { uint32 asn = 1; uint32 repeat = 2; bool use_left_most = 3; } message Actions { RouteAction route_action = 1; CommunityAction community = 2; MedAction med = 3; AsPrependAction as_prepend = 4; CommunityAction ext_community = 5; } message Statement { string name = 1; Conditions conditions = 2; Actions actions = 3; } message Policy { string name = 1; repeated Statement statements = 2; } enum PolicyType { IN = 0; IMPORT = 1; EXPORT = 2; } message PolicyAssignment { PolicyType type = 1; Resource resource = 2; string name = 3; repeated Policy policies = 4; RouteAction default = 5; } message MrtMessage { bytes data = 1; } message RPKIConf { string address = 1; string remote_port = 2; } message RPKIState { int64 uptime = 1; int64 downtime = 2; bool up = 3; uint32 record_ipv4 = 4; uint32 record_ipv6 = 5; uint32 prefix_ipv4 = 6; uint32 prefix_ipv6 = 7; uint32 serial = 8; int64 received_ipv4 = 9; int64 received_ipv6 = 10; int64 serial_notify = 11; int64 cache_reset = 12; int64 cache_response = 13; int64 end_of_data = 14; int64 error = 15; int64 serial_query = 16; int64 reset_query = 17; } message RPKI { RPKIConf conf = 1; RPKIState state = 2; } message ROA { uint32 as = 1; uint32 prefixlen = 2; uint32 maxlen = 3; string prefix = 4; RPKIConf conf = 5; } message ROAResult { enum ValidationReason { UPDATE = 0; WITHDRAW = 1; PEER_DOWN = 2; REVALIDATE = 3; } ValidationReason reason = 1; string address = 2; int64 timestamp = 3; bytes aspath_attr = 4; uint32 origin_as = 5; string prefix = 6; enum ValidationResult { NONE = 0; NOT_FOUND = 1; VALID = 2; INVALID = 3; } ValidationResult old_result = 7; ValidationResult new_result = 8; repeated ROA roas = 9; } message Vrf { string name = 1; bytes rd = 2; repeated bytes import_rt = 3; repeated bytes export_rt = 4; } message Global { uint32 as = 1; string router_id = 2; int32 listen_port = 3; repeated string listen_addresses = 4; repeated uint32 families = 5; uint32 mpls_label_min = 6; uint32 mpls_label_max = 7; bool collector = 8; } fastnetmon-1.1.4/src/actions/pfring_hardware_filter_action.cpp000066400000000000000000000041531343111404700246730ustar00rootroot00000000000000#include "pfring.h" #include #include #include #include "../fastnetmon_actions.h" // Got it from global namespace extern pfring* pf_ring_descr; void pfring_hardware_filter_action_block(std::string client_ip_as_string) { /* 6 - tcp, 17 - udp, 0 - other (non tcp and non udp) */ std::vector banned_protocols; banned_protocols.push_back(17); banned_protocols.push_back(6); banned_protocols.push_back(0); int rule_number = 10; // Iterate over incoming and outgoing direction for (int rule_direction = 0; rule_direction < 2; rule_direction++) { for (std::vector::iterator banned_protocol = banned_protocols.begin(); banned_protocol != banned_protocols.end(); ++banned_protocol) { /* On 82599 NIC we can ban traffic using hardware filtering rules */ // Difference between fie tuple and perfect filters: // http://www.ntop.org/products/pf_ring/hardware-packet-filtering/ hw_filtering_rule rule; intel_82599_five_tuple_filter_hw_rule* ft_rule; ft_rule = &rule.rule_family.five_tuple_rule; memset(&rule, 0, sizeof(rule)); rule.rule_family_type = intel_82599_five_tuple_rule; rule.rule_id = rule_number++; ft_rule->queue_id = -1; // drop traffic ft_rule->proto = *banned_protocol; std::string hw_filter_rule_direction = ""; if (rule_direction == 0) { hw_filter_rule_direction = "outgoing"; ft_rule->s_addr = ntohl(inet_addr(client_ip_as_string.c_str())); } else { hw_filter_rule_direction = "incoming"; ft_rule->d_addr = ntohl(inet_addr(client_ip_as_string.c_str())); } if (pfring_add_hw_rule(pf_ring_descr, &rule) != 0) { logger << log4cpp::Priority::ERROR << "Can't add hardware filtering rule for protocol: " << *banned_protocol << " in direction: " << hw_filter_rule_direction; } rule_number++; } } } fastnetmon-1.1.4/src/actions/pfring_hardware_filter_action.h000066400000000000000000000002671343111404700243420ustar00rootroot00000000000000#ifndef PFRING_HARDWARE_FILTER_ACTION_H #define PFRING_HARDWARE_FILTER_ACTION_H #include void pfring_hardware_filter_action_block(std::string client_ip_as_string); #endif fastnetmon-1.1.4/src/afpacket_plugin/000077500000000000000000000000001343111404700176145ustar00rootroot00000000000000fastnetmon-1.1.4/src/afpacket_plugin/afpacket_collector.cpp000066400000000000000000000266551343111404700241620ustar00rootroot00000000000000// log4cpp logging facility #include "log4cpp/Category.hh" #include "log4cpp/Appender.hh" #include "log4cpp/FileAppender.hh" #include "log4cpp/OstreamAppender.hh" #include "log4cpp/Layout.hh" #include "log4cpp/BasicLayout.hh" #include "log4cpp/PatternLayout.hh" #include "log4cpp/Priority.hh" #include #include #include "../fast_library.h" // For support uint32_t, uint16_t #include // For config map operations #include #include #include #include #include #include "../fastnetmon_packet_parser.h" // For support: IPPROTO_TCP, IPPROTO_ICMP, IPPROTO_UDP #include #include #include #include "afpacket_collector.h" #include #include #include #include #include #include #include #include #include /* the L2 protocols */ #include "../unified_parser.hpp" bool afpacket_read_packet_length_from_ip_header = false; // Get log4cpp logger from main program extern log4cpp::Category& logger; // Pass unparsed packets number to main program extern uint64_t total_unparsed_packets; // Global configuration map extern std::map configuration_map; // This variable name should be uniq for every plugin! process_packet_pointer afpacket_process_func_ptr = NULL; // 4194304 bytes unsigned int blocksiz = 1 << 22; // 2048 bytes unsigned int framesiz = 1 << 11; unsigned int blocknum = 64; struct block_desc { uint32_t version; uint32_t offset_to_priv; struct tpacket_hdr_v1 h1; }; // Get interface number by name int get_interface_number_by_device_name(int socket_fd, std::string interface_name) { struct ifreq ifr; memset(&ifr, 0, sizeof(ifr)); if (interface_name.size() > IFNAMSIZ) { return -1; } strncpy(ifr.ifr_name, interface_name.c_str(), sizeof(ifr.ifr_name)); if (ioctl(socket_fd, SIOCGIFINDEX, &ifr) == -1) { return -1; } return ifr.ifr_ifindex; } void flush_block(struct block_desc *pbd) { pbd->h1.block_status = TP_STATUS_KERNEL; } void walk_block(struct block_desc *pbd, const int block_num) { int num_pkts = pbd->h1.num_pkts, i; unsigned long bytes = 0; struct tpacket3_hdr *ppd; ppd = (struct tpacket3_hdr *) ((uint8_t *) pbd + pbd->h1.offset_to_first_pkt); for (i = 0; i < num_pkts; ++i) { bytes += ppd->tp_snaplen; // struct ethhdr *eth = (struct ethhdr *) ((uint8_t *) ppd + ppd->tp_mac); // Print packets struct pfring_pkthdr packet_header; memset(&packet_header, 0, sizeof(packet_header)); packet_header.len = ppd->tp_snaplen; packet_header.caplen = ppd->tp_snaplen; u_int8_t timestamp = 0; u_int8_t add_hash = 0; u_char* data_pointer = (u_char*)((uint8_t *) ppd + ppd->tp_mac); simple_packet packet; int parser_result = parse_raw_packet_to_simple_packet((u_char*)data_pointer, ppd->tp_snaplen, packet, afpacket_read_packet_length_from_ip_header); //char print_buffer[512]; //fastnetmon_print_parsed_pkt(print_buffer, 512, data_pointer, &packet_header); //printf("%s\n", print_buffer); ppd = (struct tpacket3_hdr *) ((uint8_t *) ppd + ppd->tp_next_offset); if (parser_result) { afpacket_process_func_ptr(packet); } else { total_unparsed_packets++; } } } int setup_socket(std::string interface_name, int fanout_group_id) { // More details here: http://man7.org/linux/man-pages/man7/packet.7.html // We could use SOCK_RAW or SOCK_DGRAM for second argument // SOCK_RAW - raw packets pass from the kernel // SOCK_DGRAM - some amount of processing // Third argument manage ether type of captured packets int packet_socket = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); if (packet_socket == -1) { logger << log4cpp::Priority::ERROR << "Can't create AF_PACKET socket"; return -1; } // We whould use V3 bcause it could read/pool in per block basis instead per packet int version = TPACKET_V3; int setsockopt_packet_version = setsockopt(packet_socket, SOL_PACKET, PACKET_VERSION, &version, sizeof(version)); if (setsockopt_packet_version < 0) { logger << log4cpp::Priority::ERROR << "Can't set packet v3 version"; return -1; } int interface_number = get_interface_number_by_device_name(packet_socket, interface_name); if (interface_number == -1) { logger << log4cpp::Priority::ERROR << "Can't get interface number by interface name for " << interface_name; return -1; } // Switch to PROMISC mode struct packet_mreq sock_params; memset(&sock_params, 0, sizeof(sock_params)); sock_params.mr_type = PACKET_MR_PROMISC; sock_params.mr_ifindex = interface_number; int set_promisc = setsockopt(packet_socket, SOL_PACKET, PACKET_ADD_MEMBERSHIP, (void *)&sock_params, sizeof(sock_params)); if (set_promisc == -1) { logger << log4cpp::Priority::ERROR << "Can't enable promisc mode"; return -1; } struct sockaddr_ll bind_address; memset(&bind_address, 0, sizeof(bind_address)); bind_address.sll_family = AF_PACKET; bind_address.sll_protocol = htons(ETH_P_ALL); bind_address.sll_ifindex = interface_number; // We will follow http://yusufonlinux.blogspot.ru/2010/11/data-link-access-and-zero-copy.html // And this: https://www.kernel.org/doc/Documentation/networking/packet_mmap.txt struct tpacket_req3 req; memset(&req, 0, sizeof(req)); req.tp_block_size = blocksiz; req.tp_frame_size = framesiz; req.tp_block_nr = blocknum; req.tp_frame_nr = (blocksiz * blocknum) / framesiz; req.tp_retire_blk_tov = 60; // Timeout in msec req.tp_feature_req_word = TP_FT_REQ_FILL_RXHASH; int setsockopt_rx_ring = setsockopt(packet_socket, SOL_PACKET , PACKET_RX_RING , (void*)&req , sizeof(req)); if (setsockopt_rx_ring == -1) { logger << log4cpp::Priority::ERROR << "Can't enable RX_RING for AF_PACKET socket"; return -1; } // We use per thread structures uint8_t* mapped_buffer = NULL; struct iovec* rd = NULL; mapped_buffer = (uint8_t*)mmap(NULL, req.tp_block_size * req.tp_block_nr, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_LOCKED, packet_socket, 0); if (mapped_buffer == MAP_FAILED) { logger << log4cpp::Priority::ERROR << "MMAP failed"; return -1; } // Allocate iov structure for each block rd = (struct iovec*)malloc(req.tp_block_nr * sizeof(struct iovec)); // Initilize iov structures for (int i = 0; i < req.tp_block_nr; ++i) { rd[i].iov_base = mapped_buffer + (i * req.tp_block_size); rd[i].iov_len = req.tp_block_size; } int bind_result = bind(packet_socket, (struct sockaddr *)&bind_address, sizeof(bind_address)); if (bind_result == -1) { logger << log4cpp::Priority::ERROR << "Can't bind to AF_PACKET socket"; return -1; } if (fanout_group_id) { // PACKET_FANOUT_LB - round robin // PACKET_FANOUT_CPU - send packets to CPU where packet arrived int fanout_type = PACKET_FANOUT_CPU; int fanout_arg = (fanout_group_id | (fanout_type << 16)); int setsockopt_fanout = setsockopt(packet_socket, SOL_PACKET, PACKET_FANOUT, &fanout_arg, sizeof(fanout_arg)); if (setsockopt_fanout < 0) { logger << log4cpp::Priority::ERROR << "Can't configure fanout error number: "<< errno << " error: " << strerror(errno); return -1; } } unsigned int current_block_num = 0; struct pollfd pfd; memset(&pfd, 0, sizeof(pfd)); pfd.fd = packet_socket; pfd.events = POLLIN | POLLERR; pfd.revents = 0; while (true) { struct block_desc *pbd = (struct block_desc *) rd[current_block_num].iov_base; if ((pbd->h1.block_status & TP_STATUS_USER) == 0) { poll(&pfd, 1, -1); continue; } walk_block(pbd, current_block_num); flush_block(pbd); current_block_num = (current_block_num + 1) % blocknum; } return packet_socket; } void start_af_packet_capture(std::string interface_name, int fanout_group_id) { setup_socket(interface_name, fanout_group_id); } void get_af_packet_stats() { // getsockopt PACKET_STATISTICS } // Could get some speed up on NUMA servers bool afpacket_execute_strict_cpu_affinity = true; void start_afpacket_collection(process_packet_pointer func_ptr) { logger << log4cpp::Priority::INFO << "AF_PACKET plugin started"; afpacket_process_func_ptr = func_ptr; if (configuration_map.count("netmap_read_packet_length_from_ip_header") != 0) { afpacket_read_packet_length_from_ip_header = configuration_map["netmap_read_packet_length_from_ip_header"] == "on"; } std::string interfaces_list = ""; if (configuration_map.count("interfaces") != 0) { interfaces_list = configuration_map["interfaces"]; } std::vector interfaces_for_listen; boost::split(interfaces_for_listen, interfaces_list, boost::is_any_of(","), boost::token_compress_on); logger << log4cpp::Priority::INFO << "AF_PACKET will listen on " << interfaces_for_listen.size() << " interfaces"; if (interfaces_for_listen.size() == 0) { logger << log4cpp::Priority::ERROR << "Please specify intreface for AF_PACKET"; return; } if (interfaces_for_listen.size() > 1) { logger << log4cpp::Priority::WARN << "We support only single interface for AF_PACKET, sorry!"; } std::string capture_interface = interfaces_for_listen[0]; int fanout_group_id = getpid() & 0xffff; unsigned int num_cpus = sysconf(_SC_NPROCESSORS_ONLN);; logger.info("We have %d cpus for AF_PACKET", num_cpus); if (num_cpus > 1) { boost::thread_group packet_receiver_thread_group; for (int cpu = 0; cpu < num_cpus; cpu++) { // Well, we have thread attributes from Boost 1.50 #if defined(BOOST_THREAD_PLATFORM_PTHREAD) && BOOST_VERSION / 100 % 1000 >= 50 && defined(__GLIBC__) boost::thread::attributes thread_attrs; if (afpacket_execute_strict_cpu_affinity) { cpu_set_t current_cpu_set; int cpu_to_bind = cpu % num_cpus; CPU_ZERO(¤t_cpu_set); // We count cpus from zero CPU_SET(cpu_to_bind, ¤t_cpu_set); int set_affinity_result = pthread_attr_setaffinity_np(thread_attrs.native_handle(), sizeof(cpu_set_t), ¤t_cpu_set); if (set_affinity_result != 0) { logger << log4cpp::Priority::ERROR << "Can't set CPU affinity for thread"; } } packet_receiver_thread_group.add_thread( new boost::thread(thread_attrs, boost::bind(start_af_packet_capture, capture_interface, fanout_group_id)) ); #else logger.error("Sorry but CPU affinity did not supported for your platform"); packet_receiver_thread_group.add_thread( new boost::thread(start_af_packet_capture, capture_interface, fanout_group_id) ); #endif } // Wait all processes for finish packet_receiver_thread_group.join_all(); } else { start_af_packet_capture(capture_interface, 0); } } fastnetmon-1.1.4/src/afpacket_plugin/afpacket_collector.h000066400000000000000000000002401343111404700236050ustar00rootroot00000000000000#ifndef AFPACKET_PLUGIN_H #define AFPACKET_PLUGIN_H #include "../fastnetmon_types.h" void start_afpacket_collection(process_packet_pointer func_ptr); #endif fastnetmon-1.1.4/src/asn_geoip_update.sh000077500000000000000000000022201343111404700203210ustar00rootroot00000000000000#!/usr/bin/env bash # Current dir pushd "$(dirname $0)" >/dev/null SCRIPT_DIR="$(pwd -P)" popd >/dev/null OS_TYPE=$(uname) echo "Getting GeoIPASNum.dat GeoIPASNumv6.dat..." # ASN (+IPv6) Database if [ "$OS_TYPE" = "Linux" ];then wget -qO - "http://geolite.maxmind.com/download/geoip/database/asnum/GeoIPASNum.dat.gz" | gunzip -cv > "${SCRIPT_DIR}/GeoIPASNum.dat" wget -qO - "http://download.maxmind.com/download/geoip/database/asnum/GeoIPASNumv6.dat.gz" | gunzip -cv > "${SCRIPT_DIR}/GeoIPASNumv6.dat" elif [ "$OS_TYPE" = "Darwin" ];then curl -sq "http://geolite.maxmind.com/download/geoip/database/asnum/GeoIPASNum.dat.gz" | gunzip -v -c > "${SCRIPT_DIR}/GeoIPASNum.dat" curl -sq "http://download.maxmind.com/download/geoip/database/asnum/GeoIPASNumv6.dat.gz" | gunzip -v -c > "${SCRIPT_DIR}/GeoIPASNumv6.dat" elif [ "$OS_TYPE" = "FreeBSD" ];then fetch -qo - "http://geolite.maxmind.com/download/geoip/database/asnum/GeoIPASNum.dat.gz" | gunzip -cv > "${SCRIPT_DIR}/GeoIPASNum.dat" fetch -qo - "http://download.maxmind.com/download/geoip/database/asnum/GeoIPASNumv6.dat.gz" | gunzip -cv > "${SCRIPT_DIR}/GeoIPASNumv6.dat" fi echo "Done." fastnetmon-1.1.4/src/bgp_flow_spec.cpp000066400000000000000000000004461343111404700200010ustar00rootroot00000000000000#include "bgp_flow_spec.h" #include "log4cpp/Category.hh" #include "log4cpp/Appender.hh" #include "log4cpp/FileAppender.hh" #include "log4cpp/OstreamAppender.hh" #include "log4cpp/Layout.hh" #include "log4cpp/BasicLayout.hh" #include "log4cpp/PatternLayout.hh" #include "log4cpp/Priority.hh" fastnetmon-1.1.4/src/bgp_flow_spec.h000066400000000000000000000404341343111404700174470ustar00rootroot00000000000000#ifndef BGP_FLOW_SPEC_H #define BGP_FLOW_SPEC_H #include #include #include #include "fastnetmon_types.h" #include "fast_library.h" // Helper for serialization by comma template std::string serialize_vector_by_string(const std::vector vect, std::string delimiter) { std::ostringstream output_buffer; std::copy(vect.begin(), vect.end() - 1, std::ostream_iterator(output_buffer, delimiter.c_str())); output_buffer << vect.back(); return output_buffer.str(); } template std::string serialize_vector_by_string_with_prefix(const std::vector vect, std::string delimiter, std::string prefix) { std::vector elements_with_prefix; for (typename std::vector::const_iterator itr = vect.begin(); itr != vect.end(); ++itr) { elements_with_prefix.push_back(prefix + convert_int_to_string(*itr)); } return serialize_vector_by_string(elements_with_prefix, delimiter); } // All possible values for BGP Flow Spec fragmentation field enum flow_spec_fragmentation_types_t { FLOW_SPEC_DONT_FRAGMENT, FLOW_SPEC_IS_A_FRAGMENT, FLOW_SPEC_FIRST_FRAGMENT, FLOW_SPEC_LAST_FRAGMENT, FLOW_NOT_A_FRAGMENT, }; // TCP flags for Flow Spec enum flow_spec_tcp_flags_t { FLOW_TCP_FLAG_SYN, FLOW_TCP_FLAG_FIN, FLOW_TCP_FLAG_URG, FLOW_TCP_FLAG_ACK, FLOW_TCP_FLAG_PSH, FLOW_TCP_FLAG_RST, }; // Flow spec actions enum bgp_flow_spec_action_types_t { FLOW_SPEC_ACTION_DISCARD, FLOW_SPEC_ACTION_ACCEPT, FLOW_SPEC_ACTION_RATE_LIMIT, // TBD }; enum bgp_flow_spec_protocol_t { FLOW_SPEC_PROTOCOL_UDP, FLOW_SPEC_PROTOCOL_TCP, FLOW_SPEC_PROTOCOL_ICMP, }; // Enable custom casts from our own types std::ostream &operator<<(std::ostream &os, bgp_flow_spec_protocol_t const &protocol) { if (protocol == FLOW_SPEC_PROTOCOL_UDP) { return os << "udp"; } else if (protocol == FLOW_SPEC_PROTOCOL_TCP) { return os << "tcp"; } else if (protocol == FLOW_SPEC_PROTOCOL_ICMP) { return os << "icmp"; } else { return os; } } std::ostream &operator<<(std::ostream &os, flow_spec_fragmentation_types_t const &fragment_flag) { // Nice docs here: https://github.com/Exa-Networks/exabgp/blob/71157d560096ec20084cf96cfe0f60203721e93b/lib/exabgp/protocol/ip/fragment.py if (fragment_flag == FLOW_SPEC_DONT_FRAGMENT) { return os << "dont-fragment"; } else if (fragment_flag == FLOW_SPEC_IS_A_FRAGMENT) { return os << "is-fragment"; } else if (fragment_flag == FLOW_SPEC_FIRST_FRAGMENT) { return os << "first-fragment"; } else if (fragment_flag == FLOW_SPEC_LAST_FRAGMENT) { return os << "last-fragment"; } else if (fragment_flag == FLOW_NOT_A_FRAGMENT) { return os << "not-a-fragment"; } else { return os; } } std::ostream &operator<<(std::ostream &os, flow_spec_tcp_flags_t const &tcp_flag) { if (tcp_flag == FLOW_TCP_FLAG_SYN) { return os << "syn"; } else if (tcp_flag == FLOW_TCP_FLAG_ACK) { return os << "ack"; } else if (tcp_flag == FLOW_TCP_FLAG_FIN) { return os << "fin"; } else if (tcp_flag == FLOW_TCP_FLAG_URG) { return os << "urgent"; } else if (tcp_flag == FLOW_TCP_FLAG_PSH) { return os << "push"; } else if(tcp_flag == FLOW_TCP_FLAG_RST) { return os << "rst"; } else { return os; } } class bgp_flow_spec_action_t { public: bgp_flow_spec_action_t() { this->action_type = FLOW_SPEC_ACTION_ACCEPT; this->rate_limit = 9600; sentence_separator = ";"; } void set_type(bgp_flow_spec_action_types_t action_type) { this->action_type = action_type; } void set_rate_limit(unsigned int rate_limit) { this->rate_limit = rate_limit; } void set_sentence_separator(std::string sentence_separator) { this->sentence_separator = sentence_separator; } std::string serialize() { if (this->action_type == FLOW_SPEC_ACTION_ACCEPT) { return "accept" + sentence_separator; } else if (this->action_type == FLOW_SPEC_ACTION_DISCARD) { return "discard" + sentence_separator; } else if (this->action_type == FLOW_SPEC_ACTION_RATE_LIMIT) { return "rate-limit " + convert_int_to_string(this->rate_limit) + sentence_separator; } } private: bgp_flow_spec_action_types_t action_type; unsigned int rate_limit; std::string sentence_separator; // TBD // Community, rate-limit value }; // We do not use < and > operators at all, sorry class flow_spec_rule_t { public: flow_spec_rule_t() { // We should explidictly initialize it! source_subnet_used = false; destination_subnet_used = false; } bool announce_is_correct() { if (source_subnet_used || destination_subnet_used) { return true; } else { return false; } } void set_source_subnet(subnet_t source_subnet) { this->source_subnet = source_subnet; this->source_subnet_used = true; } void set_destination_subnet(subnet_t destination_subnet) { this->destination_subnet = destination_subnet; this->destination_subnet_used = true; } void add_source_port(uint16_t source_port) { this->source_ports.push_back(source_port); } void add_destination_port(uint16_t destination_port) { this->destination_ports.push_back(destination_port); } void add_packet_length(uint16_t packet_length) { this->packet_lengths.push_back(packet_length); } void add_protocol(bgp_flow_spec_protocol_t protocol) { this->protocols.push_back(protocol); } /* std::string icmp_flags; bool icmp_flags_used; std::string icmp_type; bool icmp_type_used; std::string dscp; bool dscp_used; */ void add_fragmentation_flag(flow_spec_fragmentation_types_t flag) { this->fragmentation_flags.push_back(flag); } void add_tcp_flag(flow_spec_tcp_flags_t flag) { this->tcp_flags.push_back(flag); } void set_action(bgp_flow_spec_action_t action) { this->action = action; } protected: // Only IPv4 supported subnet_t source_subnet; bool source_subnet_used; subnet_t destination_subnet; bool destination_subnet_used; std::vector source_ports; std::vector destination_ports; std::vector packet_lengths; std::vector protocols; std::vector fragmentation_flags; std::vector tcp_flags; bgp_flow_spec_action_t action; }; class exabgp_flow_spec_rule_t : public flow_spec_rule_t { public: exabgp_flow_spec_rule_t() { four_spaces = " "; sentence_separator = ";"; this->enabled_indents = true; this->enble_block_headers = true; } void disable_indents() { enabled_indents = false; } std::string serialize_source_ports() { std::ostringstream output_buffer; output_buffer << "source-port [ " << serialize_vector_by_string_with_prefix(this->source_ports, " ", "=") << " ]" << sentence_separator; return output_buffer.str(); } std::string serialize_destination_ports() { std::ostringstream output_buffer; output_buffer << "destination-port [ " << serialize_vector_by_string_with_prefix(this->destination_ports, " ", "=") << " ]" << sentence_separator; return output_buffer.str(); } std::string serialize_packet_lengths() { std::ostringstream output_buffer; output_buffer << "packet-length [ " << serialize_vector_by_string_with_prefix(this->packet_lengths, " ", "=") << " ]" << sentence_separator; return output_buffer.str(); } std::string serialize_protocols() { std::ostringstream output_buffer; output_buffer << "protocol [ " << serialize_vector_by_string(this->protocols, " ") << " ]" << sentence_separator; return output_buffer.str(); } std::string serialize_fragmentation_flags() { std::ostringstream output_buffer; output_buffer << "fragment [ " << serialize_vector_by_string(this->fragmentation_flags, " ") << " ]" << sentence_separator; return output_buffer.str(); } std::string serialize_tcp_flags() { std::ostringstream output_buffer; output_buffer << "tcp-flags [ " << serialize_vector_by_string(this->tcp_flags, " ") << " ]" << sentence_separator; return output_buffer.str(); } std::string serialize_source_subnet() { return "source " + convert_subnet_to_string(this->source_subnet) + sentence_separator; } std::string serialize_destination_subnet() { return "destination " + convert_subnet_to_string(this->destination_subnet) + sentence_separator; } // More details regarding format: https://github.com/Exa-Networks/exabgp/blob/master/qa/conf/api-flow.run // https://plus.google.com/+ThomasMangin/posts/bL6w16BXcJ4 // This format is INCOMPATIBLE with ExaBGP v3, please be careful! std::string serialize_single_line_exabgp_v4_configuration() { this->enabled_indents = false; this->enble_block_headers = false; sentence_separator = " "; return "flow route " + this->serialize_match() + this->serialize_then(); sentence_separator = ";"; this->enabled_indents = true; this->enble_block_headers = true; } std::string serialize_complete_exabgp_configuration() { std::ostringstream buffer; buffer << "neighbor 127.0.0.1 {" << "\n" << four_spaces << "router-id 1.2.3.4;" << "\n" << four_spaces << "local-address 127.0.0.1;" << "\n" << four_spaces << "local-as 1;" << "\n" << four_spaces << "peer-as 1;" << "\n" << four_spaces << "group-updates false;" << "\n\n"; buffer << four_spaces << "family {" << "\n" << four_spaces << four_spaces << "ipv4 flow;" << "\n" << four_spaces << four_spaces << "ipv6 flow;" << "\n" << four_spaces << "}" << "\n"; buffer << "flow {" << "\n"; buffer << this->serialize(); buffer << "}" << "\n"; buffer << "}" << "\n"; return buffer.str(); } std::string serialize() { std::ostringstream buffer; buffer << "route {"; if (enabled_indents) { buffer << "\n"; } buffer << this->serialize_match(); buffer << this->serialize_then(); if (enabled_indents) { buffer << "\n"; } buffer << "}"; if (enabled_indents) { buffer << "\n"; } return buffer.str(); } std::string serialize_match() { std::ostringstream buffer; if (enabled_indents) { buffer << four_spaces; } if (enble_block_headers) { buffer << "match {"; } if (enabled_indents) { buffer << "\n"; } // Match block if (this->source_subnet_used) { if (enabled_indents) { buffer << four_spaces << four_spaces; } buffer << serialize_source_subnet(); if (enabled_indents) { buffer << "\n"; } } if (this->destination_subnet_used) { if (enabled_indents) { buffer << four_spaces << four_spaces; } buffer << serialize_destination_subnet(); if (enabled_indents) { buffer << "\n"; } } if (!this->protocols.empty()) { if (enabled_indents) { buffer << four_spaces << four_spaces; } buffer << this->serialize_protocols(); if (enabled_indents) { buffer << "\n"; } } // If we have TCP in protocols list explicitly, we add flags if (find(this->protocols.begin(), this->protocols.end(), FLOW_SPEC_PROTOCOL_TCP) != this->protocols.end() ) { if (!this->tcp_flags.empty()) { if (enabled_indents) { buffer << four_spaces << four_spaces; } buffer << this->serialize_tcp_flags(); if (enabled_indents) { buffer << "\n"; } } } if (!this->source_ports.empty()) { if (enabled_indents) { buffer << four_spaces << four_spaces; } buffer << this->serialize_source_ports(); if (enabled_indents) { buffer << "\n"; } } if (!this->destination_ports.empty()) { if (enabled_indents) { buffer << four_spaces << four_spaces; } buffer << this->serialize_destination_ports(); if (enabled_indents) { buffer << "\n"; } } if (!this->packet_lengths.empty()) { if (enabled_indents) { buffer << four_spaces << four_spaces; } buffer << this->serialize_packet_lengths(); if (enabled_indents) { buffer << "\n"; } } if (!this->fragmentation_flags.empty()) { if (enabled_indents) { buffer << four_spaces << four_spaces; } buffer << this->serialize_fragmentation_flags(); if (enabled_indents) { buffer << "\n"; } } // Match block end if (enabled_indents) { buffer << four_spaces; } if (enble_block_headers) { buffer << "}"; } return buffer.str(); } std::string serialize_then() { std::ostringstream buffer; if (enabled_indents) { buffer << "\n" << four_spaces; } if (enble_block_headers) { buffer << "then {"; } if (enabled_indents) { buffer << "\n"; buffer << four_spaces << four_spaces; } // Set same sentence separator as in main class this->action.set_sentence_separator(this->sentence_separator); buffer << this->action.serialize(); if (enabled_indents) { buffer << "\n"; buffer << four_spaces; } if (enble_block_headers) { buffer << "}"; } return buffer.str(); } private: std::string four_spaces; bool enabled_indents; bool enble_block_headers; std::string sentence_separator; }; void exabgp_flow_spec_rule_ban_manage(std::string action, flow_spec_rule_t flow_spec_rule) { // "announce flow route {\\n match {\\n source 10.0.0.1/32;\\nsource-port =" + str(i) + // ";\\n destination 1.2.3.4/32;\\n }\\n then {\\n discard;\\n }\\n }\\n\n") } #endif fastnetmon-1.1.4/src/ci/000077500000000000000000000000001343111404700150535ustar00rootroot00000000000000fastnetmon-1.1.4/src/ci/parallel_builder.go000066400000000000000000000135731343111404700207150ustar00rootroot00000000000000package main import "fmt" import "sync" import "os" import "bytes" import "os/exec" import "regexp" import "log" import "strconv" import "io/ioutil" // In this folder we will store all results of our work var target_directory = "" var public_key_path = "/root/.ssh/id_rsa.pub" var container_private_path = "/vz_zram/private" /* Download all images vztmpl-dl --update centos-6-x86_64 centos-6-x86 centos-7-x86_64 debian-7.0-x86 debian-7.0-x86_64 debian-8.0-x86_64 ubuntu-12.04-x86 ubuntu-12.04-x86_64 ubuntu-14.04-x86 ubuntu-14.04-x86_64 debian-6.0-x86_64 Please configure NAT before: Fix /etc/modprobe.d/openvz.conf to following content: options nf_conntrack ip_conntrack_disable_ve0=0 vim /etc/sysct.conf Uncomment: net.ipv4.ip_forward=1 sysctl -p iptables -t nat -A POSTROUTING -s 10.10.10.1/24 -o eth0 -j SNAT --to 192.168.0.241 Save iptables config /etc/init.d/iptables save Enable iptables: chkconfig iptables on Generate ssh key: ssh-keygen -t rsa -q -f /root/.ssh/id_rsa -P "" Disable quotas: vim /etc/vz/vz.conf # Disable quota DISK_QUOTA=no Create zram disk for build speedup: modprobe zram num_devices=1 echo $((20*1024*1024*1024)) > /sys/block/zram0/disksize mkdir /vz_zram mkfs.ext4 /dev/zram0 mount /dev/zram0 /vz_zram mkdir /vz_zram/private */ /* For renaming of result packages you could use: find -type f| perl -e 'do{ chomp; my @m=split "/", $_; my @n = split /\./, $_; rename($_, "fastnetmon-git-447aa5b86bb5a248e310c15a4d5945e72594d6cf-$m[1]_x86_64.$n[-1]"); } for <>' */ var distros_x86_64 = []string{"centos-6-x86_64", "centos-7-x86_64", "debian-6.0-x86_64", "debian-7.0-x86_64", "debian-8.0-x86_64", "ubuntu-12.04-x86_64", "ubuntu-14.04-x86_64"} var distros_x86 = []string{"centos-6-x86", "debian-6.0-x86", "debian-7.0-x86", "ubuntu-12.04-x86", "ubuntu-14.04-x86"} var start_ctid_number = 1000 func main() { target_directory, err := ioutil.TempDir("/root", "builded_packages") if err != nil { log.Fatal("Can't create temp folder", err) } fmt.Println("We will store result data to folder", target_directory) _ = distros_x86 var wg sync.WaitGroup if _, err := os.Stat(public_key_path); os.IsNotExist(err) { log.Fatal("Please generate ssh keys for root here") } for element_number, distro := range distros_x86_64 { // Increment the WaitGroup counter. wg.Add(1) go func(position int, distribution_name string) { // Decrement the counter when the goroutine completes. defer wg.Done() ip_address := fmt.Sprintf("10.10.10.%d", position) ctid := start_ctid_number + position ctid_as_string := strconv.Itoa(ctid) vzctl_create_as_string := fmt.Sprintf("create %d --ostemplate %s --config vswap-4g --layout simfs --ipadd %s --diskspace 20G --hostname ct%d.test.com --private %s/%d", ctid, distribution_name, ip_address, ctid, container_private_path, ctid) r := regexp.MustCompile("[^\\s]+") vzctl_create_as_list := r.FindAllString(vzctl_create_as_string, -1) fmt.Println("Create container ", ctid_as_string) create_cmd := exec.Command("/usr/sbin/vzctl", vzctl_create_as_list...) //cmd.Stdout = os.Stdout //cmd.Stderr = os.Stderr err := create_cmd.Run() if err != nil { log.Println("create failed") log.Fatal(err) } // Run it fmt.Println("Start container ", ctid_as_string) // We whould wait here for full CT startup start_cmd := exec.Command("/usr/sbin/vzctl", "start", ctid_as_string, "--wait") // start_cmd.Stdout = os.Stdout // start_cmd.Stderr = os.Stderr err = start_cmd.Run() if err != nil { log.Println("start failed") log.Fatal(err) } vzroot_path := fmt.Sprintf("/vz/root/%d", ctid) auth_keys_path := vzroot_path + "/root/.ssh/authorized_keys" os.Mkdir(vzroot_path+"/root/.ssh", 0600) copy_key_command := exec.Command("cp", public_key_path, auth_keys_path) copy_key_command.Run() if err != nil { log.Println("Can't copy ssh keys to container") log.Fatal(err) } os.Chmod(auth_keys_path, 0400) wget_installer_cmd := exec.Command("wget", "--no-check-certificate", "https://raw.githubusercontent.com/pavel-odintsov/fastnetmon/master/src/fastnetmon_install.pl", "-O"+vzroot_path+"/root/fastnetmon_install.pl") wget_installer_cmd.Run() if err != nil { log.Println("Can't download FastNetMon installer to container") log.Fatal(err) } // Remove ssh known hosst file because in other case ssh will fail os.Remove("/root/.ssh/known_hosts") // perl /root/fastnetmon_install.pl --use-git-master --create-binary-bundle --build-binary-environment" // install_cmd := exec.Command("ssh", "-lroot", ip_address, "perl", "/root/fastnetmon_install.pl") install_cmd := exec.Command("ssh", "-o", "UserKnownHostsFile=/dev/null", "-o", "StrictHostKeyChecking=no", "-lroot", ip_address, "perl", "/root/fastnetmon_install.pl", "--use-git-master", "--create-binary-bundle", "--build-binary-environment") var stdout_output bytes.Buffer var stderr_output bytes.Buffer install_cmd.Stdout = &stdout_output install_cmd.Stderr = &stderr_output install_cmd.Run() fmt.Println("Command call on " + distribution_name + " finished") fmt.Println("stdout") fmt.Println(stdout_output.String()) fmt.Println("stderr") fmt.Println(stderr_output.String()) fmt.Println("Get produced data from container to host system") copy_cmd := exec.Command("cp", "-rf", "/vz/root/"+ctid_as_string+"/tmp/result_data", target_directory+"/"+distribution_name) copy_cmd.Run() // Stop it fmt.Println("Stop container ", ctid_as_string) stop_cmd := exec.Command("/usr/sbin/vzctl", "stop", ctid_as_string) err = stop_cmd.Run() if err != nil { log.Println("stop failed") log.Fatal(err) } fmt.Println("Destroy container ", ctid_as_string) destroy_cmd := exec.Command("/usr/sbin/vzctl", "destroy", ctid_as_string) err = destroy_cmd.Run() if err != nil { log.Println("destroy failed") log.Fatal(err) } }(element_number, distro) } wg.Wait() } fastnetmon-1.1.4/src/concurrentqueue.h000066400000000000000000004252031343111404700200660ustar00rootroot00000000000000// Provides a C++11 implementation of a multi-producer, multi-consumer lock-free queue. // An overview, including benchmark results, is provided here: // http://moodycamel.com/blog/2014/a-fast-general-purpose-lock-free-queue-for-c++ // The full design is also described in excruciating detail at: // http://moodycamel.com/blog/2014/detailed-design-of-a-lock-free-queue // Simplified BSD license: // Copyright (c) 2013-2015, Cameron Desrochers. // All rights reserved. // // Redistribution and use in source and binary forms, with or without modification, // are permitted provided that the following conditions are met: // // - Redistributions of source code must retain the above copyright notice, this list of // conditions and the following disclaimer. // - Redistributions in binary form must reproduce the above copyright notice, this list of // conditions and the following disclaimer in the documentation and/or other materials // provided with the distribution. // // 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 HOLDER 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. #pragma once #if defined(__GNUC__) // Disable -Wconversion warnings (spuriously triggered when Traits::size_t and // Traits::index_t are set to < 32 bits, causing integer promotion, causing warnings // upon assigning any computed values) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wconversion" #ifdef MCDBGQ_USE_RELACY #pragma GCC diagnostic ignored "-Wint-to-pointer-cast" #endif #endif #ifdef MCDBGQ_USE_RELACY #include "relacy/relacy_std.hpp" #include "relacy_shims.h" // We only use malloc/free anyway, and the delete macro messes up `= delete` method declarations. // We'll override the default trait malloc ourselves without a macro. #undef new #undef delete #undef malloc #undef free #else #include // Requires C++11. Sorry VS2010. #include #endif #include #include #include #include #include #include #include // for CHAR_BIT #include #include // for __WINPTHREADS_VERSION if on MinGW-w64 w/ POSIX threading // Platform-specific definitions of a numeric thread ID type and an invalid value #if defined(MCDBGQ_USE_RELACY) namespace moodycamel { namespace details { typedef std::uint32_t thread_id_t; static const thread_id_t invalid_thread_id = 0xFFFFFFFFU; static const thread_id_t invalid_thread_id2 = 0xFFFFFFFEU; static inline thread_id_t thread_id() { return rl::thread_index(); } } } #elif defined(_WIN32) || defined(__WINDOWS__) || defined(__WIN32__) // No sense pulling in windows.h in a header, we'll manually declare the function // we use and rely on backwards-compatibility for this not to break extern "C" __declspec(dllimport) unsigned long __stdcall GetCurrentThreadId(void); namespace moodycamel { namespace details { static_assert(sizeof(unsigned long) == sizeof(std::uint32_t), "Expected size of unsigned long to be 32 bits on Windows"); typedef std::uint32_t thread_id_t; static const thread_id_t invalid_thread_id = 0; // See http://blogs.msdn.com/b/oldnewthing/archive/2004/02/23/78395.aspx static const thread_id_t invalid_thread_id2 = 0xFFFFFFFFU; // Not technically guaranteed to be invalid, but is never used in practice. Note that all Win32 thread IDs are presently multiples of 4. static inline thread_id_t thread_id() { return static_cast(::GetCurrentThreadId()); } } } #else // Use a nice trick from this answer: http://stackoverflow.com/a/8438730/21475 // In order to get a numeric thread ID in a platform-independent way, we use a thread-local // static variable's address as a thread identifier :-) #if defined(__GNUC__) || defined(__INTEL_COMPILER) #define MOODYCAMEL_THREADLOCAL __thread #elif defined(_MSC_VER) #define MOODYCAMEL_THREADLOCAL __declspec(thread) #else // Assume C++11 compliant compiler #define MOODYCAMEL_THREADLOCAL thread_local #endif namespace moodycamel { namespace details { typedef std::uintptr_t thread_id_t; static const thread_id_t invalid_thread_id = 0; // Address can't be nullptr static const thread_id_t invalid_thread_id2 = 1; // Member accesses off a null pointer are also generally invalid. Plus it's not aligned. static inline thread_id_t thread_id() { static MOODYCAMEL_THREADLOCAL int x; return reinterpret_cast(&x); } } } #endif // Exceptions #ifndef MOODYCAMEL_EXCEPTIONS_ENABLED #if (defined(_MSC_VER) && defined(_CPPUNWIND)) || (defined(__GNUC__) && defined(__EXCEPTIONS)) || (!defined(_MSC_VER) && !defined(__GNUC__)) #define MOODYCAMEL_EXCEPTIONS_ENABLED #define MOODYCAMEL_TRY try #define MOODYCAMEL_CATCH(...) catch(__VA_ARGS__) #define MOODYCAMEL_RETHROW throw #define MOODYCAMEL_THROW(expr) throw (expr) #else #define MOODYCAMEL_TRY if (true) #define MOODYCAMEL_CATCH(...) else if (false) #define MOODYCAMEL_RETHROW #define MOODYCAMEL_THROW(expr) #endif #endif #ifndef MOODYCAMEL_NOEXCEPT #if !defined(MOODYCAMEL_EXCEPTIONS_ENABLED) #define MOODYCAMEL_NOEXCEPT #define MOODYCAMEL_NOEXCEPT_CTOR(type, valueType, expr) true #define MOODYCAMEL_NOEXCEPT_ASSIGN(type, valueType, expr) true #elif defined(_MSC_VER) && defined(_NOEXCEPT) && _MSC_VER < 1800 // VS2012's std::is_nothrow_[move_]constructible is broken and returns true when it shouldn't :-( // We have to assume *all* non-trivial constructors may throw on VS2012! #define MOODYCAMEL_NOEXCEPT _NOEXCEPT #define MOODYCAMEL_NOEXCEPT_CTOR(type, valueType, expr) (std::is_rvalue_reference::value && std::is_move_constructible::value ? std::is_trivially_move_constructible::value : std::is_trivially_copy_constructible::value) #define MOODYCAMEL_NOEXCEPT_ASSIGN(type, valueType, expr) ((std::is_rvalue_reference::value && std::is_move_assignable::value ? std::is_trivially_move_assignable::value || std::is_nothrow_move_assignable::value : std::is_trivially_copy_assignable::value || std::is_nothrow_copy_assignable::value) && MOODYCAMEL_NOEXCEPT_CTOR(type, valueType, expr)) #elif defined(_MSC_VER) && defined(_NOEXCEPT) && _MSC_VER < 1900 #define MOODYCAMEL_NOEXCEPT _NOEXCEPT #define MOODYCAMEL_NOEXCEPT_CTOR(type, valueType, expr) (std::is_rvalue_reference::value && std::is_move_constructible::value ? std::is_trivially_move_constructible::value || std::is_nothrow_move_constructible::value : std::is_trivially_copy_constructible::value || std::is_nothrow_copy_constructible::value) #define MOODYCAMEL_NOEXCEPT_ASSIGN(type, valueType, expr) ((std::is_rvalue_reference::value && std::is_move_assignable::value ? std::is_trivially_move_assignable::value || std::is_nothrow_move_assignable::value : std::is_trivially_copy_assignable::value || std::is_nothrow_copy_assignable::value) && MOODYCAMEL_NOEXCEPT_CTOR(type, valueType, expr)) #else #define MOODYCAMEL_NOEXCEPT noexcept #define MOODYCAMEL_NOEXCEPT_CTOR(type, valueType, expr) noexcept(expr) #define MOODYCAMEL_NOEXCEPT_ASSIGN(type, valueType, expr) noexcept(expr) #endif #endif #ifndef MOODYCAMEL_CPP11_THREAD_LOCAL_SUPPORTED #ifdef MCDBGQ_USE_RELACY #define MOODYCAMEL_CPP11_THREAD_LOCAL_SUPPORTED #else //// VS2013 doesn't support `thread_local`, and MinGW-w64 w/ POSIX threading has a crippling bug: http://sourceforge.net/p/mingw-w64/bugs/445 //// g++ <=4.7 doesn't support thread_local either //#if (!defined(_MSC_VER) || _MSC_VER >= 1900) && (!defined(__MINGW32__) && !defined(__MINGW64__) || !defined(__WINPTHREADS_VERSION)) && (!defined(__GNUC__) || __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) //// Assume `thread_local` is fully supported in all other C++11 compilers/runtimes //#define MOODYCAMEL_CPP11_THREAD_LOCAL_SUPPORTED //#endif #endif #endif // VS2012 doesn't support deleted functions. // In this case, we declare the function normally but don't define it. A link error will be generated if the function is called. #ifndef MOODYCAMEL_DELETE_FUNCTION #if defined(_MSC_VER) && _MSC_VER < 1800 #define MOODYCAMEL_DELETE_FUNCTION #else #define MOODYCAMEL_DELETE_FUNCTION = delete #endif #endif // Compiler-specific likely/unlikely hints namespace moodycamel { namespace details { #if defined(__GNUC__) inline bool likely(bool x) { return __builtin_expect((x), true); } inline bool unlikely(bool x) { return __builtin_expect((x), false); } #else inline bool likely(bool x) { return x; } inline bool unlikely(bool x) { return x; } #endif } } #ifdef MOODYCAMEL_QUEUE_INTERNAL_DEBUG #include "internal/concurrentqueue_internal_debug.h" #endif namespace moodycamel { namespace details { template struct const_numeric_max { static_assert(std::is_integral::value, "const_numeric_max can only be used with integers"); static const T value = std::numeric_limits::is_signed ? (static_cast(1) << (sizeof(T) * CHAR_BIT - 1)) - static_cast(1) : static_cast(-1); }; } // Default traits for the ConcurrentQueue. To change some of the // traits without re-implementing all of them, inherit from this // struct and shadow the declarations you wish to be different; // since the traits are used as a template type parameter, the // shadowed declarations will be used where defined, and the defaults // otherwise. struct ConcurrentQueueDefaultTraits { // General-purpose size type. std::size_t is strongly recommended. typedef std::size_t size_t; // The type used for the enqueue and dequeue indices. Must be at least as // large as size_t. Should be significantly larger than the number of elements // you expect to hold at once, especially if you have a high turnover rate; // for example, on 32-bit x86, if you expect to have over a hundred million // elements or pump several million elements through your queue in a very // short space of time, using a 32-bit type *may* trigger a race condition. // A 64-bit int type is recommended in that case, and in practice will // prevent a race condition no matter the usage of the queue. Note that // whether the queue is lock-free with a 64-int type depends on the whether // std::atomic is lock-free, which is platform-specific. typedef std::size_t index_t; // Internally, all elements are enqueued and dequeued from multi-element // blocks; this is the smallest controllable unit. If you expect few elements // but many producers, a smaller block size should be favoured. For few producers // and/or many elements, a larger block size is preferred. A sane default // is provided. Must be a power of 2. static const size_t BLOCK_SIZE = 32; // For explicit producers (i.e. when using a producer token), the block is // checked for being empty by iterating through a list of flags, one per element. // For large block sizes, this is too inefficient, and switching to an atomic // counter-based approach is faster. The switch is made for block sizes strictly // larger than this threshold. static const size_t EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD = 32; // How many full blocks can be expected for a single explicit producer? This should // reflect that number's maximum for optimal performance. Must be a power of 2. static const size_t EXPLICIT_INITIAL_INDEX_SIZE = 32; // How many full blocks can be expected for a single implicit producer? This should // reflect that number's maximum for optimal performance. Must be a power of 2. static const size_t IMPLICIT_INITIAL_INDEX_SIZE = 32; // The initial size of the hash table mapping thread IDs to implicit producers. // Note that the hash is resized every time it becomes half full. // Must be a power of two, and either 0 or at least 1. If 0, implicit production // (using the enqueue methods without an explicit producer token) is disabled. static const size_t INITIAL_IMPLICIT_PRODUCER_HASH_SIZE = 32; // Controls the number of items that an explicit consumer (i.e. one with a token) // must consume before it causes all consumers to rotate and move on to the next // internal queue. static const std::uint32_t EXPLICIT_CONSUMER_CONSUMPTION_QUOTA_BEFORE_ROTATE = 256; // The maximum number of elements (inclusive) that can be enqueued to a sub-queue. // Enqueue operations that would cause this limit to be surpassed will fail. Note // that this limit is enforced at the block level (for performance reasons), i.e. // it's rounded up to the nearest block size. static const size_t MAX_SUBQUEUE_SIZE = details::const_numeric_max::value; #ifndef MCDBGQ_USE_RELACY // Memory allocation can be customized if needed. // malloc should return nullptr on failure, and handle alignment like std::malloc. static inline void* malloc(size_t size) { return std::malloc(size); } static inline void free(void* ptr) { return std::free(ptr); } #else // Debug versions when running under the Relacy race detector (ignore // these in user code) static inline void* malloc(size_t size) { return rl::rl_malloc(size, $); } static inline void free(void* ptr) { return rl::rl_free(ptr, $); } #endif }; // When producing or consuming many elements, the most efficient way is to: // 1) Use one of the bulk-operation methods of the queue with a token // 2) Failing that, use the bulk-operation methods without a token // 3) Failing that, create a token and use that with the single-item methods // 4) Failing that, use the single-parameter methods of the queue // Having said that, don't create tokens willy-nilly -- ideally there should be // a maximum of one token per thread (of each kind). struct ProducerToken; struct ConsumerToken; template class ConcurrentQueue; template class BlockingConcurrentQueue; class ConcurrentQueueTests; namespace details { struct ConcurrentQueueProducerTypelessBase { ConcurrentQueueProducerTypelessBase* next; std::atomic inactive; ProducerToken* token; ConcurrentQueueProducerTypelessBase() : inactive(false), token(nullptr) { } }; template struct _hash_32_or_64 { static inline std::uint32_t hash(std::uint32_t h) { // MurmurHash3 finalizer -- see https://code.google.com/p/smhasher/source/browse/trunk/MurmurHash3.cpp // Since the thread ID is already unique, all we really want to do is propagate that // uniqueness evenly across all the bits, so that we can use a subset of the bits while // reducing collisions significantly h ^= h >> 16; h *= 0x85ebca6b; h ^= h >> 13; h *= 0xc2b2ae35; return h ^ (h >> 16); } }; template<> struct _hash_32_or_64<1> { static inline std::uint64_t hash(std::uint64_t h) { h ^= h >> 33; h *= 0xff51afd7ed558ccd; h ^= h >> 33; h *= 0xc4ceb9fe1a85ec53; return h ^ (h >> 33); } }; template struct hash_32_or_64 : public _hash_32_or_64<(size > 4)> { }; static inline size_t hash_thread_id(thread_id_t id) { static_assert(sizeof(thread_id_t) <= 8, "Expected a platform where thread IDs are at most 64-bit values"); return static_cast(hash_32_or_64::hash(id)); } template static inline bool circular_less_than(T a, T b) { #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable: 4554) #endif static_assert(std::is_integral::value && !std::numeric_limits::is_signed, "circular_less_than is intended to be used only with unsigned integer types"); return static_cast(a - b) > static_cast(static_cast(1) << static_cast(sizeof(T) * CHAR_BIT - 1)); #ifdef _MSC_VER #pragma warning(pop) #endif } template static inline char* align_for(char* ptr) { const std::size_t alignment = std::alignment_of::value; return ptr + (alignment - (reinterpret_cast(ptr) % alignment)) % alignment; } template static inline T ceil_to_pow_2(T x) { static_assert(std::is_integral::value && !std::numeric_limits::is_signed, "ceil_to_pow_2 is intended to be used only with unsigned integer types"); // Adapted from http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 --x; x |= x >> 1; x |= x >> 2; x |= x >> 4; for (std::size_t i = 1; i < sizeof(T); i <<= 1) { x |= x >> (i << 3); } ++x; return x; } template static inline void swap_relaxed(std::atomic& left, std::atomic& right) { T temp = std::move(left.load(std::memory_order_relaxed)); left.store(std::move(right.load(std::memory_order_relaxed)), std::memory_order_relaxed); right.store(std::move(temp), std::memory_order_relaxed); } template static inline T const& nomove(T const& x) { return x; } template struct nomove_if { template static inline T const& eval(T const& x) { return x; } }; template<> struct nomove_if { template static inline auto eval(U&& x) -> decltype(std::forward(x)) { return std::forward(x); } }; template static inline auto deref_noexcept(It& it) MOODYCAMEL_NOEXCEPT -> decltype(*it) { return *it; } #if defined(__APPLE__) || defined(__clang__) || !defined(__GNUC__) || __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8) template struct is_trivially_destructible : std::is_trivially_destructible { }; #else template struct is_trivially_destructible : std::has_trivial_destructor { }; #endif #ifdef MOODYCAMEL_CPP11_THREAD_LOCAL_SUPPORTED #ifdef MCDBGQ_USE_RELACY typedef RelacyThreadExitListener ThreadExitListener; typedef RelacyThreadExitNotifier ThreadExitNotifier; #else struct ThreadExitListener { typedef void (*callback_t)(void*); callback_t callback; void* userData; ThreadExitListener* next; // reserved for use by the ThreadExitNotifier }; class ThreadExitNotifier { public: static void subscribe(ThreadExitListener* listener) { auto& tlsInst = instance(); listener->next = tlsInst.tail; tlsInst.tail = listener; } static void unsubscribe(ThreadExitListener* listener) { auto& tlsInst = instance(); ThreadExitListener** prev = &tlsInst.tail; for (auto ptr = tlsInst.tail; ptr != nullptr; ptr = ptr->next) { if (ptr == listener) { *prev = ptr->next; break; } prev = &ptr->next; } } private: ThreadExitNotifier() : tail(nullptr) { } ThreadExitNotifier(ThreadExitNotifier const&) MOODYCAMEL_DELETE_FUNCTION; ThreadExitNotifier& operator=(ThreadExitNotifier const&) MOODYCAMEL_DELETE_FUNCTION; ~ThreadExitNotifier() { // This thread is about to exit, let everyone know! assert(this == &instance() && "If this assert fails, you likely have a buggy compiler! Change the preprocessor conditions such that MOODYCAMEL_CPP11_THREAD_LOCAL_SUPPORTED is no longer defined."); for (auto ptr = tail; ptr != nullptr; ptr = ptr->next) { ptr->callback(ptr->userData); } } // Thread-local static inline ThreadExitNotifier& instance() { static thread_local ThreadExitNotifier notifier; return notifier; } private: ThreadExitListener* tail; }; #endif #endif template struct static_is_lock_free_num { enum { value = 0 }; }; template<> struct static_is_lock_free_num { enum { value = ATOMIC_CHAR_LOCK_FREE }; }; template<> struct static_is_lock_free_num { enum { value = ATOMIC_SHORT_LOCK_FREE }; }; template<> struct static_is_lock_free_num { enum { value = ATOMIC_INT_LOCK_FREE }; }; template<> struct static_is_lock_free_num { enum { value = ATOMIC_LONG_LOCK_FREE }; }; template<> struct static_is_lock_free_num { enum { value = ATOMIC_LLONG_LOCK_FREE }; }; template struct static_is_lock_free : static_is_lock_free_num::type> { }; template<> struct static_is_lock_free { enum { value = ATOMIC_BOOL_LOCK_FREE }; }; template struct static_is_lock_free { enum { value = ATOMIC_POINTER_LOCK_FREE }; }; } struct ProducerToken { template explicit ProducerToken(ConcurrentQueue& queue); template explicit ProducerToken(BlockingConcurrentQueue& queue); explicit ProducerToken(ProducerToken&& other) MOODYCAMEL_NOEXCEPT : producer(other.producer) { other.producer = nullptr; if (producer != nullptr) { producer->token = this; } } inline ProducerToken& operator=(ProducerToken&& other) MOODYCAMEL_NOEXCEPT { swap(other); return *this; } void swap(ProducerToken& other) MOODYCAMEL_NOEXCEPT { std::swap(producer, other.producer); if (producer != nullptr) { producer->token = this; } if (other.producer != nullptr) { other.producer->token = &other; } } // A token is always valid unless: // 1) Memory allocation failed during construction // 2) It was moved via the move constructor // (Note: assignment does a swap, leaving both potentially valid) // 3) The associated queue was destroyed // Note that if valid() returns true, that only indicates // that the token is valid for use with a specific queue, // but not which one; that's up to the user to track. inline bool valid() const { return producer != nullptr; } ~ProducerToken() { if (producer != nullptr) { producer->token = nullptr; producer->inactive.store(true, std::memory_order_release); } } // Disable copying and assignment ProducerToken(ProducerToken const&) MOODYCAMEL_DELETE_FUNCTION; ProducerToken& operator=(ProducerToken const&) MOODYCAMEL_DELETE_FUNCTION; private: template friend class ConcurrentQueue; friend class ConcurrentQueueTests; protected: details::ConcurrentQueueProducerTypelessBase* producer; }; struct ConsumerToken { template explicit ConsumerToken(ConcurrentQueue& q); template explicit ConsumerToken(BlockingConcurrentQueue& q); explicit ConsumerToken(ConsumerToken&& other) MOODYCAMEL_NOEXCEPT : initialOffset(other.initialOffset), lastKnownGlobalOffset(other.lastKnownGlobalOffset), itemsConsumedFromCurrent(other.itemsConsumedFromCurrent), currentProducer(other.currentProducer), desiredProducer(other.desiredProducer) { } inline ConsumerToken& operator=(ConsumerToken&& other) MOODYCAMEL_NOEXCEPT { swap(other); return *this; } void swap(ConsumerToken& other) MOODYCAMEL_NOEXCEPT { std::swap(initialOffset, other.initialOffset); std::swap(lastKnownGlobalOffset, other.lastKnownGlobalOffset); std::swap(itemsConsumedFromCurrent, other.itemsConsumedFromCurrent); std::swap(currentProducer, other.currentProducer); std::swap(desiredProducer, other.desiredProducer); } // Disable copying and assignment ConsumerToken(ConsumerToken const&) MOODYCAMEL_DELETE_FUNCTION; ConsumerToken& operator=(ConsumerToken const&) MOODYCAMEL_DELETE_FUNCTION; private: template friend class ConcurrentQueue; friend class ConcurrentQueueTests; private: // but shared with ConcurrentQueue std::uint32_t initialOffset; std::uint32_t lastKnownGlobalOffset; std::uint32_t itemsConsumedFromCurrent; details::ConcurrentQueueProducerTypelessBase* currentProducer; details::ConcurrentQueueProducerTypelessBase* desiredProducer; }; // Need to forward-declare this swap because it's in a namespace. // See http://stackoverflow.com/questions/4492062/why-does-a-c-friend-class-need-a-forward-declaration-only-in-other-namespaces template inline void swap(typename ConcurrentQueue::ImplicitProducerKVP& a, typename ConcurrentQueue::ImplicitProducerKVP& b) MOODYCAMEL_NOEXCEPT; template class ConcurrentQueue { public: typedef ::moodycamel::ProducerToken producer_token_t; typedef ::moodycamel::ConsumerToken consumer_token_t; typedef typename Traits::index_t index_t; typedef typename Traits::size_t size_t; static const size_t BLOCK_SIZE = static_cast(Traits::BLOCK_SIZE); static const size_t EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD = static_cast(Traits::EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD); static const size_t EXPLICIT_INITIAL_INDEX_SIZE = static_cast(Traits::EXPLICIT_INITIAL_INDEX_SIZE); static const size_t IMPLICIT_INITIAL_INDEX_SIZE = static_cast(Traits::IMPLICIT_INITIAL_INDEX_SIZE); static const size_t INITIAL_IMPLICIT_PRODUCER_HASH_SIZE = static_cast(Traits::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE); static const std::uint32_t EXPLICIT_CONSUMER_CONSUMPTION_QUOTA_BEFORE_ROTATE = static_cast(Traits::EXPLICIT_CONSUMER_CONSUMPTION_QUOTA_BEFORE_ROTATE); #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable: 4307) // + integral constant overflow (that's what the ternary expression is for!) #pragma warning(disable: 4309) // static_cast: Truncation of constant value #endif static const size_t MAX_SUBQUEUE_SIZE = (details::const_numeric_max::value - static_cast(Traits::MAX_SUBQUEUE_SIZE) < BLOCK_SIZE) ? details::const_numeric_max::value : ((static_cast(Traits::MAX_SUBQUEUE_SIZE) + (BLOCK_SIZE - 1)) / BLOCK_SIZE * BLOCK_SIZE); #ifdef _MSC_VER #pragma warning(pop) #endif static_assert(!std::numeric_limits::is_signed && std::is_integral::value, "Traits::size_t must be an unsigned integral type"); static_assert(!std::numeric_limits::is_signed && std::is_integral::value, "Traits::index_t must be an unsigned integral type"); static_assert(sizeof(index_t) >= sizeof(size_t), "Traits::index_t must be at least as wide as Traits::size_t"); static_assert((BLOCK_SIZE > 1) && !(BLOCK_SIZE & (BLOCK_SIZE - 1)), "Traits::BLOCK_SIZE must be a power of 2 (and at least 2)"); static_assert((EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD > 1) && !(EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD & (EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD - 1)), "Traits::EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD must be a power of 2 (and greater than 1)"); static_assert((EXPLICIT_INITIAL_INDEX_SIZE > 1) && !(EXPLICIT_INITIAL_INDEX_SIZE & (EXPLICIT_INITIAL_INDEX_SIZE - 1)), "Traits::EXPLICIT_INITIAL_INDEX_SIZE must be a power of 2 (and greater than 1)"); static_assert((IMPLICIT_INITIAL_INDEX_SIZE > 1) && !(IMPLICIT_INITIAL_INDEX_SIZE & (IMPLICIT_INITIAL_INDEX_SIZE - 1)), "Traits::IMPLICIT_INITIAL_INDEX_SIZE must be a power of 2 (and greater than 1)"); static_assert((INITIAL_IMPLICIT_PRODUCER_HASH_SIZE == 0) || !(INITIAL_IMPLICIT_PRODUCER_HASH_SIZE & (INITIAL_IMPLICIT_PRODUCER_HASH_SIZE - 1)), "Traits::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE must be a power of 2"); static_assert(INITIAL_IMPLICIT_PRODUCER_HASH_SIZE == 0 || INITIAL_IMPLICIT_PRODUCER_HASH_SIZE >= 1, "Traits::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE must be at least 1 (or 0 to disable implicit enqueueing)"); public: // Creates a queue with at least `capacity` element slots; note that the // actual number of elements that can be inserted without additional memory // allocation depends on the number of producers and the block size (e.g. if // the block size is equal to `capacity`, only a single block will be allocated // up-front, which means only a single producer will be able to enqueue elements // without an extra allocation -- blocks aren't shared between producers). // This method is not thread safe -- it is up to the user to ensure that the // queue is fully constructed before it starts being used by other threads (this // includes making the memory effects of construction visible, possibly with a // memory barrier). explicit ConcurrentQueue(size_t capacity = 6 * BLOCK_SIZE) : producerListTail(nullptr), producerCount(0), initialBlockPoolIndex(0), nextExplicitConsumerId(0), globalExplicitConsumerOffset(0) { implicitProducerHashResizeInProgress.clear(std::memory_order_relaxed); populate_initial_implicit_producer_hash(); populate_initial_block_list(capacity / BLOCK_SIZE + ((capacity & (BLOCK_SIZE - 1)) == 0 ? 0 : 1)); #ifdef MOODYCAMEL_QUEUE_INTERNAL_DEBUG // Track all the producers using a fully-resolved typed list for // each kind; this makes it possible to debug them starting from // the root queue object (otherwise wacky casts are needed that // don't compile in the debugger's expression evaluator). explicitProducers.store(nullptr, std::memory_order_relaxed); implicitProducers.store(nullptr, std::memory_order_relaxed); #endif } // Computes the correct amount of pre-allocated blocks for you based // on the minimum number of elements you want available at any given // time, and the maximum concurrent number of each type of producer. ConcurrentQueue(size_t minCapacity, size_t maxExplicitProducers, size_t maxImplicitProducers) : producerListTail(nullptr), producerCount(0), initialBlockPoolIndex(0), nextExplicitConsumerId(0), globalExplicitConsumerOffset(0) { implicitProducerHashResizeInProgress.clear(std::memory_order_relaxed); populate_initial_implicit_producer_hash(); size_t blocks = ((((minCapacity + BLOCK_SIZE - 1) / BLOCK_SIZE) - 1) * (maxExplicitProducers + 1) + 2 * (maxExplicitProducers + maxImplicitProducers)) * BLOCK_SIZE; populate_initial_block_list(blocks); #ifdef MOODYCAMEL_QUEUE_INTERNAL_DEBUG explicitProducers.store(nullptr, std::memory_order_relaxed); implicitProducers.store(nullptr, std::memory_order_relaxed); #endif } // Note: The queue should not be accessed concurrently while it's // being deleted. It's up to the user to synchronize this. // This method is not thread safe. ~ConcurrentQueue() { // Destroy producers auto ptr = producerListTail.load(std::memory_order_relaxed); while (ptr != nullptr) { auto next = ptr->next_prod(); if (ptr->token != nullptr) { ptr->token->producer = nullptr; } destroy(ptr); ptr = next; } // Destroy implicit producer hash tables if (INITIAL_IMPLICIT_PRODUCER_HASH_SIZE != 0) { auto hash = implicitProducerHash.load(std::memory_order_relaxed); while (hash != nullptr) { auto prev = hash->prev; if (prev != nullptr) { // The last hash is part of this object and was not allocated dynamically for (size_t i = 0; i != hash->capacity; ++i) { hash->entries[i].~ImplicitProducerKVP(); } hash->~ImplicitProducerHash(); Traits::free(hash); } hash = prev; } } // Destroy global free list auto block = freeList.head_unsafe(); while (block != nullptr) { auto next = block->freeListNext.load(std::memory_order_relaxed); if (block->dynamicallyAllocated) { destroy(block); } block = next; } // Destroy initial free list destroy_array(initialBlockPool, initialBlockPoolSize); } // Disable copying and copy assignment ConcurrentQueue(ConcurrentQueue const&) MOODYCAMEL_DELETE_FUNCTION; ConcurrentQueue& operator=(ConcurrentQueue const&) MOODYCAMEL_DELETE_FUNCTION; // Moving is supported, but note that it is *not* a thread-safe operation. // Nobody can use the queue while it's being moved, and the memory effects // of that move must be propagated to other threads before they can use it. // Note: When a queue is moved, its tokens are still valid but can only be // used with the destination queue (i.e. semantically they are moved along // with the queue itself). ConcurrentQueue(ConcurrentQueue&& other) MOODYCAMEL_NOEXCEPT : producerListTail(other.producerListTail.load(std::memory_order_relaxed)), producerCount(other.producerCount.load(std::memory_order_relaxed)), initialBlockPoolIndex(other.initialBlockPoolIndex.load(std::memory_order_relaxed)), initialBlockPool(other.initialBlockPool), initialBlockPoolSize(other.initialBlockPoolSize), freeList(std::move(other.freeList)), nextExplicitConsumerId(other.nextExplicitConsumerId.load(std::memory_order_relaxed)), globalExplicitConsumerOffset(other.globalExplicitConsumerOffset.load(std::memory_order_relaxed)) { // Move the other one into this, and leave the other one as an empty queue implicitProducerHashResizeInProgress.clear(std::memory_order_relaxed); populate_initial_implicit_producer_hash(); swap_implicit_producer_hashes(other); other.producerListTail.store(nullptr, std::memory_order_relaxed); other.producerCount.store(0, std::memory_order_relaxed); other.nextExplicitConsumerId.store(0, std::memory_order_relaxed); other.globalExplicitConsumerOffset.store(0, std::memory_order_relaxed); #ifdef MOODYCAMEL_QUEUE_INTERNAL_DEBUG explicitProducers.store(other.explicitProducers.load(std::memory_order_relaxed), std::memory_order_relaxed); other.explicitProducers.store(nullptr, std::memory_order_relaxed); implicitProducers.store(other.implicitProducers.load(std::memory_order_relaxed), std::memory_order_relaxed); other.implicitProducers.store(nullptr, std::memory_order_relaxed); #endif other.initialBlockPoolIndex.store(0, std::memory_order_relaxed); other.initialBlockPoolSize = 0; other.initialBlockPool = nullptr; reown_producers(); } inline ConcurrentQueue& operator=(ConcurrentQueue&& other) MOODYCAMEL_NOEXCEPT { return swap_internal(other); } // Swaps this queue's state with the other's. Not thread-safe. // Swapping two queues does not invalidate their tokens, however // the tokens that were created for one queue must be used with // only the swapped queue (i.e. the tokens are tied to the // queue's movable state, not the object itself). inline void swap(ConcurrentQueue& other) MOODYCAMEL_NOEXCEPT { swap_internal(other); } private: ConcurrentQueue& swap_internal(ConcurrentQueue& other) { if (this == &other) { return *this; } details::swap_relaxed(producerListTail, other.producerListTail); details::swap_relaxed(producerCount, other.producerCount); details::swap_relaxed(initialBlockPoolIndex, other.initialBlockPoolIndex); std::swap(initialBlockPool, other.initialBlockPool); std::swap(initialBlockPoolSize, other.initialBlockPoolSize); freeList.swap(other.freeList); details::swap_relaxed(nextExplicitConsumerId, other.nextExplicitConsumerId); details::swap_relaxed(globalExplicitConsumerOffset, other.globalExplicitConsumerOffset); swap_implicit_producer_hashes(other); reown_producers(); other.reown_producers(); #ifdef MOODYCAMEL_QUEUE_INTERNAL_DEBUG details::swap_relaxed(explicitProducers, other.explicitProducers); details::swap_relaxed(implicitProducers, other.implicitProducers); #endif return *this; } public: // Enqueues a single item (by copying it). // Allocates memory if required. Only fails if memory allocation fails (or implicit // production is disabled because Traits::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE is 0, // or Traits::MAX_SUBQUEUE_SIZE has been defined and would be surpassed). // Thread-safe. inline bool enqueue(T const& item) { if (INITIAL_IMPLICIT_PRODUCER_HASH_SIZE == 0) return false; return inner_enqueue(item); } // Enqueues a single item (by moving it, if possible). // Allocates memory if required. Only fails if memory allocation fails (or implicit // production is disabled because Traits::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE is 0, // or Traits::MAX_SUBQUEUE_SIZE has been defined and would be surpassed). // Thread-safe. inline bool enqueue(T&& item) { if (INITIAL_IMPLICIT_PRODUCER_HASH_SIZE == 0) return false; return inner_enqueue(std::move(item)); } // Enqueues a single item (by copying it) using an explicit producer token. // Allocates memory if required. Only fails if memory allocation fails (or // Traits::MAX_SUBQUEUE_SIZE has been defined and would be surpassed). // Thread-safe. inline bool enqueue(producer_token_t const& token, T const& item) { return inner_enqueue(token, item); } // Enqueues a single item (by moving it, if possible) using an explicit producer token. // Allocates memory if required. Only fails if memory allocation fails (or // Traits::MAX_SUBQUEUE_SIZE has been defined and would be surpassed). // Thread-safe. inline bool enqueue(producer_token_t const& token, T&& item) { return inner_enqueue(token, std::move(item)); } // Enqueues several items. // Allocates memory if required. Only fails if memory allocation fails (or // implicit production is disabled because Traits::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE // is 0, or Traits::MAX_SUBQUEUE_SIZE has been defined and would be surpassed). // Note: Use std::make_move_iterator if the elements should be moved instead of copied. // Thread-safe. template bool enqueue_bulk(It itemFirst, size_t count) { if (INITIAL_IMPLICIT_PRODUCER_HASH_SIZE == 0) return false; return inner_enqueue_bulk(std::forward(itemFirst), count); } // Enqueues several items using an explicit producer token. // Allocates memory if required. Only fails if memory allocation fails // (or Traits::MAX_SUBQUEUE_SIZE has been defined and would be surpassed). // Note: Use std::make_move_iterator if the elements should be moved // instead of copied. // Thread-safe. template bool enqueue_bulk(producer_token_t const& token, It itemFirst, size_t count) { return inner_enqueue_bulk(token, std::forward(itemFirst), count); } // Enqueues a single item (by copying it). // Does not allocate memory. Fails if not enough room to enqueue (or implicit // production is disabled because Traits::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE // is 0). // Thread-safe. inline bool try_enqueue(T const& item) { if (INITIAL_IMPLICIT_PRODUCER_HASH_SIZE == 0) return false; return inner_enqueue(item); } // Enqueues a single item (by moving it, if possible). // Does not allocate memory (except for one-time implicit producer). // Fails if not enough room to enqueue (or implicit production is // disabled because Traits::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE is 0). // Thread-safe. inline bool try_enqueue(T&& item) { if (INITIAL_IMPLICIT_PRODUCER_HASH_SIZE == 0) return false; return inner_enqueue(std::move(item)); } // Enqueues a single item (by copying it) using an explicit producer token. // Does not allocate memory. Fails if not enough room to enqueue. // Thread-safe. inline bool try_enqueue(producer_token_t const& token, T const& item) { return inner_enqueue(token, item); } // Enqueues a single item (by moving it, if possible) using an explicit producer token. // Does not allocate memory. Fails if not enough room to enqueue. // Thread-safe. inline bool try_enqueue(producer_token_t const& token, T&& item) { return inner_enqueue(token, std::move(item)); } // Enqueues several items. // Does not allocate memory (except for one-time implicit producer). // Fails if not enough room to enqueue (or implicit production is // disabled because Traits::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE is 0). // Note: Use std::make_move_iterator if the elements should be moved // instead of copied. // Thread-safe. template bool try_enqueue_bulk(It itemFirst, size_t count) { if (INITIAL_IMPLICIT_PRODUCER_HASH_SIZE == 0) return false; return inner_enqueue_bulk(std::forward(itemFirst), count); } // Enqueues several items using an explicit producer token. // Does not allocate memory. Fails if not enough room to enqueue. // Note: Use std::make_move_iterator if the elements should be moved // instead of copied. // Thread-safe. template bool try_enqueue_bulk(producer_token_t const& token, It itemFirst, size_t count) { return inner_enqueue_bulk(token, std::forward(itemFirst), count); } // Attempts to dequeue from the queue. // Returns false if all producer streams appeared empty at the time they // were checked (so, the queue is likely but not guaranteed to be empty). // Never allocates. Thread-safe. template bool try_dequeue(U& item) { // Instead of simply trying each producer in turn (which could cause needless contention on the first // producer), we score them heuristically. size_t nonEmptyCount = 0; ProducerBase* best = nullptr; size_t bestSize = 0; for (auto ptr = producerListTail.load(std::memory_order_acquire); nonEmptyCount < 3 && ptr != nullptr; ptr = ptr->next_prod()) { auto size = ptr->size_approx(); if (size > 0) { if (size > bestSize) { bestSize = size; best = ptr; } ++nonEmptyCount; } } // If there was at least one non-empty queue but it appears empty at the time // we try to dequeue from it, we need to make sure every queue's been tried if (nonEmptyCount > 0) { if (details::likely(best->dequeue(item))) { return true; } for (auto ptr = producerListTail.load(std::memory_order_acquire); ptr != nullptr; ptr = ptr->next_prod()) { if (ptr != best && ptr->dequeue(item)) { return true; } } } return false; } // Attempts to dequeue from the queue. // Returns false if all producer streams appeared empty at the time they // were checked (so, the queue is likely but not guaranteed to be empty). // This differs from the try_dequeue(item) method in that this one does // not attempt to reduce contention by interleaving the order that producer // streams are dequeued from. So, using this method can reduce overall throughput // under contention, but will give more predictable results in single-threaded // consumer scenarios. This is mostly only useful for internal unit tests. // Never allocates. Thread-safe. template bool try_dequeue_non_interleaved(U& item) { for (auto ptr = producerListTail.load(std::memory_order_acquire); ptr != nullptr; ptr = ptr->next_prod()) { if (ptr->dequeue(item)) { return true; } } return false; } // Attempts to dequeue from the queue using an explicit consumer token. // Returns false if all producer streams appeared empty at the time they // were checked (so, the queue is likely but not guaranteed to be empty). // Never allocates. Thread-safe. template bool try_dequeue(consumer_token_t& token, U& item) { // The idea is roughly as follows: // Every 256 items from one producer, make everyone rotate (increase the global offset) -> this means the highest efficiency consumer dictates the rotation speed of everyone else, more or less // If you see that the global offset has changed, you must reset your consumption counter and move to your designated place // If there's no items where you're supposed to be, keep moving until you find a producer with some items // If the global offset has not changed but you've run out of items to consume, move over from your current position until you find an producer with something in it if (token.desiredProducer == nullptr || token.lastKnownGlobalOffset != globalExplicitConsumerOffset.load(std::memory_order_relaxed)) { if (!update_current_producer_after_rotation(token)) { return false; } } // If there was at least one non-empty queue but it appears empty at the time // we try to dequeue from it, we need to make sure every queue's been tried if (static_cast(token.currentProducer)->dequeue(item)) { if (++token.itemsConsumedFromCurrent == EXPLICIT_CONSUMER_CONSUMPTION_QUOTA_BEFORE_ROTATE) { globalExplicitConsumerOffset.fetch_add(1, std::memory_order_relaxed); } return true; } auto tail = producerListTail.load(std::memory_order_acquire); auto ptr = static_cast(token.currentProducer)->next_prod(); if (ptr == nullptr) { ptr = tail; } while (ptr != static_cast(token.currentProducer)) { if (ptr->dequeue(item)) { token.currentProducer = ptr; token.itemsConsumedFromCurrent = 1; return true; } ptr = ptr->next_prod(); if (ptr == nullptr) { ptr = tail; } } return false; } // Attempts to dequeue several elements from the queue. // Returns the number of items actually dequeued. // Returns 0 if all producer streams appeared empty at the time they // were checked (so, the queue is likely but not guaranteed to be empty). // Never allocates. Thread-safe. template size_t try_dequeue_bulk(It itemFirst, size_t max) { size_t count = 0; for (auto ptr = producerListTail.load(std::memory_order_acquire); ptr != nullptr; ptr = ptr->next_prod()) { count += ptr->dequeue_bulk(itemFirst, max - count); if (count == max) { break; } } return count; } // Attempts to dequeue several elements from the queue using an explicit consumer token. // Returns the number of items actually dequeued. // Returns 0 if all producer streams appeared empty at the time they // were checked (so, the queue is likely but not guaranteed to be empty). // Never allocates. Thread-safe. template size_t try_dequeue_bulk(consumer_token_t& token, It itemFirst, size_t max) { if (token.desiredProducer == nullptr || token.lastKnownGlobalOffset != globalExplicitConsumerOffset.load(std::memory_order_relaxed)) { if (!update_current_producer_after_rotation(token)) { return false; } } size_t count = static_cast(token.currentProducer)->dequeue_bulk(itemFirst, max); if (count == max) { if ((token.itemsConsumedFromCurrent += static_cast(max)) >= EXPLICIT_CONSUMER_CONSUMPTION_QUOTA_BEFORE_ROTATE) { globalExplicitConsumerOffset.fetch_add(1, std::memory_order_relaxed); } return max; } token.itemsConsumedFromCurrent += static_cast(count); max -= count; auto tail = producerListTail.load(std::memory_order_acquire); auto ptr = static_cast(token.currentProducer)->next_prod(); if (ptr == nullptr) { ptr = tail; } while (ptr != static_cast(token.currentProducer)) { auto dequeued = ptr->dequeue_bulk(itemFirst, max); count += dequeued; if (dequeued != 0) { token.currentProducer = ptr; token.itemsConsumedFromCurrent = static_cast(dequeued); } if (dequeued == max) { break; } max -= dequeued; ptr = ptr->next_prod(); if (ptr == nullptr) { ptr = tail; } } return count; } // Attempts to dequeue from a specific producer's inner queue. // If you happen to know which producer you want to dequeue from, this // is significantly faster than using the general-case try_dequeue methods. // Returns false if the producer's queue appeared empty at the time it // was checked (so, the queue is likely but not guaranteed to be empty). // Never allocates. Thread-safe. template inline bool try_dequeue_from_producer(producer_token_t const& producer, U& item) { return static_cast(producer.producer)->dequeue(item); } // Attempts to dequeue several elements from a specific producer's inner queue. // Returns the number of items actually dequeued. // If you happen to know which producer you want to dequeue from, this // is significantly faster than using the general-case try_dequeue methods. // Returns 0 if the producer's queue appeared empty at the time it // was checked (so, the queue is likely but not guaranteed to be empty). // Never allocates. Thread-safe. template inline size_t try_dequeue_bulk_from_producer(producer_token_t const& producer, It itemFirst, size_t max) { return static_cast(producer.producer)->dequeue_bulk(itemFirst, max); } // Returns an estimate of the total number of elements currently in the queue. This // estimate is only accurate if the queue has completely stabilized before it is called // (i.e. all enqueue and dequeue operations have completed and their memory effects are // visible on the calling thread, and no further operations start while this method is // being called). // Thread-safe. size_t size_approx() const { size_t size = 0; for (auto ptr = producerListTail.load(std::memory_order_acquire); ptr != nullptr; ptr = ptr->next_prod()) { size += ptr->size_approx(); } return size; } // Returns true if the underlying atomic variables used by // the queue are lock-free (they should be on most platforms). // Thread-safe. static bool is_lock_free() { return details::static_is_lock_free::value == 2 && details::static_is_lock_free::value == 2 && details::static_is_lock_free::value == 2 && details::static_is_lock_free::value == 2 && details::static_is_lock_free::value == 2 && details::static_is_lock_free::value == 2; } private: friend struct ProducerToken; friend struct ConsumerToken; friend struct ExplicitProducer; friend class ConcurrentQueueTests; enum AllocationMode { CanAlloc, CannotAlloc }; /////////////////////////////// // Queue methods /////////////////////////////// template inline bool inner_enqueue(producer_token_t const& token, U&& element) { return static_cast(token.producer)->ConcurrentQueue::ExplicitProducer::template enqueue(std::forward(element)); } template inline bool inner_enqueue(U&& element) { auto producer = get_or_add_implicit_producer(); return producer == nullptr ? false : producer->ConcurrentQueue::ImplicitProducer::template enqueue(std::forward(element)); } template inline bool inner_enqueue_bulk(producer_token_t const& token, It itemFirst, size_t count) { return static_cast(token.producer)->ConcurrentQueue::ExplicitProducer::template enqueue_bulk(std::forward(itemFirst), count); } template inline bool inner_enqueue_bulk(It itemFirst, size_t count) { auto producer = get_or_add_implicit_producer(); return producer == nullptr ? false : producer->ConcurrentQueue::ImplicitProducer::template enqueue_bulk(std::forward(itemFirst), count); } inline bool update_current_producer_after_rotation(consumer_token_t& token) { // Ah, there's been a rotation, figure out where we should be! auto tail = producerListTail.load(std::memory_order_acquire); if (token.desiredProducer == nullptr && tail == nullptr) { return false; } auto prodCount = producerCount.load(std::memory_order_relaxed); auto globalOffset = globalExplicitConsumerOffset.load(std::memory_order_relaxed); if (details::unlikely(token.desiredProducer == nullptr)) { // Aha, first time we're dequeueing anything. // Figure out our local position // Note: offset is from start, not end, but we're traversing from end -- subtract from count first std::uint32_t offset = prodCount - 1 - (token.initialOffset % prodCount); token.desiredProducer = tail; for (std::uint32_t i = 0; i != offset; ++i) { token.desiredProducer = static_cast(token.desiredProducer)->next_prod(); if (token.desiredProducer == nullptr) { token.desiredProducer = tail; } } } std::uint32_t delta = globalOffset - token.lastKnownGlobalOffset; if (delta >= prodCount) { delta = delta % prodCount; } for (std::uint32_t i = 0; i != delta; ++i) { token.desiredProducer = static_cast(token.desiredProducer)->next_prod(); if (token.desiredProducer == nullptr) { token.desiredProducer = tail; } } token.lastKnownGlobalOffset = globalOffset; token.currentProducer = token.desiredProducer; token.itemsConsumedFromCurrent = 0; return true; } /////////////////////////// // Free list /////////////////////////// template struct FreeListNode { FreeListNode() : freeListRefs(0), freeListNext(nullptr) { } std::atomic freeListRefs; std::atomic freeListNext; }; // A simple CAS-based lock-free free list. Not the fastest thing in the world under heavy contention, but // simple and correct (assuming nodes are never freed until after the free list is destroyed), and fairly // speedy under low contention. template // N must inherit FreeListNode or have the same fields (and initialization of them) struct FreeList { FreeList() : freeListHead(nullptr) { } FreeList(FreeList&& other) : freeListHead(other.freeListHead.load(std::memory_order_relaxed)) { other.freeListHead.store(nullptr, std::memory_order_relaxed); } void swap(FreeList& other) { details::swap_relaxed(freeListHead, other.freeListHead); } FreeList(FreeList const&) MOODYCAMEL_DELETE_FUNCTION; FreeList& operator=(FreeList const&) MOODYCAMEL_DELETE_FUNCTION; inline void add(N* node) { #if MCDBGQ_NOLOCKFREE_FREELIST debug::DebugLock lock(mutex); #endif // We know that the should-be-on-freelist bit is 0 at this point, so it's safe to // set it using a fetch_add if (node->freeListRefs.fetch_add(SHOULD_BE_ON_FREELIST, std::memory_order_acq_rel) == 0) { // Oh look! We were the last ones referencing this node, and we know // we want to add it to the free list, so let's do it! add_knowing_refcount_is_zero(node); } } inline N* try_get() { #if MCDBGQ_NOLOCKFREE_FREELIST debug::DebugLock lock(mutex); #endif auto head = freeListHead.load(std::memory_order_acquire); while (head != nullptr) { auto prevHead = head; auto refs = head->freeListRefs.load(std::memory_order_relaxed); if ((refs & REFS_MASK) == 0 || !head->freeListRefs.compare_exchange_strong(refs, refs + 1, std::memory_order_acquire, std::memory_order_relaxed)) { head = freeListHead.load(std::memory_order_acquire); continue; } // Good, reference count has been incremented (it wasn't at zero), which means we can read the // next and not worry about it changing between now and the time we do the CAS auto next = head->freeListNext.load(std::memory_order_relaxed); if (freeListHead.compare_exchange_strong(head, next, std::memory_order_acquire, std::memory_order_relaxed)) { // Yay, got the node. This means it was on the list, which means shouldBeOnFreeList must be false no // matter the refcount (because nobody else knows it's been taken off yet, it can't have been put back on). assert((head->freeListRefs.load(std::memory_order_relaxed) & SHOULD_BE_ON_FREELIST) == 0); // Decrease refcount twice, once for our ref, and once for the list's ref head->freeListRefs.fetch_add(-2, std::memory_order_release); return head; } // OK, the head must have changed on us, but we still need to decrease the refcount we increased. // Note that we don't need to release any memory effects, but we do need to ensure that the reference // count decrement happens-after the CAS on the head. refs = prevHead->freeListRefs.fetch_add(-1, std::memory_order_acq_rel); if (refs == SHOULD_BE_ON_FREELIST + 1) { add_knowing_refcount_is_zero(prevHead); } } return nullptr; } // Useful for traversing the list when there's no contention (e.g. to destroy remaining nodes) N* head_unsafe() const { return freeListHead.load(std::memory_order_relaxed); } private: inline void add_knowing_refcount_is_zero(N* node) { // Since the refcount is zero, and nobody can increase it once it's zero (except us, and we run // only one copy of this method per node at a time, i.e. the single thread case), then we know // we can safely change the next pointer of the node; however, once the refcount is back above // zero, then other threads could increase it (happens under heavy contention, when the refcount // goes to zero in between a load and a refcount increment of a node in try_get, then back up to // something non-zero, then the refcount increment is done by the other thread) -- so, if the CAS // to add the node to the actual list fails, decrease the refcount and leave the add operation to // the next thread who puts the refcount back at zero (which could be us, hence the loop). auto head = freeListHead.load(std::memory_order_relaxed); while (true) { node->freeListNext.store(head, std::memory_order_relaxed); node->freeListRefs.store(1, std::memory_order_release); if (!freeListHead.compare_exchange_strong(head, node, std::memory_order_release, std::memory_order_relaxed)) { // Hmm, the add failed, but we can only try again when the refcount goes back to zero if (node->freeListRefs.fetch_add(SHOULD_BE_ON_FREELIST - 1, std::memory_order_release) == 1) { continue; } } return; } } private: // Implemented like a stack, but where node order doesn't matter (nodes are inserted out of order under contention) std::atomic freeListHead; static const std::uint32_t REFS_MASK = 0x7FFFFFFF; static const std::uint32_t SHOULD_BE_ON_FREELIST = 0x80000000; #if MCDBGQ_NOLOCKFREE_FREELIST debug::DebugMutex mutex; #endif }; /////////////////////////// // Block /////////////////////////// enum InnerQueueContext { implicit_context = 0, explicit_context = 1 }; struct Block { Block() : elementsCompletelyDequeued(0), freeListRefs(0), freeListNext(nullptr), shouldBeOnFreeList(false), dynamicallyAllocated(true) { #if MCDBGQ_TRACKMEM owner = nullptr; #endif } template inline bool is_empty() const { if (context == explicit_context && BLOCK_SIZE <= EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD) { // Check flags for (size_t i = 0; i < BLOCK_SIZE; ++i) { if (!emptyFlags[i].load(std::memory_order_relaxed)) { return false; } } // Aha, empty; make sure we have all other memory effects that happened before the empty flags were set std::atomic_thread_fence(std::memory_order_acquire); return true; } else { // Check counter if (elementsCompletelyDequeued.load(std::memory_order_relaxed) == BLOCK_SIZE) { std::atomic_thread_fence(std::memory_order_acquire); return true; } assert(elementsCompletelyDequeued.load(std::memory_order_relaxed) <= BLOCK_SIZE); return false; } } // Returns true if the block is now empty (does not apply in explicit context) template inline bool set_empty(index_t i) { if (context == explicit_context && BLOCK_SIZE <= EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD) { // Set flag assert(!emptyFlags[BLOCK_SIZE - 1 - static_cast(i & static_cast(BLOCK_SIZE - 1))].load(std::memory_order_relaxed)); emptyFlags[BLOCK_SIZE - 1 - static_cast(i & static_cast(BLOCK_SIZE - 1))].store(true, std::memory_order_release); return false; } else { // Increment counter auto prevVal = elementsCompletelyDequeued.fetch_add(1, std::memory_order_release); assert(prevVal < BLOCK_SIZE); return prevVal == BLOCK_SIZE - 1; } } // Sets multiple contiguous item statuses to 'empty' (assumes no wrapping and count > 0). // Returns true if the block is now empty (does not apply in explicit context). template inline bool set_many_empty(index_t i, size_t count) { if (context == explicit_context && BLOCK_SIZE <= EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD) { // Set flags std::atomic_thread_fence(std::memory_order_release); i = BLOCK_SIZE - 1 - static_cast(i & static_cast(BLOCK_SIZE - 1)) - count + 1; for (size_t j = 0; j != count; ++j) { assert(!emptyFlags[i + j].load(std::memory_order_relaxed)); emptyFlags[i + j].store(true, std::memory_order_relaxed); } return false; } else { // Increment counter auto prevVal = elementsCompletelyDequeued.fetch_add(count, std::memory_order_release); assert(prevVal + count <= BLOCK_SIZE); return prevVal + count == BLOCK_SIZE; } } template inline void set_all_empty() { if (context == explicit_context && BLOCK_SIZE <= EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD) { // Set all flags for (size_t i = 0; i != BLOCK_SIZE; ++i) { emptyFlags[i].store(true, std::memory_order_relaxed); } } else { // Reset counter elementsCompletelyDequeued.store(BLOCK_SIZE, std::memory_order_relaxed); } } template inline void reset_empty() { if (context == explicit_context && BLOCK_SIZE <= EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD) { // Reset flags for (size_t i = 0; i != BLOCK_SIZE; ++i) { emptyFlags[i].store(false, std::memory_order_relaxed); } } else { // Reset counter elementsCompletelyDequeued.store(0, std::memory_order_relaxed); } } inline T* operator[](index_t idx) MOODYCAMEL_NOEXCEPT { return reinterpret_cast(elements) + static_cast(idx & static_cast(BLOCK_SIZE - 1)); } inline T const* operator[](index_t idx) const MOODYCAMEL_NOEXCEPT { return reinterpret_cast(elements) + static_cast(idx & static_cast(BLOCK_SIZE - 1)); } public: Block* next; std::atomic elementsCompletelyDequeued; std::atomic emptyFlags[BLOCK_SIZE <= EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD ? BLOCK_SIZE : 1]; private: char elements[sizeof(T) * BLOCK_SIZE]; public: std::atomic freeListRefs; std::atomic freeListNext; std::atomic shouldBeOnFreeList; bool dynamicallyAllocated; // Perhaps a better name for this would be 'isNotPartOfInitialBlockPool' #if MCDBGQ_TRACKMEM void* owner; #endif }; #if MCDBGQ_TRACKMEM public: struct MemStats; private: #endif /////////////////////////// // Producer base /////////////////////////// struct ProducerBase : public details::ConcurrentQueueProducerTypelessBase { ProducerBase(ConcurrentQueue* parent, bool isExplicit) : tailIndex(0), headIndex(0), dequeueOptimisticCount(0), dequeueOvercommit(0), tailBlock(nullptr), isExplicit(isExplicit), parent(parent) { } virtual ~ProducerBase() { }; template inline bool dequeue(U& element) { if (isExplicit) { return static_cast(this)->dequeue(element); } else { return static_cast(this)->dequeue(element); } } template inline size_t dequeue_bulk(It& itemFirst, size_t max) { if (isExplicit) { return static_cast(this)->dequeue_bulk(itemFirst, max); } else { return static_cast(this)->dequeue_bulk(itemFirst, max); } } inline ProducerBase* next_prod() const { return static_cast(next); } inline size_t size_approx() const { auto tail = tailIndex.load(std::memory_order_relaxed); auto head = headIndex.load(std::memory_order_relaxed); return details::circular_less_than(head, tail) ? static_cast(tail - head) : 0; } inline index_t getTail() const { return tailIndex.load(std::memory_order_relaxed); } protected: std::atomic tailIndex; // Where to enqueue to next std::atomic headIndex; // Where to dequeue from next std::atomic dequeueOptimisticCount; std::atomic dequeueOvercommit; Block* tailBlock; public: bool isExplicit; ConcurrentQueue* parent; protected: #if MCDBGQ_TRACKMEM friend struct MemStats; #endif }; /////////////////////////// // Explicit queue /////////////////////////// struct ExplicitProducer : public ProducerBase { explicit ExplicitProducer(ConcurrentQueue* parent) : ProducerBase(parent, true), blockIndex(nullptr), pr_blockIndexSlotsUsed(0), pr_blockIndexSize(EXPLICIT_INITIAL_INDEX_SIZE >> 1), pr_blockIndexFront(0), pr_blockIndexEntries(nullptr), pr_blockIndexRaw(nullptr) { size_t poolBasedIndexSize = details::ceil_to_pow_2(parent->initialBlockPoolSize) >> 1; if (poolBasedIndexSize > pr_blockIndexSize) { pr_blockIndexSize = poolBasedIndexSize; } new_block_index(0); // This creates an index with double the number of current entries, i.e. EXPLICIT_INITIAL_INDEX_SIZE } ~ExplicitProducer() { // Destruct any elements not yet dequeued. // Since we're in the destructor, we can assume all elements // are either completely dequeued or completely not (no halfways). if (this->tailBlock != nullptr) { // Note this means there must be a block index too // First find the block that's partially dequeued, if any Block* halfDequeuedBlock = nullptr; if ((this->headIndex.load(std::memory_order_relaxed) & static_cast(BLOCK_SIZE - 1)) != 0) { // The head's not on a block boundary, meaning a block somewhere is partially dequeued // (or the head block is the tail block and was fully dequeued, but the head/tail are still not on a boundary) size_t i = (pr_blockIndexFront - pr_blockIndexSlotsUsed) & (pr_blockIndexSize - 1); while (details::circular_less_than(pr_blockIndexEntries[i].base + BLOCK_SIZE, this->headIndex.load(std::memory_order_relaxed))) { i = (i + 1) & (pr_blockIndexSize - 1); } assert(details::circular_less_than(pr_blockIndexEntries[i].base, this->headIndex.load(std::memory_order_relaxed))); halfDequeuedBlock = pr_blockIndexEntries[i].block; } // Start at the head block (note the first line in the loop gives us the head from the tail on the first iteration) auto block = this->tailBlock; do { block = block->next; if (block->ConcurrentQueue::Block::template is_empty()) { continue; } size_t i = 0; // Offset into block if (block == halfDequeuedBlock) { i = static_cast(this->headIndex.load(std::memory_order_relaxed) & static_cast(BLOCK_SIZE - 1)); } // Walk through all the items in the block; if this is the tail block, we need to stop when we reach the tail index auto lastValidIndex = (this->tailIndex.load(std::memory_order_relaxed) & static_cast(BLOCK_SIZE - 1)) == 0 ? BLOCK_SIZE : static_cast(this->tailIndex.load(std::memory_order_relaxed) & static_cast(BLOCK_SIZE - 1)); while (i != BLOCK_SIZE && (block != this->tailBlock || i != lastValidIndex)) { (*block)[i++]->~T(); } } while (block != this->tailBlock); } // Destroy all blocks that we own if (this->tailBlock != nullptr) { auto block = this->tailBlock; do { auto next = block->next; if (block->dynamicallyAllocated) { destroy(block); } block = next; } while (block != this->tailBlock); } // Destroy the block indices auto header = static_cast(pr_blockIndexRaw); while (header != nullptr) { auto prev = static_cast(header->prev); header->~BlockIndexHeader(); Traits::free(header); header = prev; } } template inline bool enqueue(U&& element) { index_t currentTailIndex = this->tailIndex.load(std::memory_order_relaxed); index_t newTailIndex = 1 + currentTailIndex; if ((currentTailIndex & static_cast(BLOCK_SIZE - 1)) == 0) { // We reached the end of a block, start a new one auto startBlock = this->tailBlock; auto originalBlockIndexSlotsUsed = pr_blockIndexSlotsUsed; if (this->tailBlock != nullptr && this->tailBlock->next->ConcurrentQueue::Block::template is_empty()) { // We can re-use the block ahead of us, it's empty! this->tailBlock = this->tailBlock->next; this->tailBlock->ConcurrentQueue::Block::template reset_empty(); // We'll put the block on the block index (guaranteed to be room since we're conceptually removing the // last block from it first -- except instead of removing then adding, we can just overwrite). // Note that there must be a valid block index here, since even if allocation failed in the ctor, // it would have been re-attempted when adding the first block to the queue; since there is such // a block, a block index must have been successfully allocated. } else { // Whatever head value we see here is >= the last value we saw here (relatively), // and <= its current value. Since we have the most recent tail, the head must be // <= to it. auto head = this->headIndex.load(std::memory_order_relaxed); assert(!details::circular_less_than(currentTailIndex, head)); if (!details::circular_less_than(head, currentTailIndex + BLOCK_SIZE) || (MAX_SUBQUEUE_SIZE != details::const_numeric_max::value && (MAX_SUBQUEUE_SIZE == 0 || MAX_SUBQUEUE_SIZE - BLOCK_SIZE < currentTailIndex - head))) { // We can't enqueue in another block because there's not enough leeway -- the // tail could surpass the head by the time the block fills up! (Or we'll exceed // the size limit, if the second part of the condition was true.) return false; } // We're going to need a new block; check that the block index has room if (pr_blockIndexRaw == nullptr || pr_blockIndexSlotsUsed == pr_blockIndexSize) { // Hmm, the circular block index is already full -- we'll need // to allocate a new index. Note pr_blockIndexRaw can only be nullptr if // the initial allocation failed in the constructor. if (allocMode == CannotAlloc || !new_block_index(pr_blockIndexSlotsUsed)) { return false; } } // Insert a new block in the circular linked list auto newBlock = this->parent->ConcurrentQueue::template requisition_block(); if (newBlock == nullptr) { return false; } #if MCDBGQ_TRACKMEM newBlock->owner = this; #endif newBlock->ConcurrentQueue::Block::template reset_empty(); if (this->tailBlock == nullptr) { newBlock->next = newBlock; } else { newBlock->next = this->tailBlock->next; this->tailBlock->next = newBlock; } this->tailBlock = newBlock; ++pr_blockIndexSlotsUsed; } if (!MOODYCAMEL_NOEXCEPT_CTOR(T, U, new (nullptr) T(std::forward(element)))) { // The constructor may throw. We want the element not to appear in the queue in // that case (without corrupting the queue): MOODYCAMEL_TRY { new ((*this->tailBlock)[currentTailIndex]) T(std::forward(element)); } MOODYCAMEL_CATCH (...) { // Revert change to the current block, but leave the new block available // for next time pr_blockIndexSlotsUsed = originalBlockIndexSlotsUsed; this->tailBlock = startBlock == nullptr ? this->tailBlock : startBlock; MOODYCAMEL_RETHROW; } } else { (void)startBlock; (void)originalBlockIndexSlotsUsed; } // Add block to block index auto& entry = blockIndex.load(std::memory_order_relaxed)->entries[pr_blockIndexFront]; entry.base = currentTailIndex; entry.block = this->tailBlock; blockIndex.load(std::memory_order_relaxed)->front.store(pr_blockIndexFront, std::memory_order_release); pr_blockIndexFront = (pr_blockIndexFront + 1) & (pr_blockIndexSize - 1); if (!MOODYCAMEL_NOEXCEPT_CTOR(T, U, new (nullptr) T(std::forward(element)))) { this->tailIndex.store(newTailIndex, std::memory_order_release); return true; } } // Enqueue new ((*this->tailBlock)[currentTailIndex]) T(std::forward(element)); this->tailIndex.store(newTailIndex, std::memory_order_release); return true; } template bool dequeue(U& element) { auto tail = this->tailIndex.load(std::memory_order_relaxed); auto overcommit = this->dequeueOvercommit.load(std::memory_order_relaxed); if (details::circular_less_than(this->dequeueOptimisticCount.load(std::memory_order_relaxed) - overcommit, tail)) { // Might be something to dequeue, let's give it a try // Note that this if is purely for performance purposes in the common case when the queue is // empty and the values are eventually consistent -- we may enter here spuriously. // Note that whatever the values of overcommit and tail are, they are not going to change (unless we // change them) and must be the same value at this point (inside the if) as when the if condition was // evaluated. // We insert an acquire fence here to synchronize-with the release upon incrementing dequeueOvercommit below. // This ensures that whatever the value we got loaded into overcommit, the load of dequeueOptisticCount in // the fetch_add below will result in a value at least as recent as that (and therefore at least as large). // Note that I believe a compiler (signal) fence here would be sufficient due to the nature of fetch_add (all // read-modify-write operations are guaranteed to work on the latest value in the modification order), but // unfortunately that can't be shown to be correct using only the C++11 standard. // See http://stackoverflow.com/questions/18223161/what-are-the-c11-memory-ordering-guarantees-in-this-corner-case std::atomic_thread_fence(std::memory_order_acquire); // Increment optimistic counter, then check if it went over the boundary auto myDequeueCount = this->dequeueOptimisticCount.fetch_add(1, std::memory_order_relaxed); // Note that since dequeueOvercommit must be <= dequeueOptimisticCount (because dequeueOvercommit is only ever // incremented after dequeueOptimisticCount -- this is enforced in the `else` block below), and since we now // have a version of dequeueOptimisticCount that is at least as recent as overcommit (due to the release upon // incrementing dequeueOvercommit and the acquire above that synchronizes with it), overcommit <= myDequeueCount. assert(overcommit <= myDequeueCount); // Note that we reload tail here in case it changed; it will be the same value as before or greater, since // this load is sequenced after (happens after) the earlier load above. This is supported by read-read // coherency (as defined in the standard), explained here: http://en.cppreference.com/w/cpp/atomic/memory_order tail = this->tailIndex.load(std::memory_order_acquire); if (details::likely(details::circular_less_than(myDequeueCount - overcommit, tail))) { // Guaranteed to be at least one element to dequeue! // Get the index. Note that since there's guaranteed to be at least one element, this // will never exceed tail. We need to do an acquire-release fence here since it's possible // that whatever condition got us to this point was for an earlier enqueued element (that // we already see the memory effects for), but that by the time we increment somebody else // has incremented it, and we need to see the memory effects for *that* element, which is // in such a case is necessarily visible on the thread that incremented it in the first // place with the more current condition (they must have acquired a tail that is at least // as recent). auto index = this->headIndex.fetch_add(1, std::memory_order_acq_rel); // Determine which block the element is in auto localBlockIndex = blockIndex.load(std::memory_order_acquire); auto localBlockIndexHead = localBlockIndex->front.load(std::memory_order_acquire); // We need to be careful here about subtracting and dividing because of index wrap-around. // When an index wraps, we need to preserve the sign of the offset when dividing it by the // block size (in order to get a correct signed block count offset in all cases): auto headBase = localBlockIndex->entries[localBlockIndexHead].base; auto blockBaseIndex = index & ~static_cast(BLOCK_SIZE - 1); auto offset = static_cast(static_cast::type>(blockBaseIndex - headBase) / BLOCK_SIZE); auto block = localBlockIndex->entries[(localBlockIndexHead + offset) & (localBlockIndex->size - 1)].block; // Dequeue auto& el = *((*block)[index]); if (!MOODYCAMEL_NOEXCEPT_ASSIGN(T, T&&, element = std::move(el))) { // Make sure the element is still fully dequeued and destroyed even if the assignment // throws struct Guard { Block* block; index_t index; ~Guard() { (*block)[index]->~T(); block->ConcurrentQueue::Block::template set_empty(index); } } guard = { block, index }; element = std::move(el); } else { element = std::move(el); el.~T(); block->ConcurrentQueue::Block::template set_empty(index); } return true; } else { // Wasn't anything to dequeue after all; make the effective dequeue count eventually consistent this->dequeueOvercommit.fetch_add(1, std::memory_order_release); // Release so that the fetch_add on dequeueOptimisticCount is guaranteed to happen before this write } } return false; } template bool enqueue_bulk(It itemFirst, size_t count) { // First, we need to make sure we have enough room to enqueue all of the elements; // this means pre-allocating blocks and putting them in the block index (but only if // all the allocations succeeded). index_t startTailIndex = this->tailIndex.load(std::memory_order_relaxed); auto startBlock = this->tailBlock; auto originalBlockIndexFront = pr_blockIndexFront; auto originalBlockIndexSlotsUsed = pr_blockIndexSlotsUsed; Block* firstAllocatedBlock = nullptr; // Figure out how many blocks we'll need to allocate, and do so size_t blockBaseDiff = ((startTailIndex + count - 1) & ~static_cast(BLOCK_SIZE - 1)) - ((startTailIndex - 1) & ~static_cast(BLOCK_SIZE - 1)); index_t currentTailIndex = (startTailIndex - 1) & ~static_cast(BLOCK_SIZE - 1); if (blockBaseDiff > 0) { // Allocate as many blocks as possible from ahead while (blockBaseDiff > 0 && this->tailBlock != nullptr && this->tailBlock->next != firstAllocatedBlock && this->tailBlock->next->ConcurrentQueue::Block::template is_empty()) { blockBaseDiff -= static_cast(BLOCK_SIZE); currentTailIndex += static_cast(BLOCK_SIZE); this->tailBlock = this->tailBlock->next; firstAllocatedBlock = firstAllocatedBlock == nullptr ? this->tailBlock : firstAllocatedBlock; auto& entry = blockIndex.load(std::memory_order_relaxed)->entries[pr_blockIndexFront]; entry.base = currentTailIndex; entry.block = this->tailBlock; pr_blockIndexFront = (pr_blockIndexFront + 1) & (pr_blockIndexSize - 1); } // Now allocate as many blocks as necessary from the block pool while (blockBaseDiff > 0) { blockBaseDiff -= static_cast(BLOCK_SIZE); currentTailIndex += static_cast(BLOCK_SIZE); auto head = this->headIndex.load(std::memory_order_relaxed); assert(!details::circular_less_than(currentTailIndex, head)); bool full = !details::circular_less_than(head, currentTailIndex + BLOCK_SIZE) || (MAX_SUBQUEUE_SIZE != details::const_numeric_max::value && (MAX_SUBQUEUE_SIZE == 0 || MAX_SUBQUEUE_SIZE - BLOCK_SIZE < currentTailIndex - head)); if (pr_blockIndexRaw == nullptr || pr_blockIndexSlotsUsed == pr_blockIndexSize || full) { if (allocMode == CannotAlloc || full || !new_block_index(originalBlockIndexSlotsUsed)) { // Failed to allocate, undo changes (but keep injected blocks) pr_blockIndexFront = originalBlockIndexFront; pr_blockIndexSlotsUsed = originalBlockIndexSlotsUsed; this->tailBlock = startBlock == nullptr ? firstAllocatedBlock : startBlock; return false; } // pr_blockIndexFront is updated inside new_block_index, so we need to // update our fallback value too (since we keep the new index even if we // later fail) originalBlockIndexFront = originalBlockIndexSlotsUsed; } // Insert a new block in the circular linked list auto newBlock = this->parent->ConcurrentQueue::template requisition_block(); if (newBlock == nullptr) { pr_blockIndexFront = originalBlockIndexFront; pr_blockIndexSlotsUsed = originalBlockIndexSlotsUsed; this->tailBlock = startBlock == nullptr ? firstAllocatedBlock : startBlock; return false; } #if MCDBGQ_TRACKMEM newBlock->owner = this; #endif newBlock->ConcurrentQueue::Block::template set_all_empty(); if (this->tailBlock == nullptr) { newBlock->next = newBlock; } else { newBlock->next = this->tailBlock->next; this->tailBlock->next = newBlock; } this->tailBlock = newBlock; firstAllocatedBlock = firstAllocatedBlock == nullptr ? this->tailBlock : firstAllocatedBlock; ++pr_blockIndexSlotsUsed; auto& entry = blockIndex.load(std::memory_order_relaxed)->entries[pr_blockIndexFront]; entry.base = currentTailIndex; entry.block = this->tailBlock; pr_blockIndexFront = (pr_blockIndexFront + 1) & (pr_blockIndexSize - 1); } // Excellent, all allocations succeeded. Reset each block's emptiness before we fill them up, and // publish the new block index front auto block = firstAllocatedBlock; while (true) { block->ConcurrentQueue::Block::template reset_empty(); if (block == this->tailBlock) { break; } block = block->next; } if (MOODYCAMEL_NOEXCEPT_CTOR(T, decltype(*itemFirst), new (nullptr) T(details::deref_noexcept(itemFirst)))) { blockIndex.load(std::memory_order_relaxed)->front.store((pr_blockIndexFront - 1) & (pr_blockIndexSize - 1), std::memory_order_release); } } // Enqueue, one block at a time index_t newTailIndex = startTailIndex + static_cast(count); currentTailIndex = startTailIndex; auto endBlock = this->tailBlock; this->tailBlock = startBlock; assert((startTailIndex & static_cast(BLOCK_SIZE - 1)) != 0 || firstAllocatedBlock != nullptr || count == 0); if ((startTailIndex & static_cast(BLOCK_SIZE - 1)) == 0 && firstAllocatedBlock != nullptr) { this->tailBlock = firstAllocatedBlock; } while (true) { auto stopIndex = (currentTailIndex & ~static_cast(BLOCK_SIZE - 1)) + static_cast(BLOCK_SIZE); if (details::circular_less_than(newTailIndex, stopIndex)) { stopIndex = newTailIndex; } if (MOODYCAMEL_NOEXCEPT_CTOR(T, decltype(*itemFirst), new (nullptr) T(details::deref_noexcept(itemFirst)))) { while (currentTailIndex != stopIndex) { new ((*this->tailBlock)[currentTailIndex++]) T(*itemFirst++); } } else { MOODYCAMEL_TRY { while (currentTailIndex != stopIndex) { // Must use copy constructor even if move constructor is available // because we may have to revert if there's an exception. // Sorry about the horrible templated next line, but it was the only way // to disable moving *at compile time*, which is important because a type // may only define a (noexcept) move constructor, and so calls to the // cctor will not compile, even if they are in an if branch that will never // be executed new ((*this->tailBlock)[currentTailIndex]) T(details::nomove_if<(bool)!MOODYCAMEL_NOEXCEPT_CTOR(T, decltype(*itemFirst), new (nullptr) T(details::deref_noexcept(itemFirst)))>::eval(*itemFirst)); ++currentTailIndex; ++itemFirst; } } MOODYCAMEL_CATCH (...) { // Oh dear, an exception's been thrown -- destroy the elements that // were enqueued so far and revert the entire bulk operation (we'll keep // any allocated blocks in our linked list for later, though). auto constructedStopIndex = currentTailIndex; auto lastBlockEnqueued = this->tailBlock; pr_blockIndexFront = originalBlockIndexFront; pr_blockIndexSlotsUsed = originalBlockIndexSlotsUsed; this->tailBlock = startBlock == nullptr ? firstAllocatedBlock : startBlock; if (!details::is_trivially_destructible::value) { auto block = startBlock; if ((startTailIndex & static_cast(BLOCK_SIZE - 1)) == 0) { block = firstAllocatedBlock; } currentTailIndex = startTailIndex; while (true) { auto stopIndex = (currentTailIndex & ~static_cast(BLOCK_SIZE - 1)) + static_cast(BLOCK_SIZE); if (details::circular_less_than(constructedStopIndex, stopIndex)) { stopIndex = constructedStopIndex; } while (currentTailIndex != stopIndex) { (*block)[currentTailIndex++]->~T(); } if (block == lastBlockEnqueued) { break; } block = block->next; } } MOODYCAMEL_RETHROW; } } if (this->tailBlock == endBlock) { assert(currentTailIndex == newTailIndex); break; } this->tailBlock = this->tailBlock->next; } if (!MOODYCAMEL_NOEXCEPT_CTOR(T, decltype(*itemFirst), new (nullptr) T(details::deref_noexcept(itemFirst))) && firstAllocatedBlock != nullptr) { blockIndex.load(std::memory_order_relaxed)->front.store((pr_blockIndexFront - 1) & (pr_blockIndexSize - 1), std::memory_order_release); } this->tailIndex.store(newTailIndex, std::memory_order_release); return true; } template size_t dequeue_bulk(It& itemFirst, size_t max) { auto tail = this->tailIndex.load(std::memory_order_relaxed); auto overcommit = this->dequeueOvercommit.load(std::memory_order_relaxed); auto desiredCount = static_cast(tail - (this->dequeueOptimisticCount.load(std::memory_order_relaxed) - overcommit)); if (details::circular_less_than(0, desiredCount)) { desiredCount = desiredCount < max ? desiredCount : max; std::atomic_thread_fence(std::memory_order_acquire); auto myDequeueCount = this->dequeueOptimisticCount.fetch_add(desiredCount, std::memory_order_relaxed); assert(overcommit <= myDequeueCount); tail = this->tailIndex.load(std::memory_order_acquire); auto actualCount = static_cast(tail - (myDequeueCount - overcommit)); if (details::circular_less_than(0, actualCount)) { actualCount = desiredCount < actualCount ? desiredCount : actualCount; if (actualCount < desiredCount) { this->dequeueOvercommit.fetch_add(desiredCount - actualCount, std::memory_order_release); } // Get the first index. Note that since there's guaranteed to be at least actualCount elements, this // will never exceed tail. auto firstIndex = this->headIndex.fetch_add(actualCount, std::memory_order_acq_rel); // Determine which block the first element is in auto localBlockIndex = blockIndex.load(std::memory_order_acquire); auto localBlockIndexHead = localBlockIndex->front.load(std::memory_order_acquire); auto headBase = localBlockIndex->entries[localBlockIndexHead].base; auto firstBlockBaseIndex = firstIndex & ~static_cast(BLOCK_SIZE - 1); auto offset = static_cast(static_cast::type>(firstBlockBaseIndex - headBase) / BLOCK_SIZE); auto indexIndex = (localBlockIndexHead + offset) & (localBlockIndex->size - 1); // Iterate the blocks and dequeue auto index = firstIndex; do { auto firstIndexInBlock = index; auto endIndex = (index & ~static_cast(BLOCK_SIZE - 1)) + static_cast(BLOCK_SIZE); endIndex = details::circular_less_than(firstIndex + static_cast(actualCount), endIndex) ? firstIndex + static_cast(actualCount) : endIndex; auto block = localBlockIndex->entries[indexIndex].block; if (MOODYCAMEL_NOEXCEPT_ASSIGN(T, T&&, details::deref_noexcept(itemFirst) = std::move((*(*block)[index])))) { while (index != endIndex) { auto& el = *((*block)[index]); *itemFirst++ = std::move(el); el.~T(); ++index; } } else { MOODYCAMEL_TRY { while (index != endIndex) { auto& el = *((*block)[index]); *itemFirst = std::move(el); ++itemFirst; el.~T(); ++index; } } MOODYCAMEL_CATCH (...) { // It's too late to revert the dequeue, but we can make sure that all // the dequeued objects are properly destroyed and the block index // (and empty count) are properly updated before we propagate the exception do { block = localBlockIndex->entries[indexIndex].block; while (index != endIndex) { (*block)[index++]->~T(); } block->ConcurrentQueue::Block::template set_many_empty(firstIndexInBlock, static_cast(endIndex - firstIndexInBlock)); indexIndex = (indexIndex + 1) & (localBlockIndex->size - 1); firstIndexInBlock = index; endIndex = (index & ~static_cast(BLOCK_SIZE - 1)) + static_cast(BLOCK_SIZE); endIndex = details::circular_less_than(firstIndex + static_cast(actualCount), endIndex) ? firstIndex + static_cast(actualCount) : endIndex; } while (index != firstIndex + actualCount); MOODYCAMEL_RETHROW; } } block->ConcurrentQueue::Block::template set_many_empty(firstIndexInBlock, static_cast(endIndex - firstIndexInBlock)); indexIndex = (indexIndex + 1) & (localBlockIndex->size - 1); } while (index != firstIndex + actualCount); return actualCount; } else { // Wasn't anything to dequeue after all; make the effective dequeue count eventually consistent this->dequeueOvercommit.fetch_add(desiredCount, std::memory_order_release); } } return 0; } private: struct BlockIndexEntry { index_t base; Block* block; }; struct BlockIndexHeader { size_t size; std::atomic front; // Current slot (not next, like pr_blockIndexFront) BlockIndexEntry* entries; void* prev; }; bool new_block_index(size_t numberOfFilledSlotsToExpose) { auto prevBlockSizeMask = pr_blockIndexSize - 1; // Create the new block pr_blockIndexSize <<= 1; auto newRawPtr = static_cast(Traits::malloc(sizeof(BlockIndexHeader) + std::alignment_of::value - 1 + sizeof(BlockIndexEntry) * pr_blockIndexSize)); if (newRawPtr == nullptr) { pr_blockIndexSize >>= 1; // Reset to allow graceful retry return false; } auto newBlockIndexEntries = reinterpret_cast(details::align_for(newRawPtr + sizeof(BlockIndexHeader))); // Copy in all the old indices, if any size_t j = 0; if (pr_blockIndexSlotsUsed != 0) { auto i = (pr_blockIndexFront - pr_blockIndexSlotsUsed) & prevBlockSizeMask; do { newBlockIndexEntries[j++] = pr_blockIndexEntries[i]; i = (i + 1) & prevBlockSizeMask; } while (i != pr_blockIndexFront); } // Update everything auto header = new (newRawPtr) BlockIndexHeader; header->size = pr_blockIndexSize; header->front.store(numberOfFilledSlotsToExpose - 1, std::memory_order_relaxed); header->entries = newBlockIndexEntries; header->prev = pr_blockIndexRaw; // we link the new block to the old one so we can free it later pr_blockIndexFront = j; pr_blockIndexEntries = newBlockIndexEntries; pr_blockIndexRaw = newRawPtr; blockIndex.store(header, std::memory_order_release); return true; } private: std::atomic blockIndex; // To be used by producer only -- consumer must use the ones in referenced by blockIndex size_t pr_blockIndexSlotsUsed; size_t pr_blockIndexSize; size_t pr_blockIndexFront; // Next slot (not current) BlockIndexEntry* pr_blockIndexEntries; void* pr_blockIndexRaw; #ifdef MOODYCAMEL_QUEUE_INTERNAL_DEBUG public: ExplicitProducer* nextExplicitProducer; private: #endif #if MCDBGQ_TRACKMEM friend struct MemStats; #endif }; ////////////////////////////////// // Implicit queue ////////////////////////////////// struct ImplicitProducer : public ProducerBase { ImplicitProducer(ConcurrentQueue* parent) : ProducerBase(parent, false), nextBlockIndexCapacity(IMPLICIT_INITIAL_INDEX_SIZE), blockIndex(nullptr) { new_block_index(); } ~ImplicitProducer() { // Note that since we're in the destructor we can assume that all enqueue/dequeue operations // completed already; this means that all undequeued elements are placed contiguously across // contiguous blocks, and that only the first and last remaining blocks can be only partially // empty (all other remaining blocks must be completely full). #ifdef MOODYCAMEL_CPP11_THREAD_LOCAL_SUPPORTED // Unregister ourselves for thread termination notification if (!this->inactive.load(std::memory_order_relaxed)) { details::ThreadExitNotifier::unsubscribe(&threadExitListener); } #endif // Destroy all remaining elements! auto tail = this->tailIndex.load(std::memory_order_relaxed); auto index = this->headIndex.load(std::memory_order_relaxed); Block* block = nullptr; assert(index == tail || details::circular_less_than(index, tail)); bool forceFreeLastBlock = index != tail; // If we enter the loop, then the last (tail) block will not be freed while (index != tail) { if ((index & static_cast(BLOCK_SIZE - 1)) == 0 || block == nullptr) { if (block != nullptr && block->dynamicallyAllocated) { // Free the old block this->parent->destroy(block); } block = get_block_index_entry_for_index(index)->value.load(std::memory_order_relaxed); } ((*block)[index])->~T(); ++index; } // Even if the queue is empty, there's still one block that's not on the free list // (unless the head index reached the end of it, in which case the tail will be poised // to create a new block). if (this->tailBlock != nullptr && (forceFreeLastBlock || (tail & static_cast(BLOCK_SIZE - 1)) != 0) && this->tailBlock->dynamicallyAllocated) { this->parent->destroy(this->tailBlock); } // Destroy block index auto localBlockIndex = blockIndex.load(std::memory_order_relaxed); if (localBlockIndex != nullptr) { for (size_t i = 0; i != localBlockIndex->capacity; ++i) { localBlockIndex->index[i]->~BlockIndexEntry(); } do { auto prev = localBlockIndex->prev; localBlockIndex->~BlockIndexHeader(); Traits::free(localBlockIndex); localBlockIndex = prev; } while (localBlockIndex != nullptr); } } template inline bool enqueue(U&& element) { index_t currentTailIndex = this->tailIndex.load(std::memory_order_relaxed); index_t newTailIndex = 1 + currentTailIndex; if ((currentTailIndex & static_cast(BLOCK_SIZE - 1)) == 0) { // We reached the end of a block, start a new one auto head = this->headIndex.load(std::memory_order_relaxed); assert(!details::circular_less_than(currentTailIndex, head)); if (!details::circular_less_than(head, currentTailIndex + BLOCK_SIZE) || (MAX_SUBQUEUE_SIZE != details::const_numeric_max::value && (MAX_SUBQUEUE_SIZE == 0 || MAX_SUBQUEUE_SIZE - BLOCK_SIZE < currentTailIndex - head))) { return false; } #if MCDBGQ_NOLOCKFREE_IMPLICITPRODBLOCKINDEX debug::DebugLock lock(mutex); #endif // Find out where we'll be inserting this block in the block index BlockIndexEntry* idxEntry; if (!insert_block_index_entry(idxEntry, currentTailIndex)) { return false; } // Get ahold of a new block auto newBlock = this->parent->ConcurrentQueue::template requisition_block(); if (newBlock == nullptr) { rewind_block_index_tail(); idxEntry->value.store(nullptr, std::memory_order_relaxed); return false; } #if MCDBGQ_TRACKMEM newBlock->owner = this; #endif newBlock->ConcurrentQueue::Block::template reset_empty(); if (!MOODYCAMEL_NOEXCEPT_CTOR(T, U, new (nullptr) T(std::forward(element)))) { // May throw, try to insert now before we publish the fact that we have this new block MOODYCAMEL_TRY { new ((*newBlock)[currentTailIndex]) T(std::forward(element)); } MOODYCAMEL_CATCH (...) { rewind_block_index_tail(); idxEntry->value.store(nullptr, std::memory_order_relaxed); this->parent->add_block_to_free_list(newBlock); MOODYCAMEL_RETHROW; } } // Insert the new block into the index idxEntry->value.store(newBlock, std::memory_order_relaxed); this->tailBlock = newBlock; if (!MOODYCAMEL_NOEXCEPT_CTOR(T, U, new (nullptr) T(std::forward(element)))) { this->tailIndex.store(newTailIndex, std::memory_order_release); return true; } } // Enqueue new ((*this->tailBlock)[currentTailIndex]) T(std::forward(element)); this->tailIndex.store(newTailIndex, std::memory_order_release); return true; } template bool dequeue(U& element) { // See ExplicitProducer::dequeue for rationale and explanation index_t tail = this->tailIndex.load(std::memory_order_relaxed); index_t overcommit = this->dequeueOvercommit.load(std::memory_order_relaxed); if (details::circular_less_than(this->dequeueOptimisticCount.load(std::memory_order_relaxed) - overcommit, tail)) { std::atomic_thread_fence(std::memory_order_acquire); index_t myDequeueCount = this->dequeueOptimisticCount.fetch_add(1, std::memory_order_relaxed); assert(overcommit <= myDequeueCount); tail = this->tailIndex.load(std::memory_order_acquire); if (details::likely(details::circular_less_than(myDequeueCount - overcommit, tail))) { index_t index = this->headIndex.fetch_add(1, std::memory_order_acq_rel); // Determine which block the element is in auto entry = get_block_index_entry_for_index(index); // Dequeue auto block = entry->value.load(std::memory_order_relaxed); auto& el = *((*block)[index]); if (!MOODYCAMEL_NOEXCEPT_ASSIGN(T, T&&, element = std::move(el))) { #if MCDBGQ_NOLOCKFREE_IMPLICITPRODBLOCKINDEX // Note: Acquiring the mutex with every dequeue instead of only when a block // is released is very sub-optimal, but it is, after all, purely debug code. debug::DebugLock lock(producer->mutex); #endif struct Guard { Block* block; index_t index; BlockIndexEntry* entry; ConcurrentQueue* parent; ~Guard() { (*block)[index]->~T(); if (block->ConcurrentQueue::Block::template set_empty(index)) { entry->value.store(nullptr, std::memory_order_relaxed); parent->add_block_to_free_list(block); } } } guard = { block, index, entry, this->parent }; element = std::move(el); } else { element = std::move(el); el.~T(); if (block->ConcurrentQueue::Block::template set_empty(index)) { { #if MCDBGQ_NOLOCKFREE_IMPLICITPRODBLOCKINDEX debug::DebugLock lock(mutex); #endif // Add the block back into the global free pool (and remove from block index) entry->value.store(nullptr, std::memory_order_relaxed); } this->parent->add_block_to_free_list(block); // releases the above store } } return true; } else { this->dequeueOvercommit.fetch_add(1, std::memory_order_release); } } return false; } template bool enqueue_bulk(It itemFirst, size_t count) { // First, we need to make sure we have enough room to enqueue all of the elements; // this means pre-allocating blocks and putting them in the block index (but only if // all the allocations succeeded). // Note that the tailBlock we start off with may not be owned by us any more; // this happens if it was filled up exactly to the top (setting tailIndex to // the first index of the next block which is not yet allocated), then dequeued // completely (putting it on the free list) before we enqueue again. index_t startTailIndex = this->tailIndex.load(std::memory_order_relaxed); auto startBlock = this->tailBlock; Block* firstAllocatedBlock = nullptr; auto endBlock = this->tailBlock; // Figure out how many blocks we'll need to allocate, and do so size_t blockBaseDiff = ((startTailIndex + count - 1) & ~static_cast(BLOCK_SIZE - 1)) - ((startTailIndex - 1) & ~static_cast(BLOCK_SIZE - 1)); index_t currentTailIndex = (startTailIndex - 1) & ~static_cast(BLOCK_SIZE - 1); if (blockBaseDiff > 0) { #if MCDBGQ_NOLOCKFREE_IMPLICITPRODBLOCKINDEX debug::DebugLock lock(mutex); #endif do { blockBaseDiff -= static_cast(BLOCK_SIZE); currentTailIndex += static_cast(BLOCK_SIZE); // Find out where we'll be inserting this block in the block index BlockIndexEntry* idxEntry; Block* newBlock; bool indexInserted = false; auto head = this->headIndex.load(std::memory_order_relaxed); assert(!details::circular_less_than(currentTailIndex, head)); bool full = !details::circular_less_than(head, currentTailIndex + BLOCK_SIZE) || (MAX_SUBQUEUE_SIZE != details::const_numeric_max::value && (MAX_SUBQUEUE_SIZE == 0 || MAX_SUBQUEUE_SIZE - BLOCK_SIZE < currentTailIndex - head)); if (full || !(indexInserted = insert_block_index_entry(idxEntry, currentTailIndex)) || (newBlock = this->parent->ConcurrentQueue::template requisition_block()) == nullptr) { // Index allocation or block allocation failed; revert any other allocations // and index insertions done so far for this operation if (indexInserted) { rewind_block_index_tail(); idxEntry->value.store(nullptr, std::memory_order_relaxed); } currentTailIndex = (startTailIndex - 1) & ~static_cast(BLOCK_SIZE - 1); for (auto block = firstAllocatedBlock; block != nullptr; block = block->next) { currentTailIndex += static_cast(BLOCK_SIZE); idxEntry = get_block_index_entry_for_index(currentTailIndex); idxEntry->value.store(nullptr, std::memory_order_relaxed); rewind_block_index_tail(); } this->parent->add_blocks_to_free_list(firstAllocatedBlock); this->tailBlock = startBlock; return false; } #if MCDBGQ_TRACKMEM newBlock->owner = this; #endif newBlock->ConcurrentQueue::Block::template reset_empty(); newBlock->next = nullptr; // Insert the new block into the index idxEntry->value.store(newBlock, std::memory_order_relaxed); // Store the chain of blocks so that we can undo if later allocations fail, // and so that we can find the blocks when we do the actual enqueueing if ((startTailIndex & static_cast(BLOCK_SIZE - 1)) != 0 || firstAllocatedBlock != nullptr) { assert(this->tailBlock != nullptr); this->tailBlock->next = newBlock; } this->tailBlock = newBlock; endBlock = newBlock; firstAllocatedBlock = firstAllocatedBlock == nullptr ? newBlock : firstAllocatedBlock; } while (blockBaseDiff > 0); } // Enqueue, one block at a time index_t newTailIndex = startTailIndex + static_cast(count); currentTailIndex = startTailIndex; this->tailBlock = startBlock; assert((startTailIndex & static_cast(BLOCK_SIZE - 1)) != 0 || firstAllocatedBlock != nullptr || count == 0); if ((startTailIndex & static_cast(BLOCK_SIZE - 1)) == 0 && firstAllocatedBlock != nullptr) { this->tailBlock = firstAllocatedBlock; } while (true) { auto stopIndex = (currentTailIndex & ~static_cast(BLOCK_SIZE - 1)) + static_cast(BLOCK_SIZE); if (details::circular_less_than(newTailIndex, stopIndex)) { stopIndex = newTailIndex; } if (MOODYCAMEL_NOEXCEPT_CTOR(T, decltype(*itemFirst), new (nullptr) T(details::deref_noexcept(itemFirst)))) { while (currentTailIndex != stopIndex) { new ((*this->tailBlock)[currentTailIndex++]) T(*itemFirst++); } } else { MOODYCAMEL_TRY { while (currentTailIndex != stopIndex) { new ((*this->tailBlock)[currentTailIndex]) T(details::nomove_if<(bool)!MOODYCAMEL_NOEXCEPT_CTOR(T, decltype(*itemFirst), new (nullptr) T(details::deref_noexcept(itemFirst)))>::eval(*itemFirst)); ++currentTailIndex; ++itemFirst; } } MOODYCAMEL_CATCH (...) { auto constructedStopIndex = currentTailIndex; auto lastBlockEnqueued = this->tailBlock; if (!details::is_trivially_destructible::value) { auto block = startBlock; if ((startTailIndex & static_cast(BLOCK_SIZE - 1)) == 0) { block = firstAllocatedBlock; } currentTailIndex = startTailIndex; while (true) { auto stopIndex = (currentTailIndex & ~static_cast(BLOCK_SIZE - 1)) + static_cast(BLOCK_SIZE); if (details::circular_less_than(constructedStopIndex, stopIndex)) { stopIndex = constructedStopIndex; } while (currentTailIndex != stopIndex) { (*block)[currentTailIndex++]->~T(); } if (block == lastBlockEnqueued) { break; } block = block->next; } } currentTailIndex = (startTailIndex - 1) & ~static_cast(BLOCK_SIZE - 1); for (auto block = firstAllocatedBlock; block != nullptr; block = block->next) { currentTailIndex += static_cast(BLOCK_SIZE); auto idxEntry = get_block_index_entry_for_index(currentTailIndex); idxEntry->value.store(nullptr, std::memory_order_relaxed); rewind_block_index_tail(); } this->parent->add_blocks_to_free_list(firstAllocatedBlock); this->tailBlock = startBlock; MOODYCAMEL_RETHROW; } } if (this->tailBlock == endBlock) { assert(currentTailIndex == newTailIndex); break; } this->tailBlock = this->tailBlock->next; } this->tailIndex.store(newTailIndex, std::memory_order_release); return true; } template size_t dequeue_bulk(It& itemFirst, size_t max) { auto tail = this->tailIndex.load(std::memory_order_relaxed); auto overcommit = this->dequeueOvercommit.load(std::memory_order_relaxed); auto desiredCount = static_cast(tail - (this->dequeueOptimisticCount.load(std::memory_order_relaxed) - overcommit)); if (details::circular_less_than(0, desiredCount)) { desiredCount = desiredCount < max ? desiredCount : max; std::atomic_thread_fence(std::memory_order_acquire); auto myDequeueCount = this->dequeueOptimisticCount.fetch_add(desiredCount, std::memory_order_relaxed); assert(overcommit <= myDequeueCount); tail = this->tailIndex.load(std::memory_order_acquire); auto actualCount = static_cast(tail - (myDequeueCount - overcommit)); if (details::circular_less_than(0, actualCount)) { actualCount = desiredCount < actualCount ? desiredCount : actualCount; if (actualCount < desiredCount) { this->dequeueOvercommit.fetch_add(desiredCount - actualCount, std::memory_order_release); } // Get the first index. Note that since there's guaranteed to be at least actualCount elements, this // will never exceed tail. auto firstIndex = this->headIndex.fetch_add(actualCount, std::memory_order_acq_rel); // Iterate the blocks and dequeue auto index = firstIndex; BlockIndexHeader* localBlockIndex; auto indexIndex = get_block_index_index_for_index(index, localBlockIndex); do { auto blockStartIndex = index; auto endIndex = (index & ~static_cast(BLOCK_SIZE - 1)) + static_cast(BLOCK_SIZE); endIndex = details::circular_less_than(firstIndex + static_cast(actualCount), endIndex) ? firstIndex + static_cast(actualCount) : endIndex; auto entry = localBlockIndex->index[indexIndex]; auto block = entry->value.load(std::memory_order_relaxed); if (MOODYCAMEL_NOEXCEPT_ASSIGN(T, T&&, details::deref_noexcept(itemFirst) = std::move((*(*block)[index])))) { while (index != endIndex) { auto& el = *((*block)[index]); *itemFirst++ = std::move(el); el.~T(); ++index; } } else { MOODYCAMEL_TRY { while (index != endIndex) { auto& el = *((*block)[index]); *itemFirst = std::move(el); ++itemFirst; el.~T(); ++index; } } MOODYCAMEL_CATCH (...) { do { entry = localBlockIndex->index[indexIndex]; block = entry->value.load(std::memory_order_relaxed); while (index != endIndex) { (*block)[index++]->~T(); } if (block->ConcurrentQueue::Block::template set_many_empty(blockStartIndex, static_cast(endIndex - blockStartIndex))) { #if MCDBGQ_NOLOCKFREE_IMPLICITPRODBLOCKINDEX debug::DebugLock lock(mutex); #endif entry->value.store(nullptr, std::memory_order_relaxed); this->parent->add_block_to_free_list(block); } indexIndex = (indexIndex + 1) & (localBlockIndex->capacity - 1); blockStartIndex = index; endIndex = (index & ~static_cast(BLOCK_SIZE - 1)) + static_cast(BLOCK_SIZE); endIndex = details::circular_less_than(firstIndex + static_cast(actualCount), endIndex) ? firstIndex + static_cast(actualCount) : endIndex; } while (index != firstIndex + actualCount); MOODYCAMEL_RETHROW; } } if (block->ConcurrentQueue::Block::template set_many_empty(blockStartIndex, static_cast(endIndex - blockStartIndex))) { { #if MCDBGQ_NOLOCKFREE_IMPLICITPRODBLOCKINDEX debug::DebugLock lock(mutex); #endif // Note that the set_many_empty above did a release, meaning that anybody who acquires the block // we're about to free can use it safely since our writes (and reads!) will have happened-before then. entry->value.store(nullptr, std::memory_order_relaxed); } this->parent->add_block_to_free_list(block); // releases the above store } indexIndex = (indexIndex + 1) & (localBlockIndex->capacity - 1); } while (index != firstIndex + actualCount); return actualCount; } else { this->dequeueOvercommit.fetch_add(desiredCount, std::memory_order_release); } } return 0; } private: // The block size must be > 1, so any number with the low bit set is an invalid block base index static const index_t INVALID_BLOCK_BASE = 1; struct BlockIndexEntry { std::atomic key; std::atomic value; }; struct BlockIndexHeader { size_t capacity; std::atomic tail; BlockIndexEntry* entries; BlockIndexEntry** index; BlockIndexHeader* prev; }; template inline bool insert_block_index_entry(BlockIndexEntry*& idxEntry, index_t blockStartIndex) { auto localBlockIndex = blockIndex.load(std::memory_order_relaxed); // We're the only writer thread, relaxed is OK auto newTail = (localBlockIndex->tail.load(std::memory_order_relaxed) + 1) & (localBlockIndex->capacity - 1); idxEntry = localBlockIndex->index[newTail]; if (idxEntry->key.load(std::memory_order_relaxed) == INVALID_BLOCK_BASE || idxEntry->value.load(std::memory_order_relaxed) == nullptr) { idxEntry->key.store(blockStartIndex, std::memory_order_relaxed); localBlockIndex->tail.store(newTail, std::memory_order_release); return true; } // No room in the old block index, try to allocate another one! if (allocMode == CannotAlloc || !new_block_index()) { return false; } localBlockIndex = blockIndex.load(std::memory_order_relaxed); newTail = (localBlockIndex->tail.load(std::memory_order_relaxed) + 1) & (localBlockIndex->capacity - 1); idxEntry = localBlockIndex->index[newTail]; assert(idxEntry->key.load(std::memory_order_relaxed) == INVALID_BLOCK_BASE); idxEntry->key.store(blockStartIndex, std::memory_order_relaxed); localBlockIndex->tail.store(newTail, std::memory_order_release); return true; } inline void rewind_block_index_tail() { auto localBlockIndex = blockIndex.load(std::memory_order_relaxed); localBlockIndex->tail.store((localBlockIndex->tail.load(std::memory_order_relaxed) - 1) & (localBlockIndex->capacity - 1), std::memory_order_relaxed); } inline BlockIndexEntry* get_block_index_entry_for_index(index_t index) const { BlockIndexHeader* localBlockIndex; auto idx = get_block_index_index_for_index(index, localBlockIndex); return localBlockIndex->index[idx]; } inline size_t get_block_index_index_for_index(index_t index, BlockIndexHeader*& localBlockIndex) const { #if MCDBGQ_NOLOCKFREE_IMPLICITPRODBLOCKINDEX debug::DebugLock lock(mutex); #endif index &= ~static_cast(BLOCK_SIZE - 1); localBlockIndex = blockIndex.load(std::memory_order_acquire); auto tail = localBlockIndex->tail.load(std::memory_order_acquire); auto tailBase = localBlockIndex->index[tail]->key.load(std::memory_order_relaxed); assert(tailBase != INVALID_BLOCK_BASE); // Note: Must use division instead of shift because the index may wrap around, causing a negative // offset, whose negativity we want to preserve auto offset = static_cast(static_cast::type>(index - tailBase) / BLOCK_SIZE); size_t idx = (tail + offset) & (localBlockIndex->capacity - 1); assert(localBlockIndex->index[idx]->key.load(std::memory_order_relaxed) == index && localBlockIndex->index[idx]->value.load(std::memory_order_relaxed) != nullptr); return idx; } bool new_block_index() { auto prev = blockIndex.load(std::memory_order_relaxed); size_t prevCapacity = prev == nullptr ? 0 : prev->capacity; auto entryCount = prev == nullptr ? nextBlockIndexCapacity : prevCapacity; auto raw = static_cast(Traits::malloc( sizeof(BlockIndexHeader) + std::alignment_of::value - 1 + sizeof(BlockIndexEntry) * entryCount + std::alignment_of::value - 1 + sizeof(BlockIndexEntry*) * nextBlockIndexCapacity)); if (raw == nullptr) { return false; } auto header = new (raw) BlockIndexHeader; auto entries = reinterpret_cast(details::align_for(raw + sizeof(BlockIndexHeader))); auto index = reinterpret_cast(details::align_for(reinterpret_cast(entries) + sizeof(BlockIndexEntry) * entryCount)); if (prev != nullptr) { auto prevTail = prev->tail.load(std::memory_order_relaxed); auto prevPos = prevTail; size_t i = 0; do { prevPos = (prevPos + 1) & (prev->capacity - 1); index[i++] = prev->index[prevPos]; } while (prevPos != prevTail); assert(i == prevCapacity); } for (size_t i = 0; i != entryCount; ++i) { new (entries + i) BlockIndexEntry; entries[i].key.store(INVALID_BLOCK_BASE, std::memory_order_relaxed); index[prevCapacity + i] = entries + i; } header->prev = prev; header->entries = entries; header->index = index; header->capacity = nextBlockIndexCapacity; header->tail.store((prevCapacity - 1) & (nextBlockIndexCapacity - 1), std::memory_order_relaxed); blockIndex.store(header, std::memory_order_release); nextBlockIndexCapacity <<= 1; return true; } private: size_t nextBlockIndexCapacity; std::atomic blockIndex; #ifdef MOODYCAMEL_CPP11_THREAD_LOCAL_SUPPORTED public: details::ThreadExitListener threadExitListener; private: #endif #ifdef MOODYCAMEL_QUEUE_INTERNAL_DEBUG public: ImplicitProducer* nextImplicitProducer; private: #endif #if MCDBGQ_NOLOCKFREE_IMPLICITPRODBLOCKINDEX mutable debug::DebugMutex mutex; #endif #if MCDBGQ_TRACKMEM friend struct MemStats; #endif }; ////////////////////////////////// // Block pool manipulation ////////////////////////////////// void populate_initial_block_list(size_t blockCount) { initialBlockPoolSize = blockCount; if (initialBlockPoolSize == 0) { initialBlockPool = nullptr; return; } initialBlockPool = create_array(blockCount); if (initialBlockPool == nullptr) { initialBlockPoolSize = 0; } for (size_t i = 0; i < initialBlockPoolSize; ++i) { initialBlockPool[i].dynamicallyAllocated = false; } } inline Block* try_get_block_from_initial_pool() { if (initialBlockPoolIndex.load(std::memory_order_relaxed) >= initialBlockPoolSize) { return nullptr; } auto index = initialBlockPoolIndex.fetch_add(1, std::memory_order_relaxed); return index < initialBlockPoolSize ? (initialBlockPool + index) : nullptr; } inline void add_block_to_free_list(Block* block) { #if MCDBGQ_TRACKMEM block->owner = nullptr; #endif freeList.add(block); } inline void add_blocks_to_free_list(Block* block) { while (block != nullptr) { auto next = block->next; add_block_to_free_list(block); block = next; } } inline Block* try_get_block_from_free_list() { return freeList.try_get(); } // Gets a free block from one of the memory pools, or allocates a new one (if applicable) template Block* requisition_block() { auto block = try_get_block_from_initial_pool(); if (block != nullptr) { return block; } block = try_get_block_from_free_list(); if (block != nullptr) { return block; } if (canAlloc == CanAlloc) { return create(); } return nullptr; } #if MCDBGQ_TRACKMEM public: struct MemStats { size_t allocatedBlocks; size_t usedBlocks; size_t freeBlocks; size_t ownedBlocksExplicit; size_t ownedBlocksImplicit; size_t implicitProducers; size_t explicitProducers; size_t elementsEnqueued; size_t blockClassBytes; size_t queueClassBytes; size_t implicitBlockIndexBytes; size_t explicitBlockIndexBytes; friend class ConcurrentQueue; private: static MemStats getFor(ConcurrentQueue* q) { MemStats stats = { 0 }; stats.elementsEnqueued = q->size_approx(); auto block = q->freeList.head_unsafe(); while (block != nullptr) { ++stats.allocatedBlocks; ++stats.freeBlocks; block = block->freeListNext.load(std::memory_order_relaxed); } for (auto ptr = q->producerListTail.load(std::memory_order_acquire); ptr != nullptr; ptr = ptr->next_prod()) { bool implicit = dynamic_cast(ptr) != nullptr; stats.implicitProducers += implicit ? 1 : 0; stats.explicitProducers += implicit ? 0 : 1; if (implicit) { auto prod = static_cast(ptr); stats.queueClassBytes += sizeof(ImplicitProducer); auto head = prod->headIndex.load(std::memory_order_relaxed); auto tail = prod->tailIndex.load(std::memory_order_relaxed); auto hash = prod->blockIndex.load(std::memory_order_relaxed); if (hash != nullptr) { for (size_t i = 0; i != hash->capacity; ++i) { if (hash->index[i]->key.load(std::memory_order_relaxed) != ImplicitProducer::INVALID_BLOCK_BASE && hash->index[i]->value.load(std::memory_order_relaxed) != nullptr) { ++stats.allocatedBlocks; ++stats.ownedBlocksImplicit; } } stats.implicitBlockIndexBytes += hash->capacity * sizeof(typename ImplicitProducer::BlockIndexEntry); for (; hash != nullptr; hash = hash->prev) { stats.implicitBlockIndexBytes += sizeof(typename ImplicitProducer::BlockIndexHeader) + hash->capacity * sizeof(typename ImplicitProducer::BlockIndexEntry*); } } for (; details::circular_less_than(head, tail); head += BLOCK_SIZE) { //auto block = prod->get_block_index_entry_for_index(head); ++stats.usedBlocks; } } else { auto prod = static_cast(ptr); stats.queueClassBytes += sizeof(ExplicitProducer); auto tailBlock = prod->tailBlock; bool wasNonEmpty = false; if (tailBlock != nullptr) { auto block = tailBlock; do { ++stats.allocatedBlocks; if (!block->ConcurrentQueue::Block::template is_empty() || wasNonEmpty) { ++stats.usedBlocks; wasNonEmpty = wasNonEmpty || block != tailBlock; } ++stats.ownedBlocksExplicit; block = block->next; } while (block != tailBlock); } auto index = prod->blockIndex.load(std::memory_order_relaxed); while (index != nullptr) { stats.explicitBlockIndexBytes += sizeof(typename ExplicitProducer::BlockIndexHeader) + index->size * sizeof(typename ExplicitProducer::BlockIndexEntry); index = static_cast(index->prev); } } } auto freeOnInitialPool = q->initialBlockPoolIndex.load(std::memory_order_relaxed) >= q->initialBlockPoolSize ? 0 : q->initialBlockPoolSize - q->initialBlockPoolIndex.load(std::memory_order_relaxed); stats.allocatedBlocks += freeOnInitialPool; stats.freeBlocks += freeOnInitialPool; stats.blockClassBytes = sizeof(Block) * stats.allocatedBlocks; stats.queueClassBytes += sizeof(ConcurrentQueue); return stats; } }; // For debugging only. Not thread-safe. MemStats getMemStats() { return MemStats::getFor(this); } private: friend struct MemStats; #endif ////////////////////////////////// // Producer list manipulation ////////////////////////////////// ProducerBase* recycle_or_create_producer(bool isExplicit) { bool recycled; return recycle_or_create_producer(isExplicit, recycled); } ProducerBase* recycle_or_create_producer(bool isExplicit, bool& recycled) { #if MCDBGQ_NOLOCKFREE_IMPLICITPRODHASH debug::DebugLock lock(implicitProdMutex); #endif // Try to re-use one first for (auto ptr = producerListTail.load(std::memory_order_acquire); ptr != nullptr; ptr = ptr->next_prod()) { if (ptr->inactive.load(std::memory_order_relaxed) && ptr->isExplicit == isExplicit) { bool expected = true; if (ptr->inactive.compare_exchange_strong(expected, /* desired */ false, std::memory_order_acquire, std::memory_order_relaxed)) { // We caught one! It's been marked as activated, the caller can have it recycled = true; return ptr; } } } recycled = false; return add_producer(isExplicit ? static_cast(create(this)) : create(this)); } ProducerBase* add_producer(ProducerBase* producer) { // Handle failed memory allocation if (producer == nullptr) { return nullptr; } producerCount.fetch_add(1, std::memory_order_relaxed); // Add it to the lock-free list auto prevTail = producerListTail.load(std::memory_order_relaxed); do { producer->next = prevTail; } while (!producerListTail.compare_exchange_weak(prevTail, producer, std::memory_order_release, std::memory_order_relaxed)); #ifdef MOODYCAMEL_QUEUE_INTERNAL_DEBUG if (producer->isExplicit) { auto prevTailExplicit = explicitProducers.load(std::memory_order_relaxed); do { static_cast(producer)->nextExplicitProducer = prevTailExplicit; } while (!explicitProducers.compare_exchange_weak(prevTailExplicit, static_cast(producer), std::memory_order_release, std::memory_order_relaxed)); } else { auto prevTailImplicit = implicitProducers.load(std::memory_order_relaxed); do { static_cast(producer)->nextImplicitProducer = prevTailImplicit; } while (!implicitProducers.compare_exchange_weak(prevTailImplicit, static_cast(producer), std::memory_order_release, std::memory_order_relaxed)); } #endif return producer; } void reown_producers() { // After another instance is moved-into/swapped-with this one, all the // producers we stole still think their parents are the other queue. // So fix them up! for (auto ptr = producerListTail.load(std::memory_order_relaxed); ptr != nullptr; ptr = ptr->next_prod()) { ptr->parent = this; } } ////////////////////////////////// // Implicit producer hash ////////////////////////////////// struct ImplicitProducerKVP { std::atomic key; ImplicitProducer* value; // No need for atomicity since it's only read by the thread that sets it in the first place ImplicitProducerKVP() { } ImplicitProducerKVP(ImplicitProducerKVP&& other) MOODYCAMEL_NOEXCEPT { key.store(other.key.load(std::memory_order_relaxed), std::memory_order_relaxed); value = other.value; } inline ImplicitProducerKVP& operator=(ImplicitProducerKVP&& other) MOODYCAMEL_NOEXCEPT { swap(other); return *this; } inline void swap(ImplicitProducerKVP& other) MOODYCAMEL_NOEXCEPT { if (this != &other) { details::swap_relaxed(key, other.key); std::swap(value, other.value); } } }; template friend void moodycamel::swap(typename ConcurrentQueue::ImplicitProducerKVP&, typename ConcurrentQueue::ImplicitProducerKVP&) MOODYCAMEL_NOEXCEPT; struct ImplicitProducerHash { size_t capacity; ImplicitProducerKVP* entries; ImplicitProducerHash* prev; }; inline void populate_initial_implicit_producer_hash() { if (INITIAL_IMPLICIT_PRODUCER_HASH_SIZE == 0) return; implicitProducerHashCount.store(0, std::memory_order_relaxed); auto hash = &initialImplicitProducerHash; hash->capacity = INITIAL_IMPLICIT_PRODUCER_HASH_SIZE; hash->entries = &initialImplicitProducerHashEntries[0]; for (size_t i = 0; i != INITIAL_IMPLICIT_PRODUCER_HASH_SIZE; ++i) { initialImplicitProducerHashEntries[i].key.store(details::invalid_thread_id, std::memory_order_relaxed); } hash->prev = nullptr; implicitProducerHash.store(hash, std::memory_order_relaxed); } void swap_implicit_producer_hashes(ConcurrentQueue& other) { if (INITIAL_IMPLICIT_PRODUCER_HASH_SIZE == 0) return; // Swap (assumes our implicit producer hash is initialized) initialImplicitProducerHashEntries.swap(other.initialImplicitProducerHashEntries); initialImplicitProducerHash.entries = &initialImplicitProducerHashEntries[0]; other.initialImplicitProducerHash.entries = &other.initialImplicitProducerHashEntries[0]; details::swap_relaxed(implicitProducerHashCount, other.implicitProducerHashCount); details::swap_relaxed(implicitProducerHash, other.implicitProducerHash); if (implicitProducerHash.load(std::memory_order_relaxed) == &other.initialImplicitProducerHash) { implicitProducerHash.store(&initialImplicitProducerHash, std::memory_order_relaxed); } else { ImplicitProducerHash* hash; for (hash = implicitProducerHash.load(std::memory_order_relaxed); hash->prev != &other.initialImplicitProducerHash; hash = hash->prev) { continue; } hash->prev = &initialImplicitProducerHash; } if (other.implicitProducerHash.load(std::memory_order_relaxed) == &initialImplicitProducerHash) { other.implicitProducerHash.store(&other.initialImplicitProducerHash, std::memory_order_relaxed); } else { ImplicitProducerHash* hash; for (hash = other.implicitProducerHash.load(std::memory_order_relaxed); hash->prev != &initialImplicitProducerHash; hash = hash->prev) { continue; } hash->prev = &other.initialImplicitProducerHash; } } // Only fails (returns nullptr) if memory allocation fails ImplicitProducer* get_or_add_implicit_producer() { // Note that since the data is essentially thread-local (key is thread ID), // there's a reduced need for fences (memory ordering is already consistent // for any individual thread), except for the current table itself. // Start by looking for the thread ID in the current and all previous hash tables. // If it's not found, it must not be in there yet, since this same thread would // have added it previously to one of the tables that we traversed. // Code and algorithm adapted from http://preshing.com/20130605/the-worlds-simplest-lock-free-hash-table #if MCDBGQ_NOLOCKFREE_IMPLICITPRODHASH debug::DebugLock lock(implicitProdMutex); #endif auto id = details::thread_id(); auto hashedId = details::hash_thread_id(id); auto mainHash = implicitProducerHash.load(std::memory_order_acquire); for (auto hash = mainHash; hash != nullptr; hash = hash->prev) { // Look for the id in this hash auto index = hashedId; while (true) { // Not an infinite loop because at least one slot is free in the hash table index &= hash->capacity - 1; auto probedKey = hash->entries[index].key.load(std::memory_order_relaxed); if (probedKey == id) { // Found it! If we had to search several hashes deep, though, we should lazily add it // to the current main hash table to avoid the extended search next time. // Note there's guaranteed to be room in the current hash table since every subsequent // table implicitly reserves space for all previous tables (there's only one // implicitProducerHashCount). auto value = hash->entries[index].value; if (hash != mainHash) { index = hashedId; while (true) { index &= mainHash->capacity - 1; probedKey = mainHash->entries[index].key.load(std::memory_order_relaxed); auto empty = details::invalid_thread_id; #ifdef MOODYCAMEL_CPP11_THREAD_LOCAL_SUPPORTED auto reusable = details::invalid_thread_id2; if ((probedKey == empty && mainHash->entries[index].key.compare_exchange_strong(empty, id, std::memory_order_relaxed)) || (probedKey == reusable && mainHash->entries[index].key.compare_exchange_strong(reusable, id, std::memory_order_acquire))) { #else if ((probedKey == empty && mainHash->entries[index].key.compare_exchange_strong(empty, id, std::memory_order_relaxed))) { #endif mainHash->entries[index].value = value; break; } ++index; } } return value; } if (probedKey == details::invalid_thread_id) { break; // Not in this hash table } ++index; } } // Insert! auto newCount = 1 + implicitProducerHashCount.fetch_add(1, std::memory_order_relaxed); while (true) { if (newCount >= (mainHash->capacity >> 1) && !implicitProducerHashResizeInProgress.test_and_set(std::memory_order_acquire)) { // We've acquired the resize lock, try to allocate a bigger hash table. // Note the acquire fence synchronizes with the release fence at the end of this block, and hence when // we reload implicitProducerHash it must be the most recent version (it only gets changed within this // locked block). mainHash = implicitProducerHash.load(std::memory_order_acquire); if (newCount >= (mainHash->capacity >> 1)) { auto newCapacity = mainHash->capacity << 1; while (newCount >= (newCapacity >> 1)) { newCapacity <<= 1; } auto raw = static_cast(Traits::malloc(sizeof(ImplicitProducerHash) + std::alignment_of::value - 1 + sizeof(ImplicitProducerKVP) * newCapacity)); if (raw == nullptr) { // Allocation failed implicitProducerHashCount.fetch_add(-1, std::memory_order_relaxed); implicitProducerHashResizeInProgress.clear(std::memory_order_relaxed); return nullptr; } auto newHash = new (raw) ImplicitProducerHash; newHash->capacity = newCapacity; newHash->entries = reinterpret_cast(details::align_for(raw + sizeof(ImplicitProducerHash))); for (size_t i = 0; i != newCapacity; ++i) { new (newHash->entries + i) ImplicitProducerKVP; newHash->entries[i].key.store(details::invalid_thread_id, std::memory_order_relaxed); } newHash->prev = mainHash; implicitProducerHash.store(newHash, std::memory_order_release); implicitProducerHashResizeInProgress.clear(std::memory_order_release); mainHash = newHash; } else { implicitProducerHashResizeInProgress.clear(std::memory_order_release); } } // If it's < three-quarters full, add to the old one anyway so that we don't have to wait for the next table // to finish being allocated by another thread (and if we just finished allocating above, the condition will // always be true) if (newCount < (mainHash->capacity >> 1) + (mainHash->capacity >> 2)) { bool recycled; auto producer = static_cast(recycle_or_create_producer(false, recycled)); if (producer == nullptr) { implicitProducerHashCount.fetch_add(-1, std::memory_order_relaxed); return nullptr; } if (recycled) { implicitProducerHashCount.fetch_add(-1, std::memory_order_relaxed); } #ifdef MOODYCAMEL_CPP11_THREAD_LOCAL_SUPPORTED producer->threadExitListener.callback = &ConcurrentQueue::implicit_producer_thread_exited_callback; producer->threadExitListener.userData = producer; details::ThreadExitNotifier::subscribe(&producer->threadExitListener); #endif auto index = hashedId; while (true) { index &= mainHash->capacity - 1; auto probedKey = mainHash->entries[index].key.load(std::memory_order_relaxed); auto empty = details::invalid_thread_id; #ifdef MOODYCAMEL_CPP11_THREAD_LOCAL_SUPPORTED auto reusable = details::invalid_thread_id2; if ((probedKey == empty && mainHash->entries[index].key.compare_exchange_strong(empty, id, std::memory_order_relaxed)) || (probedKey == reusable && mainHash->entries[index].key.compare_exchange_strong(reusable, id, std::memory_order_acquire))) { #else if ((probedKey == empty && mainHash->entries[index].key.compare_exchange_strong(empty, id, std::memory_order_relaxed))) { #endif mainHash->entries[index].value = producer; break; } ++index; } return producer; } // Hmm, the old hash is quite full and somebody else is busy allocating a new one. // We need to wait for the allocating thread to finish (if it succeeds, we add, if not, // we try to allocate ourselves). mainHash = implicitProducerHash.load(std::memory_order_acquire); } } #ifdef MOODYCAMEL_CPP11_THREAD_LOCAL_SUPPORTED void implicit_producer_thread_exited(ImplicitProducer* producer) { // Remove from thread exit listeners details::ThreadExitNotifier::unsubscribe(&producer->threadExitListener); // Remove from hash #if MCDBGQ_NOLOCKFREE_IMPLICITPRODHASH debug::DebugLock lock(implicitProdMutex); #endif auto hash = implicitProducerHash.load(std::memory_order_acquire); assert(hash != nullptr); // The thread exit listener is only registered if we were added to a hash in the first place auto id = details::thread_id(); auto hashedId = details::hash_thread_id(id); details::thread_id_t probedKey; // We need to traverse all the hashes just in case other threads aren't on the current one yet and are // trying to add an entry thinking there's a free slot (because they reused a producer) for (; hash != nullptr; hash = hash->prev) { auto index = hashedId; do { index &= hash->capacity - 1; probedKey = hash->entries[index].key.load(std::memory_order_relaxed); if (probedKey == id) { hash->entries[index].key.store(details::invalid_thread_id2, std::memory_order_release); break; } ++index; } while (probedKey != details::invalid_thread_id); // Can happen if the hash has changed but we weren't put back in it yet, or if we weren't added to this hash in the first place } // Mark the queue as being recyclable producer->inactive.store(true, std::memory_order_release); } static void implicit_producer_thread_exited_callback(void* userData) { auto producer = static_cast(userData); auto queue = producer->parent; queue->implicit_producer_thread_exited(producer); } #endif ////////////////////////////////// // Utility functions ////////////////////////////////// template static inline U* create_array(size_t count) { assert(count > 0); auto p = static_cast(Traits::malloc(sizeof(U) * count)); if (p == nullptr) { return nullptr; } for (size_t i = 0; i != count; ++i) { new (p + i) U(); } return p; } template static inline void destroy_array(U* p, size_t count) { if (p != nullptr) { assert(count > 0); for (size_t i = count; i != 0; ) { (p + --i)->~U(); } Traits::free(p); } } template static inline U* create() { auto p = Traits::malloc(sizeof(U)); return p != nullptr ? new (p) U : nullptr; } template static inline U* create(A1&& a1) { auto p = Traits::malloc(sizeof(U)); return p != nullptr ? new (p) U(std::forward(a1)) : nullptr; } template static inline void destroy(U* p) { if (p != nullptr) { p->~U(); } Traits::free(p); } private: std::atomic producerListTail; std::atomic producerCount; std::atomic initialBlockPoolIndex; Block* initialBlockPool; size_t initialBlockPoolSize; #if !MCDBGQ_USEDEBUGFREELIST FreeList freeList; #else debug::DebugFreeList freeList; #endif std::atomic implicitProducerHash; std::atomic implicitProducerHashCount; // Number of slots logically used ImplicitProducerHash initialImplicitProducerHash; std::array initialImplicitProducerHashEntries; std::atomic_flag implicitProducerHashResizeInProgress; std::atomic nextExplicitConsumerId; std::atomic globalExplicitConsumerOffset; #if MCDBGQ_NOLOCKFREE_IMPLICITPRODHASH debug::DebugMutex implicitProdMutex; #endif #ifdef MOODYCAMEL_QUEUE_INTERNAL_DEBUG std::atomic explicitProducers; std::atomic implicitProducers; #endif }; template ProducerToken::ProducerToken(ConcurrentQueue& queue) : producer(queue.recycle_or_create_producer(true)) { if (producer != nullptr) { producer->token = this; } } template ProducerToken::ProducerToken(BlockingConcurrentQueue& queue) : producer(reinterpret_cast*>(&queue)->recycle_or_create_producer(true)) { if (producer != nullptr) { producer->token = this; } } template ConsumerToken::ConsumerToken(ConcurrentQueue& queue) : itemsConsumedFromCurrent(0), currentProducer(nullptr), desiredProducer(nullptr) { initialOffset = queue.nextExplicitConsumerId.fetch_add(1, std::memory_order_release); lastKnownGlobalOffset = -1; } template ConsumerToken::ConsumerToken(BlockingConcurrentQueue& queue) : itemsConsumedFromCurrent(0), currentProducer(nullptr), desiredProducer(nullptr) { initialOffset = reinterpret_cast*>(&queue)->nextExplicitConsumerId.fetch_add(1, std::memory_order_release); lastKnownGlobalOffset = -1; } template inline void swap(ConcurrentQueue& a, ConcurrentQueue& b) MOODYCAMEL_NOEXCEPT { a.swap(b); } inline void swap(ProducerToken& a, ProducerToken& b) MOODYCAMEL_NOEXCEPT { a.swap(b); } inline void swap(ConsumerToken& a, ConsumerToken& b) MOODYCAMEL_NOEXCEPT { a.swap(b); } template inline void swap(typename ConcurrentQueue::ImplicitProducerKVP& a, typename ConcurrentQueue::ImplicitProducerKVP& b) MOODYCAMEL_NOEXCEPT { a.swap(b); } } #if defined(__GNUC__) #pragma GCC diagnostic pop #endif fastnetmon-1.1.4/src/example_plugin/000077500000000000000000000000001343111404700174715ustar00rootroot00000000000000fastnetmon-1.1.4/src/example_plugin/example_collector.cpp000066400000000000000000000042401343111404700236760ustar00rootroot00000000000000// log4cpp logging facility #include "log4cpp/Category.hh" #include "log4cpp/Appender.hh" #include "log4cpp/FileAppender.hh" #include "log4cpp/OstreamAppender.hh" #include "log4cpp/Layout.hh" #include "log4cpp/BasicLayout.hh" #include "log4cpp/PatternLayout.hh" #include "log4cpp/Priority.hh" // For support uint32_t, uint16_t #include // For config map operations #include #include // For support: IPPROTO_TCP, IPPROTO_ICMP, IPPROTO_UDP #include #include #include #include "example_collector.h" // Get log4cpp logger from main program extern log4cpp::Category& logger; // Global configuration map extern std::map configuration_map; // This variable name should be uniq for every plugin! process_packet_pointer example_process_func_ptr = NULL; void start_example_collection(process_packet_pointer func_ptr) { logger << log4cpp::Priority::INFO << "Example plugin started"; example_process_func_ptr = func_ptr; std::string example_plugin_config_param = ""; if (configuration_map.count("some_plugin_param_from_global_config") != 0) { example_plugin_config_param = configuration_map["some_plugin_param_from_global_config"]; } // We should fill this structure for passing to FastNetMon simple_packet current_packet; current_packet.src_ip = 0; current_packet.dst_ip = 0; current_packet.ts.tv_sec = 0; current_packet.ts.tv_usec = 0; current_packet.flags = 0; // There we store packet length or total length of aggregated stream current_packet.length = 128; // Number of received packets, it's not equal to 1 only for aggregated data like netflow current_packet.number_of_packets = 1; // If your data sampled current_packet.sample_ratio = 1; /* ICMP */ current_packet.protocol = IPPROTO_ICMP; /* TCP */ current_packet.protocol = IPPROTO_TCP; current_packet.source_port = 0; current_packet.destination_port = 0; /* UDP */ current_packet.protocol = IPPROTO_UDP; current_packet.source_port = 0; current_packet.destination_port = 0; example_process_func_ptr(current_packet); } fastnetmon-1.1.4/src/example_plugin/example_collector.h000066400000000000000000000003161343111404700233430ustar00rootroot00000000000000#ifndef EXAMPLE_PLUGIN_H #define EXAMPLE_PLUGIN_H #include "../fastnetmon_types.h" // This function should be implemented in plugin void start_example_collection(process_packet_pointer func_ptr); #endif fastnetmon-1.1.4/src/fast_dpi.cpp000066400000000000000000000031011343111404700167500ustar00rootroot00000000000000#include "fast_dpi.h" void debug_printf(u_int32_t protocol, void *id_struct, ndpi_log_level_t log_level, const char *format, ...) { va_list va_ap; struct tm result; char buf[8192], out_buf[8192]; char theDate[32]; const char *extra_msg = ""; time_t theTime = time(NULL); va_start (va_ap, format); /* if(log_level == NDPI_LOG_ERROR) extra_msg = "ERROR: "; else if(log_level == NDPI_LOG_TRACE) extra_msg = "TRACE: "; else extra_msg = "DEBUG: "; */ memset(buf, 0, sizeof(buf)); strftime(theDate, 32, "%d/%b/%Y %H:%M:%S", localtime_r(&theTime, &result) ); vsnprintf(buf, sizeof(buf)-1, format, va_ap); snprintf(out_buf, sizeof(out_buf), "%s %s%s", theDate, extra_msg, buf); printf("%s", out_buf); fflush(stdout); va_end(va_ap); } struct ndpi_detection_module_struct* init_ndpi() { u_int32_t detection_tick_resolution = 1000; struct ndpi_detection_module_struct* my_ndpi_struct = #if NDPI_MAJOR >= 2 ndpi_init_detection_module(); #else ndpi_init_detection_module(detection_tick_resolution, malloc, free, debug_printf); #endif if (my_ndpi_struct == NULL) { // printf("Can't init nDPI"); return NULL; } NDPI_PROTOCOL_BITMASK all; // enable all protocols NDPI_BITMASK_SET_ALL(all); ndpi_set_protocol_detection_bitmask2(my_ndpi_struct, &all); // Load custom protocols // ndpi_load_protocols_file(ndpi_thread_info[thread_id].ndpi_struct, _protoFilePath); //printf("nDPI started correctly\n"); return my_ndpi_struct; } fastnetmon-1.1.4/src/fast_dpi.h000066400000000000000000000002241343111404700164200ustar00rootroot00000000000000#ifndef FAST_DPI_H #define FAST_DPI_H #include #include "libndpi/ndpi_api.h" struct ndpi_detection_module_struct* init_ndpi(); #endif fastnetmon-1.1.4/src/fast_library.cpp000066400000000000000000001243211343111404700176500ustar00rootroot00000000000000#include #include #include "fast_library.h" #include #include // atoi #include #include #include #include #include #include #include #include #include #include #include "log4cpp/Category.hh" #include "log4cpp/Appender.hh" #include "log4cpp/FileAppender.hh" #include "log4cpp/OstreamAppender.hh" #include "log4cpp/Layout.hh" #include "log4cpp/BasicLayout.hh" #include "log4cpp/PatternLayout.hh" #include "log4cpp/Priority.hh" #include #if defined(__APPLE__) #include // Source: https://gist.github.com/pavel-odintsov/d13684600423d1c5e64e #define be64toh(x) OSSwapBigToHostInt64(x) #define htobe64(x) OSSwapHostToBigInt64(x) #endif // For be64toh and htobe64 #if defined(__FreeBSD__) || defined(__DragonFly__) #include #endif boost::regex regular_expression_cidr_pattern("^\\d+\\.\\d+\\.\\d+\\.\\d+\\/\\d+$"); boost::regex regular_expression_host_pattern("^\\d+\\.\\d+\\.\\d+\\.\\d+$"); // convert string to integer int convert_string_to_integer(std::string line) { return atoi(line.c_str()); } // Type safe versions of ntohl, ntohs with type control uint16_t fast_ntoh(uint16_t value) { return ntohs(value); } uint32_t fast_ntoh(uint32_t value) { return ntohl(value); } // network (big endian) byte order to host byte order uint64_t fast_ntoh(uint64_t value) { return be64toh(value); } // Type safe version of htonl, htons uint16_t fast_hton(uint16_t value) { return htons(value); } uint32_t fast_hton(uint32_t value) { return htonl(value); } uint64_t fast_hton(uint64_t value) { // host to big endian (network byte order) return htobe64(value); } uint32_t convert_ip_as_string_to_uint(std::string ip) { struct in_addr ip_addr; inet_aton(ip.c_str(), &ip_addr); // in network byte order return ip_addr.s_addr; } std::string convert_ip_as_uint_to_string(uint32_t ip_as_integer) { struct in_addr ip_addr; ip_addr.s_addr = ip_as_integer; return (std::string)inet_ntoa(ip_addr); } // convert integer to string std::string convert_int_to_string(int value) { std::stringstream out; out << value; return out.str(); } // BE AWARE! WE USE NON STANDARD SUBNET_T HERE! WE USE NON CIDR MASK HERE! subnet_t convert_subnet_from_string_to_binary(std::string subnet_cidr) { std::vector subnet_as_string; split(subnet_as_string, subnet_cidr, boost::is_any_of("/"), boost::token_compress_on); unsigned int cidr = convert_string_to_integer(subnet_as_string[1]); uint32_t subnet_as_int = convert_ip_as_string_to_uint(subnet_as_string[0]); uint32_t netmask_as_int = convert_cidr_to_binary_netmask(cidr); return std::make_pair(subnet_as_int, netmask_as_int); } subnet_t convert_subnet_from_string_to_binary_with_cidr_format(std::string subnet_cidr) { std::vector subnet_as_string; split(subnet_as_string, subnet_cidr, boost::is_any_of("/"), boost::token_compress_on); unsigned int cidr = convert_string_to_integer(subnet_as_string[1]); uint32_t subnet_as_int = convert_ip_as_string_to_uint(subnet_as_string[0]); return std::make_pair(subnet_as_int, cidr); } void copy_networks_from_string_form_to_binary(std::vector networks_list_as_string, std::vector& our_networks) { for (std::vector::iterator ii = networks_list_as_string.begin(); ii != networks_list_as_string.end(); ++ii) { subnet_t current_subnet = convert_subnet_from_string_to_binary(*ii); our_networks.push_back(current_subnet); } } std::string convert_subnet_to_string(subnet_t my_subnet) { std::stringstream buffer; buffer< subnet_as_string; split(subnet_as_string, network_cidr_format, boost::is_any_of("/"), boost::token_compress_on); if (subnet_as_string.size() != 2) { return 0; } return convert_string_to_integer(subnet_as_string[1]); } std::string print_time_t_in_fastnetmon_format(time_t current_time) { struct tm* timeinfo; char buffer[80]; timeinfo = localtime(¤t_time); strftime(buffer, sizeof(buffer), "%d_%m_%y_%H:%M:%S", timeinfo); return std::string(buffer); } // extract 192.168.1.1 from 192.168.1.1/24 std::string get_net_address_from_network_as_string(std::string network_cidr_format) { std::vector subnet_as_string; split(subnet_as_string, network_cidr_format, boost::is_any_of("/"), boost::token_compress_on); if (subnet_as_string.size() != 2) { return 0; } return subnet_as_string[0]; } std::string get_printable_protocol_name(unsigned int protocol) { std::string proto_name; switch (protocol) { case IPPROTO_TCP: proto_name = "tcp"; break; case IPPROTO_UDP: proto_name = "udp"; break; case IPPROTO_ICMP: proto_name = "icmp"; break; default: proto_name = "unknown"; break; } return proto_name; } uint32_t convert_cidr_to_binary_netmask(unsigned int cidr) { uint32_t binary_netmask = 0xFFFFFFFF; binary_netmask = binary_netmask << (32 - cidr); // htonl from host byte order to network // ntohl from network byte order to host // We need network byte order at output return htonl(binary_netmask); } bool is_cidr_subnet(std::string subnet) { boost::cmatch what; return regex_match(subnet.c_str(), what, regular_expression_cidr_pattern); } bool is_v4_host(std::string host) { boost::cmatch what; return regex_match(host.c_str(), what, regular_expression_host_pattern); } // check file existence bool file_exists(std::string path) { FILE* check_file = fopen(path.c_str(), "r"); if (check_file) { fclose(check_file); return true; } else { return false; } } bool folder_exists(std::string path) { if (access(path.c_str(), 0) == 0) { struct stat status; stat(path.c_str(), &status); if (status.st_mode & S_IFDIR) { return true; } } return false; } #define BIG_CONSTANT(x) (x##LLU) /* // calculate hash unsigned int seed = 11; uint64_t hash = MurmurHash64A(¤t_packet, sizeof(current_packet), seed); */ // https://code.google.com/p/smhasher/source/browse/trunk/MurmurHash2.cpp // 64-bit hash for 64-bit platforms uint64_t MurmurHash64A(const void* key, int len, uint64_t seed) { const uint64_t m = BIG_CONSTANT(0xc6a4a7935bd1e995); const int r = 47; uint64_t h = seed ^ (len * m); const uint64_t* data = (const uint64_t*)key; const uint64_t* end = data + (len / 8); while (data != end) { uint64_t k = *data++; k *= m; k ^= k >> r; k *= m; h ^= k; h *= m; } const unsigned char* data2 = (const unsigned char*)data; switch (len & 7) { case 7: h ^= uint64_t(data2[6]) << 48; case 6: h ^= uint64_t(data2[5]) << 40; case 5: h ^= uint64_t(data2[4]) << 32; case 4: h ^= uint64_t(data2[3]) << 24; case 3: h ^= uint64_t(data2[2]) << 16; case 2: h ^= uint64_t(data2[1]) << 8; case 1: h ^= uint64_t(data2[0]); h *= m; }; h ^= h >> r; h *= m; h ^= h >> r; return h; } // http://www.gnu.org/software/libc/manual/html_node/Elapsed-Time.html int timeval_subtract(struct timeval* result, struct timeval* x, struct timeval* y) { /* Perform the carry for the later subtraction by updating y. */ if (x->tv_usec < y->tv_usec) { int nsec = (y->tv_usec - x->tv_usec) / 1000000 + 1; y->tv_usec -= 1000000 * nsec; y->tv_sec += nsec; } if (x->tv_usec - y->tv_usec > 1000000) { int nsec = (x->tv_usec - y->tv_usec) / 1000000; y->tv_usec += 1000000 * nsec; y->tv_sec -= nsec; } /* Compute the time remaining to wait. tv_usec is certainly positive. */ result->tv_sec = x->tv_sec - y->tv_sec; result->tv_usec = x->tv_usec - y->tv_usec; /* Return 1 if result is negative. */ return x->tv_sec < y->tv_sec; } std::string print_tcp_flags(uint8_t flag_value) { if (flag_value == 0) { return "-"; } // cod from pfring.h // (tcp->fin * TH_FIN_MULTIPLIER) + (tcp->syn * TH_SYN_MULTIPLIER) + // (tcp->rst * TH_RST_MULTIPLIER) + (tcp->psh * TH_PUSH_MULTIPLIER) + // (tcp->ack * TH_ACK_MULTIPLIER) + (tcp->urg * TH_URG_MULTIPLIER); /* // Required for decoding tcp flags #define TH_FIN_MULTIPLIER 0x01 #define TH_SYN_MULTIPLIER 0x02 #define TH_RST_MULTIPLIER 0x04 #define TH_PUSH_MULTIPLIER 0x08 #define TH_ACK_MULTIPLIER 0x10 #define TH_URG_MULTIPLIER 0x20 */ std::vector all_flags; if (extract_bit_value(flag_value, TCP_FIN_FLAG_SHIFT)) { all_flags.push_back("fin"); } if (extract_bit_value(flag_value, TCP_SYN_FLAG_SHIFT)) { all_flags.push_back("syn"); } if (extract_bit_value(flag_value, TCP_RST_FLAG_SHIFT)) { all_flags.push_back("rst"); } if (extract_bit_value(flag_value, TCP_PSH_FLAG_SHIFT)) { all_flags.push_back("psh"); } if (extract_bit_value(flag_value, TCP_ACK_FLAG_SHIFT)) { all_flags.push_back("ack"); } if (extract_bit_value(flag_value, TCP_URG_FLAG_SHIFT)) { all_flags.push_back("urg"); } std::ostringstream flags_as_string; if (all_flags.empty()) { return "-"; } // concatenate all vector elements with comma std::copy(all_flags.begin(), all_flags.end() - 1, std::ostream_iterator(flags_as_string, ",")); // add last element flags_as_string << all_flags.back(); return flags_as_string.str(); } std::vector split_strings_to_vector_by_comma(std::string raw_string) { std::vector splitted_strings; boost::split(splitted_strings, raw_string, boost::is_any_of(","), boost::token_compress_on); return splitted_strings; } // http://stackoverflow.com/questions/14528233/bit-masking-in-c-how-to-get-first-bit-of-a-byte int extract_bit_value(uint8_t num, int bit) { if (bit > 0 && bit <= 8) { return ((num >> (bit - 1)) & 1); } else { return 0; } } // Overloaded version with 16 bit integer support int extract_bit_value(uint16_t num, int bit) { if (bit > 0 && bit <= 16) { return ((num >> (bit - 1)) & 1); } else { return 0; } } int set_bit_value(uint8_t& num, int bit) { if (bit > 0 && bit <= 8) { num = num | 1 << (bit - 1); return 1; } else { return 0; } } int set_bit_value(uint16_t& num, int bit) { if (bit > 0 && bit <= 16) { num = num | 1 << (bit - 1); return 1; } else { return 0; } } int clear_bit_value(uint8_t& num, int bit) { if (bit > 0 && bit <= 8) { num = num & ~(1 << (bit - 1) ); return 1; } else { return 0; } } // http://stackoverflow.com/questions/47981/how-do-you-set-clear-and-toggle-a-single-bit-in-c-c int clear_bit_value(uint16_t& num, int bit) { if (bit > 0 && bit <= 16) { num = num & ~(1 << (bit - 1) ); return 1; } else { return 0; } } std::string print_simple_packet(simple_packet packet) { std::stringstream buffer; if (packet.ts.tv_sec == 0) { // PF_RING and netmap do not generate timestamp for all packets because it's very CPU // intensive operation // But we want pretty attack report and fill it there gettimeofday(&packet.ts, NULL); } buffer << convert_timeval_to_date(packet.ts) << " "; std::string source_ip_as_string = ""; std::string destination_ip_as_string = ""; if (packet.ip_protocol_version == 4) { source_ip_as_string = convert_ip_as_uint_to_string(packet.src_ip); destination_ip_as_string = convert_ip_as_uint_to_string(packet.dst_ip); } else if (packet.ip_protocol_version == 6) { source_ip_as_string = print_ipv6_address(packet.src_ipv6); destination_ip_as_string = print_ipv6_address(packet.dst_ipv6); } else { // WTF? } buffer << source_ip_as_string << ":" << packet.source_port << " > " << destination_ip_as_string << ":" << packet.destination_port << " protocol: " << get_printable_protocol_name(packet.protocol); // Print flags only for TCP if (packet.protocol == IPPROTO_TCP) { buffer << " flags: " << print_tcp_flags(packet.flags); } buffer << " frag: " << packet.ip_fragmented << " "; buffer << " "; buffer << "packets: " << packet.number_of_packets << " "; buffer << "size: " << packet.length << " bytes "; // We should cast it to integer because otherwise it will be interpreted as char buffer << "ttl: " << unsigned(packet.ttl) << " "; buffer << "sample ratio: " << packet.sample_ratio << " "; buffer << " \n"; return buffer.str(); } std::string convert_timeval_to_date(struct timeval tv) { time_t nowtime = tv.tv_sec; struct tm* nowtm = localtime(&nowtime); char tmbuf[64]; char buf[64]; strftime(tmbuf, sizeof(tmbuf), "%Y-%m-%d %H:%M:%S", nowtm); #if defined(__APPLE__) snprintf(buf, sizeof(buf), "%s.%06d", tmbuf, tv.tv_usec); #else snprintf(buf, sizeof(buf), "%s.%06ld", tmbuf, tv.tv_usec); #endif return std::string(buf); } uint64_t convert_speed_to_mbps(uint64_t speed_in_bps) { return uint64_t((double)speed_in_bps / 1024 / 1024 * 8); } std::string get_protocol_name_by_number(unsigned int proto_number) { struct protoent* proto_ent = getprotobynumber(proto_number); std::string proto_name = proto_ent->p_name; return proto_name; } // exec command in shell std::vector exec(std::string cmd) { std::vector output_list; FILE* pipe = popen(cmd.c_str(), "r"); if (!pipe) return output_list; char buffer[256]; while (!feof(pipe)) { if (fgets(buffer, 256, pipe) != NULL) { size_t newbuflen = strlen(buffer); // remove newline at the end if (buffer[newbuflen - 1] == '\n') { buffer[newbuflen - 1] = '\0'; } output_list.push_back(buffer); } } pclose(pipe); return output_list; } bool print_pid_to_file(pid_t pid, std::string pid_path) { std::ofstream pid_file; pid_file.open(pid_path.c_str(), std::ios::trunc); if (pid_file.is_open()) { pid_file << pid << "\n"; pid_file.close(); return true; } else { return false; } } bool read_pid_from_file(pid_t& pid, std::string pid_path) { std::fstream pid_file(pid_path.c_str(), std::ios_base::in); if (pid_file.is_open()) { pid_file >> pid; pid_file.close(); return true; } else { return false; } } bool store_data_to_graphite(unsigned short int graphite_port, std::string graphite_host, graphite_data_t graphite_data) { // Do not bother Graphite if we do not have any metrics here if (graphite_data.size() == 0) { return true; } int client_sockfd = socket(AF_INET, SOCK_STREAM, 0); if (client_sockfd < 0) { return false; } struct sockaddr_in serv_addr; memset(&serv_addr, 0, sizeof(serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_port = htons(graphite_port); int pton_result = inet_pton(AF_INET, graphite_host.c_str(), &serv_addr.sin_addr); if (pton_result <= 0) { close(client_sockfd); return false; } int connect_result = connect(client_sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)); if (connect_result < 0) { close(client_sockfd); return false; } std::stringstream buffer; time_t current_time = time(NULL); for (graphite_data_t::iterator itr = graphite_data.begin(); itr != graphite_data.end(); ++itr) { buffer << itr->first << " " << itr->second << " " << current_time << "\n"; } std::string buffer_as_string = buffer.str(); int write_result = write(client_sockfd, buffer_as_string.c_str(), buffer_as_string.size()); close(client_sockfd); if (write_result > 0) { return true; } else { return false; } } // Get list of all available interfaces on the server interfaces_list_t get_interfaces_list() { interfaces_list_t interfaces_list; // Format: 1: eth0: < .... boost::regex interface_name_pattern("^\\d+:\\s+(\\w+):.*?$"); std::vector output_list = exec("ip -o link show"); if (output_list.empty()) { return interfaces_list; } for (std::vector::iterator iter = output_list.begin(); iter != output_list.end(); ++iter) { boost::match_results regex_results; if (boost::regex_match(*iter, regex_results, interface_name_pattern)) { // std::cout<<"Interface: "< output_list = exec("ip address show dev " + interface); if (output_list.empty()) { return ip_list; } boost::regex interface_alias_pattern("^\\s+inet\\s+(\\d+\\.\\d+\\.\\d+\\.\\d+).*?$"); // inet 188.40.35.142 for (std::vector::iterator iter = output_list.begin(); iter != output_list.end(); ++iter) { boost::match_results regex_results; if (boost::regex_match(*iter, regex_results, interface_alias_pattern)) { ip_list.push_back(regex_results[1]); // std::cout<<"IP: "< list_of_ignored_interfaces; list_of_ignored_interfaces.push_back("lo"); list_of_ignored_interfaces.push_back("venet0"); interfaces_list_t interfaces_list = get_interfaces_list(); if (interfaces_list.empty()) { return ip_list; } for (interfaces_list_t::iterator iter = interfaces_list.begin(); iter != interfaces_list.end(); ++iter) { std::vector::iterator iter_exclude_list = std::find(list_of_ignored_interfaces.begin(), list_of_ignored_interfaces.end(), *iter); // Skip ignored interface if (iter_exclude_list != list_of_ignored_interfaces.end()) { continue; } // std::cout<<*iter<add.sin.s_addr); return address + "/" + convert_int_to_string(prefix->bitlen); } std::string find_subnet_by_ip_in_string_format(patricia_tree_t* patricia_tree, std::string ip) { patricia_node_t* found_patrica_node = NULL; // Convert IP to integer uint32_t client_ip = convert_ip_as_string_to_uint(ip); prefix_t prefix_for_check_adreess; prefix_for_check_adreess.add.sin.s_addr = client_ip; prefix_for_check_adreess.family = AF_INET; prefix_for_check_adreess.bitlen = 32; found_patrica_node = patricia_search_best2(patricia_tree, &prefix_for_check_adreess, 1); if (found_patrica_node != NULL) { return convert_prefix_to_string_representation(found_patrica_node->prefix); } else { return ""; } } // It could not be on start or end of the line boost::regex ipv6_address_compression_algorithm("(0000:){2,}"); std::string print_ipv6_address(struct in6_addr& ipv6_address) { char buffer[128]; // For short print uint8_t* b = ipv6_address.s6_addr; sprintf(buffer, "%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x", b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7], b[8], b[9], b[10], b[11], b[12], b[13], b[14], b[15]); std::string buffer_string(buffer); // Compress IPv6 address std::string result = boost::regex_replace(buffer_string, ipv6_address_compression_algorithm, ":", boost::format_first_only); return result; } direction get_packet_direction_ipv6(patricia_tree_t* lookup_tree, struct in6_addr src_ipv6, struct in6_addr dst_ipv6) { direction packet_direction; bool our_ip_is_destination = false; bool our_ip_is_source = false; prefix_t prefix_for_check_address; prefix_for_check_address.family = AF_INET6; prefix_for_check_address.bitlen = 128; patricia_node_t* found_patrica_node = NULL; prefix_for_check_address.add.sin6 = dst_ipv6; found_patrica_node = patricia_search_best2(lookup_tree, &prefix_for_check_address, 1); if (found_patrica_node) { our_ip_is_destination = true; } found_patrica_node = NULL; prefix_for_check_address.add.sin6 = src_ipv6; found_patrica_node = patricia_search_best2(lookup_tree, &prefix_for_check_address, 1); if (found_patrica_node) { our_ip_is_source = true; } if (our_ip_is_source && our_ip_is_destination) { packet_direction = INTERNAL; } else if (our_ip_is_source) { packet_direction = OUTGOING; } else if (our_ip_is_destination) { packet_direction = INCOMING; } else { packet_direction = OTHER; } return packet_direction; } /* Get traffic type: check it belongs to our IPs */ direction get_packet_direction(patricia_tree_t* lookup_tree, uint32_t src_ip, uint32_t dst_ip, unsigned long& subnet, unsigned int& subnet_cidr_mask) { direction packet_direction; bool our_ip_is_destination = false; bool our_ip_is_source = false; prefix_t prefix_for_check_adreess; prefix_for_check_adreess.family = AF_INET; prefix_for_check_adreess.bitlen = 32; patricia_node_t* found_patrica_node = NULL; prefix_for_check_adreess.add.sin.s_addr = dst_ip; unsigned long destination_subnet = 0; unsigned int destination_subnet_cidr_mask = 0; found_patrica_node = patricia_search_best2(lookup_tree, &prefix_for_check_adreess, 1); if (found_patrica_node) { our_ip_is_destination = true; destination_subnet = found_patrica_node->prefix->add.sin.s_addr; destination_subnet_cidr_mask = found_patrica_node->prefix->bitlen; } found_patrica_node = NULL; prefix_for_check_adreess.add.sin.s_addr = src_ip; unsigned long source_subnet = 0; unsigned int source_subnet_cidr_mask = 0; found_patrica_node = patricia_search_best2(lookup_tree, &prefix_for_check_adreess, 1); if (found_patrica_node) { our_ip_is_source = true; source_subnet = found_patrica_node->prefix->add.sin.s_addr; source_subnet_cidr_mask = found_patrica_node->prefix->bitlen; } subnet = 0; if (our_ip_is_source && our_ip_is_destination) { packet_direction = INTERNAL; } else if (our_ip_is_source) { subnet = source_subnet; subnet_cidr_mask = source_subnet_cidr_mask; packet_direction = OUTGOING; } else if (our_ip_is_destination) { subnet = destination_subnet; subnet_cidr_mask = destination_subnet_cidr_mask; packet_direction = INCOMING; } else { packet_direction = OTHER; } return packet_direction; } std::string get_direction_name(direction direction_value) { std::string direction_name; switch (direction_value) { case INCOMING: direction_name = "incoming"; break; case OUTGOING: direction_name = "outgoing"; break; case INTERNAL: direction_name = "internal"; break; case OTHER: direction_name = "other"; break; default: direction_name = "unknown"; break; } return direction_name; } // We haven't this code for FreeBSD yet #ifdef __linux__ bool manage_interface_promisc_mode(std::string interface_name, bool switch_on) { extern log4cpp::Category& logger; // We need really any socket for ioctl int fd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); if (!fd) { logger << log4cpp::Priority::ERROR << "Can't create socket for promisc mode manager"; return false; } struct ifreq ethreq; memset(ðreq, 0, sizeof(ethreq)); strncpy(ethreq.ifr_name, interface_name.c_str(), IFNAMSIZ); int ioctl_res = ioctl(fd, SIOCGIFFLAGS, ðreq); if (ioctl_res == -1) { logger << log4cpp::Priority::ERROR << "Can't get interface flags"; return false; } bool promisc_enabled_on_device = ethreq.ifr_flags & IFF_PROMISC; if (switch_on) { if (promisc_enabled_on_device) { logger << log4cpp::Priority::INFO << "Interface " << interface_name << " in promisc mode already"; return true; } else { logger << log4cpp::Priority::INFO << "Interface in non promisc mode now, switch it on"; ethreq.ifr_flags |= IFF_PROMISC; int ioctl_res_set = ioctl(fd, SIOCSIFFLAGS, ðreq); if (ioctl_res_set == -1) { logger << log4cpp::Priority::ERROR << "Can't set interface flags"; return false; } return true; } } else { if (!promisc_enabled_on_device) { logger << log4cpp::Priority::INFO << "Interface " << interface_name << " in normal mode already"; return true; } else { logger << log4cpp::Priority::INFO << "Interface in promisc mode now, switch it off"; ethreq.ifr_flags &= ~IFF_PROMISC; int ioctl_res_set = ioctl(fd, SIOCSIFFLAGS, ðreq); if (ioctl_res_set == -1) { logger << log4cpp::Priority::ERROR << "Can't set interface flags"; return false; } return true; } } } #endif #ifdef ENABLE_LUA_HOOKS lua_State* init_lua_jit(std::string lua_hooks_path) { extern log4cpp::Category& logger; lua_State* lua_state = luaL_newstate(); if (lua_state == NULL) { logger << log4cpp::Priority::ERROR << "Can't create LUA session"; return NULL; } // load libraries luaL_openlibs(lua_state); int lua_load_file_result = luaL_dofile(lua_state, lua_hooks_path.c_str()); if (lua_load_file_result != 0) { logger << log4cpp::Priority::ERROR << "LuaJIT can't load file correctly from path: " << lua_hooks_path << " disable LUA support"; return NULL; } return lua_state; } bool call_lua_function(std::string function_name, lua_State* lua_state_param, std::string client_addres_in_string_format, void* ptr) { extern log4cpp::Category& logger; /* Function name */ lua_getfield(lua_state_param, LUA_GLOBALSINDEX, function_name.c_str()); /* Function params */ lua_pushstring(lua_state_param, client_addres_in_string_format.c_str()); lua_pushlightuserdata(lua_state_param, ptr); // Call with 1 argumnents and 1 result lua_call(lua_state_param, 2, 1); if (lua_gettop(lua_state_param) == 1) { bool result = lua_toboolean(lua_state_param, -1) == 1 ? true : false; // pop returned value lua_pop(lua_state_param, 1); return result; } else { logger << log4cpp::Priority::ERROR << "We got " << lua_gettop(lua_state_param) << " return values from the LUA, it's error, please check your LUA code"; return false; } return false; } #endif json_object* serialize_attack_description_to_json(attack_details& current_attack) { json_object* jobj = json_object_new_object(); attack_type_t attack_type = detect_attack_type(current_attack); std::string printable_attack_type = get_printable_attack_name(attack_type); json_object_object_add(jobj, "attack_type", json_object_new_string(printable_attack_type.c_str())); json_object_object_add(jobj, "initial_attack_power", json_object_new_int(current_attack.attack_power)); json_object_object_add(jobj, "peak_attack_power", json_object_new_int(current_attack.max_attack_power)); json_object_object_add(jobj, "attack_direction", json_object_new_string(get_direction_name(current_attack.attack_direction).c_str())); json_object_object_add(jobj, "attack_protocol", json_object_new_string(get_printable_protocol_name(current_attack.attack_protocol).c_str())); json_object_object_add(jobj, "total_incoming_traffic", json_object_new_int(current_attack.in_bytes)); json_object_object_add(jobj, "total_outgoing_traffic", json_object_new_int(current_attack.out_bytes)); json_object_object_add(jobj, "total_incoming_pps", json_object_new_int(current_attack.in_packets)); json_object_object_add(jobj, "total_outgoing_pps", json_object_new_int(current_attack.out_packets)); json_object_object_add(jobj, "total_incoming_flows", json_object_new_int(current_attack.in_flows)); json_object_object_add(jobj, "total_outgoing_flows", json_object_new_int(current_attack.out_flows)); json_object_object_add(jobj, "average_incoming_traffic", json_object_new_int(current_attack.average_in_bytes)); json_object_object_add(jobj, "average_outgoing_traffic", json_object_new_int(current_attack.average_out_bytes)); json_object_object_add(jobj, "average_incoming_pps", json_object_new_int(current_attack.average_in_packets)); json_object_object_add(jobj, "average_outgoing_pps", json_object_new_int(current_attack.average_out_packets)); json_object_object_add(jobj, "average_incoming_flows", json_object_new_int(current_attack.average_in_flows)); json_object_object_add(jobj, "average_outgoing_flows", json_object_new_int(current_attack.average_out_flows)); json_object_object_add(jobj, "incoming_ip_fragmented_traffic", json_object_new_int( current_attack.fragmented_in_bytes )); json_object_object_add(jobj, "outgoing_ip_fragmented_traffic", json_object_new_int( current_attack.fragmented_out_bytes )); json_object_object_add(jobj, "incoming_ip_fragmented_pps", json_object_new_int( current_attack.fragmented_in_packets )); json_object_object_add(jobj, "outgoing_ip_fragmented_pps", json_object_new_int( current_attack.fragmented_out_packets )); json_object_object_add(jobj, "incoming_tcp_traffic", json_object_new_int( current_attack.tcp_in_bytes )); json_object_object_add(jobj, "outgoing_tcp_traffic", json_object_new_int( current_attack.tcp_out_bytes )); json_object_object_add(jobj, "incoming_tcp_pps", json_object_new_int( current_attack.tcp_in_packets )); json_object_object_add(jobj, "outgoing_tcp_pps", json_object_new_int(current_attack.tcp_out_packets )); json_object_object_add(jobj, "incoming_syn_tcp_traffic", json_object_new_int( current_attack.tcp_syn_in_bytes )); json_object_object_add(jobj, "outgoing_syn_tcp_traffic", json_object_new_int( current_attack.tcp_syn_out_bytes )); json_object_object_add(jobj, "incoming_syn_tcp_pps", json_object_new_int( current_attack.tcp_syn_in_packets )); json_object_object_add(jobj, "outgoing_syn_tcp_pps", json_object_new_int( current_attack.tcp_syn_out_packets )); json_object_object_add(jobj, "incoming_udp_traffic", json_object_new_int( current_attack.udp_in_bytes )); json_object_object_add(jobj, "outgoing_udp_traffic", json_object_new_int( current_attack.udp_out_bytes )); json_object_object_add(jobj, "incoming_udp_pps", json_object_new_int( current_attack.udp_in_packets )); json_object_object_add(jobj, "outgoing_udp_pps", json_object_new_int( current_attack.udp_out_packets )); json_object_object_add(jobj, "incoming_icmp_traffic", json_object_new_int( current_attack.icmp_in_bytes )); json_object_object_add(jobj, "outgoing_icmp_traffic", json_object_new_int( current_attack.icmp_out_bytes )); json_object_object_add(jobj, "incoming_icmp_pps", json_object_new_int( current_attack.icmp_in_packets )); json_object_object_add(jobj, "outgoing_icmp_pps", json_object_new_int( current_attack.icmp_out_packets )); return jobj; } std::string serialize_attack_description(attack_details& current_attack) { std::stringstream attack_description; attack_type_t attack_type = detect_attack_type(current_attack); std::string printable_attack_type = get_printable_attack_name(attack_type); attack_description << "Attack type: " << printable_attack_type << "\n" << "Initial attack power: " << current_attack.attack_power << " packets per second\n" << "Peak attack power: " << current_attack.max_attack_power << " packets per second\n" << "Attack direction: " << get_direction_name(current_attack.attack_direction) << "\n" << "Attack protocol: " << get_printable_protocol_name(current_attack.attack_protocol) << "\n"; attack_description << "Total incoming traffic: " << convert_speed_to_mbps(current_attack.in_bytes) << " mbps\n" << "Total outgoing traffic: " << convert_speed_to_mbps(current_attack.out_bytes) << " mbps\n" << "Total incoming pps: " << current_attack.in_packets << " packets per second\n" << "Total outgoing pps: " << current_attack.out_packets << " packets per second\n" << "Total incoming flows: " << current_attack.in_flows << " flows per second\n" << "Total outgoing flows: " << current_attack.out_flows << " flows per second\n"; // Add average counters attack_description << "Average incoming traffic: " << convert_speed_to_mbps(current_attack.average_in_bytes) << " mbps\n" << "Average outgoing traffic: " << convert_speed_to_mbps(current_attack.average_out_bytes) << " mbps\n" << "Average incoming pps: " << current_attack.average_in_packets << " packets per second\n" << "Average outgoing pps: " << current_attack.average_out_packets << " packets per second\n" << "Average incoming flows: " << current_attack.average_in_flows << " flows per second\n" << "Average outgoing flows: " << current_attack.average_out_flows << " flows per second\n"; attack_description << "Incoming ip fragmented traffic: " << convert_speed_to_mbps(current_attack.fragmented_in_bytes) << " mbps\n" << "Outgoing ip fragmented traffic: " << convert_speed_to_mbps(current_attack.fragmented_out_bytes) << " mbps\n" << "Incoming ip fragmented pps: " << current_attack.fragmented_in_packets << " packets per second\n" << "Outgoing ip fragmented pps: " << current_attack.fragmented_out_packets << " packets per second\n" << "Incoming tcp traffic: " << convert_speed_to_mbps(current_attack.tcp_in_bytes) << " mbps\n" << "Outgoing tcp traffic: " << convert_speed_to_mbps(current_attack.tcp_out_bytes) << " mbps\n" << "Incoming tcp pps: " << current_attack.tcp_in_packets << " packets per second\n" << "Outgoing tcp pps: " << current_attack.tcp_out_packets << " packets per second\n" << "Incoming syn tcp traffic: " << convert_speed_to_mbps(current_attack.tcp_syn_in_bytes) << " mbps\n" << "Outgoing syn tcp traffic: " << convert_speed_to_mbps(current_attack.tcp_syn_out_bytes) << " mbps\n" << "Incoming syn tcp pps: " << current_attack.tcp_syn_in_packets << " packets per second\n" << "Outgoing syn tcp pps: " << current_attack.tcp_syn_out_packets << " packets per second\n" << "Incoming udp traffic: " << convert_speed_to_mbps(current_attack.udp_in_bytes) << " mbps\n" << "Outgoing udp traffic: " << convert_speed_to_mbps(current_attack.udp_out_bytes) << " mbps\n" << "Incoming udp pps: " << current_attack.udp_in_packets << " packets per second\n" << "Outgoing udp pps: " << current_attack.udp_out_packets << " packets per second\n" << "Incoming icmp traffic: " << convert_speed_to_mbps(current_attack.icmp_in_bytes) << " mbps\n" << "Outgoing icmp traffic: " << convert_speed_to_mbps(current_attack.icmp_out_bytes) << " mbps\n" << "Incoming icmp pps: " << current_attack.icmp_in_packets << " packets per second\n" << "Outgoing icmp pps: " << current_attack.icmp_out_packets << " packets per second\n"; return attack_description.str(); } attack_type_t detect_attack_type(attack_details& current_attack) { double threshold_value = 0.9; if (current_attack.attack_direction == INCOMING) { if (current_attack.tcp_syn_in_packets > threshold_value * current_attack.in_packets) { return ATTACK_SYN_FLOOD; } else if (current_attack.icmp_in_packets > threshold_value * current_attack.in_packets) { return ATTACK_ICMP_FLOOD; } else if (current_attack.fragmented_in_packets > threshold_value * current_attack.in_packets) { return ATTACK_IP_FRAGMENTATION_FLOOD; } else if (current_attack.udp_in_packets > threshold_value * current_attack.in_packets) { return ATTACK_UDP_FLOOD; } } else if (current_attack.attack_direction == OUTGOING) { if (current_attack.tcp_syn_out_packets > threshold_value * current_attack.out_packets) { return ATTACK_SYN_FLOOD; } else if (current_attack.icmp_out_packets > threshold_value * current_attack.out_packets) { return ATTACK_ICMP_FLOOD; } else if (current_attack.fragmented_out_packets > threshold_value * current_attack.out_packets) { return ATTACK_IP_FRAGMENTATION_FLOOD; } else if (current_attack.udp_out_packets > threshold_value * current_attack.out_packets) { return ATTACK_UDP_FLOOD; } } return ATTACK_UNKNOWN; } std::string get_printable_attack_name(attack_type_t attack) { if (attack == ATTACK_SYN_FLOOD) { return "syn_flood"; } else if (attack == ATTACK_ICMP_FLOOD) { return "icmp_flood"; } else if (attack == ATTACK_UDP_FLOOD) { return "udp_flood"; } else if (attack == ATTACK_IP_FRAGMENTATION_FLOOD) { return "ip_fragmentation"; } else if (attack == ATTACK_UNKNOWN) { return "unknown"; } else { return "unknown"; } } std::string serialize_network_load_to_text(map_element& network_speed_meter, bool average) { std::stringstream buffer; std::string prefix = "Network"; if (average) { prefix = "Average network"; } buffer << prefix << " incoming traffic: "<< convert_speed_to_mbps(network_speed_meter.in_bytes) << " mbps\n" << prefix << " outgoing traffic: "<< convert_speed_to_mbps(network_speed_meter.out_bytes) << " mbps\n" << prefix << " incoming pps: "<< network_speed_meter.in_packets << " packets per second\n" << prefix << " outgoing pps: "<< network_speed_meter.out_packets << " packets per second\n"; return buffer.str(); } json_object* serialize_network_load_to_json(map_element& network_speed_meter) { json_object* jobj = json_object_new_object(); json_object_object_add(jobj, "incoming traffic", json_object_new_int(network_speed_meter.in_bytes)); json_object_object_add(jobj, "outgoing traffic", json_object_new_int(network_speed_meter.out_bytes)); json_object_object_add(jobj, "incoming pps", json_object_new_int(network_speed_meter.in_packets)); json_object_object_add(jobj, "outgoing pps", json_object_new_int(network_speed_meter.out_packets)); return jobj; } std::string serialize_statistic_counters_about_attack(attack_details& current_attack) { std::stringstream attack_description; double average_packet_size_for_incoming_traffic = 0; double average_packet_size_for_outgoing_traffic = 0; if (current_attack.average_in_packets > 0) { average_packet_size_for_incoming_traffic = (double)current_attack.average_in_bytes / (double)current_attack.average_in_packets; } if (current_attack.average_out_packets > 0) { average_packet_size_for_outgoing_traffic = (double)current_attack.average_out_bytes / (double)current_attack.average_out_packets; } // We do not need very accurate size attack_description.precision(1); attack_description << "Average packet size for incoming traffic: " << std::fixed << average_packet_size_for_incoming_traffic << " bytes \n" << "Average packet size for outgoing traffic: " << std::fixed << average_packet_size_for_outgoing_traffic << " bytes \n"; return attack_description.str(); } std::string dns_lookup(std::string domain_name) { try { boost::asio::io_service io_service; boost::asio::ip::tcp::resolver resolver(io_service); boost::asio::ip::tcp::resolver::query query(domain_name, ""); for (boost::asio::ip::tcp::resolver::iterator i = resolver.resolve(query); i != boost::asio::ip::tcp::resolver::iterator(); ++i) { boost::asio::ip::tcp::endpoint end = *i; return end.address().to_string(); } } catch (std::exception& e) { return ""; } return ""; } bool store_data_to_stats_server(unsigned short int graphite_port, std::string graphite_host, std::string buffer_as_string) { int client_sockfd = socket(AF_INET, SOCK_STREAM, 0); if (client_sockfd < 0) { return false; } struct sockaddr_in serv_addr; memset(&serv_addr, 0, sizeof(serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_port = htons(graphite_port); int pton_result = inet_pton(AF_INET, graphite_host.c_str(), &serv_addr.sin_addr); if (pton_result <= 0) { close(client_sockfd); return false; } int connect_result = connect(client_sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)); if (connect_result < 0) { close(client_sockfd); return false; } int write_result = write(client_sockfd, buffer_as_string.c_str(), buffer_as_string.size()); close(client_sockfd); if (write_result > 0) { return true; } else { return false; } } bool convert_hex_as_string_to_uint(std::string hex, uint32_t& value) { std::stringstream ss; ss << std::hex << hex; ss >> value; return ss.fail(); } fastnetmon-1.1.4/src/fast_library.h000066400000000000000000000121221343111404700173100ustar00rootroot00000000000000#ifndef FAST_LIBRARY_H #define FAST_LIBRARY_H #include "fastnetmon_types.h" #include #include #include #include #include #include #include #include #include #include #include #include #include // Boost libs #include #include "libpatricia/patricia.h" #ifdef ENABLE_LUA_HOOKS #include #endif #define TCP_FIN_FLAG_SHIFT 1 #define TCP_SYN_FLAG_SHIFT 2 #define TCP_RST_FLAG_SHIFT 3 #define TCP_PSH_FLAG_SHIFT 4 #define TCP_ACK_FLAG_SHIFT 5 #define TCP_URG_FLAG_SHIFT 6 typedef std::map graphite_data_t; typedef std::vector interfaces_list_t; typedef std::vector ip_addresses_list_t; ip_addresses_list_t get_local_ip_v4_addresses_list(); ip_addresses_list_t get_ip_list_for_interface(std::string interface); interfaces_list_t get_interfaces_list(); bool store_data_to_graphite(unsigned short int graphite_port, std::string graphite_host, graphite_data_t graphite_data); std::string get_protocol_name_by_number(unsigned int proto_number); uint64_t convert_speed_to_mbps(uint64_t speed_in_bps); std::vector exec(std::string cmd); uint32_t convert_ip_as_string_to_uint(std::string ip); std::string convert_ip_as_uint_to_string(uint32_t ip_as_integer); std::string convert_int_to_string(int value); std::string print_ipv6_address(struct in6_addr& ipv6_address); std::string print_simple_packet(simple_packet packet); std::string convert_timeval_to_date(struct timeval tv); bool convert_hex_as_string_to_uint(std::string hex, uint32_t& value); int extract_bit_value(uint8_t num, int bit); int extract_bit_value(uint16_t num, int bit); int clear_bit_value(uint8_t& num, int bit); int clear_bit_value(uint16_t& num, int bit); int set_bit_value(uint8_t& num, int bit); int set_bit_value(uint16_t& num, int bit); std::string print_tcp_flags(uint8_t flag_value); uint64_t MurmurHash64A(const void* key, int len, uint64_t seed); std::string print_tcp_flags(uint8_t flag_value); int timeval_subtract(struct timeval* result, struct timeval* x, struct timeval* y); bool folder_exists(std::string path); bool is_cidr_subnet(std::string subnet); bool is_v4_host(std::string host); bool file_exists(std::string path); uint32_t convert_cidr_to_binary_netmask(unsigned int cidr); std::string get_printable_protocol_name(unsigned int protocol); std::string get_net_address_from_network_as_string(std::string network_cidr_format); std::string print_time_t_in_fastnetmon_format(time_t current_time); unsigned int get_cidr_mask_from_network_as_string(std::string network_cidr_format); void copy_networks_from_string_form_to_binary(std::vector networks_list_as_string, std::vector& our_networks); int convert_string_to_integer(std::string line); // Byte order type safe converters uint16_t fast_ntoh(uint16_t value); uint32_t fast_ntoh(uint32_t value); uint64_t fast_ntoh(uint64_t value); uint16_t fast_hton(uint16_t value); uint32_t fast_hton(uint32_t value); uint64_t fast_hton(uint64_t value); bool print_pid_to_file(pid_t pid, std::string pid_path); bool read_pid_from_file(pid_t& pid, std::string pid_path); direction get_packet_direction(patricia_tree_t* lookup_tree, uint32_t src_ip, uint32_t dst_ip, unsigned long& subnet, unsigned int& subnet_cidr_mask); direction get_packet_direction_ipv6(patricia_tree_t* lookup_tree, struct in6_addr src_ipv6, struct in6_addr dst_ipv6); std::string convert_prefix_to_string_representation(prefix_t* prefix); std::string find_subnet_by_ip_in_string_format(patricia_tree_t* patricia_tree, std::string ip); std::string convert_subnet_to_string(subnet_t my_subnet); std::string get_direction_name(direction direction_value); subnet_t convert_subnet_from_string_to_binary(std::string subnet_cidr); std::vector split_strings_to_vector_by_comma(std::string raw_string); subnet_t convert_subnet_from_string_to_binary_with_cidr_format(std::string subnet_cidr); #ifdef __linux__ bool manage_interface_promisc_mode(std::string interface_name, bool switch_on); #endif #ifdef ENABLE_LUA_HOOKS lua_State* init_lua_jit(std::string lua_hooks_path); bool call_lua_function(std::string function_name, lua_State* lua_state_param, std::string client_addres_in_string_format, void* ptr); #endif std::string serialize_attack_description(attack_details& current_attack); attack_type_t detect_attack_type(attack_details& current_attack); std::string get_printable_attack_name(attack_type_t attack); std::string serialize_network_load_to_text(map_element& network_speed_meter, bool average); json_object* serialize_attack_description_to_json(attack_details& current_attack); json_object* serialize_network_load_to_json(map_element& network_speed_meter); std::string serialize_statistic_counters_about_attack(attack_details& current_attack); std::string dns_lookup(std::string domain_name); bool store_data_to_stats_server(unsigned short int graphite_port, std::string graphite_host, std::string buffer_as_string); #endif fastnetmon-1.1.4/src/fast_platform.h.template000066400000000000000000000015151343111404700213060ustar00rootroot00000000000000#ifndef FAST_PLATFORM_H #define FAST_PLATFORM_H // This file automatically generated for your platform (Linux, FreeBSD and others) with cmake /* Platform specific paths */ std::string fastnetmon_version = "${FASTNETMON_APPLICATION_VERSION}"; std::string pid_path = "/var/run/fastnetmon.pid"; std::string global_config_path = "/etc/fastnetmon.conf"; std::string log_file_path = "/var/log/fastnetmon.log"; std::string attack_details_folder = "/var/log/fastnetmon_attacks"; // Default path to notify script std::string notify_script_path = "/usr/local/bin/notify_about_attack.sh"; // Default path to file with networks for whitelising std::string white_list_path = "/etc/networks_whitelist"; // Default path to file with all networks listing std::string networks_list_path = "/etc/networks_list"; /* Platform specific paths end */ #endif fastnetmon-1.1.4/src/fast_priority_queue.cpp000066400000000000000000000044561343111404700212770ustar00rootroot00000000000000bool compare_min(unsigned int a, unsigned int b) { return a > b; } bool compare_max(unsigned int a, unsigned int b) { return a < b; } template fast_priority_queue::fast_priority_queue(unsigned int queue_size) { this->queue_size = queue_size; internal_list.reserve(queue_size); } template void fast_priority_queue::insert(order_by_template_type main_value, int data) { // Because it's ehap we can remove // Append new element to the end of list internal_list.push_back(main_value); // Convert list to the complete heap // Up to logarithmic in the distance between first and last: Compares elements and potentially // swaps (or moves) them until rearranged as a longer heap. std::push_heap(internal_list.begin(), internal_list.end(), compare_min); if (this->internal_list.size() >= queue_size) { // And now we should remove minimal element from the internal_list // Prepare heap to remove min element std::pop_heap(internal_list.begin(), internal_list.end(), compare_min); // Remove element from the head internal_list.pop_back(); } } template order_by_template_type fast_priority_queue::get_min_element() { // We will return head of list because it's consists minimum element return internal_list.front(); } template void fast_priority_queue::print_internal_list() { for (unsigned int i = 0; i < internal_list.size(); i++) { std::cout << internal_list[i] << std::endl; } } template void fast_priority_queue::print() { // Create new list for sort because we can't do it in place std::vector sorted_list; // Allocate enough space sorted_list.reserve(internal_list.size()); // Copy to new vector with copy constructor sorted_list = internal_list; // Execute heap sort because array paritally sorted already std::sort_heap(sorted_list.begin(), sorted_list.end(), compare_min); for (unsigned int i = 0; i < sorted_list.size(); i++) { std::cout << sorted_list[i] << std::endl; } } fastnetmon-1.1.4/src/fast_priority_queue.h000066400000000000000000000014421343111404700207340ustar00rootroot00000000000000#ifndef fast_priority_queue_h #define fast_priority_queue_h #include #include #include #include #include #include template class fast_priority_queue { public: fast_priority_queue(unsigned int queue_size); void insert(order_by_template_type main_value, int data); order_by_template_type get_min_element(); void print_internal_list(); void print(); private: order_by_template_type max_number; order_by_template_type min_number; unsigned int queue_size; // We can't use list here! std::vector internal_list; // std::priority_queue, std::less > class_priority_queue; }; #include "fast_priority_queue.cpp" #endif fastnetmon-1.1.4/src/fastnetmon.conf000066400000000000000000000221511343111404700175060ustar00rootroot00000000000000### ### Main configuration params ### ### Logging configuration # enable this option if you want to send logs to local syslog facility logging:local_syslog_logging = off # enable this option if you want to send logs to a remote syslog server via UDP logging:remote_syslog_logging = off # specify a custom server and port for remote logging logging:remote_syslog_server = 10.10.10.10 logging:remote_syslog_port = 514 # Enable/Disable any actions in case of attack enable_ban = on # disable processing for certain direction of traffic process_incoming_traffic = on process_outgoing_traffic = on # How many packets will be collected from attack traffic ban_details_records_count = 500 # How long (in seconds) we should keep an IP in blocked state # If you set 0 here it completely disables unban capability ban_time = 1900 # Check if the attack is still active, before triggering an unban callback with this option # If the attack is still active, check each run of the unban watchdog unban_only_if_attack_finished = on # enable per subnet speed meters # For each subnet, list track speed in bps and pps for both directions enable_subnet_counters = off # list of all your networks in CIDR format networks_list_path = /etc/networks_list # list networks in CIDR format which will be not monitored for attacks white_list_path = /etc/networks_whitelist # redraw period for client's screen check_period = 1 # Connection tracking is very useful for attack detection because it provides huge amounts of information, # but it's very CPU intensive and not recommended in big networks enable_connection_tracking = off # Different approaches to attack detection ban_for_pps = on ban_for_bandwidth = on ban_for_flows = off # Limits for Dos/DDoS attacks threshold_pps = 20000 threshold_mbps = 1000 threshold_flows = 3500 # Per protocol attack thresholds # We don't implement per protocol flow limits, sorry :( # These limits should be smaller than global pps/mbps limits threshold_tcp_mbps = 100000 threshold_udp_mbps = 100000 threshold_icmp_mbps = 100000 threshold_tcp_pps = 100000 threshold_udp_pps = 100000 threshold_icmp_pps = 100000 ban_for_tcp_bandwidth = off ban_for_udp_bandwidth = off ban_for_icmp_bandwidth = off ban_for_tcp_pps = off ban_for_udp_pps = off ban_for_icmp_pps = off ### ### Traffic capture methods ### # PF_RING traffic capture, fast enough but the wirespeed version needs a paid license mirror = off # Port mirroring sample rate pfring_sampling_ratio = 1 # Netmap traffic capture (very fast but needs patched drivers) mirror_netmap = off # SnabbSwitch traffic capture mirror_snabbswitch = off # AF_PACKET capture engine # Please use it only with modern Linux kernels (3.6 and more) # And please install birq for irq ditribution over cores mirror_afpacket = off # use PCI-e addresses here instead of OS device names. You can find them in "lspci" output interfaces_snabbswitch = 0000:04:00.0,0000:04:00.1,0000:03:00.0,0000:03:00.1 # Port mirroring sampling ratio netmap_sampling_ratio = 1 # This option should be enabled if you are using Juniper with mirroring of the first X bytes of packet: maximum-packet-length 110; netmap_read_packet_length_from_ip_header = off # Pcap mode, very slow and thus not suitable for production pcap = off # Netflow capture method with v5, v9 and IPFIX support netflow = on # sFLOW capture suitable for switches sflow = on # PF_RING configuration # If you have a license for PF_RING ZC, enable this mode and it might achieve wire speed for 10GE enable_pf_ring_zc_mode = off # Configuration for netmap, mirror, pcap modes # For pcap and PF_RING we could specify "any" # For netmap and PF_RING we could specify multiple interfaces separated by comma interfaces = eth3,eth4 # We use average values for traffic speed to certain IP and we calculate average over this time slice average_calculation_time = 5 # We use average values for traffic speed for subnet and we calculate average over this time slice average_calculation_time_for_subnets = 20 # Netflow configuration # it's possible to specify multiple ports here, using commas as delimiter netflow_port = 2055 netflow_host = 0.0.0.0 # To bind to all interfaces for all protocols: not possible yet # To bind to all interfaces for a specific protocol: :: or 0.0.0.0 # To bind to localhost for a specific protocol: ::1 or 127.0.0.1 # Netflow v9 and IPFIX agents use different and very complex approaches for notifying about sample ratio # Here you could specify a sampling ratio for all this agents # For NetFLOW v5 we extract sampling ratio from packets directely and this option not used netflow_sampling_ratio = 1 # In some cases with NetFlow we could get huge bursts related to aggregated data nature # We could try to get smoother data with this option, i.e. we will divide counters on collection interval time netflow_divide_counters_on_interval_length = off # Process each netflow packet with LUA # This option is not default and you need build it additionally # netflow_lua_hooks_path = /usr/src/fastnetmon/src/netflow_hooks.lua # sFLOW configuration # It's possible to specify multiple ports here, using commas as delimiter sflow_port = 6343 # sflow_port = 6343,6344 sflow_host = 0.0.0.0 # process each sFLOW packet with LUA # This option is not default and you need build it additionally # sflow_lua_hooks_path = /usr/src/fastnetmon/src/sflow_hooks.lua # sFlow processing QinQ sflow_qinq_process = off # sFlow ethertype of outer tag in QinQ sflow_qinq_ethertype = 0x8100 ### ### Actions when attack detected ### # This script executed for ban, unban and attack detail collection notify_script_path = /usr/local/bin/notify_about_attack.sh # pass attack details to notify_script via stdin # Pass details only in case of "ban" call # No details will be passed for "unban" call notify_script_pass_details = on # collect a full dump of the attack with full payload in pcap compatible format collect_attack_pcap_dumps = off # Execute Deep Packet Inspection on captured PCAP packets process_pcap_attack_dumps_with_dpi = off # Save attack details to Redis redis_enabled = off # Redis configuration redis_port = 6379 redis_host = 127.0.0.1 # specify a custom prefix here redis_prefix = mydc1 # We could store attack information to MongoDB mongodb_enabled = off mongodb_host = localhost mongodb_port = 27017 mongodb_database_name = fastnetmon # If you are using PF_RING non ZC version you could block traffic on host with hardware filters # Please be aware! We can not remove blocks with this action plugin pfring_hardware_filters_enabled = off # announce blocked IPs with BGP protocol with ExaBGP exabgp = off exabgp_command_pipe = /var/run/exabgp.cmd exabgp_community = 65001:666 # specify multiple communities with this syntax: # exabgp_community = [65001:666 65001:777] # specify different communities for host and subnet announces # exabgp_community_subnet = 65001:667 # exabgp_community_host = 65001:668 exabgp_next_hop = 10.0.3.114 # In complex cases you could have both options enabled and announce host and subnet simultaneously # Announce /32 host itself with BGP exabgp_announce_host = on # Announce origin subnet of IP address instead IP itself exabgp_announce_whole_subnet = off # Announce Flow Spec rules when we could detect certain attack type # Please we aware! Flow Spec announce triggered when we collect some details about attack, # i.e. when we call attack_details script # Please disable exabgp_announce_host and exabgp_announce_whole_subnet if you want to use this feature # Please use ExaBGP v4 only (Git version), for more details: https://github.com/pavel-odintsov/fastnetmon/blob/master/docs/BGP_FLOW_SPEC.md exabgp_flow_spec_announces = off # GoBGP intergation gobgp = off gobgp_next_hop = 0.0.0.0 gobgp_announce_host = on gobgp_announce_whole_subnet = off # Graphite monitoring # InfluxDB is also supported, please check our reference: # https://github.com/pavel-odintsov/fastnetmon/blob/master/docs/INFLUXDB_INTEGRATION.md graphite = off # Please use only IP because domain names are not allowed here graphite_host = 127.0.0.1 graphite_port = 2003 # Default namespace for Graphite data graphite_prefix = fastnetmon # Add local IP addresses and aliases to monitoring list # Works only for Linux monitor_local_ip_addresses = on # Create group of hosts with non-standard thresholds # You should create this group before (in configuration file) specifying any limits hostgroup = my_hosts:10.10.10.221/32,10.10.10.222/32 # Configure this group my_hosts_enable_ban = off my_hosts_ban_for_pps = off my_hosts_ban_for_bandwidth = off my_hosts_ban_for_flows = off my_hosts_threshold_pps = 20000 my_hosts_threshold_mbps = 1000 my_hosts_threshold_flows = 3500 # Path to pid file for checking "if another copy of tool is running", it's useful when you run multiple instances of tool pid_path = /var/run/fastnetmon.pid # Path to file where we store information for fastnetmon_client cli_stats_file_path = /tmp/fastnetmon.dat # Enable gRPC api (required for fastnetmon_api_client tool) enable_api = off ### ### Client configuration ### # Field used for sorting in client, valid values are: packets, bytes or flows sort_parameter = packets # How much IPs will be listed for incoming and outgoing channel eaters max_ips_in_list = 7 fastnetmon-1.1.4/src/fastnetmon.cpp000066400000000000000000005461331343111404700173560ustar00rootroot00000000000000/* Author: pavel.odintsov@gmail.com */ /* License: GPLv2 */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // struct arphdr #include #include #include "libpatricia/patricia.h" #include "fastnetmon_types.h" #include "fastnetmon_packet_parser.h" #include "fast_library.h" #include "packet_storage.h" #include "bgp_flow_spec.h" // Here we store variables which differs for different paltforms #include "fast_platform.h" #ifdef ENABLE_DPI #include "fast_dpi.h" #endif #ifdef FASTNETMON_API #include #include "fastnetmon.grpc.pb.h" #endif // Plugins #include "sflow_plugin/sflow_collector.h" #include "netflow_plugin/netflow_collector.h" #include "pcap_plugin/pcap_collector.h" #ifdef NETMAP_PLUGIN #include "netmap_plugin/netmap_collector.h" #endif #ifdef PF_RING #include "pfring_plugin/pfring_collector.h" #endif #ifdef SNABB_SWITCH #include "snabbswitch_plugin/snabbswitch_collector.h" #endif #ifdef FASTNETMON_ENABLE_AFPACKET #include "afpacket_plugin/afpacket_collector.h" #endif #ifdef PF_RING #include "actions/pfring_hardware_filter_action.h" #endif #ifdef ENABLE_GOBGP #include "actions/gobgp_action.h" #endif // Yes, maybe it's not an good idea but with this we can guarantee working code in example plugin #include "example_plugin/example_collector.h" #include #include #include #include #include #include #include #include #include #include #include // log4cpp logging facility #include "log4cpp/RemoteSyslogAppender.hh" #include "log4cpp/SyslogAppender.hh" #include "log4cpp/Category.hh" #include "log4cpp/Appender.hh" #include "log4cpp/FileAppender.hh" #include "log4cpp/OstreamAppender.hh" #include "log4cpp/Layout.hh" #include "log4cpp/BasicLayout.hh" #include "log4cpp/PatternLayout.hh" #include "log4cpp/Priority.hh" // Boost libs #include #include #ifdef GEOIP #include "GeoIP.h" #endif #ifdef REDIS #include #endif #ifdef MONGO #include #include #endif // #define IPV6_HASH_COUNTERS #ifdef IPV6_HASH_COUNTERS #include "concurrentqueue.h" #endif #ifdef FASTNETMON_API using grpc::Server; using grpc::ServerBuilder; using grpc::ServerContext; using grpc::Status; using fastmitigation::BanListRequest; using fastmitigation::BanListReply; using fastmitigation::Fastnetmon; std::unique_ptr api_server; bool enable_api = false; #endif time_t last_call_of_traffic_recalculation; std::string cli_stats_file_path = "/tmp/fastnetmon.dat"; unsigned int stats_thread_sleep_time = 3600; unsigned int stats_thread_initial_call_delay = 30; unsigned int recalculate_speed_timeout = 1; // Send or not any details about attack for ban script call over stdin bool notify_script_pass_details = true; bool pfring_hardware_filters_enabled = false; bool notify_script_enabled = true; // We could collect attack dumps in pcap format bool collect_attack_pcap_dumps = false; // We could process this dumps with DPI bool process_pcap_attack_dumps_with_dpi = false; bool unban_only_if_attack_finished = true; logging_configuration_t logging_configuration; // Variable with all data from main screen std::string screen_data_stats = ""; // Global map with parsed config file typedef std::map configuration_map_t; configuration_map_t configuration_map; // Every X seconds we will run ban list cleaner thread // If customer uses ban_time smaller than this value we will use ban_time/2 as unban_iteration_sleep_time int unban_iteration_sleep_time = 60; bool unban_enabled = true; #ifdef ENABLE_DPI struct ndpi_detection_module_struct* my_ndpi_struct = NULL; u_int32_t ndpi_size_flow_struct = 0; u_int32_t ndpi_size_id_struct = 0; #endif #ifdef ENABLE_GOBGP bool gobgp_enabled = false; #endif #ifdef MONGO std::string mongodb_host = "localhost"; unsigned int mongodb_port = 27017; bool mongodb_enabled = false; std::string mongodb_database_name = "fastnetmon"; #endif /* Configuration block, we must move it to configuration file */ #ifdef REDIS unsigned int redis_port = 6379; std::string redis_host = "127.0.0.1"; // redis key prefix std::string redis_prefix = ""; // because it's additional and very specific feature we should disable it by default bool redis_enabled = false; #endif bool monitor_local_ip_addresses = true; // This flag could enable print of ban actions and thresholds on the client's screen bool print_configuration_params_on_the_screen = false; // Trigger for enable or disable traffic counting for whole subnets bool enable_subnet_counters = false; // We will announce whole subnet instead single IP with BGP if this flag enabled bool exabgp_announce_whole_subnet = false; // We will announce only /32 host bool exabgp_announce_host = false; // With this flag we will announce more specfic then whole block Flow Spec announces bool exabgp_flow_spec_announces = false; ban_settings_t global_ban_settings; void init_global_ban_settings() { // ban Configuration params global_ban_settings.enable_ban_for_pps = false; global_ban_settings.enable_ban_for_bandwidth = false; global_ban_settings.enable_ban_for_flows_per_second = false; // We must ban IP if it exceeed this limit in PPS global_ban_settings.ban_threshold_pps = 20000; // We must ban IP of it exceed this limit for number of flows in any direction global_ban_settings.ban_threshold_flows = 3500; // We must ban client if it exceed 1GBps global_ban_settings.ban_threshold_mbps = 1000; // Disable per protocol thresholds too global_ban_settings.enable_ban_for_tcp_pps = false; global_ban_settings.enable_ban_for_tcp_bandwidth = false; global_ban_settings.enable_ban_for_udp_pps = false; global_ban_settings.enable_ban_for_udp_bandwidth = false; global_ban_settings.enable_ban_for_icmp_pps = false; global_ban_settings.enable_ban_for_icmp_bandwidth = false; // Ban enable/disable flag global_ban_settings.enable_ban = true; } bool enable_conection_tracking = true; bool enable_snabbswitch_collection = false; bool enable_afpacket_collection = false; bool enable_data_collection_from_mirror = true; bool enable_netmap_collection = false; bool enable_sflow_collection = false; bool enable_netflow_collection = false; bool enable_pcap_collection = false; // Time consumed by reaclculation for all IPs struct timeval speed_calculation_time; // Time consumed by drawing stats for all IPs struct timeval drawing_thread_execution_time; // Global thread group for packet capture threads boost::thread_group packet_capture_plugin_thread_group; // Global thread group for service processes (speed recalculation, // screen updater and ban list cleaner) boost::thread_group service_thread_group; // Total number of hosts in our networks // We need this as global variable because it's very important value for configuring data structures unsigned int total_number_of_hosts_in_our_networks = 0; #ifdef GEOIP GeoIP* geo_ip = NULL; #endif // IPv4 lookup trees patricia_tree_t* lookup_tree_ipv4, *whitelist_tree_ipv4; // IPv6 lookup trees patricia_tree_t* lookup_tree_ipv6, *whitelist_tree_ipv6; bool DEBUG = 0; // flag about dumping all packets to log bool DEBUG_DUMP_ALL_PACKETS = false; // dump "other" packets bool DEBUG_DUMP_OTHER_PACKETS = false; // Period for update screen for console version of tool unsigned int check_period = 3; // Standard ban time in seconds for all attacks but you can tune this value int global_ban_time = 1800; // We calc average pps/bps for this time double average_calculation_amount = 15; // We calc average pps/bps for subnets with this time, we use longer value for calculation average network traffic double average_calculation_amount_for_subnets = 30; // Show average or absolute value of speed bool print_average_traffic_counts = true; // Key used for sorting clients in output. Allowed sort params: packets/bytes/flows std::string sort_parameter = "packets"; // Number of lines in program output unsigned int max_ips_in_list = 7; // Number of lines for sending ben attack details to email unsigned int ban_details_records_count = 500; // We haven't option for configure it with configuration file unsigned int number_of_packets_for_pcap_attack_dump = 500; // log file log4cpp::Category& logger = log4cpp::Category::getRoot(); // We storae all active BGP Flow Spec announces here typedef std::map active_flow_spec_announces_t; active_flow_spec_announces_t active_flow_spec_announces; /* Configuration block ends */ // We count total number of incoming/outgoing/internal and other traffic type packets/bytes // And initilize by 0 all fields total_counter_element total_counters[4]; total_counter_element total_speed_counters[4]; total_counter_element total_speed_average_counters[4]; // Total amount of non parsed packets uint64_t total_unparsed_packets = 0; uint64_t total_unparsed_packets_speed = 0; // Total amount of IPv6 packets uint64_t total_ipv6_packets = 0; // IPv6 traffic which belongs to our own networks uint64_t our_ipv6_packets = 0; uint64_t incoming_total_flows_speed = 0; uint64_t outgoing_total_flows_speed = 0; map_of_vector_counters SubnetVectorMap; // Here we store taffic per subnet map_for_subnet_counters PerSubnetCountersMap; // Here we store traffic speed per subnet map_for_subnet_counters PerSubnetSpeedMap; // Here we store average speed per subnet map_for_subnet_counters PerSubnetAverageSpeedMap; // Flow tracking structures map_of_vector_counters_for_flow SubnetVectorMapFlow; /* End of our data structs */ boost::mutex ban_list_details_mutex; boost::mutex ban_list_mutex; boost::mutex flow_counter; // map for flows std::map FlowCounter; // Struct for string speed per IP map_of_vector_counters SubnetVectorMapSpeed; // Struct for storing average speed per IP for specified interval map_of_vector_counters SubnetVectorMapSpeedAverage; #ifdef GEOIP map_for_counters GeoIpCounter; #endif // In ddos info we store attack power and direction std::map ban_list; std::map > ban_list_details; host_group_map_t host_groups; // Here we store assignment from subnet to certain host group for fast lookup subnet_to_host_group_map_t subnet_to_host_groups; host_group_ban_settings_map_t host_group_ban_settings_map; std::vector our_networks; std::vector whitelist_networks; // ExaBGP support flag bool exabgp_enabled = false; std::string exabgp_community = ""; // We could use separate communities for subnet and host announces std::string exabgp_community_subnet = ""; std::string exabgp_community_host = ""; std::string exabgp_command_pipe = "/var/run/exabgp.cmd"; std::string exabgp_next_hop = ""; // Graphite monitoring bool graphite_enabled = false; std::string graphite_host = "127.0.0.1"; unsigned short int graphite_port = 2003; // Default graphite namespace std::string graphite_prefix = "fastnetmon"; bool process_incoming_traffic = true; bool process_outgoing_traffic = true; // Prototypes #ifdef ENABLE_DPI void init_current_instance_of_ndpi(); #endif inline void build_average_speed_counters_from_speed_counters( map_element* current_average_speed_element, map_element& new_speed_element, double exp_value, double exp_power); inline void build_speed_counters_from_packet_counters(map_element& new_speed_element, map_element* vector_itr, double speed_calc_period); void execute_ip_ban(uint32_t client_ip, map_element average_speed_element, std::string flow_attack_details, subnet_t customer_subnet); std::string get_attack_description_in_json(uint32_t client_ip, attack_details& current_attack); logging_configuration_t read_logging_settings(configuration_map_t configuration_map); std::string get_amplification_attack_type(amplification_attack_type_t attack_type); std::string generate_flow_spec_for_amplification_attack(amplification_attack_type_t amplification_attack_type, std::string destination_ip); bool exabgp_flow_spec_ban_manage(std::string action, std::string flow_spec_rule_as_text); void call_attack_details_handlers(uint32_t client_ip, attack_details& current_attack, std::string attack_fingerprint); void call_ban_handlers(uint32_t client_ip, attack_details& current_attack, std::string flow_attack_details); void call_unban_handlers(uint32_t client_ip, attack_details& current_attack); ban_settings_t read_ban_settings(configuration_map_t configuration_map, std::string host_group_name = ""); void exabgp_prefix_ban_manage(std::string action, std::string prefix_as_string_with_mask, std::string exabgp_next_hop, std::string exabgp_community); std::string print_subnet_load(); bool we_should_ban_this_ip(map_element* current_average_speed_element, ban_settings_t current_ban_settings); unsigned int get_max_used_protocol(uint64_t tcp, uint64_t udp, uint64_t icmp); void print_attack_details_to_file(std::string details, std::string client_ip_as_string, attack_details current_attack); std::string print_ban_thresholds(ban_settings_t current_ban_settings); bool load_configuration_file(); std::string print_flow_tracking_for_ip(conntrack_main_struct& conntrack_element, std::string client_ip); void convert_integer_to_conntrack_hash_struct(packed_session* packed_connection_data, packed_conntrack_hash* unpacked_data); uint64_t convert_conntrack_hash_struct_to_integer(packed_conntrack_hash* struct_value); void cleanup_ban_list(); std::string get_attack_description(uint32_t client_ip, attack_details& current_attack); void send_attack_details(uint32_t client_ip, attack_details current_attack_details); void free_up_all_resources(); std::string print_ddos_attack_details(); void recalculate_speed(); std::string print_channel_speed(std::string traffic_type, direction packet_direction); void process_packet(simple_packet& current_packet); void traffic_draw_program(); void interruption_signal_handler(int signal_number); #ifdef FASTNETMON_API void silent_logging_function(gpr_log_func_args *args) { // We do not want any logging here } // Logic and data behind the server's behavior. class FastnetmonApiServiceImpl final : public Fastnetmon::Service { Status GetBanlist(::grpc::ServerContext* context, const ::fastmitigation::BanListRequest* request, ::grpc::ServerWriter< ::fastmitigation::BanListReply>* writer) override { logger << log4cpp::Priority::INFO << "API we asked for banlist"; for (std::map::iterator itr = ban_list.begin(); itr != ban_list.end(); ++itr) { std::string client_ip_as_string = convert_ip_as_uint_to_string(itr->first); BanListReply reply; reply.set_ip_address( client_ip_as_string + "/32" ); writer->Write(reply); } return Status::OK; } Status ExecuteBan(ServerContext* context, const fastmitigation::ExecuteBanRequest* request, fastmitigation::ExecuteBanReply* reply) override { logger << log4cpp::Priority::INFO << "API we asked for ban for IP: " << request->ip_address(); if (!is_v4_host(request->ip_address())) { logger << log4cpp::Priority::ERROR << "IP bad format"; return Status::CANCELLED; } uint32_t client_ip = convert_ip_as_string_to_uint(request->ip_address()); struct attack_details current_attack; ban_list_mutex.lock(); ban_list[client_ip] = current_attack; ban_list_mutex.unlock(); ban_list_details_mutex.lock(); ban_list_details[client_ip] = std::vector(); ban_list_details_mutex.unlock(); logger << log4cpp::Priority::INFO << "API call ban handlers manually"; std::string flow_attack_details = "manually triggered attack"; call_ban_handlers(client_ip, current_attack, flow_attack_details); return Status::OK; } Status ExecuteUnBan(ServerContext* context, const fastmitigation::ExecuteBanRequest* request, fastmitigation::ExecuteBanReply* reply) override { logger << log4cpp::Priority::INFO << "API: We asked for unban for IP: " << request->ip_address(); if (!is_v4_host(request->ip_address())) { logger << log4cpp::Priority::ERROR << "IP bad format"; return Status::CANCELLED; } uint32_t banned_ip = convert_ip_as_string_to_uint(request->ip_address()); if (ban_list.count(banned_ip) == 0) { logger << log4cpp::Priority::ERROR << "API: Could not find IP in ban list"; return Status::CANCELLED; } banlist_item ban_details = ban_list[banned_ip]; logger << log4cpp::Priority::INFO << "API: call unban handlers"; call_unban_handlers(banned_ip, ban_details); logger << log4cpp::Priority::INFO << "API: remove IP from ban list"; ban_list_mutex.lock(); ban_list.erase(banned_ip); ban_list_mutex.unlock(); return Status::OK; } }; // We could not define this variable in top of the file because we should define class before FastnetmonApiServiceImpl api_service; std::unique_ptr StartupApiServer() { std::string server_address("127.0.0.1:50052"); ServerBuilder builder; // Listen on the given address without any authentication mechanism. builder.AddListeningPort(server_address, grpc::InsecureServerCredentials()); // Register "service" as the instance through which we'll communicate with // clients. In this case it corresponds to an *synchronous* service. builder.RegisterService(&api_service); // Finally assemble the server. std::unique_ptr current_api_server(builder.BuildAndStart()); logger << log4cpp::Priority::INFO << "API server listening on " << server_address; return current_api_server; } void RunApiServer() { api_server = StartupApiServer(); // Wait for the server to shutdown. Note that some other thread must be // responsible for shutting down the server for this call to ever return. api_server->Wait(); logger << log4cpp::Priority::INFO << "API server got shutdown signal"; } #endif /* Class for custom comparison fields by different fields */ template class TrafficComparatorClass { private: sort_type sort_field; direction sort_direction; public: TrafficComparatorClass(direction sort_direction, sort_type sort_field) { this->sort_field = sort_field; this->sort_direction = sort_direction; } bool operator()(T a, T b) { if (sort_field == FLOWS) { if (sort_direction == INCOMING) { return a.second.in_flows > b.second.in_flows; } else if (sort_direction == OUTGOING) { return a.second.out_flows > b.second.out_flows; } else { return false; } } else if (sort_field == PACKETS) { if (sort_direction == INCOMING) { return a.second.in_packets > b.second.in_packets; } else if (sort_direction == OUTGOING) { return a.second.out_packets > b.second.out_packets; } else { return false; } } else if (sort_field == BYTES) { if (sort_direction == INCOMING) { return a.second.in_bytes > b.second.in_bytes; } else if (sort_direction == OUTGOING) { return a.second.out_bytes > b.second.out_bytes; } else { return false; } } else { return false; } } }; void sigpipe_handler_for_popen(int signo) { logger << log4cpp::Priority::ERROR << "Sorry but we experienced error with popen. " << "Please check your scripts. They should receive data on stdin! Optionally you could disable passing any details with configuration param: notify_script_pass_details = no"; // Well, we do not need exit here because we have another options to notifying about atatck // exit(1); } // exec command and pass data to it stdin bool exec_with_stdin_params(std::string cmd, std::string params) { FILE* pipe = popen(cmd.c_str(), "w"); if (!pipe) { logger << log4cpp::Priority::ERROR << "Can't execute program " << cmd << " error code: " << errno << " error text: " << strerror(errno); return false; } int fputs_ret = fputs(params.c_str(), pipe); if (fputs_ret) { pclose(pipe); return true; } else { logger << log4cpp::Priority::ERROR << "Can't pass data to stdin of program " << cmd; pclose(pipe); return false; } } #ifdef GEOIP bool geoip_init() { // load GeoIP ASN database to memory geo_ip = GeoIP_open("/root/fastnetmon/GeoIPASNum.dat", GEOIP_MEMORY_CACHE); if (geo_ip == NULL) { return false; } else { return true; } } #endif #ifdef REDIS redisContext* redis_init_connection() { struct timeval timeout = { 1, 500000 }; // 1.5 seconds redisContext* redis_context = redisConnectWithTimeout(redis_host.c_str(), redis_port, timeout); if (redis_context->err) { logger << log4cpp::Priority::ERROR << "Redis connection error:" << redis_context->errstr; return NULL; } // We should check connection with ping because redis do not check connection redisReply* reply = (redisReply*)redisCommand(redis_context, "PING"); if (reply) { freeReplyObject(reply); } else { return NULL; } return redis_context; } #endif #ifdef MONGO void store_data_in_mongo(std::string key_name, std::string attack_details_json) { mongoc_client_t *client; mongoc_collection_t *collection; mongoc_cursor_t *cursor; bson_error_t error; bson_oid_t oid; bson_t *doc; mongoc_init (); std::string collection_name = "attacks"; std::string connection_string = "mongodb://" + mongodb_host + ":" + convert_int_to_string(mongodb_port) + "/"; client = mongoc_client_new (connection_string.c_str()); if (!client) { logger << log4cpp::Priority::ERROR << "Can't connect to MongoDB database"; return; } bson_error_t bson_from_json_error; bson_t* bson_data = bson_new_from_json((const uint8_t *)attack_details_json.c_str(), attack_details_json.size(), &bson_from_json_error); if (!bson_data) { logger << log4cpp::Priority::ERROR << "Could not convert JSON to BSON"; return; } // logger << log4cpp::Priority::INFO << bson_as_json(bson_data, NULL); collection = mongoc_client_get_collection (client, mongodb_database_name.c_str(), collection_name.c_str()); doc = bson_new (); bson_oid_init (&oid, NULL); BSON_APPEND_OID (doc, "_id", &oid); bson_append_document(doc, key_name.c_str(), key_name.size(), bson_data); // logger << log4cpp::Priority::INFO << bson_as_json(doc, NULL); if (!mongoc_collection_insert (collection, MONGOC_INSERT_NONE, doc, NULL, &error)) { logger << log4cpp::Priority::ERROR << "Could not store data to MongoDB: " << error.message; } // TODO: destroy bson_data too! bson_destroy (doc); mongoc_collection_destroy (collection); mongoc_client_destroy (client); } #endif #ifdef REDIS void store_data_in_redis(std::string key_name, std::string attack_details) { redisReply* reply = NULL; redisContext* redis_context = redis_init_connection(); if (!redis_context) { logger << log4cpp::Priority::ERROR << "Could not initiate connection to Redis"; return; } reply = (redisReply*)redisCommand(redis_context, "SET %s %s", key_name.c_str(), attack_details.c_str()); // If we store data correctly ... if (!reply) { logger << log4cpp::Priority::ERROR << "Can't increment traffic in redis error_code: " << redis_context->err << " error_string: " << redis_context->errstr; // Handle redis server restart corectly if (redis_context->err == 1 or redis_context->err == 3) { // Connection refused logger << log4cpp::Priority::ERROR << "Unfortunately we can't store data in Redis because server reject connection"; } } else { freeReplyObject(reply); } redisFree(redis_context); } #endif std::string draw_table(direction data_direction, bool do_redis_update, sort_type sort_item) { std::vector vector_for_sort; std::stringstream output_buffer; // Preallocate memory for sort vector // We use total networks size for this vector vector_for_sort.reserve(total_number_of_hosts_in_our_networks); // Switch to Average speed there!!! map_of_vector_counters* current_speed_map = NULL; if (print_average_traffic_counts) { current_speed_map = &SubnetVectorMapSpeedAverage; } else { current_speed_map = &SubnetVectorMapSpeed; } map_element zero_map_element; memset(&zero_map_element, 0, sizeof(zero_map_element)); unsigned int count_of_zero_speed_packets = 0; for (map_of_vector_counters::iterator itr = current_speed_map->begin(); itr != current_speed_map->end(); ++itr) { for (vector_of_counters::iterator vector_itr = itr->second.begin(); vector_itr != itr->second.end(); ++vector_itr) { int current_index = vector_itr - itr->second.begin(); // convert to host order for math operations uint32_t subnet_ip = ntohl(itr->first.first); uint32_t client_ip_in_host_bytes_order = subnet_ip + current_index; // covnert to our standard network byte order uint32_t client_ip = htonl(client_ip_in_host_bytes_order); // Do not add zero speed packets to sort list if (memcmp((void*)&zero_map_element, &*vector_itr, sizeof(map_element)) != 0) { vector_for_sort.push_back(std::make_pair(client_ip, *vector_itr)); } else { count_of_zero_speed_packets++; } } } // Sort only first X elements in this vector unsigned int shift_for_sort = max_ips_in_list; if (data_direction == INCOMING or data_direction == OUTGOING) { // Because in another case we will got segmentation fault unsigned int vector_size = vector_for_sort.size(); if (vector_size < shift_for_sort) { shift_for_sort = vector_size; } std::partial_sort(vector_for_sort.begin(), vector_for_sort.begin() + shift_for_sort, vector_for_sort.end(), TrafficComparatorClass(data_direction, sort_item)); } else { logger << log4cpp::Priority::ERROR << "Unexpected bahaviour on sort function"; return "Internal error"; } unsigned int element_number = 0; // In this loop we print only top X talkers in our subnet to screen buffer for (std::vector::iterator ii = vector_for_sort.begin(); ii != vector_for_sort.end(); ++ii) { // Print first max_ips_in_list elements in list, we will show top X "huge" channel loaders if (element_number >= max_ips_in_list) { break; } uint32_t client_ip = (*ii).first; std::string client_ip_as_string = convert_ip_as_uint_to_string((*ii).first); uint64_t pps = 0; uint64_t bps = 0; uint64_t flows = 0; uint64_t pps_average = 0; uint64_t bps_average = 0; uint64_t flows_average = 0; // Here we could have average or instantaneous speed map_element* current_speed_element = &ii->second; // Create polymorphic pps, byte and flow counters if (data_direction == INCOMING) { pps = current_speed_element->in_packets; bps = current_speed_element->in_bytes; flows = current_speed_element->in_flows; } else if (data_direction == OUTGOING) { pps = current_speed_element->out_packets; bps = current_speed_element->out_bytes; flows = current_speed_element->out_flows; } uint64_t mbps = convert_speed_to_mbps(bps); uint64_t mbps_average = convert_speed_to_mbps(bps_average); std::string is_banned = ban_list.count(client_ip) > 0 ? " *banned* " : ""; // We use setw for alignment output_buffer << client_ip_as_string << "\t\t"; output_buffer << std::setw(6) << pps << " pps "; output_buffer << std::setw(6) << mbps << " mbps "; output_buffer << std::setw(6) << flows << " flows "; output_buffer << is_banned << std::endl; element_number++; } graphite_data_t graphite_data; // TODO: add graphite operations time to the config file if (graphite_enabled) { for (std::vector::iterator ii = vector_for_sort.begin(); ii != vector_for_sort.end(); ++ii) { uint32_t client_ip = (*ii).first; std::string client_ip_as_string = convert_ip_as_uint_to_string((*ii).first); uint64_t pps = 0; uint64_t bps = 0; uint64_t flows = 0; // Here we could have average or instantaneous speed map_element* current_speed_element = &ii->second; // Create polymorphic pps, byte and flow counters if (data_direction == INCOMING) { pps = current_speed_element->in_packets; bps = current_speed_element->in_bytes; flows = current_speed_element->in_flows; } else if (data_direction == OUTGOING) { pps = current_speed_element->out_packets; bps = current_speed_element->out_bytes; flows = current_speed_element->out_flows; } std::string direction_as_string; if (data_direction == INCOMING) { direction_as_string = "incoming"; } else if (data_direction == OUTGOING) { direction_as_string = "outgoing"; } std::string ip_as_string_with_dash_delimiters = client_ip_as_string; // Replace dots by dashes std::replace(ip_as_string_with_dash_delimiters.begin(), ip_as_string_with_dash_delimiters.end(), '.', '_'); std::string graphite_current_prefix = graphite_prefix + ".hosts." + ip_as_string_with_dash_delimiters + "." + direction_as_string; if (print_average_traffic_counts) { graphite_current_prefix = graphite_current_prefix + ".average"; } // We do not store zero data to Graphite if (pps != 0) { graphite_data[ graphite_current_prefix + ".pps" ] = pps; } if (bps != 0) { graphite_data[ graphite_current_prefix + ".bps" ] = bps * 8; } if (flows != 0) { graphite_data[ graphite_current_prefix + ".flows" ] = flows; } } } // TODO: we should switch to piclke format instead text // TODO: we should check packet size for Graphite // logger << log4cpp::Priority::INFO << "We will write " << graphite_data.size() << " records to Graphite"; if (graphite_enabled) { bool graphite_put_result = store_data_to_graphite(graphite_port, graphite_host, graphite_data); if (!graphite_put_result) { logger << log4cpp::Priority::ERROR << "Can't store data to Graphite"; } } return output_buffer.str(); } // TODO: move to lirbary // read whole file to vector std::vector read_file_to_vector(std::string file_name) { std::vector data; std::string line; std::ifstream reading_file; reading_file.open(file_name.c_str(), std::ifstream::in); if (reading_file.is_open()) { while (getline(reading_file, line)) { boost::algorithm::trim(line); data.push_back(line); } } else { logger << log4cpp::Priority::ERROR << "Can't open file: " << file_name; } return data; } void parse_hostgroups(std::string name, std::string value) { // We are creating new host group of subnets if (name != "hostgroup") { return; } std::vector splitted_new_host_group; // We have new host groups in form: // hostgroup = new_host_group_name:11.22.33.44/32,.... split(splitted_new_host_group, value, boost::is_any_of(":"), boost::token_compress_on); if (splitted_new_host_group.size() != 2) { logger << log4cpp::Priority::ERROR << "We can't parse new host group"; return; } boost::algorithm::trim(splitted_new_host_group[0]); boost::algorithm::trim(splitted_new_host_group[1]); std::string host_group_name = splitted_new_host_group[0]; if (host_groups.count(host_group_name) > 0) { logger << log4cpp::Priority::WARN << "We already have this host group (" << host_group_name << "). Please check!"; return; } // Split networks std::vector hostgroup_subnets = split_strings_to_vector_by_comma(splitted_new_host_group[1]); for (std::vector::iterator itr = hostgroup_subnets.begin(); itr != hostgroup_subnets.end(); ++itr) { subnet_t subnet = convert_subnet_from_string_to_binary_with_cidr_format(*itr); host_groups[ host_group_name ].push_back( subnet ); logger << log4cpp::Priority::WARN << "We add subnet " << convert_subnet_to_string( subnet ) << " to host group " << host_group_name; // And add to subnet to host group lookup hash if (subnet_to_host_groups.count(subnet) > 0) { // Huston, we have problem! Subnet to host group mapping should map single subnet to single group! logger << log4cpp::Priority::WARN << "Seems you have specified single subnet " << *itr << " to multiple host groups, please fix it, it's prohibited"; } else { subnet_to_host_groups[ subnet ] = host_group_name; } } logger << log4cpp::Priority::INFO << "We have created host group " << host_group_name << " with " << host_groups[ host_group_name ].size() << " subnets"; } // Load configuration bool load_configuration_file() { std::ifstream config_file(global_config_path.c_str()); std::string line; if (!config_file.is_open()) { logger << log4cpp::Priority::ERROR << "Can't open config file"; return false; } while (getline(config_file, line)) { std::vector parsed_config; boost::algorithm::trim(line); if (line.find("#") == 0 or line.empty()) { // Ignore comments line continue; } boost::split(parsed_config, line, boost::is_any_of("="), boost::token_compress_on); if (parsed_config.size() == 2) { boost::algorithm::trim(parsed_config[0]); boost::algorithm::trim(parsed_config[1]); configuration_map[parsed_config[0]] = parsed_config[1]; // Well, we parse host groups here parse_hostgroups(parsed_config[0], parsed_config[1]); } else { logger << log4cpp::Priority::ERROR << "Can't parse config line: '" << line << "'"; } } if (configuration_map.count("enable_connection_tracking")) { if (configuration_map["enable_connection_tracking"] == "on") { enable_conection_tracking = true; } else { enable_conection_tracking = false; } } if (configuration_map.count("ban_time") != 0) { global_ban_time = convert_string_to_integer(configuration_map["ban_time"]); // Completely disable unban option if (global_ban_time == 0) { unban_enabled = false; } } if (configuration_map.count("pid_path") != 0) { pid_path = configuration_map["pid_path"]; } if (configuration_map.count("cli_stats_file_path") != 0) { cli_stats_file_path = configuration_map["cli_stats_file_path"]; } if (configuration_map.count("unban_only_if_attack_finished") != 0) { if (configuration_map["unban_only_if_attack_finished"] == "on") { unban_only_if_attack_finished = true; } else { unban_only_if_attack_finished = false; } } if(configuration_map.count("graphite_prefix") != 0) { graphite_prefix = configuration_map["graphite_prefix"]; } if (configuration_map.count("average_calculation_time") != 0) { average_calculation_amount = convert_string_to_integer(configuration_map["average_calculation_time"]); } if (configuration_map.count("average_calculation_time_for_subnets") != 0) { average_calculation_amount_for_subnets = convert_string_to_integer(configuration_map["average_calculation_time_for_subnets"]); } if (configuration_map.count("monitor_local_ip_addresses") != 0) { monitor_local_ip_addresses = configuration_map["monitor_local_ip_addresses"] == "on" ? true : false; } #ifdef FASTNETMON_API if (configuration_map.count("enable_api") != 0) { enable_api = configuration_map["enable_api"] == "on"; } #endif #ifdef ENABLE_GOBGP // GoBGP configuration if (configuration_map.count("gobgp") != 0) { gobgp_enabled = configuration_map["gobgp"] == "on"; } #endif // ExaBGP configuration if (configuration_map.count("exabgp") != 0) { if (configuration_map["exabgp"] == "on") { exabgp_enabled = true; } else { exabgp_enabled = false; } } if (exabgp_enabled) { // TODO: add community format validation if (configuration_map.count("exabgp_community")) { exabgp_community = configuration_map["exabgp_community"]; } if (configuration_map.count("exabgp_community_subnet")) { exabgp_community_subnet = configuration_map["exabgp_community_subnet"]; } else { exabgp_community_subnet = exabgp_community; } if (configuration_map.count("exabgp_community_host")) { exabgp_community_host = configuration_map["exabgp_community_host"]; } else { exabgp_community_host = exabgp_community; } if (exabgp_enabled && exabgp_announce_whole_subnet && exabgp_community_subnet.empty()) { logger << log4cpp::Priority::ERROR << "You enabled exabgp for subnet but not specified community, we disable exabgp support"; exabgp_enabled = false; } if (exabgp_enabled && exabgp_announce_host && exabgp_community_host.empty()) { logger << log4cpp::Priority::ERROR << "You enabled exabgp for host but not specified community, we disable exabgp support"; exabgp_enabled = false; } } if (exabgp_enabled) { exabgp_command_pipe = configuration_map["exabgp_command_pipe"]; if (exabgp_command_pipe.empty()) { logger << log4cpp::Priority::ERROR << "You enabled exabgp but not specified " "exabgp_command_pipe, so we disable exabgp " "support"; exabgp_enabled = false; } } if (exabgp_enabled) { exabgp_next_hop = configuration_map["exabgp_next_hop"]; if (exabgp_next_hop.empty()) { logger << log4cpp::Priority::ERROR << "You enabled exabgp but not specified exabgp_next_hop, so we disable exabgp support"; exabgp_enabled = false; } if (configuration_map.count("exabgp_flow_spec_announces") != 0) { exabgp_flow_spec_announces = configuration_map["exabgp_flow_spec_announces"] == "on"; } if (exabgp_enabled) { logger << log4cpp::Priority::INFO << "ExaBGP support initialized correctly"; } } if (configuration_map.count("sflow") != 0) { if (configuration_map["sflow"] == "on") { enable_sflow_collection = true; } else { enable_sflow_collection = false; } } if (configuration_map.count("pfring_hardware_filters_enabled") != 0) { pfring_hardware_filters_enabled = configuration_map["pfring_hardware_filters_enabled"] == "on"; } if (configuration_map.count("netflow") != 0) { if (configuration_map["netflow"] == "on") { enable_netflow_collection = true; } else { enable_netflow_collection = false; } } if (configuration_map.count("exabgp_announce_whole_subnet") != 0) { exabgp_announce_whole_subnet = configuration_map["exabgp_announce_whole_subnet"] == "on" ? true : false; } if (configuration_map.count("exabgp_announce_host") != 0) { exabgp_announce_host = configuration_map["exabgp_announce_host"] == "on" ? true : false; } if (configuration_map.count("enable_subnet_counters") != 0) { enable_subnet_counters = configuration_map["enable_subnet_counters"] == "on" ? true : false; } // Graphite if (configuration_map.count("graphite") != 0) { graphite_enabled = configuration_map["graphite"] == "on" ? true : false; } if (configuration_map.count("graphite_host") != 0) { graphite_host = configuration_map["graphite_host"]; } if (configuration_map.count("graphite_port") != 0) { graphite_port = convert_string_to_integer(configuration_map["graphite_port"]); } if (configuration_map.count("graphite_number_of_ips") != 0) { logger << log4cpp::Priority::ERROR << "Sorry, you have used deprecated function graphite_number_of_ips"; } if (configuration_map.count("process_incoming_traffic") != 0) { process_incoming_traffic = configuration_map["process_incoming_traffic"] == "on" ? true : false; } if (configuration_map.count("process_outgoing_traffic") != 0) { process_outgoing_traffic = configuration_map["process_outgoing_traffic"] == "on" ? true : false; } if (configuration_map.count("mirror") != 0) { if (configuration_map["mirror"] == "on") { enable_data_collection_from_mirror = true; } else { enable_data_collection_from_mirror = false; } } if (configuration_map.count("mirror_netmap") != 0) { if (configuration_map["mirror_netmap"] == "on") { enable_netmap_collection = true; } else { enable_netmap_collection = false; } } if (configuration_map.count("mirror_snabbswitch") != 0) { enable_snabbswitch_collection = configuration_map["mirror_snabbswitch"] == "on"; } if (configuration_map.count("mirror_afpacket") != 0) { enable_afpacket_collection = configuration_map["mirror_afpacket"] == "on"; } if (enable_netmap_collection && enable_data_collection_from_mirror) { logger << log4cpp::Priority::ERROR << "You have enabled pfring and netmap data collection " "from mirror which strictly prohibited, please " "select one"; exit(1); } if (configuration_map.count("pcap") != 0) { if (configuration_map["pcap"] == "on") { enable_pcap_collection = true; } else { enable_pcap_collection = false; } } // Read global ban configuration global_ban_settings = read_ban_settings(configuration_map, ""); logging_configuration = read_logging_settings(configuration_map); // logger << log4cpp::Priority::INFO << "We read global ban settings: " << print_ban_thresholds(global_ban_settings); // Read host group ban settings for (host_group_map_t::iterator hostgroup_itr = host_groups.begin(); hostgroup_itr != host_groups.end(); ++hostgroup_itr) { std::string host_group_name = hostgroup_itr->first; logger << log4cpp::Priority::INFO << "We will read ban settings for " << host_group_name; host_group_ban_settings_map[ host_group_name ] = read_ban_settings(configuration_map, host_group_name); //logger << log4cpp::Priority::INFO << "We read " << host_group_name << " ban settings " // << print_ban_thresholds(host_group_ban_settings_map[ host_group_name ]); } if (configuration_map.count("white_list_path") != 0) { white_list_path = configuration_map["white_list_path"]; } if (configuration_map.count("networks_list_path") != 0) { networks_list_path = configuration_map["networks_list_path"]; } #ifdef REDIS if (configuration_map.count("redis_port") != 0) { redis_port = convert_string_to_integer(configuration_map["redis_port"]); } if (configuration_map.count("redis_host") != 0) { redis_host = configuration_map["redis_host"]; } if (configuration_map.count("redis_prefix") != 0) { redis_prefix = configuration_map["redis_prefix"]; } if (configuration_map.count("redis_enabled") != 0) { // We use yes and on because it's stupid typo :( if (configuration_map["redis_enabled"] == "on" or configuration_map["redis_enabled"] == "yes") { redis_enabled = true; } else { redis_enabled = false; } } #endif #ifdef MONGO if (configuration_map.count("mongodb_enabled") != 0) { if (configuration_map["mongodb_enabled"] == "on") { mongodb_enabled = true; } } if (configuration_map.count("mongodb_host") != 0) { mongodb_host = configuration_map["mongodb_host"]; } if (configuration_map.count("mongodb_port") != 0) { mongodb_port = convert_string_to_integer(configuration_map["mongodb_port"]); } if (configuration_map.count("mongodb_database_name") != 0) { mongodb_database_name = configuration_map["mongodb_database_name"]; } #endif if (configuration_map.count("ban_details_records_count") != 0) { ban_details_records_count = convert_string_to_integer(configuration_map["ban_details_records_count"]); } if (configuration_map.count("check_period") != 0) { check_period = convert_string_to_integer(configuration_map["check_period"]); } if (configuration_map.count("sort_parameter") != 0) { sort_parameter = configuration_map["sort_parameter"]; } if (configuration_map.count("max_ips_in_list") != 0) { max_ips_in_list = convert_string_to_integer(configuration_map["max_ips_in_list"]); } if (configuration_map.count("notify_script_path") != 0) { notify_script_path = configuration_map["notify_script_path"]; } if (configuration_map.count("notify_script_pass_details") != 0) { notify_script_pass_details = configuration_map["notify_script_pass_details"] == "on" ? true : false; } if (file_exists(notify_script_path)) { notify_script_enabled = true; } else { logger << log4cpp::Priority::ERROR << "We can't find notify script " << notify_script_path; notify_script_enabled = false; } if (configuration_map.count("collect_attack_pcap_dumps") != 0) { collect_attack_pcap_dumps = configuration_map["collect_attack_pcap_dumps"] == "on" ? true : false; } if (configuration_map.count("process_pcap_attack_dumps_with_dpi") != 0) { if (collect_attack_pcap_dumps) { process_pcap_attack_dumps_with_dpi = configuration_map["process_pcap_attack_dumps_with_dpi"] == "on" ? true : false; } } return true; } /* Enable core dumps for simplify debug tasks */ void enable_core_dumps() { struct rlimit rlim; int result = getrlimit(RLIMIT_CORE, &rlim); if (result) { logger << log4cpp::Priority::ERROR << "Can't get current rlimit for RLIMIT_CORE"; return; } else { rlim.rlim_cur = rlim.rlim_max; setrlimit(RLIMIT_CORE, &rlim); } } void subnet_vectors_allocator(prefix_t* prefix, void* data) { // Network byte order uint32_t subnet_as_integer = prefix->add.sin.s_addr; u_short bitlen = prefix->bitlen; double base = 2; int network_size_in_ips = pow(base, 32 - bitlen); // logger<< log4cpp::Priority::INFO<<"Subnet: "<add.sin.s_addr<<" network size: // "<first; std::fill(itr->second.begin(), itr->second.end(), zero_map_element); } } void zeroify_all_flow_counters() { // On creating it initilizes by zeros conntrack_main_struct zero_conntrack_main_struct; // Iterate over map for (map_of_vector_counters_for_flow::iterator itr = SubnetVectorMapFlow.begin(); itr != SubnetVectorMapFlow.end(); ++itr) { // Iterate over vector for (vector_of_flow_counters::iterator vector_iterator = itr->second.begin(); vector_iterator != itr->second.end(); ++vector_iterator) { // TODO: rewrite this monkey code vector_iterator->in_tcp.clear(); vector_iterator->in_udp.clear(); vector_iterator->in_icmp.clear(); vector_iterator->in_other.clear(); vector_iterator->out_tcp.clear(); vector_iterator->out_udp.clear(); vector_iterator->out_icmp.clear(); vector_iterator->out_other.clear(); } } } bool load_our_networks_list() { if (file_exists(white_list_path)) { unsigned int network_entries = 0; std::vector network_list_from_config = read_file_to_vector(white_list_path); for (std::vector::iterator ii = network_list_from_config.begin(); ii != network_list_from_config.end(); ++ii) { std::string text_subnet = *ii; if (text_subnet.empty()) { continue; } if (is_v4_host(text_subnet)) { logger << log4cpp::Priority::INFO << "Assuming /32 netmask for " << text_subnet; text_subnet += "/32"; } else if (!is_cidr_subnet(text_subnet)) { logger << log4cpp::Priority::ERROR << "Can't parse line from whitelist: " << text_subnet; continue; } network_entries++; make_and_lookup(whitelist_tree_ipv4, const_cast (text_subnet.c_str())); } logger << log4cpp::Priority::INFO << "We loaded " << network_entries << " networks from whitelist file"; } std::vector networks_list_ipv4_as_string; std::vector networks_list_ipv6_as_string; // We can bould "our subnets" automatically here if (file_exists("/proc/vz/version")) { logger << log4cpp::Priority::INFO << "We found OpenVZ"; // Add /32 CIDR mask for every IP here std::vector openvz_ips = read_file_to_vector("/proc/vz/veip"); for (std::vector::iterator ii = openvz_ips.begin(); ii != openvz_ips.end(); ++ii) { // skip header if (strstr(ii->c_str(), "Version") != NULL) { continue; } /* Example data for this lines: 2a03:f480:1:17:0:0:0:19 0 185.4.72.40 0 */ if (strstr(ii->c_str(), ":") == NULL) { // IPv4 std::vector subnet_as_string; split(subnet_as_string, *ii, boost::is_any_of(" "), boost::token_compress_on); std::string openvz_subnet = subnet_as_string[1] + "/32"; networks_list_ipv4_as_string.push_back(openvz_subnet); } else { // IPv6 std::vector subnet_as_string; split(subnet_as_string, *ii, boost::is_any_of(" "), boost::token_compress_on); std::string openvz_subnet = subnet_as_string[1] + "/128"; networks_list_ipv6_as_string.push_back(openvz_subnet); } } logger << log4cpp::Priority::INFO << "We loaded " << networks_list_ipv4_as_string.size() << " IPv4 networks from /proc/vz/veip"; logger << log4cpp::Priority::INFO << "We loaded " << networks_list_ipv6_as_string.size() << " IPv6 networks from /proc/vz/veip"; } if (monitor_local_ip_addresses && file_exists("/sbin/ip")) { logger << log4cpp::Priority::INFO << "We are working on Linux and could use ip tool for detecting local IP's"; ip_addresses_list_t ip_list = get_local_ip_v4_addresses_list(); logger << log4cpp::Priority::INFO << "We found " << ip_list.size() << " local IP addresses and will monitor they"; for (ip_addresses_list_t::iterator iter = ip_list.begin(); iter != ip_list.end(); ++iter) { // TODO: add IPv6 here networks_list_ipv4_as_string.push_back(*iter + "/32"); } } if (file_exists(networks_list_path)) { std::vector network_list_from_config = read_file_to_vector(networks_list_path); for (std::vector::iterator line_itr = network_list_from_config.begin(); line_itr != network_list_from_config.end(); ++line_itr) { if (line_itr->length() == 0) { // Skip blank lines in subnet list file silently continue; } if (strstr(line_itr->c_str(), ":") == NULL) { networks_list_ipv4_as_string.push_back(*line_itr); } else { networks_list_ipv6_as_string.push_back(*line_itr); } } logger << log4cpp::Priority::INFO << "We loaded " << network_list_from_config.size() << " networks from networks file"; } // Some consistency checks assert(convert_ip_as_string_to_uint("255.255.255.0") == convert_cidr_to_binary_netmask(24)); assert(convert_ip_as_string_to_uint("255.255.255.255") == convert_cidr_to_binary_netmask(32)); logger << log4cpp::Priority::INFO << "Totally we have " << networks_list_ipv4_as_string.size() << " IPv4 subnets"; logger << log4cpp::Priority::INFO << "Totally we have " << networks_list_ipv6_as_string.size() << " IPv6 subnets"; for (std::vector::iterator ii = networks_list_ipv4_as_string.begin(); ii != networks_list_ipv4_as_string.end(); ++ii) { if (!is_cidr_subnet(*ii)) { logger << log4cpp::Priority::ERROR << "Can't parse line from subnet list: '" << *ii << "'"; continue; } std::string network_address_in_cidr_form = *ii; unsigned int cidr_mask = get_cidr_mask_from_network_as_string(network_address_in_cidr_form); std::string network_address = get_net_address_from_network_as_string(network_address_in_cidr_form); double base = 2; total_number_of_hosts_in_our_networks += pow(base, 32 - cidr_mask); // Make sure it's "subnet address" and not an host address uint32_t subnet_address_as_uint = convert_ip_as_string_to_uint(network_address); uint32_t subnet_address_netmask_binary = convert_cidr_to_binary_netmask(cidr_mask); uint32_t generated_subnet_address = subnet_address_as_uint & subnet_address_netmask_binary; if (subnet_address_as_uint != generated_subnet_address) { std::string new_network_address_as_string = convert_ip_as_uint_to_string(generated_subnet_address) + "/" + convert_int_to_string(cidr_mask); logger << log4cpp::Priority::WARN << "We will use " << new_network_address_as_string << " instead of " << network_address_in_cidr_form << " because it's host address"; network_address_in_cidr_form = new_network_address_as_string; } make_and_lookup(lookup_tree_ipv4, const_cast(network_address_in_cidr_form.c_str())); } for (std::vector::iterator ii = networks_list_ipv6_as_string.begin(); ii != networks_list_ipv6_as_string.end(); ++ii) { // TODO: add IPv6 subnet format validation make_and_lookup_ipv6(lookup_tree_ipv6, (char*)ii->c_str()); } logger << log4cpp::Priority::INFO << "Total number of monitored hosts (total size of all networks): " << total_number_of_hosts_in_our_networks; // 3 - speed counter, average speed counter and data counter uint64_t memory_requirements = 3 * sizeof(map_element) * total_number_of_hosts_in_our_networks / 1024 / 1024; logger << log4cpp::Priority::INFO << "We need " << memory_requirements << " MB of memory for storing counters for your networks"; /* Preallocate data structures */ patricia_process(lookup_tree_ipv4, (void_fn_t)subnet_vectors_allocator); logger << log4cpp::Priority::INFO << "We start total zerofication of counters"; zeroify_all_counters(); logger << log4cpp::Priority::INFO << "We finished zerofication"; logger << log4cpp::Priority::INFO << "We loaded " << networks_list_ipv4_as_string.size() << " IPv4 subnets to our in-memory list of networks"; return true; } #ifdef IPV6_HASH_COUNTERS moodycamel::ConcurrentQueue multi_process_queue_for_ipv6_counters; void ipv6_traffic_processor() { simple_packet packets_from_queue[32]; while (true) { std::size_t count = 0; while ((count = multi_process_queue_for_ipv6_counters.try_dequeue_bulk(packets_from_queue, 32)) != 0) { for (std::size_t i = 0; i != count; ++i) { #ifdef USE_NEW_ATOMIC_BUILTINS __atomic_add_fetch(&total_ipv6_packets, 1, __ATOMIC_RELAXED); #else __sync_fetch_and_add(&total_ipv6_packets, 1); #endif direction packet_direction = packets_from_queue[i].packet_direction; uint64_t sampled_number_of_packets = packets_from_queue[i].number_of_packets * packets_from_queue[i].sample_ratio; uint64_t sampled_number_of_bytes = packets_from_queue[i].length * packets_from_queue[i].sample_ratio; #ifdef USE_NEW_ATOMIC_BUILTINS __atomic_add_fetch(&total_counters[packet_direction].packets, sampled_number_of_packets, __ATOMIC_RELAXED); __atomic_add_fetch(&total_counters[packet_direction].bytes, sampled_number_of_bytes, __ATOMIC_RELAXED); #else __sync_fetch_and_add(&total_counters[packet_direction].packets, sampled_number_of_packets); __sync_fetch_and_add(&total_counters[packet_direction].bytes, sampled_number_of_bytes); #endif if (packet_direction != OTHER) { #ifdef USE_NEW_ATOMIC_BUILTINS __atomic_add_fetch(&our_ipv6_packets, 1, __ATOMIC_RELAXED); #else __sync_fetch_and_add(&our_ipv6_packets, 1); #endif } } } } } #endif /* Process simple unified packet */ void process_packet(simple_packet& current_packet) { // Packets dump is very useful for bug hunting if (DEBUG_DUMP_ALL_PACKETS) { logger << log4cpp::Priority::INFO << "Dump: " << print_simple_packet(current_packet); } if (current_packet.ip_protocol_version == 6) { #ifdef IPV6_HASH_COUNTERS current_packet.packet_direction = get_packet_direction_ipv6(lookup_tree_ipv6, current_packet.src_ipv6, current_packet.dst_ipv6); // TODO: move to bulk operations here! multi_process_queue_for_ipv6_counters.enqueue(current_packet); #else #ifdef USE_NEW_ATOMIC_BUILTINS __atomic_add_fetch(&total_ipv6_packets, 1, __ATOMIC_RELAXED); #else __sync_fetch_and_add(&total_ipv6_packets, 1); #endif #endif return; } // We do not process IPv6 at all on this mement if (current_packet.ip_protocol_version != 4) { return; } // Subnet for found IPs unsigned long subnet = 0; unsigned int subnet_cidr_mask = 0; direction packet_direction = get_packet_direction(lookup_tree_ipv4, current_packet.src_ip, current_packet.dst_ip, subnet, subnet_cidr_mask); // It's useful in case when we can't find what packets do not processed correctly if (DEBUG_DUMP_OTHER_PACKETS && packet_direction == OTHER) { logger << log4cpp::Priority::INFO << "Dump other: " << print_simple_packet(current_packet); } // Skip processing of specific traffic direction if ((packet_direction == INCOMING && !process_incoming_traffic) or (packet_direction == OUTGOING && !process_outgoing_traffic)) { return; } subnet_t current_subnet = std::make_pair(subnet, subnet_cidr_mask); uint32_t subnet_in_host_byte_order = 0; // We operate in host bytes order and need to convert subnet if (subnet != 0) { subnet_in_host_byte_order = ntohl(current_subnet.first); } // Try to find map key for this subnet map_of_vector_counters::iterator itr; // Iterator for subnet counter subnet_counter_t* subnet_counter = NULL; if (packet_direction == OUTGOING or packet_direction == INCOMING) { // Find element in map of vectors itr = SubnetVectorMap.find(current_subnet); if (itr == SubnetVectorMap.end()) { logger << log4cpp::Priority::ERROR << "Can't find vector address in subnet map"; return; } if (enable_subnet_counters) { map_for_subnet_counters::iterator subnet_iterator; // Find element in map of subnet counters subnet_iterator = PerSubnetCountersMap.find(current_subnet); if (subnet_iterator == PerSubnetCountersMap.end()) { logger << log4cpp::Priority::ERROR << "Can't find counter structure for subnet"; return; } subnet_counter = &subnet_iterator->second; } } map_of_vector_counters_for_flow::iterator itr_flow; if (enable_conection_tracking) { if (packet_direction == OUTGOING or packet_direction == INCOMING) { itr_flow = SubnetVectorMapFlow.find(current_subnet); if (itr_flow == SubnetVectorMapFlow.end()) { logger << log4cpp::Priority::ERROR << "Can't find vector address in subnet flow map"; return; } } } /* Because we support mirroring, sflow and netflow we should support different cases: - One packet passed for processing (mirror) - Multiple packets ("flows") passed for processing (netflow) - One sampled packed passed for processing (netflow) - Another combinations of this three options */ uint64_t sampled_number_of_packets = current_packet.number_of_packets * current_packet.sample_ratio; uint64_t sampled_number_of_bytes = current_packet.length * current_packet.sample_ratio; #ifdef USE_NEW_ATOMIC_BUILTINS __atomic_add_fetch(&total_counters[packet_direction].packets, sampled_number_of_packets, __ATOMIC_RELAXED); __atomic_add_fetch(&total_counters[packet_direction].bytes, sampled_number_of_bytes, __ATOMIC_RELAXED); #else __sync_fetch_and_add(&total_counters[packet_direction].packets, sampled_number_of_packets); __sync_fetch_and_add(&total_counters[packet_direction].bytes, sampled_number_of_bytes); #endif // Incerementi main and per protocol packet counters if (packet_direction == OUTGOING) { int64_t shift_in_vector = (int64_t)ntohl(current_packet.src_ip) - (int64_t)subnet_in_host_byte_order; if (shift_in_vector < 0 or shift_in_vector >= itr->second.size()) { logger << log4cpp::Priority::ERROR << "We tried to access to element with index " << shift_in_vector << " which located outside allocated vector with size " << itr->second.size(); logger << log4cpp::Priority::ERROR << "We expect issues with this packet in OUTGOING direction: " << print_simple_packet(current_packet); return; } map_element* current_element = &itr->second[shift_in_vector]; // Main packet/bytes counter #ifdef USE_NEW_ATOMIC_BUILTINS __atomic_add_fetch(¤t_element->out_packets, sampled_number_of_packets, __ATOMIC_RELAXED); __atomic_add_fetch(¤t_element->out_bytes, sampled_number_of_bytes, __ATOMIC_RELAXED); #else __sync_fetch_and_add(¤t_element->out_packets, sampled_number_of_packets); __sync_fetch_and_add(¤t_element->out_bytes, sampled_number_of_bytes); #endif // Fragmented IP packets if (current_packet.ip_fragmented) { #ifdef USE_NEW_ATOMIC_BUILTINS __atomic_add_fetch(¤t_element->fragmented_out_packets, sampled_number_of_packets, __ATOMIC_RELAXED); __atomic_add_fetch(¤t_element->fragmented_out_bytes, sampled_number_of_bytes, __ATOMIC_RELAXED); #else __sync_fetch_and_add(¤t_element->fragmented_out_packets, sampled_number_of_packets); __sync_fetch_and_add(¤t_element->fragmented_out_bytes, sampled_number_of_bytes); #endif } // TODO: add another counters if (enable_subnet_counters) { #ifdef USE_NEW_ATOMIC_BUILTINS __atomic_add_fetch(&subnet_counter->out_packets, sampled_number_of_packets, __ATOMIC_RELAXED); __atomic_add_fetch(&subnet_counter->out_bytes, sampled_number_of_bytes, __ATOMIC_RELAXED); #else __sync_fetch_and_add(&subnet_counter->out_packets, sampled_number_of_packets); __sync_fetch_and_add(&subnet_counter->out_bytes, sampled_number_of_bytes); #endif } conntrack_main_struct* current_element_flow = NULL; if (enable_conection_tracking) { current_element_flow = &itr_flow->second[shift_in_vector]; } // Collect data when ban client if (!ban_list_details.empty() && ban_list_details.count(current_packet.src_ip) > 0 && ban_list_details[current_packet.src_ip].size() < ban_details_records_count) { ban_list_details_mutex.lock(); if (collect_attack_pcap_dumps) { // this code SHOULD NOT be called without mutex! if (current_packet.packet_payload_length > 0 && current_packet.packet_payload_pointer != NULL) { ban_list[current_packet.src_ip].pcap_attack_dump.write_packet(current_packet.packet_payload_pointer, current_packet.packet_payload_length); } } ban_list_details[current_packet.src_ip].push_back(current_packet); ban_list_details_mutex.unlock(); } uint64_t connection_tracking_hash = 0; if (enable_conection_tracking) { packed_conntrack_hash flow_tracking_structure; flow_tracking_structure.opposite_ip = current_packet.dst_ip; flow_tracking_structure.src_port = current_packet.source_port; flow_tracking_structure.dst_port = current_packet.destination_port; // convert this struct to 64 bit integer connection_tracking_hash = convert_conntrack_hash_struct_to_integer(&flow_tracking_structure); } if (current_packet.protocol == IPPROTO_TCP) { #ifdef USE_NEW_ATOMIC_BUILTINS __atomic_add_fetch(¤t_element->tcp_out_packets, sampled_number_of_packets, __ATOMIC_RELAXED); __atomic_add_fetch(¤t_element->tcp_out_bytes, sampled_number_of_bytes, __ATOMIC_RELAXED); #else __sync_fetch_and_add(¤t_element->tcp_out_packets, sampled_number_of_packets); __sync_fetch_and_add(¤t_element->tcp_out_bytes, sampled_number_of_bytes); #endif if (extract_bit_value(current_packet.flags, TCP_SYN_FLAG_SHIFT)) { #ifdef USE_NEW_ATOMIC_BUILTINS __atomic_add_fetch(¤t_element->tcp_syn_out_packets, sampled_number_of_packets, __ATOMIC_RELAXED); __atomic_add_fetch(¤t_element->tcp_syn_out_bytes, sampled_number_of_bytes, __ATOMIC_RELAXED); #else __sync_fetch_and_add(¤t_element->tcp_syn_out_packets, sampled_number_of_packets); __sync_fetch_and_add(¤t_element->tcp_syn_out_bytes, sampled_number_of_bytes); #endif } if (enable_conection_tracking) { flow_counter.lock(); conntrack_key_struct* conntrack_key_struct_ptr = ¤t_element_flow->out_tcp[connection_tracking_hash]; conntrack_key_struct_ptr->packets += sampled_number_of_packets; conntrack_key_struct_ptr->bytes += sampled_number_of_bytes; flow_counter.unlock(); } } else if (current_packet.protocol == IPPROTO_UDP) { #ifdef USE_NEW_ATOMIC_BUILTINS __atomic_add_fetch(¤t_element->udp_out_packets, sampled_number_of_packets, __ATOMIC_RELAXED); __atomic_add_fetch(¤t_element->udp_out_bytes, sampled_number_of_bytes, __ATOMIC_RELAXED); #else __sync_fetch_and_add(¤t_element->udp_out_packets, sampled_number_of_packets); __sync_fetch_and_add(¤t_element->udp_out_bytes, sampled_number_of_bytes); #endif if (enable_conection_tracking) { flow_counter.lock(); conntrack_key_struct* conntrack_key_struct_ptr = ¤t_element_flow->out_udp[connection_tracking_hash]; conntrack_key_struct_ptr->packets += sampled_number_of_packets; conntrack_key_struct_ptr->bytes += sampled_number_of_bytes; flow_counter.unlock(); } } else if (current_packet.protocol == IPPROTO_ICMP) { #ifdef USE_NEW_ATOMIC_BUILTINS __atomic_add_fetch(¤t_element->icmp_out_packets, sampled_number_of_packets, __ATOMIC_RELAXED); __atomic_add_fetch(¤t_element->icmp_out_bytes, sampled_number_of_bytes, __ATOMIC_RELAXED); #else __sync_fetch_and_add(¤t_element->icmp_out_packets, sampled_number_of_packets); __sync_fetch_and_add(¤t_element->icmp_out_bytes, sampled_number_of_bytes); #endif // no flow tracking for icmp } else { } } else if (packet_direction == INCOMING) { int64_t shift_in_vector = (int64_t)ntohl(current_packet.dst_ip) - (int64_t)subnet_in_host_byte_order; if (shift_in_vector < 0 or shift_in_vector >= itr->second.size()) { logger << log4cpp::Priority::ERROR << "We tried to access to element with index " << shift_in_vector << " which located outside allocated vector with size " << itr->second.size(); logger << log4cpp::Priority::ERROR << "We expect issues with this packet in INCOMING direction: " << print_simple_packet(current_packet); return; } map_element* current_element = &itr->second[shift_in_vector]; // Main packet/bytes counter #ifdef USE_NEW_ATOMIC_BUILTINS __atomic_add_fetch(¤t_element->in_packets, sampled_number_of_packets, __ATOMIC_RELAXED); __atomic_add_fetch(¤t_element->in_bytes, sampled_number_of_bytes, __ATOMIC_RELAXED); #else __sync_fetch_and_add(¤t_element->in_packets, sampled_number_of_packets); __sync_fetch_and_add(¤t_element->in_bytes, sampled_number_of_bytes); #endif if (enable_subnet_counters) { #ifdef USE_NEW_ATOMIC_BUILTINS __atomic_add_fetch(&subnet_counter->in_packets, sampled_number_of_packets, __ATOMIC_RELAXED); __atomic_add_fetch(&subnet_counter->in_bytes, sampled_number_of_bytes, __ATOMIC_RELAXED); #else __sync_fetch_and_add(&subnet_counter->in_packets, sampled_number_of_packets); __sync_fetch_and_add(&subnet_counter->in_bytes, sampled_number_of_bytes); #endif } // Count fragmented IP packets if (current_packet.ip_fragmented) { #ifdef USE_NEW_ATOMIC_BUILTINS __atomic_add_fetch(¤t_element->fragmented_in_packets, sampled_number_of_packets, __ATOMIC_RELAXED); __atomic_add_fetch(¤t_element->fragmented_in_bytes, sampled_number_of_bytes, __ATOMIC_RELAXED); #else __sync_fetch_and_add(¤t_element->fragmented_in_packets, sampled_number_of_packets); __sync_fetch_and_add(¤t_element->fragmented_in_bytes, sampled_number_of_bytes); #endif } conntrack_main_struct* current_element_flow = NULL; if (enable_conection_tracking) { current_element_flow = &itr_flow->second[shift_in_vector]; } uint64_t connection_tracking_hash = 0; if (enable_conection_tracking) { packed_conntrack_hash flow_tracking_structure; flow_tracking_structure.opposite_ip = current_packet.src_ip; flow_tracking_structure.src_port = current_packet.source_port; flow_tracking_structure.dst_port = current_packet.destination_port; // convert this struct to 64 bit integer connection_tracking_hash = convert_conntrack_hash_struct_to_integer(&flow_tracking_structure); } // Collect attack details if (!ban_list_details.empty() && ban_list_details.count(current_packet.dst_ip) > 0 && ban_list_details[current_packet.dst_ip].size() < ban_details_records_count) { ban_list_details_mutex.lock(); if (collect_attack_pcap_dumps) { // this code SHOULD NOT be called without mutex! if (current_packet.packet_payload_length > 0 && current_packet.packet_payload_pointer != NULL) { ban_list[current_packet.dst_ip].pcap_attack_dump.write_packet(current_packet.packet_payload_pointer, current_packet.packet_payload_length); } } ban_list_details[current_packet.dst_ip].push_back(current_packet); ban_list_details_mutex.unlock(); } if (current_packet.protocol == IPPROTO_TCP) { #ifdef USE_NEW_ATOMIC_BUILTINS __atomic_add_fetch(¤t_element->tcp_in_packets, sampled_number_of_packets, __ATOMIC_RELAXED); __atomic_add_fetch(¤t_element->tcp_in_bytes, sampled_number_of_bytes, __ATOMIC_RELAXED); #else __sync_fetch_and_add(¤t_element->tcp_in_packets, sampled_number_of_packets); __sync_fetch_and_add(¤t_element->tcp_in_bytes, sampled_number_of_bytes); #endif if (extract_bit_value(current_packet.flags, TCP_SYN_FLAG_SHIFT)) { #ifdef USE_NEW_ATOMIC_BUILTINS __atomic_add_fetch(¤t_element->tcp_syn_in_packets, sampled_number_of_packets, __ATOMIC_RELAXED); __atomic_add_fetch(¤t_element->tcp_syn_in_bytes, sampled_number_of_bytes, __ATOMIC_RELAXED); #else __sync_fetch_and_add(¤t_element->tcp_syn_in_packets, sampled_number_of_packets); __sync_fetch_and_add(¤t_element->tcp_syn_in_bytes, sampled_number_of_bytes); #endif } if (enable_conection_tracking) { flow_counter.lock(); conntrack_key_struct* conntrack_key_struct_ptr = ¤t_element_flow->in_tcp[connection_tracking_hash]; conntrack_key_struct_ptr->packets += sampled_number_of_packets; conntrack_key_struct_ptr->bytes += sampled_number_of_bytes; flow_counter.unlock(); } } else if (current_packet.protocol == IPPROTO_UDP) { #ifdef USE_NEW_ATOMIC_BUILTINS __atomic_add_fetch(¤t_element->udp_in_packets, sampled_number_of_packets, __ATOMIC_RELAXED); __atomic_add_fetch(¤t_element->udp_in_bytes, sampled_number_of_bytes, __ATOMIC_RELAXED); #else __sync_fetch_and_add(¤t_element->udp_in_packets, sampled_number_of_packets); __sync_fetch_and_add(¤t_element->udp_in_bytes, sampled_number_of_bytes); #endif if (enable_conection_tracking) { flow_counter.lock(); conntrack_key_struct* conntrack_key_struct_ptr = ¤t_element_flow->in_udp[connection_tracking_hash]; conntrack_key_struct_ptr->packets += sampled_number_of_packets; conntrack_key_struct_ptr->bytes += sampled_number_of_bytes; flow_counter.unlock(); } } else if (current_packet.protocol == IPPROTO_ICMP) { #ifdef USE_NEW_ATOMIC_BUILTINS __atomic_add_fetch(¤t_element->icmp_in_packets, sampled_number_of_packets, __ATOMIC_RELAXED); __atomic_add_fetch(¤t_element->icmp_in_bytes, sampled_number_of_bytes, __ATOMIC_RELAXED); #else __sync_fetch_and_add(¤t_element->icmp_in_packets, sampled_number_of_packets); __sync_fetch_and_add(¤t_element->icmp_in_bytes, sampled_number_of_bytes); #endif // no flow tracking for icmp } else { // TBD } } else if (packet_direction == INTERNAL) { } } #ifdef GEOIP unsigned int get_asn_for_ip(uint32_t ip) { char* asn_raw = GeoIP_org_by_name(geo_ip, convert_ip_as_uint_to_string(remote_ip).c_str()); uint32_t asn_number = 0; if (asn_raw == NULL) { asn_number = 0; } else { // split string: AS1299 TeliaSonera International Carrier std::vector asn_as_string; split(asn_as_string, asn_raw, boost::is_any_of(" "), boost::token_compress_on); // free up original string free(asn_raw); // extract raw number asn_number = convert_string_to_integer(asn_as_string[0].substr(2)); } return asn_number; } #endif // It's vizualization thread :) void screen_draw_thread() { // we need wait one second for calculating speed by recalculate_speed //#include // prctl(PR_SET_NAME , "fastnetmon calc thread", 0, 0, 0); // Sleep for a half second for shift against calculatiuon thread boost::this_thread::sleep(boost::posix_time::milliseconds(500)); while (true) { // Available only from boost 1.54: boost::this_thread::sleep_for( // boost::chrono::seconds(check_period) ); boost::this_thread::sleep(boost::posix_time::seconds(check_period)); traffic_draw_program(); } } void recalculate_speed_thread_handler() { while (true) { // recalculate data every one second // Available only from boost 1.54: boost::this_thread::sleep_for( boost::chrono::seconds(1) // ); boost::this_thread::sleep(boost::posix_time::seconds(recalculate_speed_timeout)); recalculate_speed(); } } // Get ban settings for this subnet or return global ban settings ban_settings_t get_ban_settings_for_this_subnet(subnet_t subnet, std::string& host_group_name) { // Try to find host group for this subnet subnet_to_host_group_map_t::iterator host_group_itr = subnet_to_host_groups.find( subnet ); if (host_group_itr == subnet_to_host_groups.end()) { // We haven't host groups for all subnets, it's OK // logger << log4cpp::Priority::INFO << "We haven't custom host groups for this network. We will use global ban settings"; host_group_name = "global"; return global_ban_settings; } host_group_name = host_group_itr->second; // We found host group for this subnet host_group_ban_settings_map_t::iterator hostgroup_settings_itr = host_group_ban_settings_map.find(host_group_itr->second); if (hostgroup_settings_itr == host_group_ban_settings_map.end()) { logger << log4cpp::Priority::ERROR << "We can't find ban settings for host group " << host_group_itr->second; return global_ban_settings; } // We found ban settings for this host group and use they instead global return hostgroup_settings_itr->second; } /* Calculate speed for all connnections */ void recalculate_speed() { // logger<< log4cpp::Priority::INFO<<"We run recalculate_speed"; struct timeval start_calc_time; gettimeofday(&start_calc_time, NULL); double speed_calc_period = recalculate_speed_timeout; time_t start_time; time(&start_time); // If we got 1+ seconds lag we should use new "delta" or skip this step double time_difference = difftime(start_time, last_call_of_traffic_recalculation); if (time_difference < 1) { // It could occur on program start logger << log4cpp::Priority::INFO << "We skip one iteration of speed_calc because it runs so early!"; return; } else if (int(time_difference) == int(speed_calc_period)) { // All fine, we run on time } else { logger << log4cpp::Priority::INFO << "Time from last run of speed_recalc is soooo big, we got ugly lags: " << time_difference; speed_calc_period = time_difference; } map_element zero_map_element; memset(&zero_map_element, 0, sizeof(zero_map_element)); uint64_t incoming_total_flows = 0; uint64_t outgoing_total_flows = 0; if (enable_subnet_counters) { for (map_for_subnet_counters::iterator itr = PerSubnetSpeedMap.begin(); itr != PerSubnetSpeedMap.end(); ++itr) { subnet_t current_subnet = itr->first; map_for_subnet_counters::iterator iter_subnet = PerSubnetCountersMap.find(current_subnet); if (iter_subnet == PerSubnetCountersMap.end()) { logger << log4cpp::Priority::INFO<<"Can't find traffic counters for subnet"; break; } subnet_counter_t* subnet_traffic = &iter_subnet->second; subnet_counter_t new_speed_element; new_speed_element.in_packets = uint64_t((double)subnet_traffic->in_packets / speed_calc_period); new_speed_element.in_bytes = uint64_t((double)subnet_traffic->in_bytes / speed_calc_period); new_speed_element.out_packets = uint64_t((double)subnet_traffic->out_packets / speed_calc_period); new_speed_element.out_bytes = uint64_t((double)subnet_traffic->out_bytes / speed_calc_period); /* Moving average recalculation for subnets */ /* http://en.wikipedia.org/wiki/Moving_average#Application_to_measuring_computer_performance */ double exp_power_subnet = -speed_calc_period / average_calculation_amount_for_subnets; double exp_value_subnet = exp(exp_power_subnet); map_element* current_average_speed_element = &PerSubnetAverageSpeedMap[current_subnet]; current_average_speed_element->in_bytes = uint64_t(new_speed_element.in_bytes + exp_value_subnet * ((double)current_average_speed_element->in_bytes - (double)new_speed_element.in_bytes)); current_average_speed_element->out_bytes = uint64_t(new_speed_element.out_bytes + exp_value_subnet * ((double)current_average_speed_element->out_bytes - (double)new_speed_element.out_bytes)); current_average_speed_element->in_packets = uint64_t(new_speed_element.in_packets + exp_value_subnet * ((double)current_average_speed_element->in_packets - (double)new_speed_element.in_packets)); current_average_speed_element->out_packets = uint64_t(new_speed_element.out_packets + exp_value_subnet * ((double)current_average_speed_element->out_packets - (double)new_speed_element.out_packets)); // Update speed calculation structure PerSubnetSpeedMap[current_subnet] = new_speed_element; *subnet_traffic = zero_map_element; //logger << log4cpp::Priority::INFO<second.begin(); vector_itr != itr->second.end(); ++vector_itr) { int current_index = vector_itr - itr->second.begin(); // New element map_element new_speed_element; // convert to host order for math operations uint32_t subnet_ip = ntohl(itr->first.first); uint32_t client_ip_in_host_bytes_order = subnet_ip + current_index; // covnert to our standard network byte order uint32_t client_ip = htonl(client_ip_in_host_bytes_order); // Calculate speed for IP or whole subnet build_speed_counters_from_packet_counters(new_speed_element, & *vector_itr, speed_calc_period); conntrack_main_struct* flow_counter_ptr = &SubnetVectorMapFlow[itr->first][current_index]; if (enable_conection_tracking) { // todo: optimize this operations! // it's really bad and SLOW CODE uint64_t total_out_flows = (uint64_t)flow_counter_ptr->out_tcp.size() + (uint64_t)flow_counter_ptr->out_udp.size() + (uint64_t)flow_counter_ptr->out_icmp.size() + (uint64_t)flow_counter_ptr->out_other.size(); uint64_t total_in_flows = (uint64_t)flow_counter_ptr->in_tcp.size() + (uint64_t)flow_counter_ptr->in_udp.size() + (uint64_t)flow_counter_ptr->in_icmp.size() + (uint64_t)flow_counter_ptr->in_other.size(); new_speed_element.out_flows = uint64_t((double)total_out_flows / speed_calc_period); new_speed_element.in_flows = uint64_t((double)total_in_flows / speed_calc_period); // Increment global counter outgoing_total_flows += new_speed_element.out_flows; incoming_total_flows += new_speed_element.in_flows; } else { new_speed_element.out_flows = 0; new_speed_element.in_flows = 0; } /* Moving average recalculation */ // http://en.wikipedia.org/wiki/Moving_average#Application_to_measuring_computer_performance // double speed_calc_period = 1; double exp_power = -speed_calc_period / average_calculation_amount; double exp_value = exp(exp_power); map_element* current_average_speed_element = &SubnetVectorMapSpeedAverage[itr->first][current_index]; // Calculate average speed from per-second speed build_average_speed_counters_from_speed_counters(current_average_speed_element, new_speed_element, exp_value, exp_power); if (enable_conection_tracking) { current_average_speed_element->out_flows = uint64_t( new_speed_element.out_flows + exp_value * ((double)current_average_speed_element->out_flows - (double)new_speed_element.out_flows)); current_average_speed_element->in_flows = uint64_t( new_speed_element.in_flows + exp_value * ((double)current_average_speed_element->in_flows - (double)new_speed_element.in_flows)); } /* Moving average recalculation end */ std::string host_group_name; ban_settings_t current_ban_settings = get_ban_settings_for_this_subnet(itr->first, host_group_name); if (we_should_ban_this_ip(current_average_speed_element, current_ban_settings)) { logger << log4cpp::Priority::DEBUG << "We have found host group for this host as: " << host_group_name; std::string flow_attack_details = ""; if (enable_conection_tracking) { flow_attack_details = print_flow_tracking_for_ip(*flow_counter_ptr, convert_ip_as_uint_to_string(client_ip)); } // TODO: we should pass type of ddos ban source (pps, flowd, bandwidth)! execute_ip_ban(client_ip, *current_average_speed_element, flow_attack_details, itr->first); } SubnetVectorMapSpeed[itr->first][current_index] = new_speed_element; *vector_itr = zero_map_element; } } // Calculate global flow speed incoming_total_flows_speed = uint64_t((double)incoming_total_flows / (double)speed_calc_period); outgoing_total_flows_speed = uint64_t((double)outgoing_total_flows / (double)speed_calc_period); if (enable_conection_tracking) { // Clean Flow Counter flow_counter.lock(); zeroify_all_flow_counters(); flow_counter.unlock(); } total_unparsed_packets_speed = uint64_t((double)total_unparsed_packets / (double)speed_calc_period); total_unparsed_packets = 0; for (unsigned int index = 0; index < 4; index++) { total_speed_counters[index].bytes = uint64_t((double)total_counters[index].bytes / (double)speed_calc_period); total_speed_counters[index].packets = uint64_t((double)total_counters[index].packets / (double)speed_calc_period); double exp_power = -speed_calc_period / average_calculation_amount; double exp_value = exp(exp_power); total_speed_average_counters[index].bytes = uint64_t(total_speed_counters[index].bytes + exp_value * ((double) total_speed_average_counters[index].bytes - (double) total_speed_counters[index].bytes)); total_speed_average_counters[index].packets = uint64_t(total_speed_counters[index].packets + exp_value * ((double) total_speed_average_counters[index].packets - (double) total_speed_counters[index].packets)); // nullify data counters after speed calculation total_counters[index].bytes = 0; total_counters[index].packets = 0; } // Set time of previous startup time(&last_call_of_traffic_recalculation); struct timeval finish_calc_time; gettimeofday(&finish_calc_time, NULL); timeval_subtract(&speed_calculation_time, &finish_calc_time, &start_calc_time); } void print_screen_contents_into_file(std::string screen_data_stats_param) { std::ofstream screen_data_file; screen_data_file.open(cli_stats_file_path.c_str(), std::ios::trunc); if (screen_data_file.is_open()) { // Set 660 permissions to file for security reasons chmod(cli_stats_file_path.c_str(), S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH); screen_data_file << screen_data_stats_param; screen_data_file.close(); } else { logger << log4cpp::Priority::ERROR << "Can't print program screen into file"; } } void traffic_draw_program() { std::stringstream output_buffer; // logger< 0) { output_buffer << "ALERT! Toolkit working incorrectly! We should calculate speed in ~1 second\n"; } #ifdef IPV6_HASH_COUNTERS output_buffer << "Total amount of IPv6 packets: " << total_ipv6_packets << "\n"; #endif output_buffer << "Total amount of IPv6 packets related to our own network: " << our_ipv6_packets << "\n"; output_buffer << "Not processed packets: " << total_unparsed_packets_speed << " pps\n"; // Print backend stats if (enable_pcap_collection) { output_buffer << get_pcap_stats() << "\n"; } #ifdef PF_RING if (enable_data_collection_from_mirror) { output_buffer << get_pf_ring_stats(); } #endif // Print thresholds if (print_configuration_params_on_the_screen) { output_buffer << "\n" << print_ban_thresholds(global_ban_settings); } if (!ban_list.empty()) { output_buffer << std::endl << "Ban list:" << std::endl; output_buffer << print_ddos_attack_details(); } if (enable_subnet_counters) { output_buffer << std::endl << "Subnet load:" << std::endl; output_buffer << print_subnet_load() << "\n"; } screen_data_stats = output_buffer.str(); // Print screen contents into file print_screen_contents_into_file(screen_data_stats); struct timeval end_calc_time; gettimeofday(&end_calc_time, NULL); timeval_subtract(&drawing_thread_execution_time, &end_calc_time, &start_calc_time); } // pretty print channel speed in pps and MBit std::string print_channel_speed(std::string traffic_type, direction packet_direction) { uint64_t speed_in_pps = total_speed_average_counters[packet_direction].packets; uint64_t speed_in_bps = total_speed_average_counters[packet_direction].bytes; unsigned int number_of_tabs = 1; // We need this for correct alignment of blocks if (traffic_type == "Other traffic") { number_of_tabs = 2; } std::stringstream stream; stream << traffic_type; for (unsigned int i = 0; i < number_of_tabs; i++) { stream << "\t"; } uint64_t speed_in_mbps = convert_speed_to_mbps(speed_in_bps); stream << std::setw(6) << speed_in_pps << " pps " << std::setw(6) << speed_in_mbps << " mbps"; if (traffic_type == "Incoming traffic" or traffic_type == "Outgoing traffic") { if (packet_direction == INCOMING) { stream << " " << std::setw(6) << incoming_total_flows_speed << " flows"; } else if (packet_direction == OUTGOING) { stream << " " << std::setw(6) << outgoing_total_flows_speed << " flows"; } if (graphite_enabled) { graphite_data_t graphite_data; std::string direction_as_string; if (packet_direction == INCOMING) { direction_as_string = "incoming"; graphite_data[graphite_prefix + ".total." + direction_as_string + ".flows"] = incoming_total_flows_speed; } else if (packet_direction == OUTGOING) { direction_as_string = "outgoing"; graphite_data[graphite_prefix + ".total." + direction_as_string + ".flows"] = outgoing_total_flows_speed; } graphite_data[graphite_prefix + ".total." + direction_as_string + ".pps"] = speed_in_pps; graphite_data[graphite_prefix + ".total." + direction_as_string + ".bps"] = speed_in_bps * 8; bool graphite_put_result = store_data_to_graphite(graphite_port, graphite_host, graphite_data); if (!graphite_put_result) { logger << log4cpp::Priority::ERROR << "Can't store data to Graphite"; } } } return stream.str(); } bool file_is_appendable(std::string path) { std::ofstream check_appendable_file; check_appendable_file.open(path.c_str(), std::ios::app); if (check_appendable_file.is_open()) { // all fine, just close file check_appendable_file.close(); return true; } else { return false; } } void init_logging() { // So log4cpp will never notify you if it could not write to log file due to permissions issues // We will check it manually if (!file_is_appendable(log_file_path)) { std::cerr << "Can't open log file " << log_file_path << " for writing! Please check file and folder permissions" << std::endl; exit(EXIT_FAILURE); } log4cpp::PatternLayout* layout = new log4cpp::PatternLayout(); layout->setConversionPattern("%d [%p] %m%n"); log4cpp::Appender* appender = new log4cpp::FileAppender("default", log_file_path); appender->setLayout(layout); logger.setPriority(log4cpp::Priority::INFO); logger.addAppender(appender); logger << log4cpp::Priority::INFO << "Logger initialized!"; } void reconfigure_logging() { log4cpp::PatternLayout* layout = new log4cpp::PatternLayout(); layout->setConversionPattern("[%p] %m%n"); if (logging_configuration.local_syslog_logging) { log4cpp::Appender* local_syslog_appender = new log4cpp::SyslogAppender("fastnetmon", "fastnetmon", LOG_USER); local_syslog_appender->setLayout(layout); logger.addAppender(local_syslog_appender); logger << log4cpp::Priority::INFO << "We start local syslog logging corectly"; } if (logging_configuration.remote_syslog_logging) { log4cpp::Appender* remote_syslog_appender = new log4cpp::RemoteSyslogAppender( "fastnetmon", "fastnetmon", logging_configuration.remote_syslog_server, LOG_USER, logging_configuration.remote_syslog_port); remote_syslog_appender->setLayout(layout); logger.addAppender(remote_syslog_appender); logger << log4cpp::Priority::INFO << "We start remote syslog logging corectly"; } } // Call fork function int do_fork() { int status = 0; switch (fork()) { case 0: // It's child break; case -1: /* fork failed */ status = -1; break; default: // We should close master process with _exit(0) // We should not call exit() because it will destroy all global variables for program _exit(0); } return status; } void redirect_fds() { // Close stdin, stdout and stderr close(0); close(1); close(2); if (open("/dev/null", O_RDWR) != 0) { // We can't notify anybody now exit(1); } // Create copy of zero decriptor for 1 and 2 fd's // We do not need return codes here but we need do it for suppressing complaints from compiler int first_dup_result = dup(0); int second_dup_result = dup(0); } int main(int argc, char** argv) { bool daemonize = false; namespace po = boost::program_options; try { po::options_description desc("Allowed options"); desc.add_options() ("help", "produce help message") ("version", "show version") ("daemonize", "detach from the terminal") ("configuration_file", po::value(), "set path to custom configuration file") ("log_file", po::value(), "set path to custom log file") ; po::variables_map vm; po::store(po::parse_command_line(argc, argv, desc), vm); po::notify(vm); if (vm.count("help")) { std::cout << desc << std::endl; exit(EXIT_SUCCESS); } if (vm.count("version")) { std::cout << "Version: " << fastnetmon_version << std::endl; exit(EXIT_SUCCESS); } if (vm.count("daemonize")) { daemonize = true; } if (vm.count("configuration_file")) { global_config_path = vm["configuration_file"].as(); std::cout << "We will use custom path to configuration file: " << global_config_path << std::endl; } if (vm.count("log_file")) { log_file_path = vm["log_file"].as(); std::cout << "We will use custom path to log file: " << log_file_path << std::endl; } } catch (po::error& e) { std::cerr << "ERROR: " << e.what() << std::endl << std::endl; exit(EXIT_FAILURE); } // We use ideas from here https://github.com/bmc/daemonize/blob/master/daemon.c if (daemonize) { int status = 0; printf("We will run in daemonized mode\n"); if ((status = do_fork()) < 0) { // fork failed status = -1; } else if (setsid() < 0) { // Create new session status = -1; } else if ((status = do_fork()) < 0) { status = -1; } else { // Clear inherited umask umask(0); // Chdir to root int chdir_result = chdir("/"); // close all descriptors because we are daemon! redirect_fds(); } } // enable core dumps enable_core_dumps(); init_logging(); #ifdef FASTNETMON_API gpr_set_log_function(silent_logging_function); #endif // Set default ban configuration init_global_ban_settings(); // We should read configurartion file _after_ logging initialization bool load_config_result = load_configuration_file(); if (!load_config_result) { std::cerr << "Can't open config file " << global_config_path << " please create it!" << std::endl; exit(1); } if (file_exists(pid_path)) { pid_t pid_from_file = 0; if (read_pid_from_file(pid_from_file, pid_path)) { // We could read pid if (pid_from_file > 0) { // We use signal zero for check process existence int kill_result = kill(pid_from_file, 0); if (kill_result == 0) { logger << log4cpp::Priority::ERROR << "FastNetMon is already running with pid: " << pid_from_file; exit(1); } else { // Yes, we have pid with pid but it's zero } } else { // pid from file is broken, we assume tool is not running } } else { // We can't open file, let's assume it's broken and tool is not running } } else { // no pid file } // If we not failed in check steps we could run toolkit bool print_pid_to_file_result = print_pid_to_file(getpid(), pid_path); if (!print_pid_to_file_result) { logger << log4cpp::Priority::ERROR << "Could not create pid file, please check permissions: " << pid_path; exit(EXIT_FAILURE); } #ifdef ENABLE_DPI init_current_instance_of_ndpi(); #endif lookup_tree_ipv4 = New_Patricia(32); whitelist_tree_ipv4 = New_Patricia(32); lookup_tree_ipv6 = New_Patricia(128); whitelist_tree_ipv6 = New_Patricia(128); // nullify total counters for (int index = 0; index < 4; index++) { total_counters[index].bytes = 0; total_counters[index].packets = 0; total_speed_counters[index].bytes = 0; total_speed_counters[index].packets = 0; total_speed_average_counters[index].bytes = 0; total_speed_average_counters[index].packets = 0; } /* Create folder for attack details */ if (!folder_exists(attack_details_folder)) { int mkdir_result = mkdir(attack_details_folder.c_str(), S_IRWXU); if (mkdir_result != 0) { logger << log4cpp::Priority::ERROR << "Can't create folder for attack details: " << attack_details_folder; exit(1); } } if (getenv("DUMP_ALL_PACKETS") != NULL) { DEBUG_DUMP_ALL_PACKETS = true; } if (getenv("DUMP_OTHER_PACKETS") != NULL) { DEBUG_DUMP_OTHER_PACKETS = true; } if (sizeof(packed_conntrack_hash) != sizeof(uint64_t) or sizeof(packed_conntrack_hash) != 8) { logger << log4cpp::Priority::INFO << "Assertion about size of packed_conntrack_hash, it's " << sizeof(packed_conntrack_hash) << " instead 8"; exit(1); } logger << log4cpp::Priority::INFO << "Read configuration file"; // Reconfigure logging. We will enable specific logging methods here reconfigure_logging(); load_our_networks_list(); // Setup CTRL+C handler if (signal(SIGINT, interruption_signal_handler) == SIG_ERR) { logger << log4cpp::Priority::ERROR << "Can't setup SIGINT handler"; exit(1); } /* Without this SIGPIPE error could shutdown toolkit on call of exec_with_stdin_params */ if (signal(SIGPIPE, sigpipe_handler_for_popen) == SIG_ERR) { logger << log4cpp::Priority::ERROR << "Can't setup SIGPIPE handler"; exit(1); } #ifdef GEOIP // Init GeoIP if (!geoip_init()) { logger << log4cpp::Priority::ERROR << "Can't load geoip tables"; exit(1); } #endif // Init previous run date time(&last_call_of_traffic_recalculation); // We call init for each action #ifdef ENABLE_GOBGP if (gobgp_enabled) { gobgp_action_init(); } #endif #ifdef IPV6_HASH_COUNTERS service_thread_group.add_thread(new boost::thread(ipv6_traffic_processor)); #endif #ifdef FASTNETMON_API if (enable_api) { service_thread_group.add_thread(new boost::thread(RunApiServer)); } #endif // Run screen draw thread service_thread_group.add_thread(new boost::thread(screen_draw_thread)); // start thread for recalculating speed in realtime service_thread_group.add_thread(new boost::thread(recalculate_speed_thread_handler)); // Run banlist cleaner thread if (unban_enabled) { service_thread_group.add_thread(new boost::thread(cleanup_ban_list)); } #ifdef PF_RING if (enable_data_collection_from_mirror) { packet_capture_plugin_thread_group.add_thread(new boost::thread(start_pfring_collection, process_packet)); } #endif #ifdef NETMAP_PLUGIN // netmap processing if (enable_netmap_collection) { packet_capture_plugin_thread_group.add_thread(new boost::thread(start_netmap_collection, process_packet)); } #endif #ifdef SNABB_SWITCH if (enable_snabbswitch_collection) { packet_capture_plugin_thread_group.add_thread(new boost::thread(start_snabbswitch_collection, process_packet)); } #endif #ifdef FASTNETMON_ENABLE_AFPACKET if (enable_afpacket_collection) { packet_capture_plugin_thread_group.add_thread(new boost::thread(start_afpacket_collection, process_packet)); } #endif if (enable_sflow_collection) { packet_capture_plugin_thread_group.add_thread(new boost::thread(start_sflow_collection, process_packet)); } if (enable_netflow_collection) { packet_capture_plugin_thread_group.add_thread(new boost::thread(start_netflow_collection, process_packet)); } if (enable_pcap_collection) { packet_capture_plugin_thread_group.add_thread(new boost::thread(start_pcap_collection, process_packet)); } // Wait for all threads in capture thread group packet_capture_plugin_thread_group.join_all(); // Wait for all service threads service_thread_group.join_all(); free_up_all_resources(); return 0; } void free_up_all_resources() { #ifdef GEOIP // Free up geoip handle GeoIP_delete(geo_ip); #endif Destroy_Patricia(lookup_tree_ipv4, (void_fn_t)0); Destroy_Patricia(whitelist_tree_ipv4, (void_fn_t)0); Destroy_Patricia(lookup_tree_ipv6, (void_fn_t)0); Destroy_Patricia(whitelist_tree_ipv6, (void_fn_t)0); } // For correct program shutdown by CTRL+C void interruption_signal_handler(int signal_number) { logger << log4cpp::Priority::INFO << "SIGNAL captured, prepare toolkit shutdown"; #ifdef FASTNETMON_API logger << log4cpp::Priority::INFO << "Send shutdown command to API server"; api_server->Shutdown(); #endif logger << log4cpp::Priority::INFO << "Interrupt service threads"; service_thread_group.interrupt_all(); logger << log4cpp::Priority::INFO << "Wait while they finished"; service_thread_group.join_all(); logger << log4cpp::Priority::INFO << "Interrupt packet capture treads"; packet_capture_plugin_thread_group.interrupt_all(); logger << log4cpp::Priority::INFO << "Wait while they finished"; packet_capture_plugin_thread_group.join_all(); logger << log4cpp::Priority::INFO << "Shutdown main process"; // TODO: we should REMOVE this exit command and wait for correct toolkit shutdown exit(1); } unsigned int detect_attack_protocol(map_element& speed_element, direction attack_direction) { if (attack_direction == INCOMING) { return get_max_used_protocol(speed_element.tcp_in_packets, speed_element.udp_in_packets, speed_element.icmp_in_packets); } else { // OUTGOING return get_max_used_protocol(speed_element.tcp_out_packets, speed_element.udp_out_packets, speed_element.icmp_out_packets); } } #define my_max_on_defines(a, b) (a > b ? a : b) unsigned int get_max_used_protocol(uint64_t tcp, uint64_t udp, uint64_t icmp) { unsigned int max = my_max_on_defines(my_max_on_defines(udp, tcp), icmp); if (max == tcp) { return IPPROTO_TCP; } else if (max == udp) { return IPPROTO_UDP; } else if (max == icmp) { return IPPROTO_ICMP; } return 0; } void exabgp_ban_manage(std::string action, std::string ip_as_string, attack_details current_attack) { // We will announce whole subent here if (exabgp_announce_whole_subnet) { std::string subnet_as_string_with_mask = convert_subnet_to_string(current_attack.customer_network); exabgp_prefix_ban_manage(action, subnet_as_string_with_mask, exabgp_next_hop, exabgp_community_subnet); } // And we could announce single host here (/32) if (exabgp_announce_host) { std::string ip_as_string_with_mask = ip_as_string + "/32"; exabgp_prefix_ban_manage(action, ip_as_string_with_mask, exabgp_next_hop, exabgp_community_host); } } // Low level ExaBGP ban management void exabgp_prefix_ban_manage(std::string action, std::string prefix_as_string_with_mask, std::string exabgp_next_hop, std::string exabgp_community) { /* Buffer for BGP message */ char bgp_message[256]; if (action == "ban") { sprintf(bgp_message, "announce route %s next-hop %s community %s\n", prefix_as_string_with_mask.c_str(), exabgp_next_hop.c_str(), exabgp_community.c_str()); } else { sprintf(bgp_message, "withdraw route %s next-hop %s\n", prefix_as_string_with_mask.c_str(), exabgp_next_hop.c_str()); } logger << log4cpp::Priority::INFO << "ExaBGP announce message: " << bgp_message; int exabgp_pipe = open(exabgp_command_pipe.c_str(), O_WRONLY); if (exabgp_pipe <= 0) { logger << log4cpp::Priority::ERROR << "Can't open ExaBGP pipe " << exabgp_command_pipe << " Ban is not executed"; return; } int wrote_bytes = write(exabgp_pipe, bgp_message, strlen(bgp_message)); if (wrote_bytes != strlen(bgp_message)) { logger << log4cpp::Priority::ERROR << "Can't write message to ExaBGP pipe"; } close(exabgp_pipe); } bool exabgp_flow_spec_ban_manage(std::string action, std::string flow_spec_rule_as_text) { std::string announce_action; if (action == "ban") { announce_action = "announce"; } else { announce_action = "withdraw"; } // Trailing \n is very important! std::string bgp_message = announce_action + " " + flow_spec_rule_as_text + "\n"; int exabgp_pipe = open(exabgp_command_pipe.c_str(), O_WRONLY); if (exabgp_pipe <= 0) { logger << log4cpp::Priority::ERROR << "Can't open ExaBGP pipe for flow spec announce " << exabgp_command_pipe; return false; } int wrote_bytes = write(exabgp_pipe, bgp_message.c_str(), bgp_message.size()); if (wrote_bytes != bgp_message.size()) { logger << log4cpp::Priority::ERROR << "Can't write message to ExaBGP pipe"; return false; } close(exabgp_pipe); return true; } void execute_ip_ban(uint32_t client_ip, map_element average_speed_element, std::string flow_attack_details, subnet_t customer_subnet) { struct attack_details current_attack; uint64_t pps = 0; uint64_t in_pps = average_speed_element.in_packets; uint64_t out_pps = average_speed_element.out_packets; uint64_t in_bps = average_speed_element.in_bytes; uint64_t out_bps = average_speed_element.out_bytes; uint64_t in_flows = average_speed_element.in_flows; uint64_t out_flows = average_speed_element.out_flows; direction data_direction; if (!global_ban_settings.enable_ban) { logger << log4cpp::Priority::INFO << "We do not ban: " << convert_ip_as_uint_to_string(client_ip) << " because ban disabled completely"; return; } // Detect attack direction with simple heuristic if (abs(int((int)in_pps - (int)out_pps)) < 1000) { // If difference between pps speed is so small we should do additional investigation using // bandwidth speed if (in_bps > out_bps) { data_direction = INCOMING; pps = in_pps; } else { data_direction = OUTGOING; pps = out_pps; } } else { if (in_pps > out_pps) { data_direction = INCOMING; pps = in_pps; } else { data_direction = OUTGOING; pps = out_pps; } } current_attack.attack_protocol = detect_attack_protocol(average_speed_element, data_direction); if (ban_list.count(client_ip) > 0) { if (ban_list[client_ip].attack_direction != data_direction) { logger << log4cpp::Priority::INFO << "We expected very strange situation: attack direction for " << convert_ip_as_uint_to_string(client_ip) << " was changed"; return; } // update attack power if (pps > ban_list[client_ip].max_attack_power) { ban_list[client_ip].max_attack_power = pps; } return; } prefix_t prefix_for_check_adreess; prefix_for_check_adreess.add.sin.s_addr = client_ip; prefix_for_check_adreess.family = AF_INET; prefix_for_check_adreess.bitlen = 32; bool in_white_list = (patricia_search_best2(whitelist_tree_ipv4, &prefix_for_check_adreess, 1) != NULL); if (in_white_list) { return; } std::string data_direction_as_string = get_direction_name(data_direction); logger << log4cpp::Priority::INFO << "We run execute_ip_ban code with following params " << " in_pps: " << in_pps << " out_pps: " << out_pps << " in_bps: " << in_bps << " out_bps: " << out_bps << " and we decide it's " << data_direction_as_string << " attack"; std::string client_ip_as_string = convert_ip_as_uint_to_string(client_ip); std::string pps_as_string = convert_int_to_string(pps); // Store information about subnet current_attack.customer_network = customer_subnet; // Store ban time time(¤t_attack.ban_timestamp); // set ban time in seconds current_attack.ban_time = global_ban_time; current_attack.unban_enabled = unban_enabled; // Pass main information about attack current_attack.attack_direction = data_direction; current_attack.attack_power = pps; current_attack.max_attack_power = pps; current_attack.in_packets = in_pps; current_attack.out_packets = out_pps; current_attack.in_bytes = in_bps; current_attack.out_bytes = out_bps; // pass flow information current_attack.in_flows = in_flows; current_attack.out_flows = out_flows; current_attack.fragmented_in_packets = average_speed_element.fragmented_in_packets; current_attack.tcp_in_packets = average_speed_element.tcp_in_packets; current_attack.tcp_syn_in_packets = average_speed_element.tcp_syn_in_packets; current_attack.udp_in_packets = average_speed_element.udp_in_packets; current_attack.icmp_in_packets = average_speed_element.icmp_in_packets; current_attack.fragmented_out_packets = average_speed_element.fragmented_out_packets; current_attack.tcp_out_packets = average_speed_element.tcp_out_packets; current_attack.tcp_syn_out_packets = average_speed_element.tcp_syn_out_packets; current_attack.udp_out_packets = average_speed_element.udp_out_packets; current_attack.icmp_out_packets = average_speed_element.icmp_out_packets; current_attack.fragmented_out_bytes = average_speed_element.fragmented_out_bytes; current_attack.tcp_out_bytes = average_speed_element.tcp_out_bytes; current_attack.tcp_syn_out_bytes = average_speed_element.tcp_syn_out_bytes; current_attack.udp_out_bytes = average_speed_element.udp_out_bytes; current_attack.icmp_out_bytes = average_speed_element.icmp_out_bytes; current_attack.fragmented_in_bytes = average_speed_element.fragmented_in_bytes; current_attack.tcp_in_bytes = average_speed_element.tcp_in_bytes; current_attack.tcp_syn_in_bytes = average_speed_element.tcp_syn_in_bytes; current_attack.udp_in_bytes = average_speed_element.udp_in_bytes; current_attack.icmp_in_bytes = average_speed_element.icmp_in_bytes; current_attack.average_in_packets = average_speed_element.in_packets; current_attack.average_in_bytes = average_speed_element.in_bytes; current_attack.average_in_flows = average_speed_element.in_flows; current_attack.average_out_packets = average_speed_element.out_packets; current_attack.average_out_bytes = average_speed_element.out_bytes; current_attack.average_out_flows = average_speed_element.out_flows; if (collect_attack_pcap_dumps) { bool buffer_allocation_result = current_attack.pcap_attack_dump.allocate_buffer( number_of_packets_for_pcap_attack_dump ); if (!buffer_allocation_result) { logger << log4cpp::Priority::ERROR << "Can't allocate buffer for attack, switch off this option completely "; collect_attack_pcap_dumps = false; } } ban_list_mutex.lock(); ban_list[client_ip] = current_attack; ban_list_mutex.unlock(); ban_list_details_mutex.lock(); ban_list_details[client_ip] = std::vector(); ban_list_details_mutex.unlock(); logger << log4cpp::Priority::INFO << "Attack with direction: " << data_direction_as_string << " IP: " << client_ip_as_string << " Power: " << pps_as_string; call_ban_handlers(client_ip, ban_list[client_ip], flow_attack_details); } void call_ban_handlers(uint32_t client_ip, attack_details& current_attack, std::string flow_attack_details) { std::string client_ip_as_string = convert_ip_as_uint_to_string(client_ip); std::string pps_as_string = convert_int_to_string(current_attack.attack_power); std::string data_direction_as_string = get_direction_name(current_attack.attack_direction); bool store_attack_details_to_file = true; std::string basic_attack_information = get_attack_description(client_ip, current_attack); std::string basic_attack_information_in_json = get_attack_description_in_json(client_ip, current_attack); std::string full_attack_description = basic_attack_information + flow_attack_details; if (store_attack_details_to_file) { print_attack_details_to_file(full_attack_description, client_ip_as_string, current_attack); } if (pfring_hardware_filters_enabled) { #ifdef PF_RING logger << log4cpp::Priority::INFO << "We will block traffic to/from this IP with hardware filters"; pfring_hardware_filter_action_block(client_ip_as_string); #else logger << log4cpp::Priority::ERROR << "You haven't compiled PF_RING hardware filters support"; #endif } if (notify_script_enabled) { std::string script_call_params = notify_script_path + " " + client_ip_as_string + " " + data_direction_as_string + " " + pps_as_string + " " + "ban"; logger << log4cpp::Priority::INFO << "Call script for ban client: " << client_ip_as_string; // We should execute external script in separate thread because any lag in this code will be // very distructive if (notify_script_pass_details) { // We will pass attack details over stdin boost::thread exec_thread(exec_with_stdin_params, script_call_params, full_attack_description); exec_thread.detach(); } else { // Do not pass anything to script boost::thread exec_thread(exec, script_call_params); exec_thread.detach(); } logger << log4cpp::Priority::INFO << "Script for ban client is finished: " << client_ip_as_string; } if (exabgp_enabled) { logger << log4cpp::Priority::INFO << "Call ExaBGP for ban client started: " << client_ip_as_string; boost::thread exabgp_thread(exabgp_ban_manage, "ban", client_ip_as_string, current_attack); exabgp_thread.detach(); logger << log4cpp::Priority::INFO << "Call to ExaBGP for ban client is finished: " << client_ip_as_string; } #ifdef ENABLE_GOBGP if (gobgp_enabled) { logger << log4cpp::Priority::INFO << "Call GoBGP for ban client started: " << client_ip_as_string; boost::thread gobgp_thread(gobgp_ban_manage, "ban", client_ip_as_string, current_attack); gobgp_thread.detach(); logger << log4cpp::Priority::INFO << "Call to GoBGP for ban client is finished: " << client_ip_as_string; } #endif #ifdef REDIS if (redis_enabled) { std::string redis_key_name = client_ip_as_string + "_information"; if (!redis_prefix.empty()) { redis_key_name = redis_prefix + "_" + client_ip_as_string + "_information"; } logger << log4cpp::Priority::INFO << "Start data save in Redis in key: " << redis_key_name; boost::thread redis_store_thread(store_data_in_redis, redis_key_name, basic_attack_information_in_json); redis_store_thread.detach(); logger << log4cpp::Priority::INFO << "Finish data save in Redis in key: " << redis_key_name; // If we have flow dump put in redis too if (!flow_attack_details.empty()) { std::string redis_key_name = client_ip_as_string + "_flow_dump"; if (!redis_prefix.empty()) { redis_key_name = redis_prefix + "_" + client_ip_as_string + "_flow_dump"; } logger << log4cpp::Priority::INFO << "Start data save in redis in key: " << redis_key_name; boost::thread redis_store_thread(store_data_in_redis, redis_key_name, flow_attack_details); redis_store_thread.detach(); logger << log4cpp::Priority::INFO << "Finish data save in redis in key: " << redis_key_name; } } #endif #ifdef MONGO if (mongodb_enabled) { std::string mongo_key_name = client_ip_as_string + "_information_" + print_time_t_in_fastnetmon_format(current_attack.ban_timestamp); // We could not use dot in key names: http://docs.mongodb.org/manual/core/document/#dot-notation std::replace(mongo_key_name.begin(), mongo_key_name.end(), '.', '_'); logger << log4cpp::Priority::INFO << "Start data save in Mongo in key: " << mongo_key_name; boost::thread mongo_store_thread(store_data_in_mongo, mongo_key_name, basic_attack_information_in_json); mongo_store_thread.detach(); logger << log4cpp::Priority::INFO << "Finish data save in Mongo in key: " << mongo_key_name; } #endif } /* Thread for cleaning up ban list */ void cleanup_ban_list() { // If we use very small ban time we should call ban_cleanup thread more often if (unban_iteration_sleep_time > global_ban_time) { unban_iteration_sleep_time = int(global_ban_time / 2); logger << log4cpp::Priority::INFO << "You are using enough small ban time " << global_ban_time << " we need reduce unban_iteration_sleep_time twices to " << unban_iteration_sleep_time << " seconds"; } logger << log4cpp::Priority::INFO << "Run banlist cleanup thread, we will awake every " << unban_iteration_sleep_time << " seconds"; while (true) { boost::this_thread::sleep(boost::posix_time::seconds(unban_iteration_sleep_time)); time_t current_time; time(¤t_time); std::vector ban_list_items_for_erase; for (std::map::iterator itr = ban_list.begin(); itr != ban_list.end(); ++itr) { uint32_t client_ip = itr->first; // This IP should be banned permanentely and we skip any processing if (!itr->second.unban_enabled) { continue; } double time_difference = difftime(current_time, itr->second.ban_timestamp); int ban_time = itr->second.ban_time; // Yes, we reached end of ban time for this customer bool we_could_unban_this_ip = time_difference > ban_time; // We haven't reached time for unban yet if (!we_could_unban_this_ip) { continue; } // Check about ongoing attack if (unban_only_if_attack_finished) { std::string client_ip_as_string = convert_ip_as_uint_to_string(client_ip); uint32_t subnet_in_host_byte_order = ntohl(itr->second.customer_network.first); int64_t shift_in_vector = (int64_t)ntohl(client_ip) - (int64_t)subnet_in_host_byte_order; // Try to find average speed element map_of_vector_counters::iterator itr_average_speed = SubnetVectorMapSpeedAverage.find(itr->second.customer_network); if (itr_average_speed == SubnetVectorMapSpeedAverage.end()) { logger << log4cpp::Priority::ERROR << "Can't find vector address in subnet map for unban function"; continue; } if (shift_in_vector < 0 or shift_in_vector >= itr_average_speed->second.size()) { logger << log4cpp::Priority::ERROR << "We tried to access to element with index " << shift_in_vector << " which located outside allocated vector with size " << itr_average_speed->second.size(); continue; } map_element* average_speed_element = &itr_average_speed->second[shift_in_vector]; // We get ban settings from host subnet std::string host_group_name; ban_settings_t current_ban_settings = get_ban_settings_for_this_subnet(itr->second.customer_network, host_group_name); if (we_should_ban_this_ip(average_speed_element, current_ban_settings)) { logger << log4cpp::Priority::ERROR << "Attack to IP " << client_ip_as_string << " still going! We should not unblock this host"; // Well, we still saw attack, skip to next iteration continue; } } // Add this IP to remove list // We will remove keyas really after this loop ban_list_items_for_erase.push_back(itr->first); // Call all hooks for unban call_unban_handlers(itr->first, itr->second); } // Remove all unbanned hosts from the ban list for (std::vector::iterator itr = ban_list_items_for_erase.begin(); itr != ban_list_items_for_erase.end(); ++itr) { ban_list_mutex.lock(); ban_list.erase(*itr); ban_list_mutex.unlock(); } } } void call_unban_handlers(uint32_t client_ip, attack_details& current_attack) { std::string client_ip_as_string = convert_ip_as_uint_to_string(client_ip); logger << log4cpp::Priority::INFO << "We will unban banned IP: " << client_ip_as_string << " because it ban time " << current_attack.ban_time << " seconds is ended"; if (notify_script_enabled) { std::string data_direction_as_string = get_direction_name(current_attack.attack_direction); std::string pps_as_string = convert_int_to_string(current_attack.attack_power); std::string script_call_params = notify_script_path + " " + client_ip_as_string + " " + data_direction_as_string + " " + pps_as_string + " unban"; logger << log4cpp::Priority::INFO << "Call script for unban client: " << client_ip_as_string; // We should execute external script in separate thread because any lag in this // code will be very distructive boost::thread exec_thread(exec, script_call_params); exec_thread.detach(); logger << log4cpp::Priority::INFO << "Script for unban client is finished: " << client_ip_as_string; } if (exabgp_enabled) { logger << log4cpp::Priority::INFO << "Call ExaBGP for unban client started: " << client_ip_as_string; boost::thread exabgp_thread(exabgp_ban_manage, "unban", client_ip_as_string, current_attack); exabgp_thread.detach(); logger << log4cpp::Priority::INFO << "Call to ExaBGP for unban client is finished: " << client_ip_as_string; } #ifdef ENABLE_GOBGP if (gobgp_enabled) { logger << log4cpp::Priority::INFO << "Call GoBGP for unban client started: " << client_ip_as_string; boost::thread gobgp_thread(gobgp_ban_manage, "unban", client_ip_as_string, current_attack); gobgp_thread.detach(); logger << log4cpp::Priority::INFO << "Call to GoBGP for unban client is finished: " << client_ip_as_string; } #endif } std::string print_ddos_attack_details() { std::stringstream output_buffer; for (std::map::iterator ii = ban_list.begin(); ii != ban_list.end(); ++ii) { uint32_t client_ip = (*ii).first; std::string client_ip_as_string = convert_ip_as_uint_to_string(client_ip); std::string max_pps_as_string = convert_int_to_string(((*ii).second).max_attack_power); std::string attack_direction = get_direction_name(((*ii).second).attack_direction); output_buffer << client_ip_as_string << "/" << max_pps_as_string << " pps " << attack_direction << " at " << print_time_t_in_fastnetmon_format(ii->second.ban_timestamp) << std::endl; send_attack_details(client_ip, (*ii).second); } return output_buffer.str(); } std::string get_attack_description(uint32_t client_ip, attack_details& current_attack) { std::stringstream attack_description; attack_description << "IP: " << convert_ip_as_uint_to_string(client_ip) << "\n"; attack_description << serialize_attack_description(current_attack) << "\n"; if (enable_subnet_counters) { // Got subnet tracking structure // TODO: we suppose case "no key exists" is not possible map_element network_speed_meter = PerSubnetSpeedMap[ current_attack.customer_network ]; map_element average_network_speed_meter = PerSubnetAverageSpeedMap[ current_attack.customer_network ]; attack_description <<"Network: " << convert_subnet_to_string(current_attack.customer_network) << "\n"; attack_description << serialize_network_load_to_text(network_speed_meter, false); attack_description << serialize_network_load_to_text(average_network_speed_meter, true); } attack_description << serialize_statistic_counters_about_attack(current_attack); return attack_description.str(); } std::string get_attack_description_in_json(uint32_t client_ip, attack_details& current_attack) { json_object* jobj = json_object_new_object(); json_object_object_add(jobj, "ip", json_object_new_string(convert_ip_as_uint_to_string(client_ip).c_str())); json_object_object_add(jobj, "attack_details", serialize_attack_description_to_json(current_attack) ); if (enable_subnet_counters) { map_element network_speed_meter = PerSubnetSpeedMap[ current_attack.customer_network ]; map_element average_network_speed_meter = PerSubnetAverageSpeedMap[ current_attack.customer_network ]; json_object_object_add(jobj, "network_load", serialize_network_load_to_json(network_speed_meter)); json_object_object_add(jobj, "network_average_load", serialize_network_load_to_json(average_network_speed_meter)); } // So we haven't statistic_counters here but from my point of view they are useless std::string json_as_text = json_object_to_json_string(jobj); // Free memory json_object_put(jobj); return json_as_text; } std::string generate_simple_packets_dump(std::vector& ban_list_details) { std::stringstream attack_details; std::map protocol_counter; for (std::vector::iterator iii = ban_list_details.begin(); iii != ban_list_details.end(); ++iii) { attack_details << print_simple_packet(*iii); protocol_counter[iii->protocol]++; } std::map::iterator max_proto = std::max_element(protocol_counter.begin(), protocol_counter.end(), protocol_counter.value_comp()); /* attack_details << "\n" << "We got more packets (" << max_proto->second << " from " << ban_details_records_count << ") for protocol: " << get_protocol_name_by_number(max_proto->first) << "\n"; */ return attack_details.str(); } void send_attack_details(uint32_t client_ip, attack_details current_attack_details) { std::string pps_as_string = convert_int_to_string(current_attack_details.attack_power); std::string attack_direction = get_direction_name(current_attack_details.attack_direction); std::string client_ip_as_string = convert_ip_as_uint_to_string(client_ip); // Very strange code but it work in 95% cases if (ban_list_details.count(client_ip) > 0 && ban_list_details[client_ip].size() >= ban_details_records_count) { std::stringstream attack_details; attack_details << get_attack_description(client_ip, current_attack_details) << "\n\n"; attack_details << generate_simple_packets_dump(ban_list_details[client_ip]); logger << log4cpp::Priority::INFO << "Attack with direction: " << attack_direction << " IP: " << client_ip_as_string << " Power: " << pps_as_string << " traffic samples collected"; call_attack_details_handlers(client_ip, current_attack_details, attack_details.str()); // TODO: here we have definitely RACE CONDITION!!! FIX IT // Remove key and prevent collection new data about this attack ban_list_details_mutex.lock(); ban_list_details.erase(client_ip); ban_list_details_mutex.unlock(); } } #ifdef ENABLE_DPI // Parse raw binary stand-alone packet with nDPI ndpi_protocol dpi_parse_packet(char* buffer, uint32_t len, uint32_t snap_len, struct ndpi_id_struct *src, struct ndpi_id_struct *dst, struct ndpi_flow_struct *flow, std::string& parsed_packet_as_string) { struct pfring_pkthdr packet_header; memset(&packet_header, 0, sizeof(packet_header)); packet_header.len = len; packet_header.caplen = snap_len; fastnetmon_parse_pkt((u_char*)buffer, &packet_header, 4, 1, 0); uint32_t current_tickt = 0; uint8_t* iph = (uint8_t*)(&buffer[packet_header.extended_hdr.parsed_pkt.offset.l3_offset]); unsigned int ipsize = packet_header.len; ndpi_protocol detected_protocol = ndpi_detection_process_packet(my_ndpi_struct, flow, iph, ipsize, current_tickt, src, dst); // So bad approach :( char print_buffer[512]; fastnetmon_print_parsed_pkt(print_buffer, 512, (u_char*)buffer, &packet_header); parsed_packet_as_string = std::string(print_buffer); return detected_protocol; } #endif #ifdef ENABLE_DPI void init_current_instance_of_ndpi() { my_ndpi_struct = init_ndpi(); if (my_ndpi_struct == NULL) { logger << log4cpp::Priority::ERROR << "Can't load nDPI, disable it!"; process_pcap_attack_dumps_with_dpi = false; return; } // Load sizes of main parsing structures ndpi_size_id_struct = ndpi_detection_get_sizeof_ndpi_id_struct(); ndpi_size_flow_struct = ndpi_detection_get_sizeof_ndpi_flow_struct(); } // Zeroify nDPI structure without memory leaks void zeroify_ndpi_flow(struct ndpi_flow_struct* flow) { if (flow->http.url) { ndpi_free(flow->http.url); } if (flow->http.content_type) { ndpi_free(flow->http.content_type); } memset(flow, 0, ndpi_size_flow_struct); } // Run flow spec mitigation rule void launch_bgp_flow_spec_rule(amplification_attack_type_t attack_type, std::string client_ip_as_string) { logger << log4cpp::Priority::INFO << "We detected this attack as: " << get_amplification_attack_type(attack_type); std::string flow_spec_rule_text = generate_flow_spec_for_amplification_attack(attack_type, client_ip_as_string); logger << log4cpp::Priority::INFO << "We have generated BGP Flow Spec rule for this attack: " << flow_spec_rule_text; if (exabgp_flow_spec_announces) { active_flow_spec_announces_t::iterator itr = active_flow_spec_announces.find(flow_spec_rule_text); if (itr == active_flow_spec_announces.end()) { // We havent this flow spec rule active yet logger << log4cpp::Priority::INFO << "We will publish flow spec announce about this attack"; bool exabgp_publish_result = exabgp_flow_spec_ban_manage("ban", flow_spec_rule_text); if (exabgp_publish_result) { active_flow_spec_announces[ flow_spec_rule_text ] = 1; } } else { // We have already blocked this attack logger << log4cpp::Priority::INFO << "The same rule was already sent to ExaBGP formerly"; } } else { logger << log4cpp::Priority::INFO << "exabgp_flow_spec_announces disabled. We will not talk to ExaBGP"; } } // Not so pretty copy and paste from pcap_reader() // TODO: rewrite to memory parser void produce_dpi_dump_for_pcap_dump(std::string pcap_file_path, std::stringstream& ss, std::string client_ip_as_string) { int filedesc = open(pcap_file_path.c_str(), O_RDONLY); if (filedesc <= 0) { logger << log4cpp::Priority::ERROR << "Can't open file for DPI"; return; } struct fastnetmon_pcap_file_header pcap_header; ssize_t file_header_readed_bytes = read(filedesc, &pcap_header, sizeof(struct fastnetmon_pcap_file_header)); if (file_header_readed_bytes != sizeof(struct fastnetmon_pcap_file_header)) { logger << log4cpp::Priority::ERROR << "Can't read pcap file header"; return; } // http://www.tcpdump.org/manpages/pcap-savefile.5.html if (pcap_header.magic == 0xa1b2c3d4 or pcap_header.magic == 0xd4c3b2a1) { // printf("Magic readed correctly\n"); } else { logger << log4cpp::Priority::ERROR << "Magic in file header broken"; return; } // Buffer for packets char packet_buffer[pcap_header.snaplen]; unsigned int total_packets_number = 0; uint64_t dns_amplification_packets = 0; uint64_t ntp_amplification_packets = 0; uint64_t ssdp_amplification_packets = 0; uint64_t snmp_amplification_packets = 0; struct ndpi_id_struct *src = (struct ndpi_id_struct*)malloc(ndpi_size_id_struct); memset(src, 0, ndpi_size_id_struct); struct ndpi_id_struct* dst = (struct ndpi_id_struct*)malloc(ndpi_size_id_struct); memset(dst, 0, ndpi_size_id_struct); struct ndpi_flow_struct* flow = (struct ndpi_flow_struct*)malloc(ndpi_size_flow_struct); memset(flow, 0, ndpi_size_flow_struct); while (1) { struct fastnetmon_pcap_pkthdr pcap_packet_header; ssize_t packet_header_readed_bytes = read(filedesc, &pcap_packet_header, sizeof(struct fastnetmon_pcap_pkthdr)); if (packet_header_readed_bytes != sizeof(struct fastnetmon_pcap_pkthdr)) { if (packet_header_readed_bytes != 0) { logger << log4cpp::Priority::INFO << "All packet read ? (" << packet_header_readed_bytes << ", " << errno << ")"; } // We haven't any packets break; } if (pcap_packet_header.incl_len > pcap_header.snaplen) { logger << log4cpp::Priority::ERROR << "Please enlarge packet buffer for DPI"; return; } ssize_t packet_payload_readed_bytes = read(filedesc, packet_buffer, pcap_packet_header.incl_len); if (pcap_packet_header.incl_len != packet_payload_readed_bytes) { logger << log4cpp::Priority::ERROR << "I read packet header but can't read packet payload"; return; } // The flow must be reset to zero state - in other case the DPI will not detect all packets properly. // To use flow properly there must be much more complicated code (with flow buffer for each flow probably) // following code is copied from ndpi_free_flow() just to be sure there will be no memory leaks due to memset() zeroify_ndpi_flow(flow); std::string parsed_packet_as_string; ndpi_protocol detected_protocol = dpi_parse_packet(packet_buffer, pcap_packet_header.orig_len, pcap_packet_header.incl_len, src, dst, flow, parsed_packet_as_string); #if NDPI_MAJOR >= 2 u_int16_t app_protocol = detected_protocol.app_protocol; #else u_int16_t app_protocol = detected_protocol.protocol; #endif char* protocol_name = ndpi_get_proto_name(my_ndpi_struct, app_protocol); char* master_protocol_name = ndpi_get_proto_name(my_ndpi_struct, detected_protocol.master_protocol); if (app_protocol == NDPI_PROTOCOL_DNS) { // It's answer for ANY request with so much if (flow->protos.dns.query_type == 255 && flow->protos.dns.num_queries < flow->protos.dns.num_answers) { dns_amplification_packets++; } } else if (app_protocol == NDPI_PROTOCOL_NTP) { // Detect packets with type MON_GETLIST_1 if (flow->protos.ntp.version == 2 && flow->protos.ntp.request_code == 42) { ntp_amplification_packets++; } } else if (app_protocol == NDPI_PROTOCOL_SSDP) { // So, this protocol completely unexpected in WAN networks ssdp_amplification_packets++; } else if (app_protocol == NDPI_PROTOCOL_SNMP) { // TODO: we need detailed tests for SNMP! snmp_amplification_packets++; } ss << parsed_packet_as_string << " protocol: " << protocol_name << " master_protocol: " << master_protocol_name << "\n"; total_packets_number++; } // Free up all memory ndpi_free_flow(flow); free(dst); free(src); close(filedesc); logger << log4cpp::Priority::INFO << "DPI pkt stats: total:" << total_packets_number << " DNS:" << dns_amplification_packets << " NTP:" << ntp_amplification_packets << " SSDP:" << ssdp_amplification_packets << " SNMP:" << snmp_amplification_packets; amplification_attack_type_t attack_type; // Attack type in unknown by default attack_type = AMPLIFICATION_ATTACK_UNKNOWN; // Detect amplification attack type if ( (double)dns_amplification_packets / (double)total_packets_number > 0.2) { launch_bgp_flow_spec_rule(AMPLIFICATION_ATTACK_DNS, client_ip_as_string); } else if ( (double)ntp_amplification_packets / (double)total_packets_number > 0.2) { launch_bgp_flow_spec_rule(AMPLIFICATION_ATTACK_NTP, client_ip_as_string); } else if ( (double)ssdp_amplification_packets / (double)total_packets_number > 0.2) { launch_bgp_flow_spec_rule(AMPLIFICATION_ATTACK_SSDP, client_ip_as_string); } else if ( (double)snmp_amplification_packets / (double)total_packets_number > 0.2) { launch_bgp_flow_spec_rule(AMPLIFICATION_ATTACK_SNMP, client_ip_as_string); } else { /*TODO - full IP ban should be announced here ! - and maybe some protocol/port based statistics could be used to filter new/unknown attacks... */ logger << log4cpp::Priority::ERROR << "We can't detect attack type with DPI. It's not so critical, only for your information"; } } #endif void call_attack_details_handlers(uint32_t client_ip, attack_details& current_attack, std::string attack_fingerprint) { std::string client_ip_as_string = convert_ip_as_uint_to_string(client_ip); std::string attack_direction = get_direction_name(current_attack.attack_direction); std::string pps_as_string = convert_int_to_string(current_attack.attack_power); // We place this variables here because we need this paths from DPI parser code std::string ban_timestamp_as_string = print_time_t_in_fastnetmon_format(current_attack.ban_timestamp); std::string attack_pcap_dump_path = attack_details_folder + "/" + client_ip_as_string + "_" + ban_timestamp_as_string + ".pcap"; if (collect_attack_pcap_dumps) { int pcap_fump_filedesc = open(attack_pcap_dump_path.c_str(), O_WRONLY|O_CREAT, S_IRUSR|S_IWUSR); if (pcap_fump_filedesc <= 0) { logger << log4cpp::Priority::ERROR << "Can't open file for storing pcap dump: " << attack_pcap_dump_path; } else { ssize_t wrote_bytes = write(pcap_fump_filedesc, (void*)current_attack.pcap_attack_dump.get_buffer_pointer(), current_attack.pcap_attack_dump.get_used_memory()); if (wrote_bytes != current_attack.pcap_attack_dump.get_used_memory()) { logger << log4cpp::Priority::ERROR << "Can't wrote all attack details to the disk correctly"; } close (pcap_fump_filedesc); // Freeup memory current_attack.pcap_attack_dump.deallocate_buffer(); } } #ifdef ENABLE_DPI // Yes, will be fine to read packets from the memory but we haven't this code yet // Thus we could read from file with not good performance because it's simpler if (collect_attack_pcap_dumps && process_pcap_attack_dumps_with_dpi) { std::stringstream string_buffer_for_dpi_data; string_buffer_for_dpi_data << "\n\nDPI\n\n"; produce_dpi_dump_for_pcap_dump(attack_pcap_dump_path, string_buffer_for_dpi_data, client_ip_as_string); attack_fingerprint = attack_fingerprint + string_buffer_for_dpi_data.str(); } #endif print_attack_details_to_file(attack_fingerprint, client_ip_as_string, current_attack); // Pass attack details to script if (notify_script_enabled) { logger << log4cpp::Priority::INFO << "Call script for notify about attack details for: " << client_ip_as_string; std::string script_params = notify_script_path + " " + client_ip_as_string + " " + attack_direction + " " + pps_as_string + " attack_details"; // We should execute external script in separate thread because any lag in this code // will be very distructive boost::thread exec_with_params_thread(exec_with_stdin_params, script_params, attack_fingerprint); exec_with_params_thread.detach(); logger << log4cpp::Priority::INFO << "Script for notify about attack details is finished: " << client_ip_as_string; } #ifdef REDIS if (redis_enabled) { std::string redis_key_name = client_ip_as_string + "_packets_dump"; if (!redis_prefix.empty()) { redis_key_name = redis_prefix + "_" + client_ip_as_string + "_packets_dump"; } logger << log4cpp::Priority::INFO << "Start data save in redis for key: " << redis_key_name; boost::thread redis_store_thread(store_data_in_redis, redis_key_name, attack_fingerprint); redis_store_thread.detach(); logger << log4cpp::Priority::INFO << "Finish data save in redis for key: " << redis_key_name; } #endif } uint64_t convert_conntrack_hash_struct_to_integer(packed_conntrack_hash* struct_value) { uint64_t unpacked_data = 0; memcpy(&unpacked_data, struct_value, sizeof(uint64_t)); return unpacked_data; } void convert_integer_to_conntrack_hash_struct(packed_session* packed_connection_data, packed_conntrack_hash* unpacked_data) { memcpy(unpacked_data, packed_connection_data, sizeof(uint64_t)); } std::string print_flow_tracking_for_specified_protocol(contrack_map_type& protocol_map, std::string client_ip, direction flow_direction) { std::stringstream buffer; // We shoud iterate over all fields int printed_records = 0; for (contrack_map_type::iterator itr = protocol_map.begin(); itr != protocol_map.end(); ++itr) { // We should limit number of records in flow dump because syn flood attacks produce // thounsands of lines if (printed_records > ban_details_records_count) { buffer << "Flows have cropped due to very long list.\n"; break; } uint64_t packed_connection_data = itr->first; packed_conntrack_hash unpacked_key_struct; convert_integer_to_conntrack_hash_struct(&packed_connection_data, &unpacked_key_struct); std::string opposite_ip_as_string = convert_ip_as_uint_to_string(unpacked_key_struct.opposite_ip); if (flow_direction == INCOMING) { buffer << client_ip << ":" << unpacked_key_struct.dst_port << " < " << opposite_ip_as_string << ":" << unpacked_key_struct.src_port << " "; } else if (flow_direction == OUTGOING) { buffer << client_ip << ":" << unpacked_key_struct.src_port << " > " << opposite_ip_as_string << ":" << unpacked_key_struct.dst_port << " "; } buffer << itr->second.bytes << " bytes " << itr->second.packets << " packets"; buffer << "\n"; printed_records++; } return buffer.str(); } /* Attack types: - syn flood: one local port, multiple remote hosts (and maybe multiple remote ports) and small packet size */ /* Iterate over all flow tracking table */ bool process_flow_tracking_table(conntrack_main_struct& conntrack_element, std::string client_ip) { std::map uniq_remote_hosts_which_generate_requests_to_us; std::map uniq_local_ports_which_target_of_connectiuons_from_inside; /* Process incoming TCP connections */ for (contrack_map_type::iterator itr = conntrack_element.in_tcp.begin(); itr != conntrack_element.in_tcp.end(); ++itr) { uint64_t packed_connection_data = itr->first; packed_conntrack_hash unpacked_key_struct; convert_integer_to_conntrack_hash_struct(&packed_connection_data, &unpacked_key_struct); uniq_remote_hosts_which_generate_requests_to_us[unpacked_key_struct.opposite_ip]++; uniq_local_ports_which_target_of_connectiuons_from_inside[unpacked_key_struct.dst_port]++; // we can calc average packet size // string opposite_ip_as_string = // convert_ip_as_uint_to_string(unpacked_key_struct.opposite_ip); // unpacked_key_struct.src_port // unpacked_key_struct.dst_port // itr->second.packets // itr->second.bytes } return true; } std::string print_flow_tracking_for_ip(conntrack_main_struct& conntrack_element, std::string client_ip) { std::stringstream buffer; std::string in_tcp = print_flow_tracking_for_specified_protocol(conntrack_element.in_tcp, client_ip, INCOMING); std::string in_udp = print_flow_tracking_for_specified_protocol(conntrack_element.in_udp, client_ip, INCOMING); unsigned long long total_number_of_incoming_tcp_flows = conntrack_element.in_tcp.size(); unsigned long long total_number_of_incoming_udp_flows = conntrack_element.in_udp.size(); unsigned long long total_number_of_outgoing_tcp_flows = conntrack_element.out_tcp.size(); unsigned long long total_number_of_outgoing_udp_flows = conntrack_element.out_udp.size(); bool we_have_incoming_flows = in_tcp.length() > 0 or in_udp.length() > 0; if (we_have_incoming_flows) { buffer << "Incoming\n\n"; if (in_tcp.length() > 0) { buffer << "TCP flows: " << total_number_of_incoming_tcp_flows << "\n"; buffer << in_tcp << "\n"; } if (in_udp.length() > 0) { buffer << "UDP flows: " << total_number_of_incoming_udp_flows << "\n"; buffer << in_udp << "\n"; } } std::string out_tcp = print_flow_tracking_for_specified_protocol(conntrack_element.out_tcp, client_ip, OUTGOING); std::string out_udp = print_flow_tracking_for_specified_protocol(conntrack_element.out_udp, client_ip, OUTGOING); bool we_have_outgoing_flows = out_tcp.length() > 0 or out_udp.length() > 0; // print delimiter if we have flows in both directions if (we_have_incoming_flows && we_have_outgoing_flows) { buffer << "\n"; } if (we_have_outgoing_flows) { buffer << "Outgoing\n\n"; if (out_tcp.length() > 0) { buffer << "TCP flows: " << total_number_of_outgoing_tcp_flows << "\n"; buffer << out_tcp << "\n"; } if (out_udp.length() > 0) { buffer << "UDP flows: " << total_number_of_outgoing_udp_flows << "\n"; buffer << out_udp << "\n"; } } return buffer.str(); } std::string print_subnet_load() { std::stringstream buffer; sort_type sorter; if (sort_parameter == "packets") { sorter = PACKETS; } else if (sort_parameter == "bytes") { sorter = BYTES; } else if (sort_parameter == "flows") { sorter = FLOWS; } else { logger << log4cpp::Priority::INFO << "Unexpected sorter type: " << sort_parameter; sorter = PACKETS; } std::vector vector_for_sort; vector_for_sort.reserve(PerSubnetSpeedMap.size()); for (map_for_subnet_counters::iterator itr = PerSubnetSpeedMap.begin(); itr != PerSubnetSpeedMap.end(); ++itr) { vector_for_sort.push_back(std::make_pair(itr->first, itr->second)); } std::sort(vector_for_sort.begin(), vector_for_sort.end(), TrafficComparatorClass(INCOMING, sorter)); graphite_data_t graphite_data; for (std::vector::iterator itr = vector_for_sort.begin(); itr != vector_for_sort.end(); ++itr) { map_element* speed = &itr->second; std::string subnet_as_string = convert_subnet_to_string(itr->first); buffer << std::setw(18) << std::left << subnet_as_string; if (graphite_enabled) { std::string subnet_as_string_as_dash_delimiters = subnet_as_string; // Replace dots by dashes std::replace(subnet_as_string_as_dash_delimiters.begin(), subnet_as_string_as_dash_delimiters.end(), '.', '_'); // Replace / by dashes too std::replace(subnet_as_string_as_dash_delimiters.begin(), subnet_as_string_as_dash_delimiters.end(), '/', '_'); graphite_data[ graphite_prefix + ".networks." + subnet_as_string_as_dash_delimiters + ".incoming.pps" ] = speed->in_packets; graphite_data[ graphite_prefix + ".networks." + subnet_as_string_as_dash_delimiters + ".outgoing.pps" ] = speed->out_packets; graphite_data[ graphite_prefix + ".networks." + subnet_as_string_as_dash_delimiters + ".incoming.bps" ] = speed->in_bytes * 8; graphite_data[ graphite_prefix + ".networks." + subnet_as_string_as_dash_delimiters + ".outgoing.bps" ] = speed->out_bytes * 8; } buffer << " " << "pps in: " << std::setw(8) << speed->in_packets << " out: " << std::setw(8) << speed->out_packets << " mbps in: " << std::setw(5) << convert_speed_to_mbps(speed->in_bytes) << " out: " << std::setw(5) << convert_speed_to_mbps(speed->out_bytes) << "\n"; } if (graphite_enabled) { bool graphite_put_result = store_data_to_graphite(graphite_port, graphite_host, graphite_data); if (!graphite_put_result) { logger << log4cpp::Priority::ERROR << "Can't store network load data to Graphite"; } } return buffer.str(); } std::string print_ban_thresholds(ban_settings_t current_ban_settings) { std::stringstream output_buffer; output_buffer << "Configuration params:\n"; if (current_ban_settings.enable_ban) { output_buffer << "We call ban script: yes\n"; } else { output_buffer << "We call ban script: no\n"; } output_buffer << "Packets per second: "; if (current_ban_settings.enable_ban_for_pps) { output_buffer << current_ban_settings.ban_threshold_pps; } else { output_buffer << "disabled"; } output_buffer << "\n"; output_buffer << "Mbps per second: "; if (current_ban_settings.enable_ban_for_bandwidth) { output_buffer << current_ban_settings.ban_threshold_mbps; } else { output_buffer << "disabled"; } output_buffer << "\n"; output_buffer << "Flows per second: "; if (current_ban_settings.enable_ban_for_flows_per_second) { output_buffer << current_ban_settings.ban_threshold_flows; } else { output_buffer << "disabled"; } output_buffer << "\n"; return output_buffer.str(); } void print_attack_details_to_file(std::string details, std::string client_ip_as_string, attack_details current_attack) { std::ofstream my_attack_details_file; std::string ban_timestamp_as_string = print_time_t_in_fastnetmon_format(current_attack.ban_timestamp); std::string attack_dump_path = attack_details_folder + "/" + client_ip_as_string + "_" + ban_timestamp_as_string + ".txt"; my_attack_details_file.open(attack_dump_path.c_str(), std::ios::app); if (my_attack_details_file.is_open()) { my_attack_details_file << details << "\n\n"; my_attack_details_file.close(); } else { logger << log4cpp::Priority::ERROR << "Can't print attack details to file"; } } logging_configuration_t read_logging_settings(configuration_map_t configuration_map) { logging_configuration_t logging_configuration_temp; if (configuration_map.count("logging:local_syslog_logging") != 0) { logging_configuration_temp.local_syslog_logging = configuration_map["logging:local_syslog_logging"] == "on"; } if (configuration_map.count("logging:remote_syslog_logging") != 0) { logging_configuration_temp.remote_syslog_logging = configuration_map["logging:remote_syslog_logging"] == "on"; } if (configuration_map.count("logging:remote_syslog_server") != 0) { logging_configuration_temp.remote_syslog_server = configuration_map["logging:remote_syslog_server"]; } if (configuration_map.count("logging:remote_syslog_port") != 0) { logging_configuration_temp.remote_syslog_port = convert_string_to_integer(configuration_map["logging:remote_syslog_port"]); } if (logging_configuration_temp.remote_syslog_logging) { if (logging_configuration_temp.remote_syslog_port > 0 && !logging_configuration_temp.remote_syslog_server.empty()) { logger << log4cpp::Priority::INFO << "We have configured remote syslog logging corectly"; } else { logger << log4cpp::Priority::ERROR << "You have enabled remote logging but haven't specified port or host"; logging_configuration_temp.remote_syslog_logging = false; } } if (logging_configuration_temp.local_syslog_logging) { logger << log4cpp::Priority::INFO << "We have configured local syslog logging corectly"; } return logging_configuration_temp; } ban_settings_t read_ban_settings(configuration_map_t configuration_map, std::string host_group_name) { ban_settings_t ban_settings; std::string prefix = ""; if (host_group_name != "") { prefix = host_group_name + "_"; } if (configuration_map.count(prefix + "enable_ban") != 0) { ban_settings.enable_ban = configuration_map[prefix + "enable_ban"] == "on"; } if (configuration_map.count(prefix + "ban_for_pps") != 0) { ban_settings.enable_ban_for_pps = configuration_map[prefix + "ban_for_pps"] == "on"; } if (configuration_map.count(prefix + "ban_for_bandwidth") != 0) { ban_settings.enable_ban_for_bandwidth = configuration_map[prefix + "ban_for_bandwidth"] == "on"; } if (configuration_map.count(prefix + "ban_for_flows") != 0) { ban_settings.enable_ban_for_flows_per_second = configuration_map[prefix + "ban_for_flows"] == "on"; } // Per protocol bandwidth triggers if (configuration_map.count(prefix + "ban_for_tcp_bandwidth") != 0) { ban_settings.enable_ban_for_tcp_bandwidth = configuration_map[prefix + "ban_for_tcp_bandwidth"] == "on"; } if (configuration_map.count(prefix + "ban_for_udp_bandwidth") != 0) { ban_settings.enable_ban_for_udp_bandwidth = configuration_map[prefix + "ban_for_udp_bandwidth"] == "on"; } if (configuration_map.count(prefix + "ban_for_icmp_bandwidth") != 0) { ban_settings.enable_ban_for_icmp_bandwidth = configuration_map[prefix + "ban_for_icmp_bandwidth"] == "on"; } // Per protocol pps ban triggers if (configuration_map.count(prefix + "ban_for_tcp_pps") != 0) { ban_settings.enable_ban_for_tcp_pps = configuration_map[prefix + "ban_for_tcp_pps"] == "on"; } if (configuration_map.count(prefix + "ban_for_udp_pps") != 0) { ban_settings.enable_ban_for_udp_pps = configuration_map[prefix + "ban_for_udp_pps"] == "on"; } if (configuration_map.count(prefix + "ban_for_icmp_pps") != 0) { ban_settings.enable_ban_for_icmp_pps = configuration_map[prefix + "ban_for_icmp_pps"] == "on"; } // Pps per protocol thresholds if (configuration_map.count(prefix + "threshold_tcp_pps") != 0) { ban_settings.ban_threshold_tcp_pps = convert_string_to_integer(configuration_map[prefix + "threshold_tcp_pps"]); } if (configuration_map.count(prefix + "threshold_udp_pps") != 0) { ban_settings.ban_threshold_udp_pps = convert_string_to_integer(configuration_map[prefix + "threshold_udp_pps"]); } if (configuration_map.count(prefix + "threshold_icmp_pps") != 0) { ban_settings.ban_threshold_icmp_pps = convert_string_to_integer(configuration_map[prefix + "threshold_icmp_pps"]); } // Bandwidth per protocol thresholds if (configuration_map.count(prefix + "threshold_tcp_mbps") != 0) { ban_settings.ban_threshold_tcp_mbps = convert_string_to_integer(configuration_map[prefix + "threshold_tcp_mbps"]); } if (configuration_map.count(prefix + "threshold_udp_mbps") != 0) { ban_settings.ban_threshold_udp_mbps = convert_string_to_integer(configuration_map[prefix + "threshold_udp_mbps"]); } if (configuration_map.count(prefix + "threshold_icmp_mbps") != 0) { ban_settings.ban_threshold_icmp_mbps = convert_string_to_integer(configuration_map[prefix + "threshold_icmp_mbps"]); } if (configuration_map.count(prefix + "threshold_pps") != 0) { ban_settings.ban_threshold_pps = convert_string_to_integer(configuration_map[prefix + "threshold_pps"]); } if (configuration_map.count(prefix + "threshold_mbps") != 0) { ban_settings.ban_threshold_mbps = convert_string_to_integer(configuration_map[prefix + "threshold_mbps"]); } if (configuration_map.count(prefix + "threshold_flows") != 0) { ban_settings.ban_threshold_flows = convert_string_to_integer(configuration_map[prefix + "threshold_flows"]); } return ban_settings; } bool exceed_pps_speed(uint64_t in_counter, uint64_t out_counter, unsigned int threshold) { if (in_counter > threshold or out_counter > threshold) { return true; } else { return false; } } bool exceed_flow_speed(uint64_t in_counter, uint64_t out_counter, unsigned int threshold) { if (in_counter > threshold or out_counter > threshold) { return true; } else { return false; } } bool exceed_mbps_speed(uint64_t in_counter, uint64_t out_counter, unsigned int threshold_mbps) { if (convert_speed_to_mbps(in_counter) > threshold_mbps or convert_speed_to_mbps(out_counter) > threshold_mbps) { return true; } else { return false; } } // Return true when we should ban this IP bool we_should_ban_this_ip(map_element* average_speed_element, ban_settings_t current_ban_settings) { // we detect overspeed by packets bool attack_detected_by_pps = false; bool attack_detected_by_bandwidth = false; bool attack_detected_by_flow = false; if (current_ban_settings.enable_ban_for_pps && exceed_pps_speed(average_speed_element->in_packets, average_speed_element->out_packets, current_ban_settings.ban_threshold_pps)) { logger << log4cpp::Priority::DEBUG << "We detected this attack by pps limit"; return true; } if (current_ban_settings.enable_ban_for_bandwidth && exceed_mbps_speed(average_speed_element->in_bytes, average_speed_element->out_bytes, current_ban_settings.ban_threshold_mbps)) { logger << log4cpp::Priority::DEBUG << "We detected this attack by mbps limit"; return true; } if (current_ban_settings.enable_ban_for_flows_per_second && exceed_flow_speed(average_speed_element->in_flows, average_speed_element->out_flows, current_ban_settings.ban_threshold_flows)) { logger << log4cpp::Priority::DEBUG << "We detected this attack by flow limit"; return true; } // We could try per protocol thresholds here // Per protocol pps thresholds if (current_ban_settings.enable_ban_for_tcp_pps && exceed_pps_speed(average_speed_element->tcp_in_packets, average_speed_element->tcp_out_packets, current_ban_settings.ban_threshold_tcp_pps)) { logger << log4cpp::Priority::DEBUG << "We detected this attack by tcp pps limit"; return true; } if (current_ban_settings.enable_ban_for_udp_pps && exceed_pps_speed(average_speed_element->udp_in_packets, average_speed_element->udp_out_packets, current_ban_settings.ban_threshold_udp_pps)) { logger << log4cpp::Priority::DEBUG << "We detected this attack by udp pps limit"; return true; } if (current_ban_settings.enable_ban_for_icmp_pps && exceed_pps_speed(average_speed_element->icmp_in_packets, average_speed_element->icmp_out_packets, current_ban_settings.ban_threshold_icmp_pps)) { logger << log4cpp::Priority::DEBUG << "We detected this attack by icmp pps limit"; return true; } // Per protocol bandwidth thresholds if (current_ban_settings.enable_ban_for_tcp_bandwidth && exceed_mbps_speed(average_speed_element->tcp_in_bytes, average_speed_element->tcp_out_bytes, current_ban_settings.ban_threshold_tcp_mbps)) { logger << log4cpp::Priority::DEBUG << "We detected this attack by tcp mbps limit"; return true; } if (current_ban_settings.enable_ban_for_udp_bandwidth && exceed_mbps_speed(average_speed_element->udp_in_bytes, average_speed_element->udp_out_bytes, current_ban_settings.ban_threshold_udp_mbps)) { logger << log4cpp::Priority::DEBUG << "We detected this attack by udp mbps limit"; return true; } if (current_ban_settings.enable_ban_for_icmp_bandwidth && exceed_mbps_speed(average_speed_element->icmp_in_bytes, average_speed_element->icmp_out_bytes, current_ban_settings.ban_threshold_icmp_mbps)) { logger << log4cpp::Priority::DEBUG << "We detected this attack by icmp mbps limit"; return true; } return false; } std::string generate_flow_spec_for_amplification_attack(amplification_attack_type_t amplification_attack_type, std::string destination_ip) { exabgp_flow_spec_rule_t exabgp_rule; bgp_flow_spec_action_t my_action; // We drop all traffic by default my_action.set_type(FLOW_SPEC_ACTION_DISCARD); // Assign action to the rule exabgp_rule.set_action( my_action ); // TODO: rewrite! exabgp_rule.set_destination_subnet( convert_subnet_from_string_to_binary_with_cidr_format( destination_ip + "/32") ); // We use only UDP here exabgp_rule.add_protocol(FLOW_SPEC_PROTOCOL_UDP); if (amplification_attack_type == AMPLIFICATION_ATTACK_DNS) { exabgp_rule.add_source_port(53); } else if (amplification_attack_type == AMPLIFICATION_ATTACK_NTP) { exabgp_rule.add_source_port(123); } else if (amplification_attack_type == AMPLIFICATION_ATTACK_SSDP) { exabgp_rule.add_source_port(1900); } else if (amplification_attack_type == AMPLIFICATION_ATTACK_SNMP) { exabgp_rule.add_source_port(161); } else if (amplification_attack_type == AMPLIFICATION_ATTACK_CHARGEN) { exabgp_rule.add_source_port(19); } return exabgp_rule.serialize_single_line_exabgp_v4_configuration(); } std::string get_amplification_attack_type(amplification_attack_type_t attack_type) { if (attack_type == AMPLIFICATION_ATTACK_UNKNOWN) { return "unknown"; } else if (attack_type == AMPLIFICATION_ATTACK_DNS) { return "dns_amplification"; } else if (attack_type == AMPLIFICATION_ATTACK_NTP) { return "ntp_amplification"; } else if (attack_type == AMPLIFICATION_ATTACK_SSDP) { return "ssdp_amplification"; } else if (attack_type == AMPLIFICATION_ATTACK_SNMP) { return "snmp_amplification"; } else if (attack_type == AMPLIFICATION_ATTACK_CHARGEN) { return "chargen_amplification"; } else { return "unexpected"; } } // We calculate speed from packet counters here inline void build_speed_counters_from_packet_counters(map_element& new_speed_element, map_element* vector_itr, double speed_calc_period) { // calculate_speed(new_speed_element speed_element, vector_itr* ); new_speed_element.in_packets = uint64_t((double)vector_itr->in_packets / speed_calc_period); new_speed_element.out_packets = uint64_t((double)vector_itr->out_packets / speed_calc_period); new_speed_element.in_bytes = uint64_t((double)vector_itr->in_bytes / speed_calc_period); new_speed_element.out_bytes = uint64_t((double)vector_itr->out_bytes / speed_calc_period); // Fragmented new_speed_element.fragmented_in_packets = uint64_t((double)vector_itr->fragmented_in_packets / speed_calc_period); new_speed_element.fragmented_out_packets = uint64_t((double)vector_itr->fragmented_out_packets / speed_calc_period); new_speed_element.fragmented_in_bytes = uint64_t((double)vector_itr->fragmented_in_bytes / speed_calc_period); new_speed_element.fragmented_out_bytes = uint64_t((double)vector_itr->fragmented_out_bytes / speed_calc_period); // By protocol counters // TCP new_speed_element.tcp_in_packets = uint64_t((double)vector_itr->tcp_in_packets / speed_calc_period); new_speed_element.tcp_out_packets = uint64_t((double)vector_itr->tcp_out_packets / speed_calc_period); new_speed_element.tcp_in_bytes = uint64_t((double)vector_itr->tcp_in_bytes / speed_calc_period); new_speed_element.tcp_out_bytes = uint64_t((double)vector_itr->tcp_out_bytes / speed_calc_period); // TCP syn new_speed_element.tcp_syn_in_packets = uint64_t((double)vector_itr->tcp_syn_in_packets / speed_calc_period); new_speed_element.tcp_syn_out_packets = uint64_t((double)vector_itr->tcp_syn_out_packets / speed_calc_period); new_speed_element.tcp_syn_in_bytes = uint64_t((double)vector_itr->tcp_syn_in_bytes / speed_calc_period); new_speed_element.tcp_syn_out_bytes = uint64_t((double)vector_itr->tcp_syn_out_bytes / speed_calc_period); // UDP new_speed_element.udp_in_packets = uint64_t((double)vector_itr->udp_in_packets / speed_calc_period); new_speed_element.udp_out_packets = uint64_t((double)vector_itr->udp_out_packets / speed_calc_period); new_speed_element.udp_in_bytes = uint64_t((double)vector_itr->udp_in_bytes / speed_calc_period); new_speed_element.udp_out_bytes = uint64_t((double)vector_itr->udp_out_bytes / speed_calc_period); // ICMP new_speed_element.icmp_in_packets = uint64_t((double)vector_itr->icmp_in_packets / speed_calc_period); new_speed_element.icmp_out_packets = uint64_t((double)vector_itr->icmp_out_packets / speed_calc_period); new_speed_element.icmp_in_bytes = uint64_t((double)vector_itr->icmp_in_bytes / speed_calc_period); new_speed_element.icmp_out_bytes = uint64_t((double)vector_itr->icmp_out_bytes / speed_calc_period); } inline void build_average_speed_counters_from_speed_counters( map_element* current_average_speed_element, map_element& new_speed_element, double exp_value, double exp_power) { // Global bytes counters current_average_speed_element->in_bytes = uint64_t( new_speed_element.in_bytes + exp_value * ((double)current_average_speed_element->in_bytes - (double)new_speed_element.in_bytes)); current_average_speed_element->out_bytes = uint64_t( new_speed_element.out_bytes + exp_value * ((double)current_average_speed_element->out_bytes - (double)new_speed_element.out_bytes)); // Global packet counters current_average_speed_element->in_packets = uint64_t( new_speed_element.in_packets + exp_value * ((double)current_average_speed_element->in_packets - (double)new_speed_element.in_packets)); current_average_speed_element->out_packets = uint64_t( new_speed_element.out_packets + exp_value * ((double)current_average_speed_element->out_packets - (double)new_speed_element.out_packets)); // Per packet type packet counters for in traffic current_average_speed_element->fragmented_in_packets = uint64_t( new_speed_element.fragmented_in_packets + exp_value * ((double)current_average_speed_element->fragmented_in_packets - (double)new_speed_element.fragmented_in_packets)); current_average_speed_element->tcp_in_packets = uint64_t( new_speed_element.tcp_in_packets + exp_value * ((double)current_average_speed_element->tcp_in_packets - (double)new_speed_element.tcp_in_packets)); current_average_speed_element->tcp_syn_in_packets = uint64_t( new_speed_element.tcp_syn_in_packets + exp_value * ((double)current_average_speed_element->tcp_syn_in_packets - (double)new_speed_element.tcp_syn_in_packets)); current_average_speed_element->udp_in_packets = uint64_t( new_speed_element.udp_in_packets + exp_value * ((double)current_average_speed_element->udp_in_packets - (double)new_speed_element.udp_in_packets)); current_average_speed_element->icmp_in_packets = uint64_t( new_speed_element.icmp_in_packets + exp_value * ((double)current_average_speed_element->icmp_in_packets - (double)new_speed_element.icmp_in_packets)); // Per packet type packets counters for out current_average_speed_element->fragmented_out_packets = uint64_t( new_speed_element.fragmented_out_packets + exp_value * ((double)current_average_speed_element->fragmented_out_packets - (double)new_speed_element.fragmented_out_packets)); current_average_speed_element->tcp_out_packets = uint64_t( new_speed_element.tcp_out_packets + exp_value * ((double)current_average_speed_element->tcp_out_packets - (double)new_speed_element.tcp_out_packets)); current_average_speed_element->tcp_syn_out_packets = uint64_t( new_speed_element.tcp_syn_out_packets + exp_value * ((double)current_average_speed_element->tcp_syn_out_packets - (double)new_speed_element.tcp_syn_out_packets)); current_average_speed_element->udp_out_packets = uint64_t( new_speed_element.udp_out_packets + exp_value * ((double)current_average_speed_element->udp_out_packets - (double)new_speed_element.udp_out_packets)); current_average_speed_element->icmp_out_packets = uint64_t( new_speed_element.icmp_out_packets + exp_value * ((double)current_average_speed_element->icmp_out_packets - (double)new_speed_element.icmp_out_packets)); // Per packet type bytes counter for out current_average_speed_element->fragmented_out_bytes = uint64_t( new_speed_element.fragmented_out_bytes + exp_value * ((double)current_average_speed_element->fragmented_out_bytes - (double)new_speed_element.fragmented_out_bytes)); current_average_speed_element->tcp_out_bytes = uint64_t( new_speed_element.tcp_out_bytes + exp_value * ((double)current_average_speed_element->tcp_out_bytes - (double)new_speed_element.tcp_out_bytes)); current_average_speed_element->tcp_syn_out_bytes = uint64_t( new_speed_element.tcp_syn_out_bytes + exp_value * ((double)current_average_speed_element->tcp_syn_out_bytes - (double)new_speed_element.tcp_syn_out_bytes)); current_average_speed_element->udp_out_bytes = uint64_t( new_speed_element.udp_out_bytes + exp_value * ((double)current_average_speed_element->udp_out_bytes - (double)new_speed_element.udp_out_bytes)); current_average_speed_element->icmp_out_bytes = uint64_t( new_speed_element.icmp_out_bytes + exp_value * ((double)current_average_speed_element->icmp_out_bytes - (double)new_speed_element.icmp_out_bytes)); // Per packet type bytes counter for in current_average_speed_element->fragmented_in_bytes = uint64_t( new_speed_element.fragmented_in_bytes + exp_value * ((double)current_average_speed_element->fragmented_in_bytes - (double)new_speed_element.fragmented_in_bytes)); current_average_speed_element->tcp_in_bytes = uint64_t( new_speed_element.tcp_in_bytes + exp_value * ((double)current_average_speed_element->tcp_in_bytes - (double)new_speed_element.tcp_in_bytes)); current_average_speed_element->tcp_syn_in_bytes = uint64_t( new_speed_element.tcp_syn_in_bytes + exp_value * ((double)current_average_speed_element->tcp_syn_in_bytes - (double)new_speed_element.tcp_syn_in_bytes)); current_average_speed_element->udp_in_bytes = uint64_t( new_speed_element.udp_in_bytes + exp_value * ((double)current_average_speed_element->udp_in_bytes - (double)new_speed_element.udp_in_bytes)); current_average_speed_element->icmp_in_bytes = uint64_t( new_speed_element.icmp_in_bytes + exp_value * ((double)current_average_speed_element->icmp_in_bytes - (double)new_speed_element.icmp_in_bytes)); } fastnetmon-1.1.4/src/fastnetmon.proto000066400000000000000000000007771343111404700177360ustar00rootroot00000000000000syntax = "proto3"; package fastmitigation; service Fastnetmon { rpc GetBanlist(BanListRequest) returns (stream BanListReply) {} rpc ExecuteBan(ExecuteBanRequest) returns (ExecuteBanReply) {} rpc ExecuteUnBan(ExecuteBanRequest) returns (ExecuteBanReply) {} } // We could not create RPC method without params message BanListRequest { } message BanListReply { string ip_address = 1; } message ExecuteBanRequest { string ip_address = 1; } message ExecuteBanReply { bool result = 1; } fastnetmon-1.1.4/src/fastnetmon.service.in000066400000000000000000000006511343111404700206270ustar00rootroot00000000000000[Unit] Description=FastNetMon - DoS/DDoS analyzer with sflow/netflow/mirror support Documentation=man:fastnetmon(1) After=syslog.target network.target remote-fs.target [Service] Type=forking ExecStart=@CMAKE_INSTALL_SBINDIR@/fastnetmon --daemonize PIDFile=/run/fastnetmon.pid Restart=on-failure RestartSec=3 #ExecReload=/bin/kill -s HUP $MAINPID #ExecStop=/bin/kill -s QUIT $MAINPID [Install] WantedBy=multi-user.target fastnetmon-1.1.4/src/fastnetmon_actions.h000066400000000000000000000007111343111404700205260ustar00rootroot00000000000000#include "log4cpp/Category.hh" #include "log4cpp/Appender.hh" #include "log4cpp/FileAppender.hh" #include "log4cpp/OstreamAppender.hh" #include "log4cpp/Layout.hh" #include "log4cpp/BasicLayout.hh" #include "log4cpp/PatternLayout.hh" #include "log4cpp/Priority.hh" #include "fast_library.h" // Get log4cpp logger from main program extern log4cpp::Category& logger; // Global configuration map extern std::map configuration_map; fastnetmon-1.1.4/src/fastnetmon_api_client.cpp000066400000000000000000000101121343111404700215240ustar00rootroot00000000000000#include #include #include #include #include "fastnetmon.grpc.pb.h" using grpc::Channel; using grpc::ClientContext; using grpc::Status; using fastmitigation::BanListRequest; using fastmitigation::BanListReply; using fastmitigation::Fastnetmon; unsigned int client_connection_timeout = 5; class FastnetmonClient { public: FastnetmonClient(std::shared_ptr channel) : stub_(Fastnetmon::NewStub(channel)) {} void ExecuteBan(std::string host, bool is_ban) { ClientContext context; fastmitigation::ExecuteBanRequest request; fastmitigation::ExecuteBanReply reply; request.set_ip_address(host); Status status; if (is_ban) { status = stub_->ExecuteBan(&context, request, &reply); } else { status = stub_->ExecuteUnBan(&context, request, &reply); } if (status.ok()) { } else { if (status.error_code() == grpc::DEADLINE_EXCEEDED) { std::cerr << "Could not connect to API server. Timeout exceed" << std::endl; return; } else { std::cerr << "RPC failed " + status.error_message() << std::endl; return; } } } void GetBanList() { // This request haven't any useful data BanListRequest request; // Container for the data we expect from the server. BanListReply reply; // Context for the client. It could be used to convey extra information to // the server and/or tweak certain RPC behaviors. ClientContext context; // Set timeout for API std::chrono::system_clock::time_point deadline = std::chrono::system_clock::now() + std::chrono::seconds(client_connection_timeout); context.set_deadline(deadline); // The actual RPC. auto announces_list = stub_->GetBanlist(&context, request); while (announces_list->Read(&reply)) { std::cout << reply.ip_address() << std::endl; } // Get status and handle errors auto status = announces_list->Finish(); if (!status.ok()) { if (status.error_code() == grpc::DEADLINE_EXCEEDED) { std::cerr << "Could not connect to API server. Timeout exceed" << std::endl; return; } else { std::cerr << "RPC failed " + status.error_message(); return; } } } private: std::unique_ptr stub_; }; void silent_logging_function(gpr_log_func_args *args) { // We do not want any logging here } int main(int argc, char** argv) { if (argc <= 1) { std::cerr << "Please provide request" << std::endl; return 1; } gpr_set_log_function(silent_logging_function); // Instantiate the client. It requires a channel, out of which the actual RPCs // are created. This channel models a connection to an endpoint (in this case, // localhost at port 50051). We indicate that the channel isn't authenticated // (use of InsecureCredentials()). FastnetmonClient fastnetmon( grpc::CreateChannel("localhost:50052", grpc::InsecureCredentials())); std::string request_command = argv[1]; if (request_command == "get_banlist") { fastnetmon.GetBanList(); } else if (request_command == "ban" or request_command == "unban") { if (argc < 2) { std::cerr << "Please provide IP for action" << std::endl; return(1); } std::string ip_for_ban = argv[2]; if (request_command == "ban") { fastnetmon.ExecuteBan(ip_for_ban, true); } else { fastnetmon.ExecuteBan(ip_for_ban, false); } } else { std::cerr << "Unknown command" << std::endl; } return 0; } fastnetmon-1.1.4/src/fastnetmon_centos6.spec000066400000000000000000000072421343111404700211600ustar00rootroot00000000000000# # Pre/post params: https://fedoraproject.org/wiki/Packaging:ScriptletSnippets # %global fastnetmon_attackdir %{_localstatedir}/log/fastnetmon_attacks %global fastnetmon_user root %global fastnetmon_group %{fastnetmon_user} %global fastnetmon_config_path %{_sysconfdir}/fastnetmon.conf Name: fastnetmon Version: 1.1.1 Release: 1%{?dist} Summary: A high performance DoS/DDoS load analyzer built on top of multiple packet capture engines (NetFlow, IPFIX, sFLOW, netmap, PF_RING, PCAP). Group: System Environment/Daemons License: GPLv2 URL: https://fastnetmon.com # Top level fodler inside archive should be named as "fastnetmon-1.1.1" #Source0: https://github.com/pavel-odintsov/fastnetmon/archive/v%{version}.tar.gz Source0: https://github.com/pavel-odintsov/fastnetmon/archive/fastnetmon-%{version}.tar.gz # Yes, it's bad idea to specify fixed version of PF_RING but they have strange issue when we use another library version BuildRequires: git, make, gcc, gcc-c++, boost-devel, GeoIP-devel, log4cpp-devel BuildRequires: ncurses-devel, boost-thread, boost-regex, libpcap-devel, gpm-devel, clang, cmake BuildRequires: pfring >= 6.0.3-9154 Requires: pfring >= 6.0.3-9154 Requires: log4cpp, daemonize, libpcap, boost-thread, boost-thread, boost-regex Requires(pre): shadow-utils Requires(post): chkconfig Requires(preun): chkconfig, initscripts Requires(postun): initscripts Provides: fastnetmon %description A high performance DoS/DDoS load analyzer built on top of multiple packet capture engines (NetFlow, IPFIX, sFLOW, netmap, PF_RING, PCAP). %prep # For production # %setup -n fastnetmon-%{version} # Testing # Specify name of folder inside rpm package %setup -n fastnetmon-master %build cd src mkdir build cd build # You could disable PF_RING support with param: -DDISABLE_PF_RING_SUPPORT=ON # WE have broken cmake library with Boost on CentOS 6 and should use library from cmake # http://public.kitware.com/Bug/view.php?id=15270 cmake .. -DWE_USE_PFRING_FROM_NTOP=ON -DBoost_NO_BOOST_CMAKE=BOOL:ON make %install # install init script install -p -D -m 0755 src/fastnetmon_init_script_centos6 %{buildroot}%{_initrddir}/fastnetmon # install daemon binary file install -p -D -m 0755 src/build/fastnetmon %{buildroot}%{_sbindir}/fastnetmon # install client binary file install -p -D -m 0755 src/build/fastnetmon_client %{buildroot}%{_bindir}/fastnetmon_client # install config install -p -D -m 0755 src/fastnetmon.conf %{buildroot}%{fastnetmon_config_path} # Create log folder install -p -d -m 0700 %{buildroot}%{fastnetmon_attackdir} %pre exit 0 %post if [ $1 -eq 1 ]; then # It's install /sbin/chkconfig --add %{name} /sbin/chkconfig %{name} on /sbin/service %{name} start # Fix pfring issue with library path echo "/usr/local/lib" > /etc/ld.so.conf.d/pfring.conf /sbin/ldconfig fi if [ $1 -eq 2 ]; then # upgrade #/sbin/service %{name} restart >/dev/null 2>&1 chmod 700 %{fastnetmon_attackdir} fi %preun # Pre remove if [ $1 -eq 0 ]; then # Uninstall # Stops fastnetmon and disable it loading at startup /sbin/service %{name} stop >/dev/null 2>&1 /sbin/chkconfig --del %{name} fi %postun # Post remove %files #%doc LICENSE CHANGES README # init.d script %{_initrddir}/fastnetmon # binary daemon %{_sbindir}/fastnetmon %{_bindir}/fastnetmon_client %config(noreplace) %{_sysconfdir}/fastnetmon.conf %attr(700,%{fastnetmon_user},%{fastnetmon_group}) %dir %{fastnetmon_attackdir} %changelog * Mon Mar 23 2015 Pavel Odintsov - 1.1.1-1 - First RPM package release fastnetmon-1.1.4/src/fastnetmon_centos7.spec000066400000000000000000000067101343111404700211600ustar00rootroot00000000000000# # Pre/post params: https://fedoraproject.org/wiki/Packaging:ScriptletSnippets # %global fastnetmon_attackdir %{_localstatedir}/log/fastnetmon_attacks %global fastnetmon_user root %global fastnetmon_group %{fastnetmon_user} %global fastnetmon_config_path %{_sysconfdir}/fastnetmon.conf Name: fastnetmon Version: 1.1.1 Release: 1%{?dist} Summary: A high performance DoS/DDoS load analyzer built on top of multiple packet capture engines (NetFlow, IPFIX, sFLOW, netmap, PF_RING, PCAP). Group: System Environment/Daemons License: GPLv2 URL: https://fastnetmon.com # Top level fodler inside archive should be named as "fastnetmon-1.1.1" #Source0: https://github.com/pavel-odintsov/fastnetmon/archive/v%{version}.tar.gz Source0: https://github.com/pavel-odintsov/fastnetmon/archive/fastnetmon-%{version}.tar.gz # Yes, it's bad idea to specify fixed version of PF_RING but they have strange issue when we use another library version BuildRequires: git, make, gcc, gcc-c++, boost-devel, GeoIP-devel, log4cpp-devel BuildRequires: ncurses-devel, boost-thread, boost-regex, libpcap-devel, gpm-devel, clang, cmake BuildRequires: pfring >= 6.0.3-9154 BuildRequires: systemd Requires: pfring >= 6.0.3-9154 Requires: log4cpp, libpcap, boost-thread, boost-thread, boost-regex Requires(pre): shadow-utils Requires(post): systemd Requires(preun): systemd Requires(postun): systemd Provides: fastnetmon %description A high performance DoS/DDoS load analyzer built on top of multiple packet capture engines (NetFlow, IPFIX, sFLOW, netmap, PF_RING, PCAP). %prep # For production # %setup -n fastnetmon-%{version} # Testing # Specify name of folder inside rpm package %setup -n fastnetmon-master %build cd src mkdir build cd build # You could disable PF_RING support with param: -DDISABLE_PF_RING_SUPPORT=ON cmake .. -DWE_USE_PFRING_FROM_NTOP=ON make %install # install init script install -p -D -m 0755 src/fastnetmon.service %{buildroot}%{_sysconfdir}/systemd/system/fastnetmon.service # install daemon binary file install -p -D -m 0755 src/build/fastnetmon %{buildroot}%{_sbindir}/fastnetmon # install client binary file install -p -D -m 0755 src/build/fastnetmon_client %{buildroot}%{_bindir}/fastnetmon_client # install config install -p -D -m 0755 src/fastnetmon.conf %{buildroot}%{fastnetmon_config_path} # Create log folder install -p -d -m 0700 %{buildroot}%{fastnetmon_attackdir} %pre exit 0 %post %systemd_post fastnetmon.service if [ $1 -eq 1 ]; then # It's install # Enable autostart /usr/bin/systemctl enable fastnetmon.service /usr/bin/systemctl start fastnetmon.service # Fix pfring issue with library path echo "/usr/local/lib" > /etc/ld.so.conf.d/pfring.conf /sbin/ldconfig fi if [ $1 -eq 2 ]; then # upgrade chmod 700 %{fastnetmon_attackdir} fi %preun %systemd_preun fastnetmon.service # Pre remove #if [ $1 -eq 0 ]; then # Uninstall #fi %postun %systemd_postun_with_restart fastnetmon.service %files #%doc LICENSE CHANGES README # init.d script %{_sysconfdir}/systemd/system # binary daemon %{_sbindir}/fastnetmon %{_bindir}/fastnetmon_client %config(noreplace) %{_sysconfdir}/fastnetmon.conf %attr(700,%{fastnetmon_user},%{fastnetmon_group}) %dir %{fastnetmon_attackdir} %changelog * Mon Mar 23 2015 Pavel Odintsov - 1.1.1-1 - First RPM package release fastnetmon-1.1.4/src/fastnetmon_client.cpp000066400000000000000000000025541343111404700207060ustar00rootroot00000000000000#include #include #include #include #include #include #include #include std::string cli_stats_file_path = "/tmp/fastnetmon.dat"; int main() { // Init ncurses screen initscr(); // disable any character output noecho(); // hide cursor curs_set(0); // Do not wait for getch timeout(0); while (true) { sleep(1); // clean up screen clear(); int c = getch(); if (c == 'q') { endwin(); exit(0); } char* cli_stats_file_path_env = getenv("cli_stats_file_path"); if (cli_stats_file_path_env != NULL) { cli_stats_file_path = std::string(cli_stats_file_path_env); } std::ifstream reading_file; reading_file.open(cli_stats_file_path.c_str(), std::ifstream::in); if (!reading_file.is_open()) { std::cout << "Can't open fastnetmon stats file: " << cli_stats_file_path; } std::string line = ""; std::stringstream screen_buffer; while (getline(reading_file, line)) { screen_buffer << line << "\n"; } reading_file.close(); printw(screen_buffer.str().c_str()); // update screen refresh(); } /* End ncurses mode */ endwin(); } fastnetmon-1.1.4/src/fastnetmon_fedora12.spec000066400000000000000000000055241343111404700212030ustar00rootroot00000000000000%global fastnetmon_attackdir %{_localstatedir}/log/fastnetmon_attacks %global fastnetmon_user root %global fastnetmon_group %{fastnetmon_user} %global fastnetmon_config_path %{_sysconfdir}/fastnetmon.conf %global fastnetmon_commit 86b951b6dffae0fc1e6cbf66fe5f0f4aa61aaa5a %global fastnetmon_project_name fastnetmon %global fastnetmon_company FastNetMon LTD Name: fastnetmon Version: 1.1.1 Release: 1%{?dist} Summary: A high performance DoS/DDoS load analyzer built on top of multiple packet capture engines (NetFlow, IPFIX, sFLOW, netmap, PF_RING, PCAP). Group: System Environment/Daemons License: GPLv2 URL: https://fastnetmon.com Source0: https://github.com/%{fastnetmon_company}/%{name}/archive/%{fastnetmon_commit}/%{name}-%{fastnetmon_commit}.tar.gz BuildRequires: git, make, gcc, gcc-c++, boost-devel, GeoIP-devel, log4cpp-devel BuildRequires: ncurses-devel, boost-thread, boost-regex, libpcap-devel, gpm-devel, clang, cmake BuildRequires: systemd Requires: log4cpp, libpcap, boost-thread, boost-thread, boost-regex Requires(pre): shadow-utils Requires(post): systemd Requires(preun): systemd Requires(postun): systemd Provides: fastnetmon %description A high performance DoS/DDoS load analyzer built on top of multiple packet capture engines (NetFlow, IPFIX, sFLOW, netmap, PF_RING, PCAP). %prep %setup -n %{name}-%{fastnetmon_commit} %build cd src mkdir build cd build # We should disable PF_RING plugon support because we did not have it in repository cmake .. -DDISABLE_PF_RING_SUPPORT=ON make %install # install init script install -p -D -m 0755 src/fastnetmon.service %{buildroot}%{_sysconfdir}/systemd/system/fastnetmon.service # install daemon binary file install -p -D -m 0755 src/build/fastnetmon %{buildroot}%{_sbindir}/fastnetmon # install client binary file install -p -D -m 0755 src/build/fastnetmon_client %{buildroot}%{_bindir}/fastnetmon_client # install config install -p -D -m 0755 src/fastnetmon.conf %{buildroot}%{fastnetmon_config_path} # Create log folder install -p -d -m 0700 %{buildroot}%{fastnetmon_attackdir} %pre exit 0 %post %systemd_post fastnetmon.service if [ $1 -eq 2 ]; then # upgrade chmod 700 %{fastnetmon_attackdir} fi %preun %systemd_preun fastnetmon.service # Pre remove #if [ $1 -eq 0 ]; then # Uninstall #fi %postun %systemd_postun_with_restart fastnetmon.service %files #%doc LICENSE CHANGES README # init script %{_sysconfdir}/systemd/system # binary daemon %{_sbindir}/fastnetmon %{_bindir}/fastnetmon_client %config(noreplace) %{_sysconfdir}/fastnetmon.conf %attr(700,%{fastnetmon_user},%{fastnetmon_group}) %dir %{fastnetmon_attackdir} %changelog * Mon Mar 23 2015 Pavel Odintsov - 1.1.1-1 - First RPM package release fastnetmon-1.1.4/src/fastnetmon_init_script_centos6000077500000000000000000000032151343111404700226350ustar00rootroot00000000000000#!/bin/bash # # fastnetmon Startup script for FastNetMon # # chkconfig: - 85 15 # description: FastNetMon - high performance DoS/DDoS analyzer with sflow/netflow/mirror support # processname: fastnemon # config: /etc/fastnetmon.conf # pidfile: /var/run/fastnetmon.pid # ### BEGIN INIT INFO # Provides: fastnetmon # Required-Start: $local_fs $remote_fs $network # Required-Stop: $local_fs $remote_fs $network # Should-Start: # Short-Description: start and stop FastNetMon # Description: high performance DoS/DDoS analyzer with sflow/netflow/mirror support ### END INIT INFO # Source function library. . /etc/rc.d/init.d/functions # We do not use this configs #if [ -f /etc/sysconfig/fastnetmon ]; then # . /etc/sysconfig/fastnetmon #fi FASTNETMON=/usr/sbin/fastnetmon PROGNAME="fastnetmon" PIDFILE=/var/run/fastnetmon.pid RETVAL=0 ARGS="--daemonize" start() { echo -n $"Starting $PROGNAME: " $FASTNETMON $ARGS > /dev/null 2>&1 && echo_success || echo_failure RETVAL=$? echo "" return $RETVAL } stop() { echo -n $"Stopping $PROGNAME: " killproc -p $PIDFILE $FASTNETMON RETVAL=$? echo "" rm -f $PIDFILE } reload() { echo "Reloading is not supported now, sorry" #echo -n $"Reloading $PROGNAME: " #kill -HUP `cat $PIDFILE` } # See how we were called. case "$1" in start) start ;; stop) stop ;; status) status -p ${PIDFILE} $PROGNAME RETVAL=$? ;; restart) stop sleep 1 start ;; reload) reload ;; *) echo $"Usage: $prog {start|stop|restart|reload|status}" RETVAL=2 esac exit $RETVAL fastnetmon-1.1.4/src/fastnetmon_init_script_debian_6_7000077500000000000000000000024631343111404700231550ustar00rootroot00000000000000#!/bin/sh ### BEGIN INIT INFO # Provides: fastnetmon # Required-Start: $local_fs $remote_fs $network $syslog # Required-Stop: $local_fs $remote_fs $network $syslog # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: Fast DDoS detection toolkit. # Description: Fast DDoS detection toolkit with sFLOW/Netflow/netmap/pf_ring support. ### END INIT INFO # test -r /etc/default/fastnetmon && . /etc/default/fastnetmon NAME="fastnetmon" . /lib/lsb/init-functions PIDFILE="/var/run/${NAME}.pid" DAEMON="/usr/sbin/fastnetmon" DAEMON_OPTS="--daemonize" START_OPTS="--start --background --exec ${DAEMON} -- ${DAEMON_OPTS}" STOP_OPTS="--stop --pidfile ${PIDFILE}" STATUS_OPTS="--status --pidfile ${PIDFILE}" case "$1" in start) echo -n "Starting $NAME: " start-stop-daemon $START_OPTS echo "$NAME." ;; stop) echo -n "Stopping $NAME: " start-stop-daemon $STOP_OPTS rm -f $PIDFILE echo "$NAME." ;; restart) $0 stop sleep 2 $0 start ;; force-reload) $0 restart ;; # no support of status on Debian squeeze # status) # start-stop-daemon $STATUS_OPTS # ;; *) N=/etc/init.d/$NAME echo "Usage: $N {start|stop|restart}" >&2 exit 1 ;; esac exit 0 fastnetmon-1.1.4/src/fastnetmon_init_script_gentoo000077500000000000000000000012571343111404700225530ustar00rootroot00000000000000#!/sbin/runscript FASTNETMON="/opt/fastnetmon/fastnetmon" PIDFILE="/var/run/fastnetmon.pid" ARGS="--daemonize" depend() { need net } start() { ebegin "Starting fastnetmon" if [ -f "${PIDFILE}" ]; then einfo " Removing stale pidfile ${PIDFILE}" rm -f "${PIDFILE}" 1>/dev/null fi $FASTNETMON $ARGS eend $? } stop() { ebegin "Stopping fastnetmon" if [ -f $PIDFILE ]; then kill -9 $(cat $PIDFILE) 2>/dev/null RETVAL=$? fi if [ -n $RETVAL ] && [ "$RETVAL" -ne "0" ]; then ACTUAL_PID=$(pidof fastnetmon) [ -n $ACTUAL_PID ] && kill -9 $ACTUAL_PID 2>/dev/null RETVAL=$? fi rm -f "${PIDFILE}" eend $RETVAL }fastnetmon-1.1.4/src/fastnetmon_install.pl000077500000000000000000001516611343111404700207360ustar00rootroot00000000000000#!/usr/bin/perl use strict; use warnings; use Getopt::Long; use File::Basename; use Term::ANSIColor; my $pf_ring_version = '6.0.3'; my $pf_ring_url = "https://github.com/ntop/PF_RING/archive/v$pf_ring_version.tar.gz"; my $pf_ring_sha = '9fb8080defd1a079ad5f0097e8a8adb5bc264d00'; my $fastnetmon_git_path = 'https://github.com/pavel-odintsov/fastnetmon.git'; my $temp_folder_for_building_project = `mktemp -d /tmp/fastnetmon.build.dir.XXXXXXXXXX`; chomp $temp_folder_for_building_project; unless ($temp_folder_for_building_project && -e $temp_folder_for_building_project) { die "Can't create temp folder in /tmp for building project: $temp_folder_for_building_project\n"; } my $start_time = time(); my $fastnetmon_code_dir = "$temp_folder_for_building_project/fastnetmon/src"; my $install_log_path = '/tmp/fastnetmon_install.log'; # Official mirror: https://github.com/ntop/nDPI.git # But we have some patches for NTP and DNS protocols here my $ndpi_repository = 'https://github.com/pavel-odintsov/nDPI.git'; my $stable_branch_name = 'v1.1.3'; my $we_use_code_from_master = ''; # By default use mirror my $use_mirror = 1; my $mirror_url = 'https://github.com/pavel-odintsov/fastnetmon_dependencies/raw/master/files'; my $os_type = ''; my $distro_type = ''; my $distro_version = ''; my $distro_architecture = ''; my $user_email = ''; # Used for VyOS and different appliances based on rpm/deb my $appliance_name = ''; # So, you could disable this option but without this feature we could not improve FastNetMon for your distribution my $do_not_track_me = ''; my $cpus_number = 1; # We could pass options to make with this variable my $make_options = ''; # We could pass options to configure with this variable my $configure_options = ''; welcome_message(); # We will build gcc, stdc++ and boost for this distribution from sources my $build_binary_environment = ''; # With this option we could build full binary package my $create_binary_bundle = ''; my $use_modern_pf_ring = ''; # Get options from command line GetOptions( 'use-git-master' => \$we_use_code_from_master, 'do-not-track-me' => \$do_not_track_me, 'build-binary-environment' => \$build_binary_environment, 'create-binary-bundle' => \$create_binary_bundle, 'use-modern-pf-ring' => \$use_modern_pf_ring, ); # Bump PF_RING version if ($use_modern_pf_ring) { $pf_ring_version = '6.6.0'; $pf_ring_url = "https://github.com/ntop/PF_RING/archive/$pf_ring_version.tar.gz"; $pf_ring_sha = '79ff86e48df857e4e884646accfc97bdcdc54b04'; } my $we_have_ndpi_support = '1'; my $we_have_luajit_support = '1'; my $we_have_hiredis_support = '1'; my $we_have_log4cpp_support = '1'; my $we_have_pfring_support = ''; my $we_have_mongo_support = '1'; my $we_have_protobuf_support = ''; my $we_have_grpc_support = ''; my $we_have_golang_support = ''; my $we_have_gobgp_support = ''; my $enable_gobgp_backend = ''; if ($enable_gobgp_backend) { $we_have_protobuf_support = 1; $we_have_grpc_support = 1; $we_have_golang_support = 1; $we_have_gobgp_support = 1; } main(); sub welcome_message { # Clear screen print "\033[2J"; # Jump to 0.0 position print "\033[0;0H"; print color('bold green'); print "Hi there!\n\n"; print color('reset'); print "We need about ten minutes of your time for installing FastNetMon toolkit\n\n"; print "Also, we have "; print color('bold cyan'); print "FastNetMon Advanced"; print color('reset'); print " version with big number of improvements: "; print color('bold cyan'); print "https://fastnetmon.com/fastnetmon-advanced/?utm_source=community_install_script&utm_medium=email\n\n"; print color('reset'); print "You could order free one-month trial for Advanced version here "; print color('bold cyan'); print "https://fastnetmon.com/trial/?utm_source=community_install_script&utm_medium=email\n\n"; print color('reset'); print "In case of any issues with install script please use "; print color('bold cyan'); print "https://fastnetmon.com/contact/?utm_source=community_install_script&utm_medium=email"; print color('reset'); print " to report them\n\n"; } sub get_logical_cpus_number { if ($os_type eq 'linux') { my @cpuinfo = `cat /proc/cpuinfo`; chomp @cpuinfo; my $cpus_number = scalar grep {/processor/} @cpuinfo; return $cpus_number; } elsif ($os_type eq 'macosx' or $os_type eq 'freebsd') { my $cpus_number = `sysctl -n hw.ncpu`; chomp $cpus_number; } } sub install_additional_repositories { if ($distro_type eq 'centos') { if ($distro_version == 6) { print "Install EPEL repository for your system\n"; yum('https://dl.fedoraproject.org/pub/epel/epel-release-latest-6.noarch.rpm'); } if ($distro_version == 7) { print "Install EPEL repository for your system\n"; yum('https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm'); } } } sub get_user_email { # http://docs.travis-ci.com/user/environment-variables/#Default-Environment-Variables if (defined($ENV{'TRAVIS'}) && $ENV{'TRAVIS'}) { return; } my $user_entered_valid_email = 0; do { print "\n"; print "Please provide your business email address to receive important information about security updates\n"; print "In addition, we can send promotional messages to this email (very rare)\n"; print "You can find our privacy policy here https://fastnetmon.com/privacy-policy/\n"; print "We will provide an option to disable any email from us\n"; print "We will not share your email with any third party companies.\n\n"; print "If you continue install process you accept our subscription rules automatically\n\n"; print "Email: "; my $raw_email = ; chomp $raw_email; if ($raw_email =~ /\@/ && length $raw_email > 3) { $user_entered_valid_email = 1; $user_email = $raw_email; } else { print "Sorry you have entered invalid email, please try again!\n"; } } while !$user_entered_valid_email; print "\nThank you so much!\n\n"; } ### Functions start here sub main { detect_distribution(); get_user_email(); $cpus_number = get_logical_cpus_number(); # We could get huge speed benefits with this option if ($cpus_number > 1) { print "You have really nice server with $cpus_number CPU's and we will use them all for build process :)\n"; $make_options = "-j $cpus_number"; } # We have PF_RING only for Linux if ($os_type eq 'linux') { $we_have_pfring_support = 1; } if ($os_type eq 'macosx') { # Really strange issue https://github.com/pavel-odintsov/fastnetmon/issues/415 $we_have_hiredis_support = 0; } # CentOS base repository is very very poor and we need EPEL for some dependencies install_additional_repositories(); # Refresh information about packages init_package_manager(); if ($os_type eq 'freebsd') { exec_command("pkg install -y wget"); } send_tracking_information('started'); if ($build_binary_environment) { install_gcc(); install_boost_builder(); install_boost(); } if ($we_have_pfring_support) { install_pf_ring(); } install_json_c(); if ($we_have_ndpi_support) { install_ndpi(); } if ($we_have_luajit_support) { install_luajit(); install_luajit_libs(); } if ($we_have_hiredis_support) { install_hiredis(); } if ($we_have_mongo_support) { install_mongo_client(); } if ($we_have_protobuf_support) { install_protobuf(); } if ($we_have_grpc_support) { install_grpc(); } if ($we_have_golang_support) { install_golang(); } if ($we_have_gobgp_support) { install_gobgp(); } if ($we_have_log4cpp_support) { install_log4cpp(); } install_fastnetmon(); send_tracking_information('finished'); if ($create_binary_bundle) { create_binary_bundle(); } my $install_time = time() - $start_time; my $pretty_install_time_in_minutes = sprintf("%.2f", $install_time / 60); print "We have built project in $pretty_install_time_in_minutes minutes\n"; } sub create_binary_bundle { chdir $temp_folder_for_building_project; chdir "fastnetmon"; my $bundle_version = ''; if ($we_use_code_from_master) { my $git_last_commit_id = `git log --format="%H" -n 1`; chomp $git_last_commit_id; $bundle_version = "git-$git_last_commit_id"; } else { $bundle_version = $stable_branch_name; } my $bundle_file_name = "fastnetmon-binary-$bundle_version-$distro_type-$distro_version-$distro_architecture.tar.gz"; my $full_bundle_path = "/tmp/$bundle_file_name"; print "We will create bundle with name $bundle_file_name\n"; exec_command("$temp_folder_for_building_project/fastnetmon/src/scripts/build_libary_bundle.pl $full_bundle_path"); print "You could download bundle here $full_bundle_path\n"; if ($distro_type eq 'ubuntu' or $distro_type eq 'debian') { print "We could build .deb packages for this OS\n"; exec_command("perl $temp_folder_for_building_project/fastnetmon/src/scripts/build_any_package.pl deb $full_bundle_path"); } if ($distro_type eq 'centos') { print "We could build .rpm packages for this OS\n"; exec_command("perl $temp_folder_for_building_project/fastnetmon/src/scripts/build_any_package.pl rpm $full_bundle_path"); } } sub send_tracking_information { my $step = shift; unless ($do_not_track_me) { my $stats_url = "http://178.62.227.110/new_fastnetmon_installation"; my $post_data = "distro_type=$distro_type&os_type=$os_type&distro_version=$distro_version&distro_architecture=$distro_architecture&step=$step&we_use_code_from_master=$we_use_code_from_master&user_email=$user_email"; my $user_agent = 'FastNetMon install tracker v1'; `wget --post-data="$post_data" --user-agent="$user_agent" -q '$stats_url'`; } } sub exec_command { my $command = shift; open my $fl, ">>", $install_log_path; print {$fl} "We are calling command: $command\n\n"; my $output = `$command 2>&1 >> $install_log_path`; print {$fl} "Command finished with code $?\n\n"; if ($? == 0) { return 1; } else { return ''; } } sub get_sha1_sum { my $path = shift; if ($os_type eq 'freebsd') { # # We should not use 'use' here because we haven't this package on non FreeBSD systems by default require Digest::SHA; # SHA1 my $sha = Digest::SHA->new(1); $sha->addfile($path); return $sha->hexdigest; } my $hasher_name = ''; if ($os_type eq 'macosx') { $hasher_name = 'shasum'; } elsif ($os_type eq 'freebsd') { $hasher_name = 'sha1'; } else { # Linux $hasher_name = 'sha1sum'; } my $output = `$hasher_name $path`; chomp $output; my ($sha1) = ($output =~ m/^(\w+)\s+/); return $sha1; } sub download_file { my ($url, $path, $expected_sha1_checksumm) = @_; # We use pretty strange format for $path and need to sue special function to extract it my ($path_filename, $path_dirs, $path_suffix) = fileparse($path); # $path_filename if ($use_mirror) { $url = $mirror_url . "/" . $path_filename; } `wget --no-check-certificate --quiet '$url' -O$path`; if ($? != 0) { print "We can't download archive $url correctly\n"; return ''; } if ($expected_sha1_checksumm) { my $calculated_checksumm = get_sha1_sum($path); if ($calculated_checksumm eq $expected_sha1_checksumm) { return 1; } else { print "Downloaded archive has incorrect sha1: $calculated_checksumm expected: $expected_sha1_checksumm\n"; return ''; } } else { return 1; } } sub install_binary_gcc { my $binary_repository_path = 'http://213.133.111.200/fastnetmon_gcc_toolchain'; my $package_distro_version = ''; if ($distro_type eq 'debian') { # Debian 6: 6.0.10 # Debian 7: 7.8 # Debian 8: 8.1 if ($distro_version =~ m/^(6)/) { $package_distro_version = $1; } else { $package_distro_version = int($distro_version); } } elsif ($distro_type eq 'ubuntu') { $package_distro_version = $distro_version; } elsif ($distro_type eq 'centos') { $package_distro_version = $distro_version; } chdir $temp_folder_for_building_project; my $distribution_file_name = "gcc-5.2.0-$distro_type-$package_distro_version-$distro_architecture.tar.gz"; my $full_path = "$binary_repository_path/$distribution_file_name"; print "We will try to download prebuilded binary gcc package for your distribution\n"; print "We will download from $full_path\n"; my $gcc_binary_download_result = download_file($full_path, $distribution_file_name); unless ($gcc_binary_download_result) { print "Download failed, skip to source compilation\n"; return ''; } print "Unpack gcc binary package\n"; # Unpack file to opt exec_command("tar -xf $distribution_file_name -C /opt"); # Remove archive unlink($distribution_file_name); return 1; } sub install_gcc { my $result = install_binary_gcc(); # Add new compiler to configure options # It's mandatory for log4cpp $configure_options = "CC=/opt/gcc520/bin/gcc CXX=/opt/gcc520/bin/g++"; # More detailes about jam lookup: http://www.boost.org/build/doc/html/bbv2/overview/configuration.html # We use non standard gcc compiler for Boost builder and Boost and specify it this way open my $fl, ">", "/root/user-config.jam" or die "Can't open $! file for writing manifest\n"; print {$fl} "using gcc : 5.2 : /opt/gcc520/bin/g++ ;\n"; close $fl; # When we run it with vzctl exec we ahve broken env and should put config in /etc too open my $etcfl, ">", "/etc/user-config.jam" or die "Can't open $! file for writing manifest\n"; print {$etcfl} "using gcc : 5.2 : /opt/gcc520/bin/g++ ;\n"; close $etcfl; # Install gcc from sources if ($distro_type eq 'debian') { my @dependency_list = ('libmpfr-dev', 'libmpc-dev'); if ($distro_version <= 7) { # We have another name for Debian 6 Squeeze push @dependency_list, 'libgmp3-dev'; } else { push @dependency_list, 'libgmp-dev'; } apt_get(@dependency_list); } elsif ($distro_type eq 'ubuntu') { my @dependency_list = ('libmpfr-dev', 'libmpc-dev', 'libgmp-dev'); apt_get(@dependency_list); } elsif ($distro_type eq 'centos') { if ($distro_version == 6) { # We haven't libmpc in base repository here and should enable EPEL yum('https://dl.fedoraproject.org/pub/epel/epel-release-latest-6.noarch.rpm'); } my @dependency_list = ('gmp-devel', 'mpfr-devel', 'libmpc-devel'); yum(@dependency_list); } # Please be careful! This libs required for binary version of gcc! We should install they! # Do not call source compilation in this case if ($result) { return; } print "Download gcc archive\n"; chdir $temp_folder_for_building_project; my $archive_file_name = 'gcc-5.2.0.tar.gz'; my $gcc_download_result = download_file("ftp://ftp.mpi-sb.mpg.de/pub/gnu/mirror/gcc.gnu.org/pub/gcc/releases/gcc-5.2.0/$archive_file_name", $archive_file_name, '713211883406b3839bdba4a22e7111a0cff5d09b'); unless ($gcc_download_result) { die "Can't download gcc sources\n"; } print "Unpack archive\n"; exec_command("tar -xf $archive_file_name"); exec_command("mkdir $temp_folder_for_building_project/gcc-5.2.0-objdir"); chdir "$temp_folder_for_building_project/gcc-5.2.0-objdir"; print "Configure build system\n"; exec_command("$temp_folder_for_building_project/gcc-5.2.0/configure --prefix=/opt/gcc520 --enable-languages=c,c++ --disable-multilib"); print "Build gcc\n"; exec_command("make $make_options"); exec_command("make $make_options install"); # We do not add it to ld.so.conf.d path because it could broke system } sub install_boost { chdir '/opt'; my $archive_file_name = 'boost_1_58_0.tar.gz'; print "Install Boost dependencies\n"; # libicu dependencies if ($distro_type eq 'ubuntu') { if ($distro_version eq '14.04') { apt_get('libicu52'); } if ($distro_version eq '12.04') { apt_get('libicu48'); } } print "Download Boost source code\n"; my $boost_download_result = download_file("http://downloads.sourceforge.net/project/boost/boost/1.58.0/boost_1_58_0.tar.gz?r=http%3A%2F%2Fwww.boost.org%2Fusers%2Fhistory%2Fversion_1_58_0.html&ts=1439207367&use_mirror=cznic", $archive_file_name, 'a27b010b9d5de0c07df9dddc9c336767725b1e6b'); unless ($boost_download_result) { die "Can't download Boost source code\n"; } print "Unpack Boost source code\n"; exec_command("tar -xf $archive_file_name"); # Remove archive unlink "$archive_file_name"; chdir "boost_1_58_0"; print "Build Boost\n"; # We have troubles when run this code with vzctl exec so we should add custom compiler in path # So without HOME=/root nothing worked correctly due to another "openvz" feature my $b2_build_result = exec_command("HOME=/root PATH=\$PATH:/opt/gcc520/bin /opt/boost_build1.5.8/bin/b2 -j$cpus_number --build-dir=/tmp/boost_build_temp_directory_1_5_8 toolset=gcc-5.2 link=shared --without-test --without-python --without-wave --without-graph --without-coroutine --without-math --without-log --without-graph_parallel --without-mpi"); # We should not do this check because b2 build return bad return code even in success case... when it can't build few non important targets unless ($b2_build_result) { ### die "Can't execute b2 build correctly\n"; } } sub install_boost_builder { chdir $temp_folder_for_building_project; # We need libc headers for compilation of this code if ($distro_type eq 'centos') { yum('glibc-devel'); } # We use another name because it uses same name as boost distribution my $archive_file_name = 'boost-builder-1.58.0.tar.gz'; print "Download boost builder\n"; my $boost_build_result = download_file("https://github.com/boostorg/build/archive/boost-1.58.0.tar.gz", $archive_file_name, 'e86375ed83ed07a79a33c76e80e8648d969b3218'); unless ($boost_build_result) { die "Can't download boost builder\n"; } print "Unpack boost builder\n"; exec_command("tar -xf $archive_file_name"); chdir "build-boost-1.58.0"; print "Build Boost builder\n"; # We haven't system compiler here and we will use custom gcc for compilation here my $bootstrap_result = exec_command("CC=/opt/gcc520/bin/gcc CXX=/opt/gcc520/bin/g++ ./bootstrap.sh --with-toolset=cc"); unless ($bootstrap_result) { die "bootstrap of Boost Builder failed, please check logs\n"; } # We should specify toolset here if we want to do build with custom compiler # We have troubles when run this code with vzctl exec so we should add custom compiler in path my $b2_install_result = exec_command("PATH=\$PATH:/opt/gcc520/bin ./b2 install --prefix=/opt/boost_build1.5.8 toolset=gcc"); unless ($b2_install_result) { die "Can't execute b2 install\n"; } } sub install_luajit { chdir $temp_folder_for_building_project; my $archive_file_name = "LuaJIT-2.0.4.tar.gz"; print "Download Luajit\n"; my $luajit_download_result = download_file( "http://luajit.org/download/$archive_file_name", $archive_file_name, '6e533675180300e85d12c4bbeea2d0e41ad21172' ); unless ($luajit_download_result) { die "Can't download luajit\n"; } print "Unpack Luajit\n"; exec_command("tar -xf LuaJIT-2.0.4.tar.gz"); chdir "LuaJIT-2.0.4"; if ($os_type eq 'macosx' or $os_type eq 'freebsd') { # FreeBSD's sed has slightly different syntax exec_command("sed -i -e 's#export PREFIX= /usr/local#export PREFIX= /opt/luajit_2.0.4#' Makefile"); } else { # Standard Linux sed exec_command("sed -i 's#export PREFIX= /usr/local#export PREFIX= /opt/luajit_2.0.4#' Makefile"); } print "Build and install Luajit\n"; if ($os_type eq 'freebsd') { exec_command("pkg install -y gcc gmake"); exec_command('gmake CC=gcc48 CXX=g++48 CPP="gcc48 -E" install') } else { exec_command("make $make_options install"); } put_library_path_to_ld_so("/etc/ld.so.conf.d/luajit.conf", "/opt/luajit_2.0.4/lib"); } sub install_luajit_libs { install_lua_lpeg(); install_lua_json(); } sub install_lua_lpeg { print "Install LUA lpeg module\n"; print "Download archive\n"; chdir $temp_folder_for_building_project; my $archive_file_name = 'lpeg-0.12.2.tar.gz'; my $lpeg_download_result = download_file("http://www.inf.puc-rio.br/~roberto/lpeg/$archive_file_name", $archive_file_name, '69eda40623cb479b4a30fb3720302d3a75f45577'); unless ($lpeg_download_result) { die "Can't download lpeg\n"; } exec_command("tar -xf lpeg-0.12.2.tar.gz"); chdir "lpeg-0.12.2"; # Set path print "Install lpeg library\n"; if ($os_type eq 'macosx' or $os_type eq 'freebsd') { exec_command("sed -i -e 's#LUADIR = ../lua/#LUADIR = /opt/luajit_2.0.4/include/luajit-2.0#' makefile"); } else { exec_command("sed -i 's#LUADIR = ../lua/#LUADIR = /opt/luajit_2.0.4/include/luajit-2.0#' makefile"); } exec_command("make $make_options"); exec_command("cp lpeg.so /opt/luajit_2.0.4/lib/lua/5.1"); } sub install_json_c { my $archive_name = 'json-c-0.12-20140410.tar.gz'; my $install_path = '/opt/json-c-0.12'; print "Install json library\n"; chdir $temp_folder_for_building_project; print "Download archive\n"; my $json_c_download_result = download_file("https://github.com/json-c/json-c/archive/$archive_name", $archive_name, 'b33872f8b2837c7909e9bd8734855669c57a67ce'); unless ($json_c_download_result) { die "Can't download json-c sources\n"; } print "Uncompress it\n"; exec_command("tar -xf $archive_name"); chdir "json-c-json-c-0.12-20140410"; # Fix bugs (assigned but not used variable) which prevent code compilation if ($os_type eq 'macosx' or $os_type eq 'freebsd') { exec_command("sed -i -e '355 s#^#//#' json_tokener.c"); exec_command("sed -i -e '360 s#^#//#' json_tokener.c"); } else { exec_command("sed -i '355 s#^#//#' json_tokener.c"); exec_command("sed -i '360 s#^#//#' json_tokener.c"); # Workaround complaints from fresh compilers if ($distro_type eq 'ubuntu' && $distro_version eq '18.04') { exec_command("sed -i -e '381 s/AM_CFLAGS =/AM_CFLAGS = -Wimplicit-fallthrough=0/ ' Makefile.in"); } } print "Build it\n"; exec_command("./configure --prefix=$install_path"); print "Install it\n"; exec_command("make $make_options install"); put_library_path_to_ld_so("/etc/ld.so.conf.d/json-c.conf", "$install_path/lib"); } sub install_lua_json { print "Install LUA json module\n"; chdir $temp_folder_for_building_project; print "Download archive\n"; my $archive_file_name = '1.3.3.tar.gz'; my $lua_json_download_result = download_file("https://github.com/harningt/luajson/archive/$archive_file_name", $archive_file_name, '53455f697c3f1d7cc955202062e97bbafbea0779'); unless ($lua_json_download_result) { die "Can't download lua json\n"; } exec_command("tar -xf $archive_file_name"); chdir "luajson-1.3.3"; print "Install it\n"; exec_command("PREFIX=/opt/luajit_2.0.4 make $make_options install"); } sub install_init_scripts { # Init file for any systemd aware distro if ( ($distro_type eq 'debian' && $distro_version > 7) or ($distro_type eq 'centos' && $distro_version >= 7) ) { my $systemd_service_path = "/etc/systemd/system/fastnetmon.service"; exec_command("cp $fastnetmon_code_dir/fastnetmon.service $systemd_service_path"); exec_command("sed -i 's#/usr/sbin/fastnetmon#/opt/fastnetmon/fastnetmon#' $systemd_service_path"); print "We found systemd enabled distro and created service: fastnetmon.service\n"; print "You could run it with command: systemctl start fastnetmon.service\n"; return 1; } # Init file for CentOS 6 if ($distro_type eq 'centos' && $distro_version == 6) { my $system_init_path = '/etc/init.d/fastnetmon'; exec_command("cp $fastnetmon_code_dir/fastnetmon_init_script_centos6 $system_init_path"); exec_command("sed -i 's#/usr/sbin/fastnetmon#/opt/fastnetmon/fastnetmon#' $system_init_path"); print "We created service fastnetmon for you\n"; print "You could run it with command: /etc/init.d/fastnetmon start\n"; return 1; } # For Gentoo if ( $distro_type eq 'gentoo' ) { my $init_path_in_src = "$fastnetmon_code_dir/fastnetmon_init_script_gentoo"; my $system_init_path = '/etc/init.d/fastnetmon'; # Checker for source code version, will work only for 1.1.3+ versions if (-e $init_path_in_src) { exec_command("cp $init_path_in_src $system_init_path"); print "We created service fastnetmon for you\n"; print "You could run it with command: /etc/init.d/fastnetmon start\n"; return 1; } } # For Debian Squeeze and Wheezy # And any stable Ubuntu version if ( ($distro_type eq 'debian' && ($distro_version == 6 or $distro_version == 7)) or $distro_type eq 'ubuntu') { my $init_path_in_src = "$fastnetmon_code_dir/fastnetmon_init_script_debian_6_7"; my $system_init_path = '/etc/init.d/fastnetmon'; # Checker for source code version, will work only for 1.1.3+ versions if (-e $init_path_in_src) { exec_command("cp $init_path_in_src $system_init_path"); exec_command("sed -i 's#/usr/sbin/fastnetmon#/opt/fastnetmon/fastnetmon#' $system_init_path"); print "We created service fastnetmon for you\n"; print "You could run it with command: /etc/init.d/fastnetmon start\n"; return 1; } } } sub install_log4cpp { my $distro_file_name = 'log4cpp-1.1.1.tar.gz'; my $log4cpp_url = 'https://sourceforge.net/projects/log4cpp/files/log4cpp-1.1.x%20%28new%29/log4cpp-1.1/log4cpp-1.1.1.tar.gz/download'; my $log4cpp_install_path = '/opt/log4cpp1.1.1'; chdir $temp_folder_for_building_project; print "Download log4cpp sources\n"; my $log4cpp_download_result = download_file($log4cpp_url, $distro_file_name, '23aa5bd7d6f79992c92bad3e1c6d64a34f8fcf68'); unless ($log4cpp_download_result) { die "Can't download log4cpp\n"; } print "Unpack log4cpp sources\n"; exec_command("tar -xf $distro_file_name"); chdir "$temp_folder_for_building_project/log4cpp"; print "Build log4cpp\n"; # TODO: we need some more reliable way to specify options here if ($configure_options) { exec_command("$configure_options ./configure --prefix=$log4cpp_install_path"); } else { exec_command("./configure --prefix=$log4cpp_install_path"); } exec_command("make $make_options install"); print "Add log4cpp to ld.so.conf\n"; put_library_path_to_ld_so("/etc/ld.so.conf.d/log4cpp.conf", "$log4cpp_install_path/lib"); } sub install_grpc { # We use this commit because 0.11.1 is broken and do not build on CentOS 6 correctly my $grpc_git_commit = "7a94236d698477636dd06282f12f706cad527029"; my $grpc_install_path = "/opt/grpc_0_11_1_$grpc_git_commit"; if ($distro_type eq 'debian' or $distro_type eq 'ubuntu') { apt_get('gcc', 'make', 'autoconf', 'automake', 'git', 'libtool', 'g++', 'python-all-dev', 'python-virtualenv'); } # TODO: add deps for CentOS chdir $temp_folder_for_building_project; print "Clone gRPC repository\n"; exec_command("git clone https://github.com/grpc/grpc.git"); chdir "grpc"; # For back compatibility with old git exec_command("git checkout $grpc_git_commit"); exec_command("git submodule update --init"); print "Build gRPC\n"; exec_command("make $make_options"); print "Install gRPC\n"; exec_command("make install prefix=$grpc_install_path"); } sub install_gobgp { chdir $temp_folder_for_building_project; my $distro_file_name = 'v1.0.tar.gz'; my $gobgp_download_result = download_file("https://github.com/osrg/gobgp/archive/$distro_file_name", $distro_file_name, 'daafc31b06d95611ca76f45630e5db140ba5d4c9'); unless ($gobgp_download_result) { die "Can't download gobgp sources\n"; } exec_command("tar -xf $distro_file_name"); chdir "gobgp-1.0"; chdir "gobgp/lib"; my $go_binary = '/usr/local/go/bin/go'; print "Build gobgp\n"; exec_command("GOPATH=\"$temp_folder_for_building_project/gofolder\" $go_binary get github.com/osrg/gobgp/gobgpd"); exec_command("GOPATH=\"$temp_folder_for_building_project/gofolder\" $go_binary get github.com/osrg/gobgp/gobgp"); print "Build gobgp library\n"; exec_command("GOPATH=\"$temp_folder_for_building_project/gofolder\" $go_binary build -buildmode=c-shared -o libgobgp.so *.go"); my $libgobgp_install_path = '/opt/libgobgp_1_0_0'; print "Install gobgp library\n"; mkdir "$libgobgp_install_path"; mkdir "$libgobgp_install_path/include"; mkdir "$libgobgp_install_path/lib"; exec_command("cp libgobgp.h $libgobgp_install_path/include"); exec_command("cp libgobgp.so $libgobgp_install_path/lib"); print "Install gobgp daemon files\n"; my $gobgp_install_path = '/opt/gobgp_1_0_0'; mkdir $gobgp_install_path; exec_command("cp $temp_folder_for_building_project/gofolder/bin/gobgp $gobgp_install_path"); exec_command("cp $temp_folder_for_building_project/gofolder/bin/gobgpd $gobgp_install_path"); } sub install_golang { chdir $temp_folder_for_building_project; my $distro_file_name = ''; my $distro_file_hash = ''; if ($distro_architecture eq 'x86_64') { $distro_file_name = "go1.5.1.linux-amd64.tar.gz"; $distro_file_hash = '46eecd290d8803887dec718c691cc243f2175fe0'; } elsif ($distro_architecture eq 'i686') { $distro_file_name = 'go1.5.1.linux-386.tar.gz'; $distro_file_hash = '6ce7328f84a863f341876658538dfdf10aff86ee'; } else { die "We haven't golang for your platform sorry :(\n"; } my $golang_download_result = download_file("https://storage.googleapis.com/golang/$distro_file_name", $distro_file_name, $distro_file_hash); unless ($golang_download_result) { die "Can't download golanguage\n"; } exec_command("tar -C /usr/local -xzf $distro_file_name"); } sub install_protobuf { if ($distro_type eq 'debian' or $distro_type eq 'ubuntu') { apt_get('gcc', 'make', 'autoconf', 'automake', 'git', 'libtool', 'g++', 'curl'); } # TODO: add deps for CentOS my $protobuf_install_path = '/opt/protobuf_3.0.0_alpha4'; my $distro_file_name = 'v3.0.0-alpha-4.tar.gz'; chdir $temp_folder_for_building_project; print "Download protocol buffers\n"; my $protobuf_download_result = download_file("https://github.com/google/protobuf/archive/$distro_file_name", $distro_file_name, 'd23048ba3218af21ba65fa39bfb6326f5bf9f7a4'); unless ($protobuf_download_result) { die "Can't download protobuf\n"; } print "Unpack protocol buffers\n"; exec_command("tar -xf $distro_file_name"); chdir "protobuf-3.0.0-alpha-4"; print "Configure protobuf\n"; exec_command("./autogen.sh"); exec_command("./configure --prefix=$protobuf_install_path"); print "Build protobuf\n"; exec_command("make $make_options install"); } sub install_mongo_client { my $distro_file_name = 'mongo-c-driver-1.1.9.tar.gz'; my $mongo_install_path = '/opt/mongo_c_driver_1_1_9'; chdir $temp_folder_for_building_project; print "Download mongo\n"; my $mongo_download_result = download_file("https://github.com/mongodb/mongo-c-driver/releases/download/1.1.9/$distro_file_name", $distro_file_name, '32452481be64a297e981846e433b2b492c302b34'); unless ($mongo_download_result) { die "Can't download mongo\n"; } exec_command("tar -xf $distro_file_name"); print "Build mongo client\n"; chdir "mongo-c-driver-1.1.9"; exec_command("./configure --prefix=$mongo_install_path"); exec_command("make $make_options install"); } sub install_hiredis { my $disto_file_name = 'v0.13.1.tar.gz'; my $hiredis_install_path = '/opt/libhiredis_0_13'; chdir $temp_folder_for_building_project; print "Download hiredis\n"; my $hiredis_download_result = download_file("https://github.com/redis/hiredis/archive/$disto_file_name", $disto_file_name, '737c4ed101096c5ec47fcaeba847664352d16204'); unless ($hiredis_download_result) { die "Can't download hiredis\n"; } exec_command("tar -xf $disto_file_name"); print "Build hiredis\n"; chdir "hiredis-0.13.1"; exec_command("PREFIX=$hiredis_install_path make $make_options install"); print "Add hiredis to ld.so.conf\n"; put_library_path_to_ld_so("/etc/ld.so.conf.d/hiredis.conf", "$hiredis_install_path/lib"); } # We use global variable $ndpi_repository here sub install_ndpi { if ($distro_type eq 'debian' or $distro_type eq 'ubuntu') { apt_get('git', 'autoconf', 'libtool', 'automake', 'libpcap-dev'); } elsif ($distro_type eq 'centos') { # We have json-c-devel for CentOS 6 and 7 and will use it for nDPI build system yum('git', 'autoconf', 'automake', 'libtool', 'libpcap-devel', 'json-c-devel'); } elsif ($os_type eq 'freebsd') { exec_command("pkg install -y git autoconf automake libtool"); } print "Download nDPI\n"; if (-e "$temp_folder_for_building_project/nDPI") { # Get new code from the repository chdir "$temp_folder_for_building_project/nDPI"; exec_command("git pull"); } else { chdir $temp_folder_for_building_project; exec_command("git clone $ndpi_repository"); chdir "$temp_folder_for_building_project/nDPI"; } print "Configure nDPI\n"; exec_command("./autogen.sh"); # We have specified direct path to json-c here because it required for example app compilation exec_command("PKG_CONFIG_PATH=/opt/json-c-0.12/lib/pkgconfig ./configure --prefix=/opt/ndpi"); if ($? != 0) { print "Configure failed\n"; return; } print "Build and install nDPI\n"; exec_command("make $make_options install"); print "Add ndpi to ld.so.conf\n"; put_library_path_to_ld_so("/etc/ld.so.conf.d/ndpi.conf", "/opt/ndpi/lib"); } sub init_package_manager { print "Update package manager cache\n"; if ($distro_type eq 'debian' or $distro_type eq 'ubuntu') { exec_command("apt-get update"); } if ($os_type eq 'freebsd') { exec_command("pkg update"); } } sub put_library_path_to_ld_so { my ($ld_so_file_path, $library_path) = @_; if ($os_type eq 'macosx' or $os_type eq 'freebsd') { return; } open my $ld_so_conf_handle, ">", $ld_so_file_path or die "Can't open file $ld_so_file_path $! for writing\n"; print {$ld_so_conf_handle} $library_path; close $ld_so_conf_handle; exec_command("ldconfig"); } sub read_file { my $file_name = shift; my $res = open my $fl, "<", $file_name; unless ($res) { return ""; } my $content = join '', <$fl>; chomp $content; return $content; } # Detect operating system of this machine sub detect_distribution { # We use following global variables here: # $os_type, $distro_type, $distro_version, $appliance_name my $uname_s_output = `uname -s`; chomp $uname_s_output; # uname -a output examples: # FreeBSD 10.1-STABLE FreeBSD 10.1-STABLE #0 r278618: Thu Feb 12 13:55:09 UTC 2015 root@:/usr/obj/usr/src/sys/KERNELWITHNETMAP amd64 # Darwin MacBook-Pro-Pavel.local 14.5.0 Darwin Kernel Version 14.5.0: Wed Jul 29 02:26:53 PDT 2015; root:xnu-2782.40.9~1/RELEASE_X86_64 x86_64 # Linux ubuntu 3.16.0-30-generic #40~14.04.1-Ubuntu SMP Thu Jan 15 17:43:14 UTC 2015 x86_64 x86_64 x86_64 GNU/Linux if ($uname_s_output =~ /FreeBSD/) { $os_type = 'freebsd'; } elsif ($uname_s_output =~ /Darwin/) { $os_type = 'macosx'; } elsif ($uname_s_output =~ /Linux/) { $os_type = 'linux'; } else { warn "Can't detect platform operating system\n"; } if ($os_type eq 'linux') { # x86_64 or i686 $distro_architecture = `uname -m`; chomp $distro_architecture; if (-e "/etc/debian_version") { # Well, on this step it could be Ubuntu or Debian # We need check issue for more details my @issue = `cat /etc/issue`; chomp @issue; my $issue_first_line = $issue[0]; # Possible /etc/issue contents: # Debian GNU/Linux 8 \n \l # Ubuntu 14.04.2 LTS \n \l # Welcome to VyOS - \n \l my $is_proxmox = ''; # Really hard to detect https://github.com/proxmox/pve-manager/blob/master/bin/pvebanner for my $issue_line (@issue) { if ($issue_line =~ m/Welcome to the Proxmox Virtual Environment/) { $is_proxmox = 1; $appliance_name = 'proxmox'; last; } } if ($issue_first_line =~ m/Debian/ or $is_proxmox) { $distro_type = 'debian'; $distro_version = `cat /etc/debian_version`; chomp $distro_version; # Debian 6 example: 6.0.10 # We will try transform it to decimal number if ($distro_version =~ /^(\d+\.\d+)\.\d+$/) { $distro_version = $1; } } elsif ($issue_first_line =~ m/Ubuntu (\d+(?:\.\d+)?)/) { $distro_type = 'ubuntu'; $distro_version = $1; } elsif ($issue_first_line =~ m/VyOS/) { # Yes, VyOS is a Debian $distro_type = 'debian'; $appliance_name = 'vyos'; my $vyos_distro_version = `cat /etc/debian_version`; chomp $vyos_distro_version; # VyOS have strange version and we should fix it if ($vyos_distro_version =~ /^(\d+)\.\d+\.\d+$/) { $distro_version = $1; } } } if (-e "/etc/redhat-release") { $distro_type = 'centos'; my $distro_version_raw = `cat /etc/redhat-release`; chomp $distro_version_raw; # CentOS 6: # CentOS release 6.6 (Final) # CentOS 7: # CentOS Linux release 7.0.1406 (Core) # Fedora release 21 (Twenty One) if ($distro_version_raw =~ /(\d+)/) { $distro_version = $1; } } if (-e "/etc/gentoo-release") { $distro_type = 'gentoo'; my $distro_version_raw = `cat /etc/gentoo-release`; chomp $distro_version_raw; } unless ($distro_type) { die "This distro is unsupported, please do manual install"; } print "We detected your OS as $distro_type Linux $distro_version\n"; } elsif ($os_type eq 'macosx') { my $mac_os_versions_raw = `sw_vers -productVersion`; chomp $mac_os_versions_raw; if ($mac_os_versions_raw =~ /(\d+\.\d+)/) { $distro_version = $1; } print "We detected your OS as Mac OS X $distro_version\n"; } elsif ($os_type eq 'freebsd') { my $freebsd_os_version_raw = `uname -r`; chomp $freebsd_os_version_raw; if ($freebsd_os_version_raw =~ /^(\d+)\.?/) { $distro_version = $1; } print "We detected your OS as FreeBSD $distro_version\n"; } } sub install_pf_ring { my $pf_ring_archive_path = "$temp_folder_for_building_project/PF_RING-$pf_ring_version.tar.gz"; my $pf_ring_sources_path = "$temp_folder_for_building_project/PF_RING-$pf_ring_version"; my $kernel_version = `uname -r`; chomp $kernel_version; print "Install PF_RING dependencies with package manager\n"; if ($distro_type eq 'debian' or $distro_type eq 'ubuntu') { my @debian_packages_for_pfring = ('build-essential', 'bison', 'flex', 'subversion', 'libnuma-dev', 'wget', 'tar', 'make', 'dpkg-dev', 'dkms', 'debhelper'); # Install kernel headers only when we could compile kernel modules there my $kernel_headers_package_name = "linux-headers-$kernel_version"; if ($appliance_name eq 'vyos') { # VyOS uses another name for package for building kernel modules $kernel_headers_package_name = 'linux-vyatta-kbuild'; } elsif ($appliance_name eq 'proxmox') { $kernel_headers_package_name = "pve-headers-$kernel_version"; } push @debian_packages_for_pfring, $kernel_headers_package_name; apt_get(@debian_packages_for_pfring); if ($appliance_name eq 'vyos') { # By default we waven't this symlink and should add it manually if ($distro_architecture eq 'x86_64') { exec_command("ln -s /usr/src/linux-image/debian/build/build-amd64-none-amd64-vyos/ /lib/modules/$kernel_version/build"); } else { # i686 exec_command("ln -s /usr/src/linux-image/debian/build/build-i386-none-586-vyos/ /lib/modules/$kernel_version/build"); } } } elsif ($distro_type eq 'centos') { my @centos_dependency_packages = ('make', 'bison', 'flex', 'gcc', 'gcc-c++', 'dkms', 'numactl-devel', 'subversion'); # This package is not going to install devel headers for current kernel! my $kernel_package_name = 'kernel-devel'; # Fix deplist for OpenVZ if ($kernel_version =~ /stab/) { $kernel_package_name = "vzkernel-devel-$kernel_version"; } push @centos_dependency_packages, $kernel_package_name; my $centos_kernel_version = `uname -r`; chomp $centos_kernel_version; # But this package will install kernel devel headers for current kernel version! push @centos_dependency_packages, "$kernel_package_name-$centos_kernel_version"; yum(@centos_dependency_packages); } elsif ($distro_type eq 'gentoo') { my @gentoo_packages_for_pfring = ('subversion', 'sys-process/numactl', 'wget', 'tar'); my $gentoo_packages_for_pfring_as_string = join " ", @gentoo_packages_for_pfring; exec_command("emerge -vu $gentoo_packages_for_pfring_as_string"); if ($? != 0) { print "Emerge fail with code $?\n"; } } # Sometimes we do not want to build kernel module (Docker, KVM and other cases) my $we_could_install_kernel_modules = 1; if ($we_could_install_kernel_modules) { print "Download PF_RING $pf_ring_version sources\n"; my $pfring_download_result = download_file($pf_ring_url, $pf_ring_archive_path, $pf_ring_sha); unless ($pfring_download_result) { die "Can't download PF_RING sources\n"; } my $archive_file_name = $pf_ring_archive_path; if ($? == 0) { print "Unpack PF_RING\n"; mkdir $pf_ring_sources_path; exec_command("tar -xf $pf_ring_archive_path -C $temp_folder_for_building_project"); print "Build PF_RING kernel module\n"; exec_command("make $make_options -C $pf_ring_sources_path/kernel clean"); exec_command("make $make_options -C $pf_ring_sources_path/kernel"); exec_command("make $make_options -C $pf_ring_sources_path/kernel install"); print "Unload PF_RING if it was installed earlier\n"; exec_command("rmmod pf_ring 2>/dev/null"); print "Load PF_RING module into kernel\n"; exec_command("modprobe pf_ring"); my @dmesg = `dmesg`; chomp @dmesg; if (scalar grep (/\[PF_RING\] Initialized correctly/, @dmesg) > 0) { print "PF_RING loaded correctly\n"; } else { warn "PF_RING load error! Please fix this issue manually\n"; # We need this headers for building userspace libs exec_command("cp $pf_ring_sources_path/kernel/linux/pf_ring.h /usr/include/linux"); } } else { warn "Can't download PF_RING source code. Disable support of PF_RING\n"; } } print "Build PF_RING lib\n"; # Because we can't run configure from another folder because it can't find ZC dependency :( chdir "$pf_ring_sources_path/userland/lib"; exec_command("./configure --prefix=/opt/pf_ring_$pf_ring_version"); exec_command("make $make_options"); exec_command("make $make_options install"); # We need do this for backward compatibility with old code (v1.1.2) exec_command("ln -s /opt/pf_ring_$pf_ring_version /opt/pf_ring"); print "Create library symlink\n"; print "Add pf_ring to ld.so.conf\n"; put_library_path_to_ld_so("/etc/ld.so.conf.d/pf_ring.conf", "/opt/pf_ring_$pf_ring_version/lib"); } sub apt_get { my @packages_list = @_; # We install one package per apt-get call because installing multiple packages in one time could fail of one package is broken for my $package (@packages_list) { exec_command("apt-get install -y --force-yes $package"); if ($? != 0) { print "Package '$package' install failed with code $?\n" } } } sub yum { my @packages_list = @_; for my $package (@packages_list) { exec_command("yum install -y $package"); if ($? != 0) { print "Package '$package' install failed with code $?\n"; } } } sub install_fastnetmon { print "Install FastNetMon dependency list\n"; if ($distro_type eq 'debian' or $distro_type eq 'ubuntu') { my @fastnetmon_deps = ("git", "g++", "gcc", "libgpm-dev", "libncurses5-dev", "liblog4cpp5-dev", "libnuma-dev", "libgeoip-dev","libpcap-dev", "cmake", "pkg-config", "libhiredis-dev", ); # Do not install Boost when we build it manually unless ($build_binary_environment) { # We add this dependencies because package libboost-all-dev is broken on VyOS if ($appliance_name eq 'vyos') { push @fastnetmon_deps, ('libboost-regex-dev', 'libboost-system-dev', 'libboost-thread-dev'); } else { push @fastnetmon_deps, "libboost-all-dev"; } } apt_get(@fastnetmon_deps); } elsif ($distro_type eq 'centos') { my @fastnetmon_deps = ('git', 'make', 'gcc', 'gcc-c++', 'GeoIP-devel', 'ncurses-devel', 'glibc-static', 'ncurses-static', 'libpcap-devel', 'gpm-static', 'gpm-devel', 'cmake', 'pkgconfig', 'hiredis-devel', ); if ($distro_type eq 'centos' && int($distro_version) == 7) { push @fastnetmon_deps, 'net-tools'; } # Do not install Boost when we build it manually unless ($build_binary_environment) { @fastnetmon_deps = (@fastnetmon_deps, 'boost-devel', 'boost-thread') } yum(@fastnetmon_deps); } elsif ($distro_type eq 'gentoo') { my @fastnetmon_deps = ("dev-vcs/git", "gcc", "sys-libs/gpm", "sys-libs/ncurses", "dev-libs/log4cpp", "dev-libs/geoip", "net-libs/libpcap", "dev-util/cmake", "pkg-config", "dev-libs/hiredis", "dev-libs/boost" ); my $fastnetmon_deps_as_string = join " ", @fastnetmon_deps; exec_command("emerge -vu $fastnetmon_deps_as_string"); if ($? != 0) { print "Emerge fail with code $?\n"; } } elsif ($os_type eq 'freebsd') { exec_command("pkg install -y cmake git ncurses boost-all log4cpp"); } print "Clone FastNetMon repo\n"; chdir $temp_folder_for_building_project; if (-e $fastnetmon_code_dir) { # Code already downloaded chdir $fastnetmon_code_dir; # Switch to master if we on stable branch if ($we_use_code_from_master) { exec_command("git checkout master"); printf("\n"); } exec_command("git pull"); } else { # Pull new code if ($we_use_code_from_master) { exec_command("git clone $fastnetmon_git_path --quiet 2>/dev/null"); } else { exec_command("git clone $fastnetmon_git_path --quiet 2>/dev/null"); } if ($? != 0) { die "Can't clone source code\n"; } } if ($we_use_code_from_master) { } else { # We use this approach because older git versions do not support git clone -b ... correctly # warning: Remote branch v1.1.2 not found in upstream origin, using HEAD instead chdir "fastnetmon"; exec_command("git checkout $stable_branch_name"); } exec_command("mkdir -p $fastnetmon_code_dir/build"); chdir "$fastnetmon_code_dir/build"; my $cmake_params = ""; unless ($we_have_pfring_support) { $cmake_params .= " -DDISABLE_PF_RING_SUPPORT=ON"; } if ($distro_type eq 'centos' && $distro_version == 6 && !$build_binary_environment) { # Disable cmake script from Boost package because it's broken: # http://public.kitware.com/Bug/view.php?id=15270 $cmake_params .= " -DBoost_NO_BOOST_CMAKE=BOOL:ON"; } if ($enable_gobgp_backend) { $cmake_params .= " -DENABLE_GOBGP_SUPPORT=ON"; } # We should specify this option if we want to build with custom gcc compiler if ($build_binary_environment) { $cmake_params .= " -DENABLE_BUILD_IN_CPP_11_CUSTOM_ENVIRONMENT=ON "; # We should specify compilir this way $cmake_params .= " -DCMAKE_C_COMPILER=/opt/gcc520/bin/gcc -DCMAKE_CXX_COMPILER=/opt/gcc520/bin/g++ "; } # Bump version in cmake build system if ($use_modern_pf_ring) { system("sed -i 's/pf_ring_6.0.3/pf_ring_$pf_ring_version/' ../CMakeLists.txt") } if (defined($ENV{'TRAVIS'}) && $ENV{'TRAVIS'}) { system("cmake .. $cmake_params"); system("make $make_options"); } else { system("cmake .. $cmake_params"); system("make $make_options"); } my $fastnetmon_dir = "/opt/fastnetmon"; my $fastnetmon_build_binary_path = "$fastnetmon_code_dir/build/fastnetmon"; unless (-e $fastnetmon_build_binary_path) { die "Can't build fastnetmon!"; } mkdir $fastnetmon_dir; print "Install fastnetmon to dir $fastnetmon_dir\n"; exec_command("cp $fastnetmon_build_binary_path $fastnetmon_dir/fastnetmon"); exec_command("cp $fastnetmon_code_dir/build/fastnetmon_client $fastnetmon_dir/fastnetmon_client"); if (-e "$fastnetmon_code_dir/build/fastnetmon_api_client") { exec_command("cp $fastnetmon_code_dir/build/fastnetmon_api_client $fastnetmon_dir/fastnetmon_api_client"); } my $fastnetmon_config_path = "/etc/fastnetmon.conf"; unless (-e $fastnetmon_config_path) { print "Create stub configuration file\n"; exec_command("cp $fastnetmon_code_dir/fastnetmon.conf $fastnetmon_config_path"); # netmap will detach interface completely and will broke network # So we do not configure it automatically if ($os_type ne 'freebsd') { my @interfaces = get_active_network_interfaces(); my $interfaces_as_list = join ',', @interfaces; print "Select $interfaces_as_list as active interfaces\n"; print "Tune config\n"; if ($os_type eq 'macosx' or $os_type eq 'freebsd') { exec_command("sed -i -e 's/interfaces.*/interfaces = $interfaces_as_list/' $fastnetmon_config_path"); } else { exec_command("sed -i 's/interfaces.*/interfaces = $interfaces_as_list/' $fastnetmon_config_path"); } } } print "If you have any issues, please check /var/log/fastnetmon.log file contents\n"; print "Please add your subnets in /etc/networks_list in CIDR format one subnet per line\n"; my $init_script_result = install_init_scripts(); # Print unified run message unless ($init_script_result) { print "You can run fastnetmon with command: $fastnetmon_dir/fastnetmon\n"; } } sub get_active_network_interfaces { my @interfaces = `LANG=C netstat -i|egrep -v 'lo|Iface|Kernel'|awk '{print \$1}'`; chomp @interfaces; my @clean_interfaces = (); for my $iface (@interfaces) { # skip aliases if ($iface =~ /:/) { next; } push @clean_interfaces, $iface; } return @clean_interfaces; } fastnetmon-1.1.4/src/fastnetmon_packet_parser.c000066400000000000000000001025621343111404700217130ustar00rootroot00000000000000#include "fastnetmon_packet_parser.h" /* This code is copy & paste from PF_RING user space library licensed under LGPL terms */ #include // For support uint32_t, uint16_t #include // gettimeofday #include #include #include // in6_addr #ifndef __OpenBSD__ #include #endif #include // memcpy #include #include // inet_ntop #if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__) || defined(__OpenBSD__) #include // AF_INET6 #endif // Fake fields #define ipv4_tos ip_tos #define ipv6_tos ip_tos #define ipv4_src ip_src.v4 #define ipv4_dst ip_dst.v4 #define ipv6_src ip_src.v6 #define ipv6_dst ip_dst.v6 #define host4_low host_low.v4 #define host4_high host_high.v4 #define host6_low host_low.v6 #define host6_high host_high.v6 #define host4_peer_a host_peer_a.v4 #define host4_peer_b host_peer_b.v4 #define host6_peer_a host_peer_a.v6 #define host6_peer_b host_peer_b.v6 // GRE tunnels #define GRE_HEADER_CHECKSUM 0x8000 #define GRE_HEADER_ROUTING 0x4000 #define GRE_HEADER_KEY 0x2000 #define GRE_HEADER_SEQ_NUM 0x1000 #define GRE_HEADER_VERSION 0x0007 struct gre_header { u_int16_t flags_and_version; u_int16_t proto; /* Optional fields */ }; // GTP tunnels #define GTP_SIGNALING_PORT 2123 #define GTP_U_DATA_PORT 2152 #define GTP_VERSION_1 0x1 #define GTP_VERSION_2 0x2 #define GTP_PROTOCOL_TYPE 0x1 #define GTP_VERSION_1 0x1 #define GTP_VERSION_2 0x2 #define GTP_PROTOCOL_TYPE 0x1 struct gtp_v1_hdr { #define GTP_FLAGS_VERSION 0xE0 #define GTP_FLAGS_VERSION_SHIFT 5 #define GTP_FLAGS_PROTOCOL_TYPE 0x10 #define GTP_FLAGS_RESERVED 0x08 #define GTP_FLAGS_EXTENSION 0x04 #define GTP_FLAGS_SEQ_NUM 0x02 #define GTP_FLAGS_NPDU_NUM 0x01 u_int8_t flags; u_int8_t message_type; u_int16_t payload_len; u_int32_t teid; } __attribute__((__packed__)); /* Optional: GTP_FLAGS_EXTENSION | GTP_FLAGS_SEQ_NUM | GTP_FLAGS_NPDU_NUM */ struct gtp_v1_opt_hdr { u_int16_t seq_num; u_int8_t npdu_num; u_int8_t next_ext_hdr; } __attribute__((__packed__)); /* Optional: GTP_FLAGS_EXTENSION && next_ext_hdr != 0 */ struct gtp_v1_ext_hdr { #define GTP_EXT_HDR_LEN_UNIT_BYTES 4 u_int8_t len; /* 4-byte unit */ /* * u_char contents[len*4-2]; * u_int8_t next_ext_hdr; */ } __attribute__((__packed__)); #define NO_TUNNEL_ID 0xFFFFFFFF #define NEXTHDR_HOP 0 #define NEXTHDR_TCP 6 #define NEXTHDR_UDP 17 #define NEXTHDR_IPV6 41 #define NEXTHDR_ROUTING 43 #define NEXTHDR_FRAGMENT 44 #define NEXTHDR_ESP 50 #define NEXTHDR_AUTH 51 #define NEXTHDR_ICMP 58 #define NEXTHDR_NONE 59 #define NEXTHDR_DEST 60 #define NEXTHDR_MOBILITY 135 // TCP flags #define TH_FIN_MULTIPLIER 0x01 #define TH_SYN_MULTIPLIER 0x02 #define TH_RST_MULTIPLIER 0x04 #define TH_PUSH_MULTIPLIER 0x08 #define TH_ACK_MULTIPLIER 0x10 #define TH_URG_MULTIPLIER 0x20 #define __LITTLE_ENDIAN_BITFIELD /* FIX */ struct tcphdr { u_int16_t source; u_int16_t dest; u_int32_t seq; u_int32_t ack_seq; #if defined(__LITTLE_ENDIAN_BITFIELD) u_int16_t res1 : 4, doff : 4, fin : 1, syn : 1, rst : 1, psh : 1, ack : 1, urg : 1, ece : 1, cwr : 1; #elif defined(__BIG_ENDIAN_BITFIELD) u_int16_t doff : 4, res1 : 4, cwr : 1, ece : 1, urg : 1, ack : 1, psh : 1, rst : 1, syn : 1, fin : 1; #else #error "Adjust your defines" #endif u_int16_t window; u_int16_t check; u_int16_t urg_ptr; }; struct udphdr { u_int16_t source; u_int16_t dest; u_int16_t len; u_int16_t check; }; struct eth_vlan_hdr { u_int16_t h_vlan_id; /* Tag Control Information (QoS, VLAN ID) */ u_int16_t h_proto; /* packet type ID field */ }; struct kcompact_ipv6_hdr { u_int8_t priority : 4, version : 4; u_int8_t flow_lbl[3]; u_int16_t payload_len; u_int8_t nexthdr; u_int8_t hop_limit; struct in6_addr saddr; struct in6_addr daddr; }; struct kcompact_ipv6_opt_hdr { u_int8_t nexthdr; u_int8_t hdrlen; u_int8_t padding[6]; } __attribute__((packed)); #define __LITTLE_ENDIAN_BITFIELD /* FIX */ struct iphdr { #if defined(__LITTLE_ENDIAN_BITFIELD) u_int8_t ihl : 4, version : 4; #elif defined(__BIG_ENDIAN_BITFIELD) u_int8_t version : 4, ihl : 4; #else #error "Please fix " #endif u_int8_t tos; u_int16_t tot_len; u_int16_t id; #define IP_CE 0x8000 #define IP_DF 0x4000 #define IP_MF 0x2000 #define IP_OFFSET 0x1FFF u_int16_t frag_off; u_int8_t ttl; u_int8_t protocol; u_int16_t check; u_int32_t saddr; u_int32_t daddr; /*The options start here. */ }; // Prototypes char* etheraddr2string(const u_char* ep, char* buf); char* intoa(unsigned int addr); char* _intoa(unsigned int addr, char* buf, u_short bufLen); static char* in6toa(struct in6_addr addr6); char* proto2str(u_short proto); #if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__) || defined(__OpenBSD__) /* This code from /usr/includes/linux/if_ether.h Linus file */ #define ETH_ALEN 6 /* Octets in one ethernet addr */ #define ETH_P_IP 0x0800 /* Internet Protocol packet */ #define ETH_P_IPV6 0x86DD /* IPv6 over bluebook */ /* * This is an Ethernet frame header. */ struct ethhdr { unsigned char h_dest[ETH_ALEN]; /* destination eth addr */ unsigned char h_source[ETH_ALEN]; /* source ether addr */ u_int16_t h_proto; /* packet type ID field */ } __attribute__((packed)); #endif #if defined(__FreeBSD__) || defined(__APPLE__) || defined(__OpenBSD__) u_int32_t pfring_hash_pkt(struct pfring_pkthdr* hdr) { if (hdr->extended_hdr.parsed_pkt.tunnel.tunnel_id == NO_TUNNEL_ID) { return hdr->extended_hdr.parsed_pkt.vlan_id + hdr->extended_hdr.parsed_pkt.l3_proto + hdr->extended_hdr.parsed_pkt.ip_src.v6.__u6_addr.__u6_addr32[0] + hdr->extended_hdr.parsed_pkt.ip_src.v6.__u6_addr.__u6_addr32[1] + hdr->extended_hdr.parsed_pkt.ip_src.v6.__u6_addr.__u6_addr32[2] + hdr->extended_hdr.parsed_pkt.ip_src.v6.__u6_addr.__u6_addr32[3] + hdr->extended_hdr.parsed_pkt.ip_dst.v6.__u6_addr.__u6_addr32[0] + hdr->extended_hdr.parsed_pkt.ip_dst.v6.__u6_addr.__u6_addr32[1] + hdr->extended_hdr.parsed_pkt.ip_dst.v6.__u6_addr.__u6_addr32[2] + hdr->extended_hdr.parsed_pkt.ip_dst.v6.__u6_addr.__u6_addr32[3] + hdr->extended_hdr.parsed_pkt.l4_src_port + hdr->extended_hdr.parsed_pkt.l4_dst_port; } else { return hdr->extended_hdr.parsed_pkt.vlan_id + hdr->extended_hdr.parsed_pkt.tunnel.tunneled_proto + hdr->extended_hdr.parsed_pkt.tunnel.tunneled_ip_src.v6.__u6_addr.__u6_addr32[1] + hdr->extended_hdr.parsed_pkt.tunnel.tunneled_ip_src.v6.__u6_addr.__u6_addr32[2] + hdr->extended_hdr.parsed_pkt.tunnel.tunneled_ip_src.v6.__u6_addr.__u6_addr32[3] + hdr->extended_hdr.parsed_pkt.tunnel.tunneled_ip_dst.v6.__u6_addr.__u6_addr32[0] + hdr->extended_hdr.parsed_pkt.tunnel.tunneled_ip_dst.v6.__u6_addr.__u6_addr32[1] + hdr->extended_hdr.parsed_pkt.tunnel.tunneled_ip_dst.v6.__u6_addr.__u6_addr32[2] + hdr->extended_hdr.parsed_pkt.tunnel.tunneled_ip_dst.v6.__u6_addr.__u6_addr32[3] + hdr->extended_hdr.parsed_pkt.tunnel.tunneled_l4_src_port + hdr->extended_hdr.parsed_pkt.tunnel.tunneled_l4_dst_port; } } #else u_int32_t pfring_hash_pkt(struct pfring_pkthdr* hdr) { if (hdr->extended_hdr.parsed_pkt.tunnel.tunnel_id == NO_TUNNEL_ID) { return hdr->extended_hdr.parsed_pkt.vlan_id + hdr->extended_hdr.parsed_pkt.l3_proto + hdr->extended_hdr.parsed_pkt.ip_src.v6.s6_addr32[0] + hdr->extended_hdr.parsed_pkt.ip_src.v6.s6_addr32[1] + hdr->extended_hdr.parsed_pkt.ip_src.v6.s6_addr32[2] + hdr->extended_hdr.parsed_pkt.ip_src.v6.s6_addr32[3] + hdr->extended_hdr.parsed_pkt.ip_dst.v6.s6_addr32[0] + hdr->extended_hdr.parsed_pkt.ip_dst.v6.s6_addr32[1] + hdr->extended_hdr.parsed_pkt.ip_dst.v6.s6_addr32[2] + hdr->extended_hdr.parsed_pkt.ip_dst.v6.s6_addr32[3] + hdr->extended_hdr.parsed_pkt.l4_src_port + hdr->extended_hdr.parsed_pkt.l4_dst_port; } else { return hdr->extended_hdr.parsed_pkt.vlan_id + hdr->extended_hdr.parsed_pkt.tunnel.tunneled_proto + hdr->extended_hdr.parsed_pkt.tunnel.tunneled_ip_src.v6.s6_addr32[1] + hdr->extended_hdr.parsed_pkt.tunnel.tunneled_ip_src.v6.s6_addr32[2] + hdr->extended_hdr.parsed_pkt.tunnel.tunneled_ip_src.v6.s6_addr32[3] + hdr->extended_hdr.parsed_pkt.tunnel.tunneled_ip_dst.v6.s6_addr32[0] + hdr->extended_hdr.parsed_pkt.tunnel.tunneled_ip_dst.v6.s6_addr32[1] + hdr->extended_hdr.parsed_pkt.tunnel.tunneled_ip_dst.v6.s6_addr32[2] + hdr->extended_hdr.parsed_pkt.tunnel.tunneled_ip_dst.v6.s6_addr32[3] + hdr->extended_hdr.parsed_pkt.tunnel.tunneled_l4_src_port + hdr->extended_hdr.parsed_pkt.tunnel.tunneled_l4_dst_port; } } #endif static int __pfring_parse_tunneled_pkt(u_char* pkt, struct pfring_pkthdr* hdr, u_int16_t ip_version, u_int16_t tunnel_offset) { u_int32_t ip_len = 0; u_int16_t fragment_offset = 0; if (ip_version == 4 /* IPv4 */) { struct iphdr* tunneled_ip; if (hdr->caplen < (tunnel_offset + sizeof(struct iphdr))) return 0; tunneled_ip = (struct iphdr*)(&pkt[tunnel_offset]); hdr->extended_hdr.parsed_pkt.tunnel.tunneled_proto = tunneled_ip->protocol; hdr->extended_hdr.parsed_pkt.tunnel.tunneled_ip_src.v4 = ntohl(tunneled_ip->saddr); hdr->extended_hdr.parsed_pkt.tunnel.tunneled_ip_dst.v4 = ntohl(tunneled_ip->daddr); fragment_offset = tunneled_ip->frag_off & htons(IP_OFFSET); /* fragment, but not the first */ ip_len = tunneled_ip->ihl * 4; tunnel_offset += ip_len; } else if (ip_version == 6 /* IPv6 */) { struct kcompact_ipv6_hdr* tunneled_ipv6; if (hdr->caplen < (tunnel_offset + sizeof(struct kcompact_ipv6_hdr))) return 0; tunneled_ipv6 = (struct kcompact_ipv6_hdr*)(&pkt[tunnel_offset]); hdr->extended_hdr.parsed_pkt.tunnel.tunneled_proto = tunneled_ipv6->nexthdr; /* Values of IPv6 addresses are stored as network byte order */ memcpy(&hdr->extended_hdr.parsed_pkt.tunnel.tunneled_ip_src.v6, &tunneled_ipv6->saddr, sizeof(tunneled_ipv6->saddr)); memcpy(&hdr->extended_hdr.parsed_pkt.tunnel.tunneled_ip_dst.v6, &tunneled_ipv6->daddr, sizeof(tunneled_ipv6->daddr)); ip_len = sizeof(struct kcompact_ipv6_hdr); /* Note: NEXTHDR_AUTH, NEXTHDR_ESP, NEXTHDR_IPV6, NEXTHDR_MOBILITY are not handled */ while (hdr->extended_hdr.parsed_pkt.tunnel.tunneled_proto == NEXTHDR_HOP || hdr->extended_hdr.parsed_pkt.tunnel.tunneled_proto == NEXTHDR_DEST || hdr->extended_hdr.parsed_pkt.tunnel.tunneled_proto == NEXTHDR_ROUTING || hdr->extended_hdr.parsed_pkt.tunnel.tunneled_proto == NEXTHDR_FRAGMENT) { struct kcompact_ipv6_opt_hdr* ipv6_opt; if (hdr->caplen < tunnel_offset + ip_len + sizeof(struct kcompact_ipv6_opt_hdr)) return 1; ipv6_opt = (struct kcompact_ipv6_opt_hdr*)(&pkt[tunnel_offset + ip_len]); ip_len += sizeof(struct kcompact_ipv6_opt_hdr); fragment_offset = 0; if (hdr->extended_hdr.parsed_pkt.tunnel.tunneled_proto == NEXTHDR_HOP || hdr->extended_hdr.parsed_pkt.tunnel.tunneled_proto == NEXTHDR_DEST || hdr->extended_hdr.parsed_pkt.tunnel.tunneled_proto == NEXTHDR_ROUTING) ip_len += ipv6_opt->hdrlen * 8; hdr->extended_hdr.parsed_pkt.tunnel.tunneled_proto = ipv6_opt->nexthdr; } if (hdr->extended_hdr.parsed_pkt.tunnel.tunneled_proto == NEXTHDR_NONE) hdr->extended_hdr.parsed_pkt.tunnel.tunneled_proto = 0; tunnel_offset += ip_len; } else return 0; if (fragment_offset) return 1; if (hdr->extended_hdr.parsed_pkt.tunnel.tunneled_proto == IPPROTO_TCP) { struct tcphdr* tcp; if (hdr->caplen < tunnel_offset + sizeof(struct tcphdr)) return 1; tcp = (struct tcphdr*)(&pkt[tunnel_offset]); hdr->extended_hdr.parsed_pkt.tunnel.tunneled_l4_src_port = ntohs(tcp->source), hdr->extended_hdr.parsed_pkt.tunnel.tunneled_l4_dst_port = ntohs(tcp->dest); } else if (hdr->extended_hdr.parsed_pkt.tunnel.tunneled_proto == IPPROTO_UDP) { struct udphdr* udp; if (hdr->caplen < tunnel_offset + sizeof(struct udphdr)) return 1; udp = (struct udphdr*)(&pkt[tunnel_offset]); hdr->extended_hdr.parsed_pkt.tunnel.tunneled_l4_src_port = ntohs(udp->source), hdr->extended_hdr.parsed_pkt.tunnel.tunneled_l4_dst_port = ntohs(udp->dest); } return 2; } int fastnetmon_parse_pkt(unsigned char* pkt, struct pfring_pkthdr* hdr, u_int8_t level /* L2..L4, 5 (tunnel) */, u_int8_t add_timestamp /* 0,1 */, u_int8_t add_hash /* 0,1 */) { struct ethhdr* eh = (struct ethhdr*)pkt; u_int32_t displ = 0, ip_len; u_int16_t analyzed = 0, fragment_offset = 0; hdr->extended_hdr.parsed_pkt.tunnel.tunnel_id = NO_TUNNEL_ID; /* Note: in order to optimize the computation, this function expects a zero-ed * or partially parsed pkthdr */ // memset(&hdr->extended_hdr.parsed_pkt, 0, sizeof(struct pkt_parsing_info)); // hdr->extended_hdr.parsed_header_len = 0; if (hdr->extended_hdr.parsed_pkt.offset.l3_offset != 0) goto L3; memcpy(&hdr->extended_hdr.parsed_pkt.dmac, eh->h_dest, sizeof(eh->h_dest)); memcpy(&hdr->extended_hdr.parsed_pkt.smac, eh->h_source, sizeof(eh->h_source)); hdr->extended_hdr.parsed_pkt.eth_type = ntohs(eh->h_proto); hdr->extended_hdr.parsed_pkt.offset.eth_offset = 0; hdr->extended_hdr.parsed_pkt.offset.vlan_offset = 0; hdr->extended_hdr.parsed_pkt.vlan_id = 0; /* Any VLAN */ if (hdr->extended_hdr.parsed_pkt.eth_type == 0x8100 /* 802.1q (VLAN) */) { struct eth_vlan_hdr* vh; hdr->extended_hdr.parsed_pkt.offset.vlan_offset = sizeof(struct ethhdr) - sizeof(struct eth_vlan_hdr); while (hdr->extended_hdr.parsed_pkt.eth_type == 0x8100 /* 802.1q (VLAN) */) { hdr->extended_hdr.parsed_pkt.offset.vlan_offset += sizeof(struct eth_vlan_hdr); vh = (struct eth_vlan_hdr*)&pkt[hdr->extended_hdr.parsed_pkt.offset.vlan_offset]; hdr->extended_hdr.parsed_pkt.vlan_id = ntohs(vh->h_vlan_id) & 0x0fff; hdr->extended_hdr.parsed_pkt.eth_type = ntohs(vh->h_proto); displ += sizeof(struct eth_vlan_hdr); } } hdr->extended_hdr.parsed_pkt.offset.l3_offset = hdr->extended_hdr.parsed_pkt.offset.eth_offset + displ + sizeof(struct ethhdr); L3: analyzed = 2; if (level < 3) goto TIMESTAMP; if (hdr->extended_hdr.parsed_pkt.offset.l4_offset != 0) goto L4; if (hdr->extended_hdr.parsed_pkt.eth_type == 0x0800 /* IPv4 */) { struct iphdr* ip; hdr->extended_hdr.parsed_pkt.ip_version = 4; if (hdr->caplen < hdr->extended_hdr.parsed_pkt.offset.l3_offset + sizeof(struct iphdr)) goto TIMESTAMP; ip = (struct iphdr*)(&pkt[hdr->extended_hdr.parsed_pkt.offset.l3_offset]); hdr->extended_hdr.parsed_pkt.ipv4_src = ntohl(ip->saddr); hdr->extended_hdr.parsed_pkt.ipv4_dst = ntohl(ip->daddr); hdr->extended_hdr.parsed_pkt.l3_proto = ip->protocol; hdr->extended_hdr.parsed_pkt.ipv4_tos = ip->tos; fragment_offset = ip->frag_off & htons(IP_OFFSET); /* fragment, but not the first */ ip_len = ip->ihl * 4; hdr->extended_hdr.parsed_pkt.ip_total_size = ntohs(ip->tot_len); // Parse fragmentation info: // Very good examples about IPv4 flags: http://lwn.net/Articles/136319/ hdr->extended_hdr.parsed_pkt.ip_fragmented = 0; hdr->extended_hdr.parsed_pkt.ip_ttl = ip->ttl; int fast_frag_off = ntohs(ip->frag_off); int fast_offset = (fast_frag_off & IP_OFFSET); if (fast_frag_off & IP_MF) { // printf("Packet with MF flag\n"); hdr->extended_hdr.parsed_pkt.ip_fragmented = 1; } if (fast_offset != 0) { // printf("Packet with non zero offset\n"); hdr->extended_hdr.parsed_pkt.ip_fragmented = 1; } } else if (hdr->extended_hdr.parsed_pkt.eth_type == 0x86DD /* IPv6 */) { struct kcompact_ipv6_hdr* ipv6; hdr->extended_hdr.parsed_pkt.ip_version = 6; if (hdr->caplen < hdr->extended_hdr.parsed_pkt.offset.l3_offset + sizeof(struct kcompact_ipv6_hdr)) goto TIMESTAMP; ipv6 = (struct kcompact_ipv6_hdr*)(&pkt[hdr->extended_hdr.parsed_pkt.offset.l3_offset]); ip_len = sizeof(struct kcompact_ipv6_hdr); /* Values of IPv6 addresses are stored as network byte order */ memcpy(&hdr->extended_hdr.parsed_pkt.ipv6_src, &ipv6->saddr, sizeof(ipv6->saddr)); memcpy(&hdr->extended_hdr.parsed_pkt.ipv6_dst, &ipv6->daddr, sizeof(ipv6->daddr)); hdr->extended_hdr.parsed_pkt.l3_proto = ipv6->nexthdr; hdr->extended_hdr.parsed_pkt.ipv6_tos = ipv6->priority; /* IPv6 class of service */ /* Note: NEXTHDR_AUTH, NEXTHDR_ESP, NEXTHDR_IPV6, NEXTHDR_MOBILITY are not handled */ while (hdr->extended_hdr.parsed_pkt.l3_proto == NEXTHDR_HOP || hdr->extended_hdr.parsed_pkt.l3_proto == NEXTHDR_DEST || hdr->extended_hdr.parsed_pkt.l3_proto == NEXTHDR_ROUTING || hdr->extended_hdr.parsed_pkt.l3_proto == NEXTHDR_FRAGMENT) { struct kcompact_ipv6_opt_hdr* ipv6_opt; if (hdr->caplen < hdr->extended_hdr.parsed_pkt.offset.l3_offset + ip_len + sizeof(struct kcompact_ipv6_opt_hdr)) goto TIMESTAMP; ipv6_opt = (struct kcompact_ipv6_opt_hdr*)(&pkt[hdr->extended_hdr.parsed_pkt.offset.l3_offset + ip_len]); ip_len += sizeof(struct kcompact_ipv6_opt_hdr); if (hdr->extended_hdr.parsed_pkt.l3_proto == NEXTHDR_HOP || hdr->extended_hdr.parsed_pkt.l3_proto == NEXTHDR_DEST || hdr->extended_hdr.parsed_pkt.l3_proto == NEXTHDR_ROUTING) ip_len += ipv6_opt->hdrlen * 8; hdr->extended_hdr.parsed_pkt.l3_proto = ipv6_opt->nexthdr; } if (hdr->extended_hdr.parsed_pkt.l3_proto == NEXTHDR_NONE) hdr->extended_hdr.parsed_pkt.l3_proto = 0; } else { hdr->extended_hdr.parsed_pkt.l3_proto = 0; goto TIMESTAMP; } hdr->extended_hdr.parsed_pkt.offset.l4_offset = hdr->extended_hdr.parsed_pkt.offset.l3_offset + ip_len; L4: analyzed = 3; if (level < 4 || fragment_offset) goto TIMESTAMP; if (hdr->extended_hdr.parsed_pkt.l3_proto == IPPROTO_TCP) { struct tcphdr* tcp; if (hdr->caplen < hdr->extended_hdr.parsed_pkt.offset.l4_offset + sizeof(struct tcphdr)) goto TIMESTAMP; tcp = (struct tcphdr*)(&pkt[hdr->extended_hdr.parsed_pkt.offset.l4_offset]); hdr->extended_hdr.parsed_pkt.l4_src_port = ntohs(tcp->source); hdr->extended_hdr.parsed_pkt.l4_dst_port = ntohs(tcp->dest); hdr->extended_hdr.parsed_pkt.offset.payload_offset = hdr->extended_hdr.parsed_pkt.offset.l4_offset + (tcp->doff * 4); hdr->extended_hdr.parsed_pkt.tcp.seq_num = ntohl(tcp->seq); hdr->extended_hdr.parsed_pkt.tcp.ack_num = ntohl(tcp->ack_seq); hdr->extended_hdr.parsed_pkt.tcp.flags = (tcp->fin * TH_FIN_MULTIPLIER) + (tcp->syn * TH_SYN_MULTIPLIER) + (tcp->rst * TH_RST_MULTIPLIER) + (tcp->psh * TH_PUSH_MULTIPLIER) + (tcp->ack * TH_ACK_MULTIPLIER) + (tcp->urg * TH_URG_MULTIPLIER); analyzed = 4; } else if (hdr->extended_hdr.parsed_pkt.l3_proto == IPPROTO_UDP) { struct udphdr* udp; if (hdr->caplen < hdr->extended_hdr.parsed_pkt.offset.l4_offset + sizeof(struct udphdr)) goto TIMESTAMP; udp = (struct udphdr*)(&pkt[hdr->extended_hdr.parsed_pkt.offset.l4_offset]); hdr->extended_hdr.parsed_pkt.l4_src_port = ntohs(udp->source), hdr->extended_hdr.parsed_pkt.l4_dst_port = ntohs(udp->dest); hdr->extended_hdr.parsed_pkt.offset.payload_offset = hdr->extended_hdr.parsed_pkt.offset.l4_offset + sizeof(struct udphdr); analyzed = 4; if (level < 5) goto TIMESTAMP; /* GTPv1 */ if ((hdr->extended_hdr.parsed_pkt.l4_src_port == GTP_SIGNALING_PORT) || (hdr->extended_hdr.parsed_pkt.l4_dst_port == GTP_SIGNALING_PORT) || (hdr->extended_hdr.parsed_pkt.l4_src_port == GTP_U_DATA_PORT) || (hdr->extended_hdr.parsed_pkt.l4_dst_port == GTP_U_DATA_PORT)) { struct gtp_v1_hdr* gtp; u_int16_t gtp_len; if (hdr->caplen < (hdr->extended_hdr.parsed_pkt.offset.payload_offset + sizeof(struct gtp_v1_hdr))) goto TIMESTAMP; gtp = (struct gtp_v1_hdr*)(&pkt[hdr->extended_hdr.parsed_pkt.offset.payload_offset]); gtp_len = sizeof(struct gtp_v1_hdr); if (((gtp->flags & GTP_FLAGS_VERSION) >> GTP_FLAGS_VERSION_SHIFT) == GTP_VERSION_1) { struct iphdr* tunneled_ip; hdr->extended_hdr.parsed_pkt.tunnel.tunnel_id = ntohl(gtp->teid); if ((hdr->extended_hdr.parsed_pkt.l4_src_port == GTP_U_DATA_PORT) || (hdr->extended_hdr.parsed_pkt.l4_dst_port == GTP_U_DATA_PORT)) { if (gtp->flags & (GTP_FLAGS_EXTENSION | GTP_FLAGS_SEQ_NUM | GTP_FLAGS_NPDU_NUM)) { struct gtp_v1_opt_hdr* gtpopt; if (hdr->caplen < (hdr->extended_hdr.parsed_pkt.offset.payload_offset + gtp_len + sizeof(struct gtp_v1_opt_hdr))) goto TIMESTAMP; gtpopt = (struct gtp_v1_opt_hdr*)(&pkt[hdr->extended_hdr.parsed_pkt.offset.payload_offset + gtp_len]); gtp_len += sizeof(struct gtp_v1_opt_hdr); if ((gtp->flags & GTP_FLAGS_EXTENSION) && gtpopt->next_ext_hdr) { struct gtp_v1_ext_hdr* gtpext; u_int8_t* next_ext_hdr; do { if (hdr->caplen < (hdr->extended_hdr.parsed_pkt.offset.payload_offset + gtp_len + 1 /* 8bit len field */)) goto TIMESTAMP; gtpext = (struct gtp_v1_ext_hdr*)(&pkt[hdr->extended_hdr.parsed_pkt.offset.payload_offset + gtp_len]); gtp_len += (gtpext->len * GTP_EXT_HDR_LEN_UNIT_BYTES); if (gtpext->len == 0 || hdr->caplen < (hdr->extended_hdr.parsed_pkt.offset.payload_offset + gtp_len)) goto TIMESTAMP; next_ext_hdr = (u_int8_t*)(&pkt[hdr->extended_hdr.parsed_pkt.offset.payload_offset + gtp_len - 1 /* 8bit next_ext_hdr field*/]); } while (*next_ext_hdr); } } if (hdr->caplen < (hdr->extended_hdr.parsed_pkt.offset.payload_offset + gtp_len + sizeof(struct iphdr))) goto TIMESTAMP; tunneled_ip = (struct iphdr*)(&pkt[hdr->extended_hdr.parsed_pkt.offset.payload_offset + gtp_len]); analyzed += __pfring_parse_tunneled_pkt(pkt, hdr, tunneled_ip->version, hdr->extended_hdr.parsed_pkt.offset.payload_offset + gtp_len); } } } } else if (hdr->extended_hdr.parsed_pkt.l3_proto == IPPROTO_GRE /* 0x47 */) { struct gre_header* gre = (struct gre_header*)(&pkt[hdr->extended_hdr.parsed_pkt.offset.l4_offset]); int gre_offset; gre->flags_and_version = ntohs(gre->flags_and_version); gre->proto = ntohs(gre->proto); gre_offset = sizeof(struct gre_header); if ((gre->flags_and_version & GRE_HEADER_VERSION) == 0) { if (gre->flags_and_version & (GRE_HEADER_CHECKSUM | GRE_HEADER_ROUTING)) gre_offset += 4; if (gre->flags_and_version & GRE_HEADER_KEY) { u_int32_t* tunnel_id = (u_int32_t*)(&pkt[hdr->extended_hdr.parsed_pkt.offset.l4_offset + gre_offset]); gre_offset += 4; hdr->extended_hdr.parsed_pkt.tunnel.tunnel_id = ntohl(*tunnel_id); } if (gre->flags_and_version & GRE_HEADER_SEQ_NUM) gre_offset += 4; hdr->extended_hdr.parsed_pkt.offset.payload_offset = hdr->extended_hdr.parsed_pkt.offset.l4_offset + gre_offset; analyzed = 4; if (level < 5) goto TIMESTAMP; if (gre->proto == ETH_P_IP /* IPv4 */ || gre->proto == ETH_P_IPV6 /* IPv6 */) analyzed += __pfring_parse_tunneled_pkt(pkt, hdr, gre->proto == ETH_P_IP ? 4 : 6, hdr->extended_hdr.parsed_pkt.offset.payload_offset); } else { /* TODO handle other GRE versions */ hdr->extended_hdr.parsed_pkt.offset.payload_offset = hdr->extended_hdr.parsed_pkt.offset.l4_offset; } } else { hdr->extended_hdr.parsed_pkt.offset.payload_offset = hdr->extended_hdr.parsed_pkt.offset.l4_offset; hdr->extended_hdr.parsed_pkt.l4_src_port = hdr->extended_hdr.parsed_pkt.l4_dst_port = 0; } TIMESTAMP: if (add_timestamp && hdr->ts.tv_sec == 0) gettimeofday(&hdr->ts, NULL); /* TODO What about using clock_gettime(CLOCK_REALTIME, ts) ? */ if (add_hash && hdr->extended_hdr.pkt_hash == 0) hdr->extended_hdr.pkt_hash = pfring_hash_pkt(hdr); return analyzed; } int fastnetmon_print_parsed_pkt(char* buff, u_int buff_len, const u_char* p, const struct pfring_pkthdr* h) { char buf1[32], buf2[32]; int buff_used = 0; buff_used += snprintf(&buff[buff_used], buff_len - buff_used, "[%s -> %s] ", etheraddr2string(h->extended_hdr.parsed_pkt.smac, buf1), etheraddr2string(h->extended_hdr.parsed_pkt.dmac, buf2)); if (h->extended_hdr.parsed_pkt.offset.vlan_offset) buff_used += snprintf(&buff[buff_used], buff_len - buff_used, "[vlan %u] ", h->extended_hdr.parsed_pkt.vlan_id); if (h->extended_hdr.parsed_pkt.eth_type == 0x0800 /* IPv4*/ || h->extended_hdr.parsed_pkt.eth_type == 0x86DD /* IPv6*/) { if (h->extended_hdr.parsed_pkt.eth_type == 0x0800 /* IPv4*/) { buff_used += snprintf(&buff[buff_used], buff_len - buff_used, "[IPv4][%s:%d ", intoa(h->extended_hdr.parsed_pkt.ipv4_src), h->extended_hdr.parsed_pkt.l4_src_port); buff_used += snprintf(&buff[buff_used], buff_len - buff_used, "-> %s:%d] ", intoa(h->extended_hdr.parsed_pkt.ipv4_dst), h->extended_hdr.parsed_pkt.l4_dst_port); } else { buff_used += snprintf(&buff[buff_used], buff_len - buff_used, "[IPv6][%s:%d ", in6toa(h->extended_hdr.parsed_pkt.ipv6_src), h->extended_hdr.parsed_pkt.l4_src_port); buff_used += snprintf(&buff[buff_used], buff_len - buff_used, "-> %s:%d] ", in6toa(h->extended_hdr.parsed_pkt.ipv6_dst), h->extended_hdr.parsed_pkt.l4_dst_port); } buff_used += snprintf(&buff[buff_used], buff_len - buff_used, "[l3_proto=%s]", proto2str(h->extended_hdr.parsed_pkt.l3_proto)); if (h->extended_hdr.parsed_pkt.tunnel.tunnel_id != NO_TUNNEL_ID) { buff_used += snprintf(&buff[buff_used], buff_len - buff_used, "[TEID=0x%08X][tunneled_proto=%s]", h->extended_hdr.parsed_pkt.tunnel.tunnel_id, proto2str(h->extended_hdr.parsed_pkt.tunnel.tunneled_proto)); if (h->extended_hdr.parsed_pkt.eth_type == 0x0800 /* IPv4*/) { buff_used += snprintf(&buff[buff_used], buff_len - buff_used, "[IPv4][%s:%d ", intoa(h->extended_hdr.parsed_pkt.tunnel.tunneled_ip_src.v4), h->extended_hdr.parsed_pkt.tunnel.tunneled_l4_src_port); buff_used += snprintf(&buff[buff_used], buff_len - buff_used, "-> %s:%d] ", intoa(h->extended_hdr.parsed_pkt.tunnel.tunneled_ip_dst.v4), h->extended_hdr.parsed_pkt.tunnel.tunneled_l4_dst_port); } else { buff_used += snprintf(&buff[buff_used], buff_len - buff_used, "[IPv6][%s:%d ", in6toa(h->extended_hdr.parsed_pkt.tunnel.tunneled_ip_src.v6), h->extended_hdr.parsed_pkt.tunnel.tunneled_l4_src_port); buff_used += snprintf(&buff[buff_used], buff_len - buff_used, "-> %s:%d] ", in6toa(h->extended_hdr.parsed_pkt.tunnel.tunneled_ip_dst.v6), h->extended_hdr.parsed_pkt.tunnel.tunneled_l4_dst_port); } } buff_used += snprintf(&buff[buff_used], buff_len - buff_used, "[ip_fragmented: %d]", h->extended_hdr.parsed_pkt.ip_fragmented); buff_used += snprintf(&buff[buff_used], buff_len - buff_used, "[hash=%u][tos=%d][tcp_seq_num=%u]", h->extended_hdr.pkt_hash, h->extended_hdr.parsed_pkt.ipv4_tos, h->extended_hdr.parsed_pkt.tcp.seq_num); } else if (h->extended_hdr.parsed_pkt.eth_type == 0x0806 /* ARP */) { buff_used += snprintf(&buff[buff_used], buff_len - buff_used, "[ARP]"); if (buff_len >= h->extended_hdr.parsed_pkt.offset.l3_offset + 30) { buff_used += snprintf(&buff[buff_used], buff_len - buff_used, "[Sender=%s/%s]", etheraddr2string(&p[h->extended_hdr.parsed_pkt.offset.l3_offset + 8], buf1), intoa(ntohl(*((u_int32_t*)&p[h->extended_hdr.parsed_pkt.offset.l3_offset + 14])))); buff_used += snprintf(&buff[buff_used], buff_len - buff_used, "[Target=%s/%s]", etheraddr2string(&p[h->extended_hdr.parsed_pkt.offset.l3_offset + 18], buf2), intoa(ntohl(*((u_int32_t*)&p[h->extended_hdr.parsed_pkt.offset.l3_offset + 24])))); } } else { buff_used += snprintf(&buff[buff_used], buff_len - buff_used, "[eth_type=0x%04X]", h->extended_hdr.parsed_pkt.eth_type); } buff_used += snprintf(&buff[buff_used], buff_len - buff_used, " [caplen=%d][len=%d][parsed_header_len=%d][" "eth_offset=%d][l3_offset=%d][l4_offset=%d][" "payload_offset=%d]\n", h->caplen, h->len, h->extended_hdr.parsed_header_len, h->extended_hdr.parsed_pkt.offset.eth_offset, h->extended_hdr.parsed_pkt.offset.l3_offset, h->extended_hdr.parsed_pkt.offset.l4_offset, h->extended_hdr.parsed_pkt.offset.payload_offset); return buff_used; } char* etheraddr2string(const u_char* ep, char* buf) { char* hex = "0123456789ABCDEF"; u_int i, j; char* cp; cp = buf; if ((j = *ep >> 4) != 0) *cp++ = hex[j]; else *cp++ = '0'; *cp++ = hex[*ep++ & 0xf]; for (i = 5; (int)--i >= 0;) { *cp++ = ':'; if ((j = *ep >> 4) != 0) *cp++ = hex[j]; else *cp++ = '0'; *cp++ = hex[*ep++ & 0xf]; } *cp = '\0'; return (buf); } char* intoa(unsigned int addr) { static char buf[sizeof "ff:ff:ff:ff:ff:ff:255.255.255.255"]; return (_intoa(addr, buf, sizeof(buf))); } char* _intoa(unsigned int addr, char* buf, u_short bufLen) { char* cp, *retStr; u_int byte; int n; cp = &buf[bufLen]; *--cp = '\0'; n = 4; do { byte = addr & 0xff; *--cp = byte % 10 + '0'; byte /= 10; if (byte > 0) { *--cp = byte % 10 + '0'; byte /= 10; if (byte > 0) *--cp = byte + '0'; } *--cp = '.'; addr >>= 8; } while (--n > 0); retStr = (char*)(cp + 1); return (retStr); } static char* in6toa(struct in6_addr addr6) { static char buf[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"]; char* ret = (char*)inet_ntop(AF_INET6, &addr6, buf, sizeof(buf)); if (ret == NULL) { // printf("Internal error (&buff[buff_used]r too short)"); buf[0] = '\0'; } return (ret); } char* proto2str(u_short proto) { static char protoName[8]; switch (proto) { case IPPROTO_TCP: return ("TCP"); case IPPROTO_UDP: return ("UDP"); case IPPROTO_ICMP: return ("ICMP"); case IPPROTO_GRE: return ("GRE"); default: snprintf(protoName, sizeof(protoName), "%d", proto); return (protoName); } } fastnetmon-1.1.4/src/fastnetmon_packet_parser.h000066400000000000000000000107611343111404700217170ustar00rootroot00000000000000#ifndef PFRING_PACKET_PARSER_H #define PFRING_PACKET_PARSER_H #include #include // in6_addr #if defined(__APPLE__) || defined(__OpenBSD__) // For Mac OS X here we can find definition of "struct timeval" #include #endif #define ETH_ALEN 6 /* Note that as offsets *can* be negative, please do not change them to unsigned */ struct pkt_offset { int16_t eth_offset; /* This offset *must* be added to all offsets below ONLY if you are inside the kernel (e.g. when you code a pf_ring plugin). Ignore it in user-space. */ int16_t vlan_offset; int16_t l3_offset; int16_t l4_offset; int16_t payload_offset; }; typedef union { struct in6_addr v6; /* IPv6 src/dst IP addresses (Network byte order) */ u_int32_t v4; /* IPv4 src/dst IP addresses */ } ip_addr; /* GPRS Tunneling Protocol */ typedef struct { u_int32_t tunnel_id; /* GTP/GRE tunnelId or NO_TUNNEL_ID for no filtering */ u_int8_t tunneled_proto; ip_addr tunneled_ip_src, tunneled_ip_dst; u_int16_t tunneled_l4_src_port, tunneled_l4_dst_port; } tunnel_info; struct pkt_parsing_info { /* Core fields (also used by NetFlow) */ u_int8_t dmac[ETH_ALEN], smac[ETH_ALEN]; /* MAC src/dst addresses */ u_int16_t eth_type; /* Ethernet type */ u_int16_t vlan_id; /* VLAN Id or NO_VLAN */ u_int8_t ip_version; u_int8_t l3_proto, ip_tos; /* Layer 3 protocol/TOS */ u_int8_t ip_fragmented; /* Layer 3 fragmentation flag */ u_int16_t ip_total_size; /* Total size of IP packet */ u_int8_t ip_ttl; /* TTL flag */ ip_addr ip_src, ip_dst; /* IPv4 src/dst IP addresses */ u_int16_t l4_src_port, l4_dst_port; /* Layer 4 src/dst ports */ struct { u_int8_t flags; /* TCP flags (0 if not available) */ u_int32_t seq_num, ack_num; /* TCP sequence number */ } tcp; tunnel_info tunnel; u_int16_t last_matched_plugin_id; /* If > 0 identifies a plugin to that matched the packet */ u_int16_t last_matched_rule_id; /* If > 0 identifies a rule that matched the packet */ struct pkt_offset offset; /* Offsets of L3/L4/payload elements */ }; struct pfring_extended_pkthdr { u_int64_t timestamp_ns; /* Packet timestamp at ns precision. Note that if your NIC supports hardware timestamp, this is the place to read timestamp from */ #define PKT_FLAGS_CHECKSUM_OFFLOAD 1 << 0 /* IP/TCP checksum offload enabled */ #define PKT_FLAGS_CHECKSUM_OK 1 << 1 /* Valid checksum (with IP/TCP checksum offload enabled) */ #define PKT_FLAGS_IP_MORE_FRAG 1 << 2 /* IP More fragments flag set */ #define PKT_FLAGS_IP_FRAG_OFFSET 1 << 3 /* IP fragment offset set (not 0) */ #define PKT_FLAGS_VLAN_HWACCEL 1 << 4 /* VLAN stripped by hw */ u_int32_t flags; /* --- short header ends here --- */ u_int8_t rx_direction; /* 1=RX: packet received by the NIC, 0=TX: packet transmitted by the NIC */ int32_t if_index; /* index of the interface on which the packet has been received. It can be also used to report other information */ u_int32_t pkt_hash; /* Hash based on the packet header */ struct { int bounce_interface; /* Interface Id where this packet will bounce after processing if its values is other than UNKNOWN_INTERFACE */ struct sk_buff* reserved; /* Kernel only pointer */ } tx; u_int16_t parsed_header_len; /* Extra parsing data before packet */ /* NOTE: leave it as last field of the memset on parse_pkt() will fail */ struct pkt_parsing_info parsed_pkt; /* packet parsing info */ }; /* NOTE: Keep 'struct pfring_pkthdr' in sync with 'struct pcap_pkthdr' */ struct pfring_pkthdr { /* pcap header */ struct timeval ts; /* time stamp */ u_int32_t caplen; /* length of portion present */ u_int32_t len; /* length of whole packet (off wire) */ struct pfring_extended_pkthdr extended_hdr; /* PF_RING extended header */ }; #ifdef __cplusplus extern "C" { #endif // Prototypes int fastnetmon_print_parsed_pkt(char* buff, u_int buff_len, const u_char* p, const struct pfring_pkthdr* h); int fastnetmon_parse_pkt(unsigned char* pkt, struct pfring_pkthdr* hdr, u_int8_t level /* L2..L4, 5 (tunnel) */, u_int8_t add_timestamp /* 0,1 */, u_int8_t add_hash /* 0,1 */); #ifdef __cplusplus } #endif #endif fastnetmon-1.1.4/src/fastnetmon_pcap_format.cpp000066400000000000000000000054251343111404700217230ustar00rootroot00000000000000#include "fastnetmon_pcap_format.h" #include #include int pcap_reader(const char* pcap_file_path, pcap_packet_parser_callback pcap_parse_packet_function_ptr) { int filedesc = open(pcap_file_path, O_RDONLY); if (filedesc <= 0) { printf("Can't open dump file, error: %s\n", strerror(errno)); return -1; } struct fastnetmon_pcap_file_header pcap_header; ssize_t file_header_readed_bytes = read(filedesc, &pcap_header, sizeof(struct fastnetmon_pcap_file_header)); if (file_header_readed_bytes != sizeof(struct fastnetmon_pcap_file_header)) { printf("Can't read pcap file header"); } // http://www.tcpdump.org/manpages/pcap-savefile.5.html if (pcap_header.magic == 0xa1b2c3d4 or pcap_header.magic == 0xd4c3b2a1) { // printf("Magic readed correctly\n"); } else { printf("Magic in file header broken\n"); return -2; } // Buffer for packets char packet_buffer[pcap_header.snaplen]; unsigned int read_packets = 0; while (1) { // printf("Start packet %d processing\n", read_packets); struct fastnetmon_pcap_pkthdr pcap_packet_header; ssize_t packet_header_readed_bytes = read(filedesc, &pcap_packet_header, sizeof(struct fastnetmon_pcap_pkthdr)); if (packet_header_readed_bytes != sizeof(struct fastnetmon_pcap_pkthdr)) { // We haven't any packets break; } if (pcap_packet_header.incl_len > pcap_header.snaplen) { printf("Please enlarge packet buffer! We got packet with size: %d but our buffer is %d " "bytes\n", pcap_packet_header.incl_len, pcap_header.snaplen); return -4; } ssize_t packet_payload_readed_bytes = read(filedesc, packet_buffer, pcap_packet_header.incl_len); if (pcap_packet_header.incl_len != packet_payload_readed_bytes) { printf("I read packet header but can't read packet payload\n"); return -3; } // printf("packet payload read\n"); pcap_parse_packet_function_ptr(packet_buffer, pcap_packet_header.orig_len, pcap_packet_header.incl_len); // printf("Process packet %d\n", read_packets); read_packets++; } printf("I correctly read %d packets from this dump\n", read_packets); return 0; } bool fill_pcap_header(struct fastnetmon_pcap_file_header* pcap_header, bpf_u_int32 snap_length) { pcap_header->magic = 0xa1b2c3d4; pcap_header->version_major = 2; pcap_header->version_minor = 4; pcap_header->thiszone = 0; pcap_header->sigfigs = 0; // TODO: fix this!!! pcap_header->snaplen = snap_length; // http://www.tcpdump.org/linktypes.html // DLT_EN10MB = 1 pcap_header->linktype = 1; return true; } fastnetmon-1.1.4/src/fastnetmon_pcap_format.h000066400000000000000000000036471343111404700213740ustar00rootroot00000000000000#ifndef FASTNETMON_PCAP_FORMAT_H #define FASTNETMON_PCAP_FORMAT_H #include #include #include #include #include #include #include /* pcap dump format: global header: struct pcap_file_header packet header: struct fastnetmon_pcap_pkthdr */ /* * Compatibility for systems that have a bpf.h that * predates the bpf typedefs for 64-bit support. */ #if BPF_RELEASE - 0 < 199406 typedef int bpf_int32; typedef u_int bpf_u_int32; #endif // We use copy and paste from pcap.h here because we do not want to link with pcap here struct fastnetmon_pcap_file_header { bpf_u_int32 magic; u_short version_major; u_short version_minor; bpf_int32 thiszone; /* gmt to local correction */ bpf_u_int32 sigfigs; /* accuracy of timestamps */ bpf_u_int32 snaplen; /* max length saved portion of each pkt */ bpf_u_int32 linktype; /* data link type (LINKTYPE_*) */ }; /* TODO: move to this code, get rid any bpf* custom types struct fastnetmon_pcap_file_header { uint32_t magic; uint16_t version_major; uint16_t version_minor; int32_t thiszone; uint32_t sigfigs; uint32_t snaplen; uint32_t linktype; }; */ // We can't use pcap_pkthdr from upstream because it uses 16 bytes timeval instead of 8 byte and // broke everything struct fastnetmon_pcap_pkthdr { uint32_t ts_sec; /* timestamp seconds */ uint32_t ts_usec; /* timestamp microseconds */ uint32_t incl_len; /* number of octets of packet saved in file */ uint32_t orig_len; /* actual length of packet */ }; typedef void (*pcap_packet_parser_callback)(char* buffer, uint32_t len, uint32_t snaplen); int pcap_reader(const char* pcap_file_path, pcap_packet_parser_callback pcap_parse_packet_function_ptr); bool fill_pcap_header(struct fastnetmon_pcap_file_header* pcap_header, bpf_u_int32 snap_length); #endif fastnetmon-1.1.4/src/fastnetmon_rc_freebsd000066400000000000000000000012661343111404700207440ustar00rootroot00000000000000#!/bin/sh # PROVIDE: fastnetmon # REQUIRE: NETWORKING SERVERS LOGIN # BEFORE: securelevel # KEYWORD: shutdown # Add the following line to /etc/rc.conf to enable `fastnetmon': # #fastnetmon_enable="YES" # . /etc/rc.subr name="fastnetmon" rcvar="${name}_enable" command="/usr/local/bin/fastnetmon" start_precmd="start_precmd" pidfile="/var/run/$name.pid" # read configuration and set defaults load_rc_config "$name" : ${fastnetmon_enable="NO"} : ${fastnetmon_user="fastnetmon"} #: ${fastnetmon_config="/usr/local/etc/$name.conf"} start_precmd() { echo -n "Starting fastnetmon deamon:" && \ su -m $fastnetmon_user -c "$command"; && \ echo . return 0 } run_rc_command "$1" fastnetmon-1.1.4/src/fastnetmon_tests.cpp000066400000000000000000000422221343111404700205660ustar00rootroot00000000000000#include #include #include "fast_library.h" #include "bgp_flow_spec.h" #include #include "log4cpp/Category.hh" #include "log4cpp/Appender.hh" #include "log4cpp/FileAppender.hh" #include "log4cpp/OstreamAppender.hh" #include "log4cpp/Layout.hh" #include "log4cpp/BasicLayout.hh" #include "log4cpp/PatternLayout.hh" #include "log4cpp/Priority.hh" #include log4cpp::Category& logger = log4cpp::Category::getRoot(); TEST(BgpFlowSpec, protocol_check_udp) { exabgp_flow_spec_rule_t exabgp_rule; exabgp_rule.add_protocol(FLOW_SPEC_PROTOCOL_UDP); EXPECT_EQ(exabgp_rule.serialize_protocols(), "protocol [ udp ];"); } TEST(BgpFlowSpec, protocol_check_tcp) { exabgp_flow_spec_rule_t exabgp_rule; exabgp_rule.add_protocol(FLOW_SPEC_PROTOCOL_TCP); EXPECT_EQ(exabgp_rule.serialize_protocols(), "protocol [ tcp ];"); } TEST(BgpFlowSpec, protocol_check_icmp) { exabgp_flow_spec_rule_t exabgp_rule; exabgp_rule.add_protocol(FLOW_SPEC_PROTOCOL_ICMP); EXPECT_EQ(exabgp_rule.serialize_protocols(), "protocol [ icmp ];"); } TEST(BgpFlowSpec, protocol_check_mix) { exabgp_flow_spec_rule_t exabgp_rule; exabgp_rule.add_protocol(FLOW_SPEC_PROTOCOL_UDP); exabgp_rule.add_protocol(FLOW_SPEC_PROTOCOL_TCP); EXPECT_EQ(exabgp_rule.serialize_protocols(), "protocol [ udp tcp ];"); } TEST(BgpFlowSpec, packet_length) { exabgp_flow_spec_rule_t exabgp_rule; exabgp_rule.add_packet_length(777); exabgp_rule.add_packet_length(1122); EXPECT_EQ(exabgp_rule.serialize_packet_lengths(), "packet-length [ =777 =1122 ];"); } TEST(BgpFlowSpec, source_subnet) { exabgp_flow_spec_rule_t exabgp_rule; exabgp_rule.set_source_subnet( convert_subnet_from_string_to_binary_with_cidr_format("4.0.0.0/24") ); EXPECT_EQ(exabgp_rule.serialize_source_subnet(), "source 4.0.0.0/24;"); } TEST(BgpFlowSpec, destination_subnet) { exabgp_flow_spec_rule_t exabgp_rule; exabgp_rule.set_destination_subnet( convert_subnet_from_string_to_binary_with_cidr_format("77.0.0.0/24") ); EXPECT_EQ(exabgp_rule.serialize_destination_subnet(), "destination 77.0.0.0/24;"); } TEST(BgpFlowSpec, source_port) { exabgp_flow_spec_rule_t exabgp_rule; exabgp_rule.add_source_port(53); EXPECT_EQ(exabgp_rule.serialize_source_ports(), "source-port [ =53 ];"); } TEST(BgpFlowSpec, destaination_port) { exabgp_flow_spec_rule_t exabgp_rule; exabgp_rule.add_destination_port(53); EXPECT_EQ(exabgp_rule.serialize_destination_ports(), "destination-port [ =53 ];"); } TEST(BgpFlowSpec, source_ports) { exabgp_flow_spec_rule_t exabgp_rule; exabgp_rule.add_source_port(53); exabgp_rule.add_source_port(7777); EXPECT_EQ(exabgp_rule.serialize_source_ports(), "source-port [ =53 =7777 ];"); } TEST(BgpFlowSpec, destaination_ports) { exabgp_flow_spec_rule_t exabgp_rule; exabgp_rule.add_destination_port(53); exabgp_rule.add_destination_port(1900); EXPECT_EQ(exabgp_rule.serialize_destination_ports(), "destination-port [ =53 =1900 ];"); } TEST(BgpFlowSpec, fragmentation_is_fragment) { exabgp_flow_spec_rule_t exabgp_rule; exabgp_rule.add_fragmentation_flag(FLOW_SPEC_IS_A_FRAGMENT); EXPECT_EQ(exabgp_rule.serialize_fragmentation_flags(), "fragment [ is-fragment ];"); } TEST(BgpFlowSpec, fragmentation_first_fragment) { exabgp_flow_spec_rule_t exabgp_rule; exabgp_rule.add_fragmentation_flag(FLOW_SPEC_FIRST_FRAGMENT); EXPECT_EQ(exabgp_rule.serialize_fragmentation_flags(), "fragment [ first-fragment ];"); } TEST(BgpFlowSpec, fragmentation_dont_fragment) { exabgp_flow_spec_rule_t exabgp_rule; exabgp_rule.add_fragmentation_flag(FLOW_SPEC_DONT_FRAGMENT); EXPECT_EQ(exabgp_rule.serialize_fragmentation_flags(), "fragment [ dont-fragment ];"); } TEST(BgpFlowSpec, fragmentation_last_fragment) { exabgp_flow_spec_rule_t exabgp_rule; exabgp_rule.add_fragmentation_flag(FLOW_SPEC_LAST_FRAGMENT); EXPECT_EQ(exabgp_rule.serialize_fragmentation_flags(), "fragment [ last-fragment ];"); } TEST(BgpFlowSpec, fragmentation_not_a_fragment) { exabgp_flow_spec_rule_t exabgp_rule; exabgp_rule.add_fragmentation_flag(FLOW_NOT_A_FRAGMENT); EXPECT_EQ(exabgp_rule.serialize_fragmentation_flags(), "fragment [ not-a-fragment ];"); } TEST(BgpFlowSpec, fragmentation_fragments) { exabgp_flow_spec_rule_t exabgp_rule; exabgp_rule.add_fragmentation_flag(FLOW_NOT_A_FRAGMENT); EXPECT_EQ(exabgp_rule.serialize_fragmentation_flags(), "fragment [ not-a-fragment ];"); } // tcp flags tests TEST(BgpFlowSpec, syn) { exabgp_flow_spec_rule_t exabgp_rule; exabgp_rule.add_tcp_flag(FLOW_TCP_FLAG_SYN); EXPECT_EQ(exabgp_rule.serialize_tcp_flags(), "tcp-flags [ syn ];" ); } TEST(BgpFlowSpec, rst) { exabgp_flow_spec_rule_t exabgp_rule; exabgp_rule.add_tcp_flag(FLOW_TCP_FLAG_RST); EXPECT_EQ(exabgp_rule.serialize_tcp_flags(), "tcp-flags [ rst ];" ); } TEST(BgpFlowSpec, ack) { exabgp_flow_spec_rule_t exabgp_rule; exabgp_rule.add_tcp_flag(FLOW_TCP_FLAG_ACK); EXPECT_EQ(exabgp_rule.serialize_tcp_flags(), "tcp-flags [ ack ];" ); } TEST(BgpFlowSpec, fin) { exabgp_flow_spec_rule_t exabgp_rule; exabgp_rule.add_tcp_flag(FLOW_TCP_FLAG_FIN); EXPECT_EQ(exabgp_rule.serialize_tcp_flags(), "tcp-flags [ fin ];" ); } TEST(BgpFlowSpec, psh) { exabgp_flow_spec_rule_t exabgp_rule; exabgp_rule.add_tcp_flag(FLOW_TCP_FLAG_PSH); EXPECT_EQ(exabgp_rule.serialize_tcp_flags(), "tcp-flags [ push ];" ); } TEST(BgpFlowSpec, urg) { exabgp_flow_spec_rule_t exabgp_rule; exabgp_rule.add_tcp_flag(FLOW_TCP_FLAG_URG); EXPECT_EQ(exabgp_rule.serialize_tcp_flags(), "tcp-flags [ urgent ];" ); } TEST(BgpFlowSpec, serialize_match_first) { exabgp_flow_spec_rule_t exabgp_rule; exabgp_rule.add_protocol(FLOW_SPEC_PROTOCOL_UDP); exabgp_rule.add_source_port(53); exabgp_rule.add_destination_port(80); exabgp_rule.add_packet_length(777); exabgp_rule.add_packet_length(1122); exabgp_rule.add_fragmentation_flag(FLOW_SPEC_IS_A_FRAGMENT); exabgp_rule.add_fragmentation_flag(FLOW_SPEC_DONT_FRAGMENT); exabgp_rule.set_destination_subnet( convert_subnet_from_string_to_binary_with_cidr_format("127.0.0.0/24") ); exabgp_rule.set_source_subnet( convert_subnet_from_string_to_binary_with_cidr_format("4.0.0.0/24") ); // Disable indentation exabgp_rule.disable_indents(); EXPECT_EQ( exabgp_rule.serialize_match(), "match {source 4.0.0.0/24;destination 127.0.0.0/24;protocol [ udp ];source-port [ =53 ];destination-port [ =80 ];packet-length [ =777 =1122 ];fragment [ is-fragment dont-fragment ];}"); } TEST(BgpFlowSpec, serialize_then_first) { exabgp_flow_spec_rule_t exabgp_rule; bgp_flow_spec_action_t my_action; //my_action.set_type(FLOW_SPEC_ACTION_ACCEPT); my_action.set_type(FLOW_SPEC_ACTION_RATE_LIMIT); my_action.set_rate_limit(1024); exabgp_rule.set_action( my_action ); exabgp_rule.disable_indents(); EXPECT_EQ( exabgp_rule.serialize_then(), "then {rate-limit 1024;}"); } TEST(BgpFlowSpec, serialize_signle_line) { bgp_flow_spec_action_t my_action; //my_action.set_type(FLOW_SPEC_ACTION_ACCEPT); my_action.set_type(FLOW_SPEC_ACTION_RATE_LIMIT); my_action.set_rate_limit(1024); exabgp_flow_spec_rule_t exabgp_rule; exabgp_rule.add_protocol(FLOW_SPEC_PROTOCOL_UDP); exabgp_rule.add_source_port(53); exabgp_rule.add_destination_port(80); exabgp_rule.add_packet_length(777); exabgp_rule.add_packet_length(1122); exabgp_rule.add_fragmentation_flag(FLOW_SPEC_IS_A_FRAGMENT); exabgp_rule.add_fragmentation_flag(FLOW_SPEC_DONT_FRAGMENT); exabgp_rule.set_destination_subnet( convert_subnet_from_string_to_binary_with_cidr_format("127.0.0.0/24") ); exabgp_rule.set_source_subnet( convert_subnet_from_string_to_binary_with_cidr_format("4.0.0.0/24") ); exabgp_rule.set_action( my_action ); EXPECT_EQ( exabgp_rule.serialize_single_line_exabgp_v4_configuration(), "flow route source 4.0.0.0/24 destination 127.0.0.0/24 protocol [ udp ] source-port [ =53 ] destination-port [ =80 ] packet-length [ =777 =1122 ] fragment [ is-fragment dont-fragment ] rate-limit 1024 "); } TEST(BgpFlowSpec, serialize_whole_single_line_form) { bgp_flow_spec_action_t my_action; //my_action.set_type(FLOW_SPEC_ACTION_ACCEPT); my_action.set_type(FLOW_SPEC_ACTION_RATE_LIMIT); my_action.set_rate_limit(1024); exabgp_flow_spec_rule_t exabgp_rule; exabgp_rule.add_protocol(FLOW_SPEC_PROTOCOL_UDP); exabgp_rule.add_source_port(53); exabgp_rule.add_destination_port(80); exabgp_rule.add_packet_length(777); exabgp_rule.add_packet_length(1122); exabgp_rule.add_fragmentation_flag(FLOW_SPEC_IS_A_FRAGMENT); exabgp_rule.add_fragmentation_flag(FLOW_SPEC_DONT_FRAGMENT); exabgp_rule.set_destination_subnet( convert_subnet_from_string_to_binary_with_cidr_format("127.0.0.0/24") ); exabgp_rule.set_source_subnet( convert_subnet_from_string_to_binary_with_cidr_format("4.0.0.0/24") ); exabgp_rule.set_action( my_action ); // TBD } TEST(BgpFlowSpec, serialize_with_real_exabgp) { bgp_flow_spec_action_t my_action; //my_action.set_type(FLOW_SPEC_ACTION_ACCEPT); my_action.set_type(FLOW_SPEC_ACTION_RATE_LIMIT); my_action.set_rate_limit(1024); exabgp_flow_spec_rule_t exabgp_rule; exabgp_rule.add_protocol(FLOW_SPEC_PROTOCOL_UDP); exabgp_rule.add_source_port(53); exabgp_rule.add_destination_port(80); exabgp_rule.add_packet_length(777); exabgp_rule.add_packet_length(1122); exabgp_rule.add_fragmentation_flag(FLOW_SPEC_IS_A_FRAGMENT); exabgp_rule.add_fragmentation_flag(FLOW_SPEC_DONT_FRAGMENT); exabgp_rule.set_destination_subnet( convert_subnet_from_string_to_binary_with_cidr_format("127.0.0.0/24") ); exabgp_rule.set_source_subnet( convert_subnet_from_string_to_binary_with_cidr_format("4.0.0.0/24") ); exabgp_rule.set_action( my_action ); //exabgp_rule.disable_indents(); std::string exabgp_configuration = exabgp_rule.serialize_complete_exabgp_configuration(); std::ofstream config_file; config_file.open("/tmp/exabgp_test_config.conf", std::ios::trunc); if (config_file.is_open()) { config_file << exabgp_configuration; config_file.close(); } int system_ret_code = system("/usr/src/exabgp/sbin/exabgp --test /tmp/exabgp_test_config.conf 2>/dev/null"); EXPECT_EQ( system_ret_code, 0 ); } // Flow Spec actions tests TEST(BgpFlowSpecAction, rate_limit) { bgp_flow_spec_action_t my_action; my_action.set_type(FLOW_SPEC_ACTION_RATE_LIMIT); my_action.set_rate_limit(1024); EXPECT_EQ( my_action.serialize(), "rate-limit 1024;"); } TEST(BgpFlowSpecAction, discard) { bgp_flow_spec_action_t my_action; my_action.set_type(FLOW_SPEC_ACTION_DISCARD); EXPECT_EQ( my_action.serialize(), "discard;"); } TEST(BgpFlowSpecAction, accept) { bgp_flow_spec_action_t my_action; my_action.set_type(FLOW_SPEC_ACTION_ACCEPT); EXPECT_EQ( my_action.serialize(), "accept;"); } TEST(BgpFlowSpecAction, default_constructor) { bgp_flow_spec_action_t my_action; EXPECT_EQ( my_action.serialize(), "accept;"); } // Serializers tests TEST(serialize_vector_by_string, single_element) { std::vector vect; vect.push_back("123"); EXPECT_EQ( serialize_vector_by_string(vect, ","), "123"); } TEST(serialize_vector_by_string, few_elements) { std::vector vect; vect.push_back("123"); vect.push_back("456"); EXPECT_EQ( serialize_vector_by_string(vect, ","), "123,456"); } TEST(serialize_vector_by_string_with_prefix, single_element) { std::vector vect; vect.push_back(123); EXPECT_EQ( serialize_vector_by_string_with_prefix(vect, ",", "^"), "^123"); } TEST(serialize_vector_by_string_with_prefix, few_elements) { std::vector vect; vect.push_back(123); vect.push_back(456); EXPECT_EQ( serialize_vector_by_string_with_prefix(vect, ",", "^"), "^123,^456"); } /* Patricia tests */ TEST (patricia, negative_lookup_ipv6_prefix) { patricia_tree_t* lookup_ipv6_tree; lookup_ipv6_tree = New_Patricia(128); make_and_lookup_ipv6(lookup_ipv6_tree, (char*)"2a03:f480::/32"); //Destroy_Patricia(lookup_ipv6_tree, (void_fn_t)0); prefix_t prefix_for_check_address; // Convert fb.com frontend address to internal structure inet_pton(AF_INET6, "2a03:2880:2130:cf05:face:b00c::1", (void*)&prefix_for_check_address.add.sin6); prefix_for_check_address.family = AF_INET6; prefix_for_check_address.bitlen = 128; bool found = patricia_search_best2(lookup_ipv6_tree, &prefix_for_check_address, 1) != NULL; EXPECT_EQ( found, false ); } TEST (patricia, positive_lookup_ipv6_prefix) { patricia_tree_t* lookup_ipv6_tree; lookup_ipv6_tree = New_Patricia(128); make_and_lookup_ipv6(lookup_ipv6_tree, (char*)"2a03:f480::/32"); //Destroy_Patricia(lookup_ipv6_tree, (void_fn_t)0); prefix_t prefix_for_check_address; inet_pton(AF_INET6, "2a03:f480:2130:cf05:face:b00c::1", (void*)&prefix_for_check_address.add.sin6); prefix_for_check_address.family = AF_INET6; prefix_for_check_address.bitlen = 128; bool found = patricia_search_best2(lookup_ipv6_tree, &prefix_for_check_address, 1) != NULL; EXPECT_EQ( found, true ); } TEST (serialize_attack_description, blank_attack) { attack_details current_attack; std::string result = serialize_attack_description(current_attack); EXPECT_EQ( result, "Attack type: unknown\nInitial attack power: 0 packets per second\nPeak attack power: 0 packets per second\nAttack direction: other\nAttack protocol: unknown\nTotal incoming traffic: 0 mbps\nTotal outgoing traffic: 0 mbps\nTotal incoming pps: 0 packets per second\nTotal outgoing pps: 0 packets per second\nTotal incoming flows: 0 flows per second\nTotal outgoing flows: 0 flows per second\nAverage incoming traffic: 0 mbps\nAverage outgoing traffic: 0 mbps\nAverage incoming pps: 0 packets per second\nAverage outgoing pps: 0 packets per second\nAverage incoming flows: 0 flows per second\nAverage outgoing flows: 0 flows per second\nIncoming ip fragmented traffic: 0 mbps\nOutgoing ip fragmented traffic: 0 mbps\nIncoming ip fragmented pps: 0 packets per second\nOutgoing ip fragmented pps: 0 packets per second\nIncoming tcp traffic: 0 mbps\nOutgoing tcp traffic: 0 mbps\nIncoming tcp pps: 0 packets per second\nOutgoing tcp pps: 0 packets per second\nIncoming syn tcp traffic: 0 mbps\nOutgoing syn tcp traffic: 0 mbps\nIncoming syn tcp pps: 0 packets per second\nOutgoing syn tcp pps: 0 packets per second\nIncoming udp traffic: 0 mbps\nOutgoing udp traffic: 0 mbps\nIncoming udp pps: 0 packets per second\nOutgoing udp pps: 0 packets per second\nIncoming icmp traffic: 0 mbps\nOutgoing icmp traffic: 0 mbps\nIncoming icmp pps: 0 packets per second\nOutgoing icmp pps: 0 packets per second\n"); } TEST (serialize_attack_description_to_json, blank_attack) { attack_details current_attack; json_object * jobj = serialize_attack_description_to_json(current_attack); EXPECT_EQ( std::string(json_object_to_json_string(jobj)), "{ \"Attack type\": \"unknown\", \"Initial attack power\": 0, \"Peak attack power\": 0, \"Attack direction\": \"other\", \"Attack protocol\": \"unknown\", \"Total incoming traffic\": 0, \"Total outgoing traffic\": 0, \"Total incoming pps\": 0, \"Total outgoing pps\": 0, \"Total incoming flows\": 0, \"Total outgoing flows\": 0, \"Average incoming traffic\": 0, \"Average outgoing traffic\": 0, \"Average incoming pps\": 0, \"Average outgoing pps\": 0, \"Average incoming flows\": 0, \"Average outgoing flows\": 0, \"Incoming ip fragmented traffic\": 0, \"Outgoing ip fragmented traffic\": 0, \"Incoming ip fragmented pps\": 0, \"Outgoing ip fragmented pps\": 0, \"Incoming tcp traffic\": 0, \"Outgoing tcp traffic\": 0, \"Incoming tcp pps\": 0, \"Outgoing tcp pps\": 0, \"Incoming syn tcp traffic\": 0, \"Outgoing syn tcp traffic\": 0, \"Incoming syn tcp pps\": 0, \"Outgoing syn tcp pps\": 0, \"Incoming udp traffic\": 0, \"Outgoing udp traffic\": 0, \"Incoming udp pps\": 0, \"Outgoing udp pps\": 0, \"Incoming icmp traffic\": 0, \"Outgoing icmp traffic\": 0, \"Incoming icmp pps\": 0, \"Outgoing icmp pps\": 0 }"); } TEST (serialize_network_load_to_text, blank_attck_average) { map_element network_speed_meter; EXPECT_EQ( serialize_network_load_to_text(network_speed_meter, true), "Average network incoming traffic: 0 mbps\nAverage network outgoing traffic: 0 mbps\nAverage network incoming pps: 0 packets per second\nAverage network outgoing pps: 0 packets per second\n"); } TEST (serialize_network_load_to_text, blank_attck_absolute) { map_element network_speed_meter; EXPECT_EQ( serialize_network_load_to_text(network_speed_meter, false), "Network incoming traffic: 0 mbps\nNetwork outgoing traffic: 0 mbps\nNetwork incoming pps: 0 packets per second\nNetwork outgoing pps: 0 packets per second\n"); } TEST (serialize_network_load_to_json, blank_attack_average) { map_element network_speed_meter; json_object * jobj = serialize_network_load_to_json(network_speed_meter); EXPECT_EQ( std::string(json_object_to_json_string(jobj)), "{ \"incoming traffic\": 0, \"outgoing traffic\": 0, \"incoming pps\": 0, \"outgoing pps\": 0 }"); } fastnetmon-1.1.4/src/fastnetmon_types.h000066400000000000000000000213721343111404700202400ustar00rootroot00000000000000#ifndef FASTNETMON_TYPES_H #define FASTNETMON_TYPES_H #include // std::pair #include // uint32_t #include // struct timeval #include // struct in6_addr #include #include #include #include "packet_storage.h" enum direction { INCOMING = 0, OUTGOING, INTERNAL, OTHER }; // simplified packet struct for lightweight save into memory class simple_packet { public: simple_packet() : sample_ratio(1), src_ip(0), dst_ip(0), source_port(0), destination_port(0), protocol(0), length(0), flags(0), number_of_packets(1), ip_fragmented(false), ip_protocol_version(4), ttl(0), packet_payload_pointer(NULL), packet_payload_length(0), packet_direction(OTHER) { ts.tv_usec = 0; ts.tv_sec = 0; } uint32_t sample_ratio; /* IPv4 */ uint32_t src_ip; uint32_t dst_ip; /* IPv6 */ struct in6_addr src_ipv6; struct in6_addr dst_ipv6; uint8_t ip_protocol_version; /* IPv4 or IPv6 */ uint8_t ttl; uint16_t source_port; uint16_t destination_port; unsigned int protocol; uint64_t length; uint64_t number_of_packets; /* for netflow */ uint8_t flags; /* tcp flags */ bool ip_fragmented; /* If IP packet fragmented */ struct timeval ts; void* packet_payload_pointer; int packet_payload_length; // We store packet direction here because direction calculation is very difficult task for cpu direction packet_direction; }; class logging_configuration_t { public: logging_configuration_t() : filesystem_logging(true), local_syslog_logging(false), remote_syslog_logging(false), remote_syslog_port(0) {} bool filesystem_logging; std::string filesystem_logging_path; bool local_syslog_logging; bool remote_syslog_logging; std::string remote_syslog_server; unsigned int remote_syslog_port; }; typedef std::pair subnet_t; typedef std::vector subnet_vector_t; typedef std::map subnet_to_host_group_map_t; typedef std::map host_group_map_t; typedef void (*process_packet_pointer)(simple_packet&); // Enum with available sort by field enum sort_type { PACKETS, BYTES, FLOWS }; // Attack types enum attack_type_t { ATTACK_UNKNOWN = 1, ATTACK_SYN_FLOOD = 2, ATTACK_ICMP_FLOOD = 3, ATTACK_UDP_FLOOD = 4, ATTACK_IP_FRAGMENTATION_FLOOD = 5, }; // Amplification types enum amplification_attack_type_t { AMPLIFICATION_ATTACK_UNKNOWN = 1, AMPLIFICATION_ATTACK_DNS = 2, AMPLIFICATION_ATTACK_NTP = 3, AMPLIFICATION_ATTACK_SSDP = 4, AMPLIFICATION_ATTACK_SNMP = 5, AMPLIFICATION_ATTACK_CHARGEN = 6, }; typedef struct { uint64_t bytes; uint64_t packets; uint64_t flows; } total_counter_element; // main data structure for storing traffic and speed data for all our IPs class map_element { public: map_element() : in_bytes(0), out_bytes(0), in_packets(0), out_packets(0), tcp_in_packets(0), tcp_out_packets(0), tcp_in_bytes(0), tcp_out_bytes(0), tcp_syn_in_packets(0), tcp_syn_out_packets(0), tcp_syn_in_bytes(0), tcp_syn_out_bytes(0), udp_in_packets(0), udp_out_packets(0), udp_in_bytes(0), udp_out_bytes(0), in_flows(0), out_flows(0), fragmented_in_packets(0), fragmented_out_packets(0), fragmented_in_bytes(0), fragmented_out_bytes(0), icmp_in_packets(0), icmp_out_packets(0), icmp_in_bytes(0), icmp_out_bytes(0) { } uint64_t in_bytes; uint64_t out_bytes; uint64_t in_packets; uint64_t out_packets; // Fragmented traffic is so recently used for attacks uint64_t fragmented_in_packets; uint64_t fragmented_out_packets; uint64_t fragmented_in_bytes; uint64_t fragmented_out_bytes; // Additional data for correct attack protocol detection uint64_t tcp_in_packets; uint64_t tcp_out_packets; uint64_t tcp_in_bytes; uint64_t tcp_out_bytes; // Additional details about one of most popular atatck type uint64_t tcp_syn_in_packets; uint64_t tcp_syn_out_packets; uint64_t tcp_syn_in_bytes; uint64_t tcp_syn_out_bytes; uint64_t udp_in_packets; uint64_t udp_out_packets; uint64_t udp_in_bytes; uint64_t udp_out_bytes; uint64_t icmp_in_packets; uint64_t icmp_out_packets; uint64_t icmp_in_bytes; uint64_t icmp_out_bytes; uint64_t in_flows; uint64_t out_flows; }; // structure with attack details class attack_details : public map_element { public: attack_details() : attack_protocol(0), attack_power(0), max_attack_power(0), average_in_bytes(0), average_out_bytes(0), average_in_packets(0), average_out_packets(0), average_in_flows(0), average_out_flows(0), ban_time(0), attack_direction(OTHER), unban_enabled(true) { customer_network.first = 0; customer_network.second = 0; } direction attack_direction; // first attackpower detected uint64_t attack_power; // max attack power uint64_t max_attack_power; unsigned int attack_protocol; // Average counters uint64_t average_in_bytes; uint64_t average_out_bytes; uint64_t average_in_packets; uint64_t average_out_packets; uint64_t average_in_flows; uint64_t average_out_flows; // time when we but this user time_t ban_timestamp; bool unban_enabled; int ban_time; // seconds of the ban subnet_t customer_network; packet_storage_t pcap_attack_dump; }; typedef attack_details banlist_item; // struct for save per direction and per protocol details for flow typedef struct { uint64_t bytes; uint64_t packets; // will be used for Garbage Collection time_t last_update_time; } conntrack_key_struct; typedef uint64_t packed_session; // Main mega structure for storing conntracks // We should use class instead struct for correct std::map allocation typedef std::map contrack_map_type; class conntrack_main_struct { public: contrack_map_type in_tcp; contrack_map_type in_udp; contrack_map_type in_icmp; contrack_map_type in_other; contrack_map_type out_tcp; contrack_map_type out_udp; contrack_map_type out_icmp; contrack_map_type out_other; }; typedef std::map map_for_counters; typedef std::vector vector_of_counters; typedef std::map map_of_vector_counters; // Flow tracking structures typedef std::vector vector_of_flow_counters; typedef std::map map_of_vector_counters_for_flow; typedef map_element subnet_counter_t; typedef std::pair pair_of_map_for_subnet_counters_elements_t; typedef std::map map_for_subnet_counters; class packed_conntrack_hash { public: packed_conntrack_hash() : opposite_ip(0), src_port(0), dst_port(0) { } // src or dst IP uint32_t opposite_ip; uint16_t src_port; uint16_t dst_port; }; // This class consists of all configuration of global or per subnet ban thresholds class ban_settings_t { public: ban_settings_t() : enable_ban(false), enable_ban_for_pps(false), enable_ban_for_bandwidth(false), enable_ban_for_flows_per_second(false), enable_ban_for_tcp_pps(false), enable_ban_for_tcp_bandwidth(false), enable_ban_for_udp_pps(false), enable_ban_for_udp_bandwidth(false), enable_ban_for_icmp_pps(false), enable_ban_for_icmp_bandwidth(false), ban_threshold_tcp_mbps(0), ban_threshold_tcp_pps(0), ban_threshold_udp_mbps(0), ban_threshold_udp_pps(0), ban_threshold_icmp_mbps(0), ban_threshold_icmp_pps(0), ban_threshold_mbps(0), ban_threshold_flows(0), ban_threshold_pps(0) { } bool enable_ban; bool enable_ban_for_pps; bool enable_ban_for_bandwidth; bool enable_ban_for_flows_per_second; bool enable_ban_for_tcp_pps; bool enable_ban_for_tcp_bandwidth; bool enable_ban_for_udp_pps; bool enable_ban_for_udp_bandwidth; bool enable_ban_for_icmp_pps; bool enable_ban_for_icmp_bandwidth; unsigned int ban_threshold_tcp_mbps; unsigned int ban_threshold_tcp_pps; unsigned int ban_threshold_udp_mbps; unsigned int ban_threshold_udp_pps; unsigned int ban_threshold_icmp_mbps; unsigned int ban_threshold_icmp_pps; unsigned int ban_threshold_mbps; unsigned int ban_threshold_flows; unsigned int ban_threshold_pps; }; typedef std::map host_group_ban_settings_map_t; // data structure for storing data in Vector typedef std::pair pair_of_map_elements; #endif fastnetmon-1.1.4/src/ipfix_csv_processor.pl000077500000000000000000000124041343111404700211120ustar00rootroot00000000000000#!/usr/bin/perl use strict; use warnings; # This script can convert data from http://www.iana.org/assignments/ipfix/ipfix.xhtml ipfix standard # represented in CSV form into form suitable for us # http://www.iana.org/assignments/ipfix/ipfix-information-elements.csv # to our C/C++ frndly storage format open my $fl, "<", "ipfix_fields.csv" or die "Can't open input file"; open my $target_h_file, ">", "ipfix_rfc.h" or die "Can't open file"; open my $target_cpp_file, ">", "ipfix_rfc.cpp" or die "Can't open file"; my $type_length_hash = { unsigned8 => 1, unsigned16 => 2, unsigned32 => 4, unsigned64 => 8, ipv4Address => 4, ipv6Address => 16, # types with unknown or variable length basicList => 0, boolean => 0, dateTimeMicroseconds => 0, dateTimeMilliseconds => 0, dateTimeNanoseconds => 0, dateTimeSeconds => 0, float64 => 0, macAddress => 0, octetArray => 0, string => 0, subTemplateList => 0, subTemplateMultiList => 0, }; my $field_id = 0; my @intervals = (); my $fields = {}; while (<$fl>) { chomp; if (/^(\d+\-\d+)/) { push @intervals, $1; } my $we_will_process_this_line = /^(\d+),/; # Skip descriptions and other crap unless ($we_will_process_this_line) { next; } # Numbers should growth monotonous if ($1 < $field_id) { next; } $field_id++; my @keys = ("id", "name", "data_type", "data_type_semantics", "status", "description", "units", "range", "reference", "requester", "revision", "date"); my %data = (); @data{ @keys } = split /,/, $_; #print "$data{id} $data{name} $data{data_type}\n"; $fields->{$data{"id"}} = \%data; } for my $interval (@intervals) { my ($interval_start, $interval_end) = $interval =~ /^(\d+)\-(\d+)$/; # Skip reserved interval up to 32767 because it's so long next if $interval_start == 433; for my $field_id ($interval_start .. $interval_end) { $fields->{$field_id} = { id => $field_id, name => 'Reserved' }; } # print "Interval start $interval_start end $interval_end\n"; } for my $field_id (sort { $a <=> $b } keys %$fields) { my $data = $fields->{$field_id}; if ($data->{name} eq 'Reserved') { #print "$data->{id} $data->{name}\n"; } else { #print "$data->{id} $data->{name} $data->{data_type}\n"; } } print {$target_h_file} < #include class ipfix_information_element_t { public: ipfix_information_element_t(std::string name, unsigned int length); ipfix_information_element_t(); std::string get_name(); unsigned int get_length(); std::string name; unsigned int length; }; typedef std::map ipfix_database_t; class ipfix_information_database { public: ipfix_information_database(); bool add_element(unsigned int field_id, std::string name, unsigned int length); std::string get_name_by_id(unsigned int field_id); unsigned int get_length_by_id(unsigned int field_id); private: ipfix_database_t database; }; #endif DOC print {$target_cpp_file} < #include #include "ipfix_rfc.h" /* This file is autogenerated with script ipfix_csv_processor.pl */ /* Please do not edit it directly */ std::string ipfix_information_element_t::get_name() { return this->name; } unsigned int ipfix_information_element_t::get_length() { return this->length; } ipfix_information_element_t::ipfix_information_element_t(std::string name, unsigned int length) { this->name = name; this->length = length; } ipfix_information_element_t::ipfix_information_element_t() { this->name = std::string(""); this->length = 0; } std::string ipfix_information_database::get_name_by_id(unsigned int field_id) { ipfix_database_t::iterator itr = database.find(field_id); if (itr == database.end()) { return std::string(""); } return itr->second.get_name(); } unsigned int ipfix_information_database::get_length_by_id(unsigned int field_id) { ipfix_database_t::iterator itr = database.find(field_id); if (itr == database.end()) { return 0; } return itr->second.get_length(); } bool ipfix_information_database::add_element(unsigned int field_id, std::string name, unsigned int length) { ipfix_database_t::iterator itr = database.find(field_id); // Duplicate ID's strictly prohibited if (itr != database.end()) { return false; } database[field_id] = ipfix_information_element_t(name, length); return true; } ipfix_information_database::ipfix_information_database() { DOC for my $field_id (sort { $a <=> $b } keys %$fields) { my $data = $fields->{$field_id}; my $field_length = 0; my $field_name = '"reserved"'; if ($data->{"data_type"}) { $field_length = $type_length_hash->{ $data->{"data_type"} }; } if (defined($data->{"name"}) && $data->{"name"}) { $field_name = "\"$data->{'name'}\""; } print {$target_cpp_file} " this->add_element($field_id, $field_name, $field_length);\n"; } print {$target_cpp_file} "}\n"; fastnetmon-1.1.4/src/ipfix_fields.csv000066400000000000000000005520661343111404700176600ustar00rootroot00000000000000ElementID,Name,Data Type,Data Type Semantics,Status,Description,Units,Range,References,Requester,Revision,Date 0,Reserved,,,,,,,,[RFC5102],,2013-02-18 1,octetDeltaCount,unsigned64,deltaCounter,current,"The number of octets since the previous report (if any) in incoming packets for this Flow at the Observation Point. The number of octets includes IP header(s) and IP payload.",octets,,,[RFC5102],0,2013-02-18 2,packetDeltaCount,unsigned64,deltaCounter,current,"The number of incoming packets since the previous report (if any) for this Flow at the Observation Point.",packets,,,[RFC5102],0,2013-02-18 3,deltaFlowCount,unsigned64,deltaCounter,current,"The conservative count of Original Flows contributing to this Aggregated Flow; may be distributed via any of the methods expressed by the valueDistributionMethod Information Element.",flows,,,[RFC7015],1,2013-06-25 4,protocolIdentifier,unsigned8,identifier,current,"The value of the protocol number in the IP packet header. The protocol number identifies the IP packet payload type. Protocol numbers are defined in the IANA Protocol Numbers registry. In Internet Protocol version 4 (IPv4), this is carried in the Protocol field. In Internet Protocol version 6 (IPv6), this is carried in the Next Header field in the last extension header of the packet.",,,"See [RFC791] for the specification of the IPv4 protocol field. See [RFC2460] for the specification of the IPv6 protocol field. See the list of protocol numbers assigned by IANA at [IANA registry protocol-numbers].",[RFC5102],0,2013-02-18 5,ipClassOfService,unsigned8,identifier,current,"For IPv4 packets, this is the value of the TOS field in the IPv4 packet header. For IPv6 packets, this is the value of the Traffic Class field in the IPv6 packet header.",,,"See [RFC1812] (Section 5.3.2) and [RFC791] for the definition of the IPv4 TOS field. See [RFC2460] for the definition of the IPv6 Traffic Class field.",[RFC5102],0,2013-02-18 6,tcpControlBits,unsigned16,flags,current,"TCP control bits observed for the packets of this Flow. This information is encoded as a bit field; for each TCP control bit, there is a bit in this set. The bit is set to 1 if any observed packet of this Flow has the corresponding TCP control bit set to 1. The bit is cleared to 0 otherwise. The values of each bit are shown below, per the definition of the bits in the TCP header [RFC793][RFC3168][RFC3540]: MSb LSb 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | | | N | C | E | U | A | P | R | S | F | | Zero | Future | S | W | C | R | C | S | S | Y | I | | (Data Offset) | Use | | R | E | G | K | H | T | N | N | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ bit flag value name description ------+-----+------------------------------------- 0x8000 Zero (see tcpHeaderLength) 0x4000 Zero (see tcpHeaderLength) 0x2000 Zero (see tcpHeaderLength) 0x1000 Zero (see tcpHeaderLength) 0x0800 Future Use 0x0400 Future Use 0x0200 Future Use 0x0100 NS ECN Nonce Sum 0x0080 CWR Congestion Window Reduced 0x0040 ECE ECN Echo 0x0020 URG Urgent Pointer field significant 0x0010 ACK Acknowledgment field significant 0x0008 PSH Push Function 0x0004 RST Reset the connection 0x0002 SYN Synchronize sequence numbers 0x0001 FIN No more data from sender As the most significant 4 bits of octets 12 and 13 (counting from zero) of the TCP header [RFC793] are used to encode the TCP data offset (header length), the corresponding bits in this Information Element MUST be exported as zero and MUST be ignored by the collector. Use the tcpHeaderLength Information Element to encode this value. Each of the 3 bits (0x800, 0x400, and 0x200), which are reserved for future use in [RFC793], SHOULD be exported as observed in the TCP headers of the packets of this Flow. If exported as a single octet with reduced-size encoding, this Information Element covers the low-order octet of this field (i.e, bits 0x80 to 0x01), omitting the ECN Nonce Sum and the three Future Use bits. A collector receiving this Information Element with reduced-size encoding must not assume anything about the content of these four bits. Exporting Processes exporting this Information Element on behalf of a Metering Process that is not capable of observing any of the ECN Nonce Sum or Future Use bits SHOULD use reduced-size encoding, and only export the least significant 8 bits of this Information Element. Note that previous revisions of this Information Element's definition specified that the CWR and ECE bits must be exported as zero, even if observed. Collectors should therefore not assume that a value of zero for these bits in this Information Element indicates the bits were never set in the observed traffic, especially if these bits are zero in every Flow Record sent by a given exporter.",,,[RFC793][RFC3168][RFC3540],[RFC7125],1,2014-01-03 7,sourceTransportPort,unsigned16,identifier,current,"The source port identifier in the transport header. For the transport protocols UDP, TCP, and SCTP, this is the source port number given in the respective header. This field MAY also be used for future transport protocols that have 16-bit source port identifiers.",,,"See [RFC768] for the definition of the UDP source port field. See [RFC793] for the definition of the TCP source port field. See [RFC4960] for the definition of SCTP. Additional information on defined UDP and TCP port numbers can be found at [IANA registry service-names-port-numbers].",[RFC5102],0,2013-02-18 8,sourceIPv4Address,ipv4Address,default,current,The IPv4 source address in the IP packet header.,,,"See [RFC791] for the definition of the IPv4 source address field.",[RFC5102],1,2014-02-03 9,sourceIPv4PrefixLength,unsigned8,,current,"The number of contiguous bits that are relevant in the sourceIPv4Prefix Information Element.",bits,0-32,,[RFC5102],0,2013-02-18 10,ingressInterface,unsigned32,identifier,current,"The index of the IP interface where packets of this Flow are being received. The value matches the value of managed object 'ifIndex' as defined in [RFC2863]. Note that ifIndex values are not assigned statically to an interface and that the interfaces may be renumbered every time the device's management system is re-initialized, as specified in [RFC2863].",,,"See [RFC2863] for the definition of the ifIndex object.",[RFC5102],0,2013-02-18 11,destinationTransportPort,unsigned16,identifier,current,"The destination port identifier in the transport header. For the transport protocols UDP, TCP, and SCTP, this is the destination port number given in the respective header. This field MAY also be used for future transport protocols that have 16-bit destination port identifiers.",,,"See [RFC768] for the definition of the UDP destination port field. See [RFC793] for the definition of the TCP destination port field. See [RFC4960] for the definition of SCTP. Additional information on defined UDP and TCP port numbers can be found at [IANA registry service-names-port-numbers].",[RFC5102],0,2013-02-18 12,destinationIPv4Address,ipv4Address,default,current,The IPv4 destination address in the IP packet header.,,,"See [RFC791] for the definition of the IPv4 destination address field.",[RFC5102],1,2014-02-03 13,destinationIPv4PrefixLength,unsigned8,,current,"The number of contiguous bits that are relevant in the destinationIPv4Prefix Information Element.",bits,0-32,,[RFC5102],0,2013-02-18 14,egressInterface,unsigned32,identifier,current,"The index of the IP interface where packets of this Flow are being sent. The value matches the value of managed object 'ifIndex' as defined in [RFC2863]. Note that ifIndex values are not assigned statically to an interface and that the interfaces may be renumbered every time the device's management system is re-initialized, as specified in [RFC2863].",,,"See [RFC2863] for the definition of the ifIndex object.",[RFC5102],0,2013-02-18 15,ipNextHopIPv4Address,ipv4Address,default,current,The IPv4 address of the next IPv4 hop.,,,,[RFC5102],1,2014-02-03 16,bgpSourceAsNumber,unsigned32,identifier,current,"The autonomous system (AS) number of the source IP address. If AS path information for this Flow is only available as an unordered AS set (and not as an ordered AS sequence), then the value of this Information Element is 0.",,,"See [RFC4271] for a description of BGP-4, and see [RFC1930] for the definition of the AS number.",[RFC5102],0,2013-02-18 17,bgpDestinationAsNumber,unsigned32,identifier,current,"The autonomous system (AS) number of the destination IP address. If AS path information for this Flow is only available as an unordered AS set (and not as an ordered AS sequence), then the value of this Information Element is 0.",,,"See [RFC4271] for a description of BGP-4, and see [RFC1930] for the definition of the AS number.",[RFC5102],0,2013-02-18 18,bgpNextHopIPv4Address,ipv4Address,default,current,The IPv4 address of the next (adjacent) BGP hop.,,,See [RFC4271] for a description of BGP-4.,[RFC5102],1,2014-02-03 19,postMCastPacketDeltaCount,unsigned64,deltaCounter,current,"The number of outgoing multicast packets since the previous report (if any) sent for packets of this Flow by a multicast daemon within the Observation Domain. This property cannot necessarily be observed at the Observation Point, but may be retrieved by other means.",packets,,,[RFC5102],0,2013-02-18 20,postMCastOctetDeltaCount,unsigned64,deltaCounter,current,"The number of octets since the previous report (if any) in outgoing multicast packets sent for packets of this Flow by a multicast daemon within the Observation Domain. This property cannot necessarily be observed at the Observation Point, but may be retrieved by other means. The number of octets includes IP header(s) and IP payload.",octets,,,[RFC5102],0,2013-02-18 21,flowEndSysUpTime,unsigned32,,current,"The relative timestamp of the last packet of this Flow. It indicates the number of milliseconds since the last (re-)initialization of the IPFIX Device (sysUpTime). sysUpTime can be calculated from systemInitTimeMilliseconds.",milliseconds,,,[RFC5102],1,2014-01-11 22,flowStartSysUpTime,unsigned32,,current,"The relative timestamp of the first packet of this Flow. It indicates the number of milliseconds since the last (re-)initialization of the IPFIX Device (sysUpTime). sysUpTime can be calculated from systemInitTimeMilliseconds.",milliseconds,,,[RFC5102],1,2014-01-11 23,postOctetDeltaCount,unsigned64,deltaCounter,current,"The definition of this Information Element is identical to the definition of Information Element 'octetDeltaCount', except that it reports a potentially modified value caused by a middlebox function after the packet passed the Observation Point.",octets,,,[RFC5102],0,2013-02-18 24,postPacketDeltaCount,unsigned64,deltaCounter,current,"The definition of this Information Element is identical to the definition of Information Element 'packetDeltaCount', except that it reports a potentially modified value caused by a middlebox function after the packet passed the Observation Point.",packets,,,[RFC5102],0,2013-02-18 25,minimumIpTotalLength,unsigned64,,current,"Length of the smallest packet observed for this Flow. The packet length includes the IP header(s) length and the IP payload length.",octets,,"See [RFC791] for the specification of the IPv4 total length. See [RFC2460] for the specification of the IPv6 payload length. See [RFC2675] for the specification of the IPv6 jumbo payload length.",[RFC5102],0,2013-02-18 26,maximumIpTotalLength,unsigned64,,current,"Length of the largest packet observed for this Flow. The packet length includes the IP header(s) length and the IP payload length.",octets,,"See [RFC791] for the specification of the IPv4 total length. See [RFC2460] for the specification of the IPv6 payload length. See [RFC2675] for the specification of the IPv6 jumbo payload length.",[RFC5102],0,2013-02-18 27,sourceIPv6Address,ipv6Address,default,current,The IPv6 source address in the IP packet header.,,,"See [RFC2460] for the definition of the Source Address field in the IPv6 header.",[RFC5102],1,2014-02-03 28,destinationIPv6Address,ipv6Address,default,current,The IPv6 destination address in the IP packet header.,,,"See [RFC2460] for the definition of the Destination Address field in the IPv6 header.",[RFC5102],1,2014-02-03 29,sourceIPv6PrefixLength,unsigned8,,current,"The number of contiguous bits that are relevant in the sourceIPv6Prefix Information Element.",bits,0-128,,[RFC5102],0,2013-02-18 30,destinationIPv6PrefixLength,unsigned8,,current,"The number of contiguous bits that are relevant in the destinationIPv6Prefix Information Element.",bits,0-128,,[RFC5102],0,2013-02-18 31,flowLabelIPv6,unsigned32,identifier,current,The value of the IPv6 Flow Label field in the IP packet header.,,0-0xFFFFF,"See [RFC2460] for the definition of the Flow Label field in the IPv6 packet header.",[RFC5102],1,2014-08-13 32,icmpTypeCodeIPv4,unsigned16,identifier,current,"Type and Code of the IPv4 ICMP message. The combination of both values is reported as (ICMP type * 256) + ICMP code.",,,"See [RFC792] for the definition of the IPv4 ICMP type and code fields.",[RFC5102],0,2013-02-18 33,igmpType,unsigned8,identifier,current,The type field of the IGMP message.,,,"See [RFC3376] for the definition of the IGMP type field.",[RFC5102],0,2013-02-18 34,samplingInterval,unsigned32,quantity,deprecated,"Deprecated in favor of 305 samplingPacketInterval. When using sampled NetFlow, the rate at which packets are sampled -- e.g., a value of 100 indicates that one of every 100 packets is sampled.",packets,,,[RFC7270],0,2014-04-04 35,samplingAlgorithm,unsigned8,identifier,deprecated,"Deprecated in favor of 304 selectorAlgorithm. The type of algorithm used for sampled NetFlow: 1 - Deterministic Sampling, 2 - Random Sampling. The values are not compatible with the selectorAlgorithm IE, where ""Deterministic"" has been replaced by ""Systematic count-based"" (1) or ""Systematic time-based"" (2), and ""Random"" is (3). Conversion is required; see [Packet Sampling (PSAMP) Parameters.]",,,,[RFC7270],0,2014-04-04 36,flowActiveTimeout,unsigned16,,current,"The number of seconds after which an active Flow is timed out anyway, even if there is still a continuous flow of packets.",seconds,,,[RFC5102],0,2013-02-18 37,flowIdleTimeout,unsigned16,,current,"A Flow is considered to be timed out if no packets belonging to the Flow have been observed for the number of seconds specified by this field.",seconds,,,[RFC5102],0,2013-02-18 38,engineType,unsigned8,identifier,deprecated,"Type of flow switching engine in a router/switch: RP = 0, VIP/Line card = 1, PFC/DFC = 2. Reserved for internal use on the Collector.",,,,[RFC7270],0,2014-04-04 39,engineId,unsigned8,identifier,deprecated,"Versatile Interface Processor (VIP) or line card slot number of the flow switching engine in a router/switch. Reserved for internal use on the Collector.",,,,[RFC7270],0,2014-04-04 40,exportedOctetTotalCount,unsigned64,totalCounter,current,"The total number of octets that the Exporting Process has sent since the Exporting Process (re-)initialization to a particular Collecting Process. The value of this Information Element is calculated by summing up the IPFIX Message Header length values of all IPFIX Messages that were successfully sent to the Collecting Process. The reported number excludes octets in the IPFIX Message that carries the counter value. If this Information Element is sent to a particular Collecting Process, then by default it specifies the number of octets sent to this Collecting Process.",octets,,,[RFC5102],0,2013-02-18 41,exportedMessageTotalCount,unsigned64,totalCounter,current,"The total number of IPFIX Messages that the Exporting Process has sent since the Exporting Process (re-)initialization to a particular Collecting Process. The reported number excludes the IPFIX Message that carries the counter value. If this Information Element is sent to a particular Collecting Process, then by default it specifies the number of IPFIX Messages sent to this Collecting Process.",messages,,,[RFC5102],0,2013-02-18 42,exportedFlowRecordTotalCount,unsigned64,totalCounter,current,"The total number of Flow Records that the Exporting Process has sent as Data Records since the Exporting Process (re-)initialization to a particular Collecting Process. The reported number excludes Flow Records in the IPFIX Message that carries the counter value. If this Information Element is sent to a particular Collecting Process, then by default it specifies the number of Flow Records sent to this process.",flows,,,[RFC5102],0,2013-02-18 43,ipv4RouterSc,ipv4Address,default,deprecated,"This is a platform-specific field for the Catalyst 5000/Catalyst 6000 family. It is used to store the address of a router that is being shortcut when performing MultiLayer Switching.",,,[CCO-MLS] describes MultiLayer Switching.,[RFC7270],0,2014-04-04 44,sourceIPv4Prefix,ipv4Address,default,current,IPv4 source address prefix.,,,,[RFC5102],0,2013-02-18 45,destinationIPv4Prefix,ipv4Address,default,current,IPv4 destination address prefix.,,,,[RFC5102],0,2013-02-18 46,mplsTopLabelType,unsigned8,identifier,current,"This field identifies the control protocol that allocated the top-of-stack label. Values for this field are listed in the MPLS label type registry. See [http://www.iana.org/assignments/ipfix/ipfix.xml#ipfix-mpls-label-type]",,,"See [RFC3031] for the MPLS label structure. See [RFC4364] for the association of MPLS labels with Virtual Private Networks (VPNs). See [RFC4271] for BGP and BGP routing. See [RFC5036] for Label Distribution Protocol (LDP). See the list of MPLS label types assigned by IANA at [IANA registry mpls-label-values].",[RFC5102],0,2013-02-18 47,mplsTopLabelIPv4Address,ipv4Address,default,current,"The IPv4 address of the system that the MPLS top label will cause this Flow to be forwarded to.",,,"See [RFC3031] for the association between MPLS labels and IP addresses.",[RFC5102],1,2014-02-03 48,samplerId,unsigned8,identifier,deprecated,"Deprecated in favor of 302 selectorId. The unique identifier associated with samplerName.",,,,[RFC7270],0,2014-04-04 49,samplerMode,unsigned8,identifier,deprecated,"Deprecated in favor of 304 selectorAlgorithm. The values are not compatible: selectorAlgorithm=3 is random sampling. The type of algorithm used for sampling data: 1 - Deterministic, 2 - Random Sampling. Use with samplerRandomInterval.",,,,[RFC7270],0,2014-04-04 50,samplerRandomInterval,unsigned32,quantity,deprecated,"Deprecated in favor of 305 samplingPacketInterval. Packet interval at which to sample -- in case of random sampling. Used in connection with the samplerMode 0x02 (random sampling) value.",,,,[RFC7270],0,2014-04-04 51,classId,unsigned8,identifier,deprecated,"Deprecated in favor of 302 selectorId. Characterizes the traffic class, i.e., QoS treatment.",,,,[RFC7270],0,2014-04-04 52,minimumTTL,unsigned8,,current,Minimum TTL value observed for any packet in this Flow.,hops,,"See [RFC791] for the definition of the IPv4 Time to Live field. See [RFC2460] for the definition of the IPv6 Hop Limit field.",[RFC5102],0,2013-02-18 53,maximumTTL,unsigned8,,current,Maximum TTL value observed for any packet in this Flow.,hops,,"See [RFC791] for the definition of the IPv4 Time to Live field. See [RFC2460] for the definition of the IPv6 Hop Limit field.",[RFC5102],0,2013-02-18 54,fragmentIdentification,unsigned32,identifier,current,"The value of the Identification field in the IPv4 packet header or in the IPv6 Fragment header, respectively. The value is 0 for IPv6 if there is no fragment header.",,,"See [RFC791] for the definition of the IPv4 Identification field. See [RFC2460] for the definition of the Identification field in the IPv6 Fragment header.",[RFC5102],0,2013-02-18 55,postIpClassOfService,unsigned8,identifier,current,"The definition of this Information Element is identical to the definition of Information Element 'ipClassOfService', except that it reports a potentially modified value caused by a middlebox function after the packet passed the Observation Point.",,,"See [RFC791] for the definition of the IPv4 TOS field. See [RFC2460] for the definition of the IPv6 Traffic Class field. See [RFC3234] for the definition of middleboxes.",[RFC5102],0,2013-02-18 56,sourceMacAddress,macAddress,default,current,The IEEE 802 source MAC address field.,,,See IEEE.802-3.2002.,[RFC5102],1,2014-02-03 57,postDestinationMacAddress,macAddress,default,current,"The definition of this Information Element is identical to the definition of Information Element 'destinationMacAddress', except that it reports a potentially modified value caused by a middlebox function after the packet passed the Observation Point.",,,See IEEE.802-3.2002.,[RFC5102],1,2014-02-03 58,vlanId,unsigned16,identifier,current,"Virtual LAN identifier associated with ingress interface. For dot1q vlans, see 243 dot1qVlanId.",,,See IEEE.802-1Q.2003.,[RFC5102],0,2013-02-18 59,postVlanId,unsigned16,identifier,current,"Virtual LAN identifier associated with egress interface. For postdot1q vlans, see 254, postDot1qVlanId.",,,See IEEE.802-1Q.2003.,[RFC5102],0,2013-02-18 60,ipVersion,unsigned8,identifier,current,The IP version field in the IP packet header.,,,"See [RFC791] for the definition of the version field in the IPv4 packet header. See [RFC2460] for the definition of the version field in the IPv6 packet header. Additional information on defined version numbers can be found at [IANA registry version-numbers].",[RFC5102],0,2013-02-18 61,flowDirection,unsigned8,identifier,current,"The direction of the Flow observed at the Observation Point. There are only two values defined. 0x00: ingress flow 0x01: egress flow",,,,[RFC5102],0,2013-02-18 62,ipNextHopIPv6Address,ipv6Address,default,current,The IPv6 address of the next IPv6 hop.,,,,[RFC5102],1,2014-02-03 63,bgpNextHopIPv6Address,ipv6Address,default,current,The IPv6 address of the next (adjacent) BGP hop.,,,See [RFC4271] for a description of BGP-4.,[RFC5102],1,2014-02-03 64,ipv6ExtensionHeaders,unsigned32,flags,current,"IPv6 extension headers observed in packets of this Flow. The information is encoded in a set of bit fields. For each IPv6 option header, there is a bit in this set. The bit is set to 1 if any observed packet of this Flow contains the corresponding IPv6 extension header. Otherwise, if no observed packet of this Flow contained the respective IPv6 extension header, the value of the corresponding bit is 0. 0 1 2 3 4 5 6 7 +-----+-----+-----+-----+-----+-----+-----+-----+ | DST | HOP | Res | UNK |FRA0 | RH |FRA1 | Res | ... +-----+-----+-----+-----+-----+-----+-----+-----+ 8 9 10 11 12 13 14 15 +-----+-----+-----+-----+-----+-----+-----+-----+ ... | Reserved | MOB | ESP | AH | PAY | ... +-----+-----+-----+-----+-----+-----+-----+-----+ 16 17 18 19 20 21 22 23 +-----+-----+-----+-----+-----+-----+-----+-----+ ... | Reserved | ... +-----+-----+-----+-----+-----+-----+-----+-----+ 24 25 26 27 28 29 30 31 +-----+-----+-----+-----+-----+-----+-----+-----+ ... | Reserved | +-----+-----+-----+-----+-----+-----+-----+-----+ Bit IPv6 Option Description 0, DST 60 Destination option header 1, HOP 0 Hop-by-hop option header 2, Res Reserved 3, UNK Unknown Layer 4 header (compressed, encrypted, not supported) 4, FRA0 44 Fragment header - first fragment 5, RH 43 Routing header 6, FRA1 44 Fragmentation header - not first fragment 7, Res Reserved 8 to 11 Reserved 12, MOB 135 IPv6 mobility [RFC3775] 13, ESP 50 Encrypted security payload 14, AH 51 Authentication Header 15, PAY 108 Payload compression header 16 to 31 Reserved",,,"See [RFC2460] for the general definition of IPv6 extension headers and for the specification of the hop-by-hop options header, the routing header, the fragment header, and the destination options header. See [RFC4302] for the specification of the authentication header. See [RFC4303] for the specification of the encapsulating security payload. The diagram provided in [RFC5102] is incorrect. The diagram in this registry is taken from Errata 1738. See [RFC Errata 1738]",[RFC5102],0,2013-02-18 65-69,Assigned for NetFlow v9 compatibility,,,,,,,[RFC3954],[RFC5102],0,2013-02-18 70,mplsTopLabelStackSection,octetArray,default,current,"The Label, Exp, and S fields from the top MPLS label stack entry, i.e., from the last label that was pushed. The size of this Information Element is 3 octets. 0 1 2 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Label | Exp |S| +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ Label: Label Value, 20 bits Exp: Experimental Use, 3 bits S: Bottom of Stack, 1 bit",,,See [RFC3032].,[RFC5102],1,2014-02-03 71,mplsLabelStackSection2,octetArray,default,current,"The Label, Exp, and S fields from the label stack entry that was pushed immediately before the label stack entry that would be reported by mplsTopLabelStackSection. See the definition of mplsTopLabelStackSection for further details. The size of this Information Element is 3 octets.",,,See [RFC3032].,[RFC5102],1,2014-02-03 72,mplsLabelStackSection3,octetArray,default,current,"The Label, Exp, and S fields from the label stack entry that was pushed immediately before the label stack entry that would be reported by mplsLabelStackSection2. See the definition of mplsTopLabelStackSection for further details. The size of this Information Element is 3 octets.",,,See [RFC3032].,[RFC5102],1,2014-02-03 73,mplsLabelStackSection4,octetArray,default,current,"The Label, Exp, and S fields from the label stack entry that was pushed immediately before the label stack entry that would be reported by mplsLabelStackSection3. See the definition of mplsTopLabelStackSection for further details. The size of this Information Element is 3 octets.",,,See [RFC3032].,[RFC5102],1,2014-02-03 74,mplsLabelStackSection5,octetArray,default,current,"The Label, Exp, and S fields from the label stack entry that was pushed immediately before the label stack entry that would be reported by mplsLabelStackSection4. See the definition of mplsTopLabelStackSection for further details. The size of this Information Element is 3 octets.",,,See [RFC3032].,[RFC5102],1,2014-02-03 75,mplsLabelStackSection6,octetArray,default,current,"The Label, Exp, and S fields from the label stack entry that was pushed immediately before the label stack entry that would be reported by mplsLabelStackSection5. See the definition of mplsTopLabelStackSection for further details. The size of this Information Element is 3 octets.",,,See [RFC3032].,[RFC5102],1,2014-02-03 76,mplsLabelStackSection7,octetArray,default,current,"The Label, Exp, and S fields from the label stack entry that was pushed immediately before the label stack entry that would be reported by mplsLabelStackSection6. See the definition of mplsTopLabelStackSection for further details. The size of this Information Element is 3 octets.",,,See [RFC3032].,[RFC5102],1,2014-02-03 77,mplsLabelStackSection8,octetArray,default,current,"The Label, Exp, and S fields from the label stack entry that was pushed immediately before the label stack entry that would be reported by mplsLabelStackSection7. See the definition of mplsTopLabelStackSection for further details. The size of this Information Element is 3 octets.",,,See [RFC3032].,[RFC5102],1,2014-02-03 78,mplsLabelStackSection9,octetArray,default,current,"The Label, Exp, and S fields from the label stack entry that was pushed immediately before the label stack entry that would be reported by mplsLabelStackSection8. See the definition of mplsTopLabelStackSection for further details. The size of this Information Element is 3 octets.",,,See [RFC3032].,[RFC5102],1,2014-02-03 79,mplsLabelStackSection10,octetArray,default,current,"The Label, Exp, and S fields from the label stack entry that was pushed immediately before the label stack entry that would be reported by mplsLabelStackSection9. See the definition of mplsTopLabelStackSection for further details. The size of this Information Element is 3 octets.",,,See [RFC3032].,[RFC5102],1,2014-02-03 80,destinationMacAddress,macAddress,default,current,The IEEE 802 destination MAC address field.,,,See IEEE.802-3.2002.,[RFC5102],1,2014-02-03 81,postSourceMacAddress,macAddress,default,current,"The definition of this Information Element is identical to the definition of Information Element 'sourceMacAddress', except that it reports a potentially modified value caused by a middlebox function after the packet passed the Observation Point.",,,See IEEE.802-3.2002.,[RFC5102],1,2014-02-03 82,interfaceName,string,default,current,"A short name uniquely describing an interface, eg ""Eth1/0"".",,,See [RFC2863] for the definition of the ifName object.,[ipfix-iana_at_cisco.com],0,2013-02-18 83,interfaceDescription,string,default,current,"The description of an interface, eg ""FastEthernet 1/0"" or ""ISP connection"".",,,See [RFC2863] for the definition of the ifDescr object.,[ipfix-iana_at_cisco.com],0,2013-02-18 84,samplerName,string,,deprecated,"Deprecated in favor of 335 selectorName. Name of the flow sampler.",,,,[RFC7270],0,2014-04-04 85,octetTotalCount,unsigned64,totalCounter,current,"The total number of octets in incoming packets for this Flow at the Observation Point since the Metering Process (re-)initialization for this Observation Point. The number of octets includes IP header(s) and IP payload.",octets,,,[RFC5102],0,2013-02-18 86,packetTotalCount,unsigned64,totalCounter,current,"The total number of incoming packets for this Flow at the Observation Point since the Metering Process (re-)initialization for this Observation Point.",packets,,,[RFC5102],0,2013-02-18 87,flagsAndSamplerId,unsigned32,identifier,deprecated,"Flow flags and the value of the sampler ID (samplerId) combined in one bitmapped field. Reserved for internal use on the Collector.",,,,[RFC7270],0,2014-04-04 88,fragmentOffset,unsigned16,quantity,current,"The value of the IP fragment offset field in the IPv4 packet header or the IPv6 Fragment header, respectively. The value is 0 for IPv6 if there is no fragment header.",,0-0x1FFF,"See [RFC791] for the specification of the fragment offset in the IPv4 header. See [RFC2460] for the specification of the fragment offset in the IPv6 Fragment header.",[RFC5102],1,2014-08-13 89,forwardingStatus,unsigned32,identifier,current,"This Information Element describes the forwarding status of the flow and any attached reasons. The reduced-size encoding rules as per [RFC7011] apply. The basic encoding is 8 bits. The future extensions could add one or three bytes. The layout of the basic encoding is as follows: MSB - 0 1 2 3 4 5 6 7 - LSB +---+---+---+---+---+---+---+---+ | Status| Reason code or flags | +---+---+---+---+---+---+---+---+ Status: 00b = Unknown 01b = Forwarded 10b = Dropped 11b = Consumed Reason Code (status = 01b, Forwarded) 01 000000b = 64 = Unknown 01 000001b = 65 = Fragmented 01 000010b = 66 = Not Fragmented Reason Code (status = 10b, Dropped) 10 000000b = 128 = Unknown 10 000001b = 129 = ACL deny 10 000010b = 130 = ACL drop 10 000011b = 131 = Unroutable 10 000100b = 132 = Adjacency 10 000101b = 133 = Fragmentation and DF set 10 000110b = 134 = Bad header checksum 10 000111b = 135 = Bad total Length 10 001000b = 136 = Bad header length 10 001001b = 137 = bad TTL 10 001010b = 138 = Policer 10 001011b = 139 = WRED 10 001100b = 140 = RPF 10 001101b = 141 = For us 10 001110b = 142 = Bad output interface 10 001111b = 143 = Hardware Reason Code (status = 11b, Consumed) 11 000000b = 192 = Unknown 11 000001b = 193 = Punt Adjacency 11 000010b = 194 = Incomplete Adjacency 11 000011b = 195 = For us Examples: value : 0x40 = 64 binary: 01000000 decode: 01 -> Forward 000000 -> No further information value : 0x89 = 137 binary: 10001001 decode: 10 -> Drop 001001 -> Fragmentation and DF set",,,"See ""NetFlow Version 9 Flow-Record Format"" [CCO-NF9FMT].",[RFC7270],0,2014-04-04 90,mplsVpnRouteDistinguisher,octetArray,default,current,"The value of the VPN route distinguisher of a corresponding entry in a VPN routing and forwarding table. Route distinguisher ensures that the same address can be used in several different MPLS VPNs and that it is possible for BGP to carry several completely different routes to that address, one for each VPN. According to [RFC4364], the size of mplsVpnRouteDistinguisher is 8 octets. However, in [RFC4382] an octet string with flexible length was chosen for representing a VPN route distinguisher by object MplsL3VpnRouteDistinguisher. This choice was made in order to be open to future changes of the size. This idea was adopted when choosing octetArray as abstract data type for this Information Element. The maximum length of this Information Element is 256 octets.",,,"See [RFC4364] for the specification of the route distinguisher. See [RFC4382] for the specification of the MPLS/BGP Layer 3 Virtual Private Network (VPN) Management Information Base.",[RFC5102],1,2014-02-03 91,mplsTopLabelPrefixLength,unsigned8,quantity,current,"The prefix length of the subnet of the mplsTopLabelIPv4Address that the MPLS top label will cause the Flow to be forwarded to.",bits,0-32,"See [RFC3031] for the association between MPLS labels and prefix lengths.",[ipfix-iana_at_cisco.com],1,2014-08-13 92,srcTrafficIndex,unsigned32,identifier,current,BGP Policy Accounting Source Traffic Index.,,,BGP policy accounting as described in [CCO-BGPPOL].,[RFC7270],0,2014-04-04 93,dstTrafficIndex,unsigned32,identifier,current,BGP Policy Accounting Destination Traffic Index.,,,BGP policy accounting as described in [CCO-BGPPOL].,[RFC7270],0,2014-04-04 94,applicationDescription,string,default,current,Specifies the description of an application.,,,,[RFC6759],1,2014-02-03 95,applicationId,octetArray,default,current,Specifies an Application ID per [RFC6759].,,,See section 4 of [RFC6759] for the applicationId Information Element Specification.,[RFC6759],1,2014-02-03 96,applicationName,string,default,current,Specifies the name of an application.,,,,[RFC6759],0,2013-02-18 97,Assigned for NetFlow v9 compatibility,,,,,,,[RFC3954],[RFC5102],0,2013-02-18 98,postIpDiffServCodePoint,unsigned8,identifier,current,"The definition of this Information Element is identical to the definition of Information Element 'ipDiffServCodePoint', except that it reports a potentially modified value caused by a middlebox function after the packet passed the Observation Point.",,0-63,"See [RFC3260] for the definition of the Differentiated Services Field. See section 5.3.2 of [RFC1812] and [RFC791] for the definition of the IPv4 TOS field. See [RFC2460] for the definition of the IPv6 Traffic Class field. See the IPFIX Information Model [RFC5102] for the 'ipDiffServCodePoint' specification.",[ipfix-iana_at_cisco.com],0,2013-02-18 99,multicastReplicationFactor,unsigned32,quantity,current,"The amount of multicast replication that's applied to a traffic stream.",,,"See [RFC1112] for the specification of reserved IPv4 multicast addresses. See [RFC4291] for the specification of reserved IPv6 multicast addresses.",[ipfix-iana_at_cisco.com],0,2013-02-18 100,className,string,,deprecated,"Deprecated in favor of 335 selectorName. Traffic Class Name, associated with the classId Information Element.",,,,[RFC7270],0,2014-04-04 101,classificationEngineId,unsigned8,identifier,current,"A unique identifier for the engine that determined the Selector ID. Thus, the Classification Engine ID defines the context for the Selector ID. The Classification Engine can be considered a specific registry for application assignments. Values for this field are listed in the Classification Engine IDs registry. See [http://www.iana.org/assignments/ipfix/ipfix.xml#classification-engine-ids]",,,,[RFC6759],0,2013-02-18 102,layer2packetSectionOffset,unsigned16,quantity,deprecated,"Deprecated in favor of 409 sectionOffset. Layer 2 packet section offset. Potentially a generic packet section offset.",,,,[RFC7270],0,2014-04-04 103,layer2packetSectionSize,unsigned16,quantity,deprecated,"Deprecated in favor of 312 dataLinkFrameSize. Layer 2 packet section size. Potentially a generic packet section size.",,,,[RFC7270],0,2014-04-04 104,layer2packetSectionData,octetArray,,deprecated,"Deprecated in favor of 315 dataLinkFrameSection. Layer 2 packet section data.",,,,[RFC7270],0,2014-04-04 105-127,Assigned for NetFlow v9 compatibility,,,,,,,[RFC3954],[RFC5102],0,2013-02-18 128,bgpNextAdjacentAsNumber,unsigned32,identifier,current,"The autonomous system (AS) number of the first AS in the AS path to the destination IP address. The path is deduced by looking up the destination IP address of the Flow in the BGP routing information base. If AS path information for this Flow is only available as an unordered AS set (and not as an ordered AS sequence), then the value of this Information Element is 0.",,,"See [RFC4271] for a description of BGP-4, and see [RFC1930] for the definition of the AS number.",[RFC5102],0,2013-02-18 129,bgpPrevAdjacentAsNumber,unsigned32,identifier,current,"The autonomous system (AS) number of the last AS in the AS path from the source IP address. The path is deduced by looking up the source IP address of the Flow in the BGP routing information base. If AS path information for this Flow is only available as an unordered AS set (and not as an ordered AS sequence), then the value of this Information Element is 0. In case of BGP asymmetry, the bgpPrevAdjacentAsNumber might not be able to report the correct value.",,,"See [RFC4271] for a description of BGP-4, and see [RFC1930] for the definition of the AS number.",[RFC5102],0,2013-02-18 130,exporterIPv4Address,ipv4Address,default,current,"The IPv4 address used by the Exporting Process. This is used by the Collector to identify the Exporter in cases where the identity of the Exporter may have been obscured by the use of a proxy.",,,,[RFC5102],1,2014-02-03 131,exporterIPv6Address,ipv6Address,default,current,"The IPv6 address used by the Exporting Process. This is used by the Collector to identify the Exporter in cases where the identity of the Exporter may have been obscured by the use of a proxy.",,,,[RFC5102],1,2014-02-03 132,droppedOctetDeltaCount,unsigned64,deltaCounter,current,"The number of octets since the previous report (if any) in packets of this Flow dropped by packet treatment. The number of octets includes IP header(s) and IP payload.",octets,,,[RFC5102],0,2013-02-18 133,droppedPacketDeltaCount,unsigned64,deltaCounter,current,"The number of packets since the previous report (if any) of this Flow dropped by packet treatment.",packets,,,[RFC5102],0,2013-02-18 134,droppedOctetTotalCount,unsigned64,totalCounter,current,"The total number of octets in packets of this Flow dropped by packet treatment since the Metering Process (re-)initialization for this Observation Point. The number of octets includes IP header(s) and IP payload.",octets,,,[RFC5102],0,2013-02-18 135,droppedPacketTotalCount,unsigned64,totalCounter,current,"The number of packets of this Flow dropped by packet treatment since the Metering Process (re-)initialization for this Observation Point.",packets,,,[RFC5102],0,2013-02-18 136,flowEndReason,unsigned8,identifier,current,"The reason for Flow termination. The range of values includes the following: 0x01: idle timeout The Flow was terminated because it was considered to be idle. 0x02: active timeout The Flow was terminated for reporting purposes while it was still active, for example, after the maximum lifetime of unreported Flows was reached. 0x03: end of Flow detected The Flow was terminated because the Metering Process detected signals indicating the end of the Flow, for example, the TCP FIN flag. 0x04: forced end The Flow was terminated because of some external event, for example, a shutdown of the Metering Process initiated by a network management application. 0x05: lack of resources The Flow was terminated because of lack of resources available to the Metering Process and/or the Exporting Process.",,,,[RFC5102],0,2013-02-18 137,commonPropertiesId,unsigned64,identifier,current,"An identifier of a set of common properties that is unique per Observation Domain and Transport Session. Typically, this Information Element is used to link to information reported in separate Data Records.",,,,[RFC5102],0,2013-02-18 138,observationPointId,unsigned64,identifier,current,"An identifier of an Observation Point that is unique per Observation Domain. It is RECOMMENDED that this identifier is also unique per IPFIX Device. Typically, this Information Element is used for limiting the scope of other Information Elements.",,,,[RFC5102][ipfix-iana_at_cisco.com],1,2013-04-11 139,icmpTypeCodeIPv6,unsigned16,identifier,current,"Type and Code of the IPv6 ICMP message. The combination of both values is reported as (ICMP type * 256) + ICMP code.",,,"See [RFC4443] for the definition of the IPv6 ICMP type and code fields.",[RFC5102],0,2013-02-18 140,mplsTopLabelIPv6Address,ipv6Address,default,current,"The IPv6 address of the system that the MPLS top label will cause this Flow to be forwarded to.",,,"See [RFC3031] for the association between MPLS labels and IP addresses.",[RFC5102],1,2014-02-03 141,lineCardId,unsigned32,identifier,current,"An identifier of a line card that is unique per IPFIX Device hosting an Observation Point. Typically, this Information Element is used for limiting the scope of other Information Elements.",,,,[RFC5102],0,2013-02-18 142,portId,unsigned32,identifier,current,"An identifier of a line port that is unique per IPFIX Device hosting an Observation Point. Typically, this Information Element is used for limiting the scope of other Information Elements.",,,,[RFC5102],0,2013-02-18 143,meteringProcessId,unsigned32,identifier,current,"An identifier of a Metering Process that is unique per IPFIX Device. Typically, this Information Element is used for limiting the scope of other Information Elements. Note that process identifiers are typically assigned dynamically. The Metering Process may be re-started with a different ID.",,,,[RFC5102],0,2013-02-18 144,exportingProcessId,unsigned32,identifier,current,"An identifier of an Exporting Process that is unique per IPFIX Device. Typically, this Information Element is used for limiting the scope of other Information Elements. Note that process identifiers are typically assigned dynamically. The Exporting Process may be re-started with a different ID.",,,,[RFC5102],0,2013-02-18 145,templateId,unsigned16,identifier,current,"An identifier of a Template that is locally unique within a combination of a Transport session and an Observation Domain. Template IDs 0-255 are reserved for Template Sets, Options Template Sets, and other reserved Sets yet to be created. Template IDs of Data Sets are numbered from 256 to 65535. Typically, this Information Element is used for limiting the scope of other Information Elements. Note that after a re-start of the Exporting Process Template identifiers may be re-assigned.",,,,[RFC5102],0,2013-02-18 146,wlanChannelId,unsigned8,identifier,current,The identifier of the 802.11 (Wi-Fi) channel used.,,,See IEEE.802-11.1999.,[RFC5102],0,2013-02-18 147,wlanSSID,string,default,current,"The Service Set IDentifier (SSID) identifying an 802.11 (Wi-Fi) network used. According to IEEE.802-11.1999, the SSID is encoded into a string of up to 32 characters.",,,See IEEE.802-11.1999.,[RFC5102],0,2013-02-18 148,flowId,unsigned64,identifier,current,"An identifier of a Flow that is unique within an Observation Domain. This Information Element can be used to distinguish between different Flows if Flow Keys such as IP addresses and port numbers are not reported or are reported in separate records.",,,,[RFC5102],0,2013-02-18 149,observationDomainId,unsigned32,identifier,current,"An identifier of an Observation Domain that is locally unique to an Exporting Process. The Exporting Process uses the Observation Domain ID to uniquely identify to the Collecting Process the Observation Domain where Flows were metered. It is RECOMMENDED that this identifier is also unique per IPFIX Device. A value of 0 indicates that no specific Observation Domain is identified by this Information Element. Typically, this Information Element is used for limiting the scope of other Information Elements.",,,,[RFC5102],0,2013-02-18 150,flowStartSeconds,dateTimeSeconds,default,current,The absolute timestamp of the first packet of this Flow.,seconds,,,[RFC5102],0,2013-02-18 151,flowEndSeconds,dateTimeSeconds,default,current,The absolute timestamp of the last packet of this Flow.,seconds,,,[RFC5102],0,2013-02-18 152,flowStartMilliseconds,dateTimeMilliseconds,default,current,The absolute timestamp of the first packet of this Flow.,milliseconds,,,[RFC5102],0,2013-02-18 153,flowEndMilliseconds,dateTimeMilliseconds,default,current,The absolute timestamp of the last packet of this Flow.,milliseconds,,,[RFC5102],0,2013-02-18 154,flowStartMicroseconds,dateTimeMicroseconds,default,current,The absolute timestamp of the first packet of this Flow.,microseconds,,,[RFC5102],0,2013-02-18 155,flowEndMicroseconds,dateTimeMicroseconds,default,current,The absolute timestamp of the last packet of this Flow.,microseconds,,,[RFC5102],0,2013-02-18 156,flowStartNanoseconds,dateTimeNanoseconds,default,current,The absolute timestamp of the first packet of this Flow.,nanoseconds,,,[RFC5102],0,2013-02-18 157,flowEndNanoseconds,dateTimeNanoseconds,default,current,The absolute timestamp of the last packet of this Flow.,nanoseconds,,,[RFC5102],0,2013-02-18 158,flowStartDeltaMicroseconds,unsigned32,,current,"This is a relative timestamp only valid within the scope of a single IPFIX Message. It contains the negative time offset of the first observed packet of this Flow relative to the export time specified in the IPFIX Message Header.",microseconds,,"See the [IPFIX protocol specification] for the definition of the IPFIX Message Header.",[RFC5102],0,2013-02-18 159,flowEndDeltaMicroseconds,unsigned32,,current,"This is a relative timestamp only valid within the scope of a single IPFIX Message. It contains the negative time offset of the last observed packet of this Flow relative to the export time specified in the IPFIX Message Header.",microseconds,,"See the [IPFIX protocol specification] for the definition of the IPFIX Message Header.",[RFC5102],0,2013-02-18 160,systemInitTimeMilliseconds,dateTimeMilliseconds,default,current,"The absolute timestamp of the last (re-)initialization of the IPFIX Device.",milliseconds,,,[RFC5102],0,2013-02-18 161,flowDurationMilliseconds,unsigned32,,current,"The difference in time between the first observed packet of this Flow and the last observed packet of this Flow.",milliseconds,,,[RFC5102],0,2013-02-18 162,flowDurationMicroseconds,unsigned32,,current,"The difference in time between the first observed packet of this Flow and the last observed packet of this Flow.",microseconds,,,[RFC5102],0,2013-02-18 163,observedFlowTotalCount,unsigned64,totalCounter,current,"The total number of Flows observed in the Observation Domain since the Metering Process (re-)initialization for this Observation Point.",flows,,,[RFC5102],0,2013-02-18 164,ignoredPacketTotalCount,unsigned64,totalCounter,current,"The total number of observed IP packets that the Metering Process did not process since the (re-)initialization of the Metering Process.",packets,,,[RFC5102],0,2013-02-18 165,ignoredOctetTotalCount,unsigned64,totalCounter,current,"The total number of octets in observed IP packets (including the IP header) that the Metering Process did not process since the (re-)initialization of the Metering Process.",octets,,,[RFC5102],0,2013-02-18 166,notSentFlowTotalCount,unsigned64,totalCounter,current,"The total number of Flow Records that were generated by the Metering Process and dropped by the Metering Process or by the Exporting Process instead of being sent to the Collecting Process. There are several potential reasons for this including resource shortage and special Flow export policies.",flows,,,[RFC5102],0,2013-02-18 167,notSentPacketTotalCount,unsigned64,totalCounter,current,"The total number of packets in Flow Records that were generated by the Metering Process and dropped by the Metering Process or by the Exporting Process instead of being sent to the Collecting Process. There are several potential reasons for this including resource shortage and special Flow export policies.",packets,,,[RFC5102],0,2013-02-18 168,notSentOctetTotalCount,unsigned64,totalCounter,current,"The total number of octets in packets in Flow Records that were generated by the Metering Process and dropped by the Metering Process or by the Exporting Process instead of being sent to the Collecting Process. There are several potential reasons for this including resource shortage and special Flow export policies.",octets,,,[RFC5102],0,2013-02-18 169,destinationIPv6Prefix,ipv6Address,default,current,IPv6 destination address prefix.,,,,[RFC5102],0,2013-02-18 170,sourceIPv6Prefix,ipv6Address,default,current,IPv6 source address prefix.,,,,[RFC5102],0,2013-02-18 171,postOctetTotalCount,unsigned64,totalCounter,current,"The definition of this Information Element is identical to the definition of Information Element 'octetTotalCount', except that it reports a potentially modified value caused by a middlebox function after the packet passed the Observation Point.",octets,,,[RFC5102],0,2013-02-18 172,postPacketTotalCount,unsigned64,totalCounter,current,"The definition of this Information Element is identical to the definition of Information Element 'packetTotalCount', except that it reports a potentially modified value caused by a middlebox function after the packet passed the Observation Point.",packets,,,[RFC5102],0,2013-02-18 173,flowKeyIndicator,unsigned64,flags,current,"This set of bit fields is used for marking the Information Elements of a Data Record that serve as Flow Key. Each bit represents an Information Element in the Data Record with the n-th bit representing the n-th Information Element. A bit set to value 1 indicates that the corresponding Information Element is a Flow Key of the reported Flow. A bit set to value 0 indicates that this is not the case. If the Data Record contains more than 64 Information Elements, the corresponding Template SHOULD be designed such that all Flow Keys are among the first 64 Information Elements, because the flowKeyIndicator only contains 64 bits. If the Data Record contains less than 64 Information Elements, then the bits in the flowKeyIndicator for which no corresponding Information Element exists MUST have the value 0.",,,,[RFC5102],0,2013-02-18 174,postMCastPacketTotalCount,unsigned64,totalCounter,current,"The total number of outgoing multicast packets sent for packets of this Flow by a multicast daemon within the Observation Domain since the Metering Process (re-)initialization. This property cannot necessarily be observed at the Observation Point, but may be retrieved by other means.",packets,,,[RFC5102],0,2013-02-18 175,postMCastOctetTotalCount,unsigned64,totalCounter,current,"The total number of octets in outgoing multicast packets sent for packets of this Flow by a multicast daemon in the Observation Domain since the Metering Process (re-)initialization. This property cannot necessarily be observed at the Observation Point, but may be retrieved by other means. The number of octets includes IP header(s) and IP payload.",octets,,,[RFC5102],0,2013-02-18 176,icmpTypeIPv4,unsigned8,identifier,current,Type of the IPv4 ICMP message.,,,"See [RFC792] for the definition of the IPv4 ICMP type field.",[RFC5102],0,2013-02-18 177,icmpCodeIPv4,unsigned8,identifier,current,Code of the IPv4 ICMP message.,,,"See [RFC792] for the definition of the IPv4 ICMP code field.",[RFC5102],0,2013-02-18 178,icmpTypeIPv6,unsigned8,identifier,current,Type of the IPv6 ICMP message.,,,"See [RFC4443] for the definition of the IPv6 ICMP type field.",[RFC5102],0,2013-02-18 179,icmpCodeIPv6,unsigned8,identifier,current,Code of the IPv6 ICMP message.,,,"See [RFC4443] for the definition of the IPv6 ICMP code field.",[RFC5102],0,2013-02-18 180,udpSourcePort,unsigned16,identifier,current,The source port identifier in the UDP header.,,,"See [RFC768] for the definition of the UDP source port field. Additional information on defined UDP port numbers can be found at [IANA registry service-names-port-numbers].",[RFC5102],0,2013-02-18 181,udpDestinationPort,unsigned16,identifier,current,The destination port identifier in the UDP header.,,,"See [RFC768] for the definition of the UDP destination port field. Additional information on defined UDP port numbers can be found at [IANA registry service-names-port-numbers].",[RFC5102],0,2013-02-18 182,tcpSourcePort,unsigned16,identifier,current,The source port identifier in the TCP header.,,,"See [RFC793] for the definition of the TCP source port field. Additional information on defined TCP port numbers can be found at [IANA registry service-names-port-numbers].",[RFC5102],0,2013-02-18 183,tcpDestinationPort,unsigned16,identifier,current,The destination port identifier in the TCP header.,,,"See [RFC793] for the definition of the TCP destination port field. Additional information on defined TCP port numbers can be found at [IANA registry service-names-port-numbers].",[RFC5102],0,2013-02-18 184,tcpSequenceNumber,unsigned32,,current,The sequence number in the TCP header.,,,"See [RFC793] for the definition of the TCP sequence number.",[RFC5102],0,2013-02-18 185,tcpAcknowledgementNumber,unsigned32,,current,The acknowledgement number in the TCP header.,,,"See [RFC793] for the definition of the TCP acknowledgement number.",[RFC5102],0,2013-02-18 186,tcpWindowSize,unsigned16,,current,"The window field in the TCP header. If the TCP window scale is supported, then TCP window scale must be known to fully interpret the value of this information.",,,"See [RFC793] for the definition of the TCP window field. See [RFC1323] for the definition of the TCP window scale.",[RFC5102],0,2013-02-18 187,tcpUrgentPointer,unsigned16,,current,The urgent pointer in the TCP header.,,,"See [RFC793] for the definition of the TCP urgent pointer.",[RFC5102],0,2013-02-18 188,tcpHeaderLength,unsigned8,,current,"The length of the TCP header. Note that the value of this Information Element is different from the value of the Data Offset field in the TCP header. The Data Offset field indicates the length of the TCP header in units of 4 octets. This Information Elements specifies the length of the TCP header in units of octets.",octets,,"See [RFC793] for the definition of the TCP header.",[RFC5102],0,2013-02-18 189,ipHeaderLength,unsigned8,,current,"The length of the IP header. For IPv6, the value of this Information Element is 40.",octets,,"See [RFC791] for the definition of the IPv4 header. See [RFC2460] for the definition of the IPv6 header.",[RFC5102],0,2013-02-18 190,totalLengthIPv4,unsigned16,,current,The total length of the IPv4 packet.,octets,,"See [RFC791] for the specification of the IPv4 total length.",[RFC5102],0,2013-02-18 191,payloadLengthIPv6,unsigned16,,current,"This Information Element reports the value of the Payload Length field in the IPv6 header. Note that IPv6 extension headers belong to the payload. Also note that in case of a jumbo payload option the value of the Payload Length field in the IPv6 header is zero and so will be the value reported by this Information Element.",octets,,"See [RFC2460] for the specification of the IPv6 payload length. See [RFC2675] for the specification of the IPv6 jumbo payload option.",[RFC5102],0,2013-02-18 192,ipTTL,unsigned8,,current,"For IPv4, the value of the Information Element matches the value of the Time to Live (TTL) field in the IPv4 packet header. For IPv6, the value of the Information Element matches the value of the Hop Limit field in the IPv6 packet header.",hops,,"See [RFC791] for the definition of the IPv4 Time to Live field. See [RFC2675] for the definition of the IPv6 Hop Limit field.",[RFC5102],0,2013-02-18 193,nextHeaderIPv6,unsigned8,,current,"The value of the Next Header field of the IPv6 header. The value identifies the type of the following IPv6 extension header or of the following IP payload. Valid values are defined in the IANA Protocol Numbers registry.",,,"See [RFC2460] for the definition of the IPv6 Next Header field. See the list of protocol numbers assigned by IANA at [IANA registry protocol-numbers].",[RFC5102],0,2013-02-18 194,mplsPayloadLength,unsigned32,,current,The size of the MPLS packet without the label stack.,octets,,"See [RFC3031] for the specification of MPLS packets. See [RFC3032] for the specification of the MPLS label stack.",[RFC5102],0,2013-02-18 195,ipDiffServCodePoint,unsigned8,identifier,current,"The value of a Differentiated Services Code Point (DSCP) encoded in the Differentiated Services field. The Differentiated Services field spans the most significant 6 bits of the IPv4 TOS field or the IPv6 Traffic Class field, respectively. This Information Element encodes only the 6 bits of the Differentiated Services field. Therefore, its value may range from 0 to 63.",,0-63,"See [RFC3260] for the definition of the Differentiated Services field. See [RFC1812] (Section 5.3.2) and [RFC791] for the definition of the IPv4 TOS field. See [RFC2460] for the definition of the IPv6 Traffic Class field.",[RFC5102],0,2013-02-18 196,ipPrecedence,unsigned8,identifier,current,"The value of the IP Precedence. The IP Precedence value is encoded in the first 3 bits of the IPv4 TOS field or the IPv6 Traffic Class field, respectively. This Information Element encodes only these 3 bits. Therefore, its value may range from 0 to 7.",,0-7,"See [RFC1812] (Section 5.3.3) and [RFC791] for the definition of the IP Precedence. See [RFC1812] (Section 5.3.2) and [RFC791] for the definition of the IPv4 TOS field. See [RFC2460] for the definition of the IPv6 Traffic Class field.",[RFC5102],0,2013-02-18 197,fragmentFlags,unsigned8,flags,current,"Fragmentation properties indicated by flags in the IPv4 packet header or the IPv6 Fragment header, respectively. Bit 0: (RS) Reserved. The value of this bit MUST be 0 until specified otherwise. Bit 1: (DF) 0 = May Fragment, 1 = Don't Fragment. Corresponds to the value of the DF flag in the IPv4 header. Will always be 0 for IPv6 unless a ""don't fragment"" feature is introduced to IPv6. Bit 2: (MF) 0 = Last Fragment, 1 = More Fragments. Corresponds to the MF flag in the IPv4 header or to the M flag in the IPv6 Fragment header, respectively. The value is 0 for IPv6 if there is no fragment header. Bits 3-7: (DC) Don't Care. The values of these bits are irrelevant. 0 1 2 3 4 5 6 7 +---+---+---+---+---+---+---+---+ | R | D | M | D | D | D | D | D | | S | F | F | C | C | C | C | C | +---+---+---+---+---+---+---+---+",,,"See [RFC791] for the specification of the IPv4 fragment flags. See [RFC2460] for the specification of the IPv6 Fragment header.",[RFC5102],0,2013-02-18 198,octetDeltaSumOfSquares,unsigned64,,current,"The sum of the squared numbers of octets per incoming packet since the previous report (if any) for this Flow at the Observation Point. The number of octets includes IP header(s) and IP payload.",,,,[RFC5102],0,2013-02-18 199,octetTotalSumOfSquares,unsigned64,,current,"The total sum of the squared numbers of octets in incoming packets for this Flow at the Observation Point since the Metering Process (re-)initialization for this Observation Point. The number of octets includes IP header(s) and IP payload.",octets,,,[RFC5102],0,2013-02-18 200,mplsTopLabelTTL,unsigned8,,current,"The TTL field from the top MPLS label stack entry, i.e., the last label that was pushed.",hops,,"See [RFC3032] for the specification of the TTL field.",[RFC5102],0,2013-02-18 201,mplsLabelStackLength,unsigned32,,current,The length of the MPLS label stack in units of octets.,octets,,"See [RFC3032] for the specification of the MPLS label stack.",[RFC5102],0,2013-02-18 202,mplsLabelStackDepth,unsigned32,,current,The number of labels in the MPLS label stack.,label stack entries,,"See [RFC3032] for the specification of the MPLS label stack.",[RFC5102],0,2013-02-18 203,mplsTopLabelExp,unsigned8,flags,current,"The Exp field from the top MPLS label stack entry, i.e., the last label that was pushed. Bits 0-4: Don't Care, value is irrelevant. Bits 5-7: MPLS Exp field. 0 1 2 3 4 5 6 7 +---+---+---+---+---+---+---+---+ | don't care | Exp | +---+---+---+---+---+---+---+---+",,,"See [RFC3032] for the specification of the Exp field. See [RFC3270] for usage of the Exp field.",[RFC5102],0,2013-02-18 204,ipPayloadLength,unsigned32,,current,"The effective length of the IP payload. For IPv4 packets, the value of this Information Element is the difference between the total length of the IPv4 packet (as reported by Information Element totalLengthIPv4) and the length of the IPv4 header (as reported by Information Element headerLengthIPv4). For IPv6, the value of the Payload Length field in the IPv6 header is reported except in the case that the value of this field is zero and that there is a valid jumbo payload option. In this case, the value of the Jumbo Payload Length field in the jumbo payload option is reported.",octets,,"See [RFC791] for the specification of IPv4 packets. See [RFC2460] for the specification of the IPv6 payload length. See [RFC2675] for the specification of the IPv6 jumbo payload length.",[RFC5102],0,2013-02-18 205,udpMessageLength,unsigned16,,current,The value of the Length field in the UDP header.,octets,,"See [RFC768] for the specification of the UDP header.",[RFC5102],0,2013-02-18 206,isMulticast,unsigned8,flags,current,"If the IP destination address is not a reserved multicast address, then the value of all bits of the octet (including the reserved ones) is zero. The first bit of this octet is set to 1 if the Version field of the IP header has the value 4 and if the Destination Address field contains a reserved multicast address in the range from 224.0.0.0 to 239.255.255.255. Otherwise, this bit is set to 0. The second and third bits of this octet are reserved for future use. The remaining bits of the octet are only set to values other than zero if the IP Destination Address is a reserved IPv6 multicast address. Then the fourth bit of the octet is set to the value of the T flag in the IPv6 multicast address and the remaining four bits are set to the value of the scope field in the IPv6 multicast address. 0 1 2 3 4 5 6 7 +------+------+------+------+------+------+------+------+ | IPv6 multicast scope | T | RES. | RES. | MCv4 | +------+------+------+------+------+------+------+------+ Bits 0-3: set to value of multicast scope if IPv6 multicast Bit 4: set to value of T flag, if IPv6 multicast Bits 5-6: reserved for future use Bit 7: set to 1 if IPv4 multicast",,,"See [RFC1112] for the specification of reserved IPv4 multicast addresses. See [RFC4291] for the specification of reserved IPv6 multicast addresses and the definition of the T flag and the IPv6 multicast scope. The diagram provided in [RFC5102] is incorrect. The diagram in this registry is taken from Errata 1736. See [RFC Errata 1736]",[RFC5102],0,2013-02-18 207,ipv4IHL,unsigned8,,current,"The value of the Internet Header Length (IHL) field in the IPv4 header. It specifies the length of the header in units of 4 octets. Please note that its unit is different from most of the other Information Elements reporting length values.",4 octets,,"See [RFC791] for the specification of the IPv4 header.",[RFC5102],0,2013-02-18 208,ipv4Options,unsigned32,flags,current,"IPv4 options in packets of this Flow. The information is encoded in a set of bit fields. For each valid IPv4 option type, there is a bit in this set. The bit is set to 1 if any observed packet of this Flow contains the corresponding IPv4 option type. Otherwise, if no observed packet of this Flow contained the respective IPv4 option type, the value of the corresponding bit is 0. The list of valid IPv4 options is maintained by IANA. Note that for identifying an option not just the 5-bit Option Number, but all 8 bits of the Option Type need to match one of the IPv4 options specified at http://www.iana.org/assignments/ip-parameters. Options are mapped to bits according to their option numbers. Option number X is mapped to bit X. The mapping is illustrated by the figure below. 0 1 2 3 4 5 6 7 +------+------+------+------+------+------+------+------+ ... | RR |CIPSO |E-SEC | TS | LSR | SEC | NOP | EOOL | +------+------+------+------+------+------+------+------+ 8 9 10 11 12 13 14 15 +------+------+------+------+------+------+------+------+ ... |ENCODE| VISA | FINN | MTUR | MTUP | ZSU | SSR | SID | ... +------+------+------+------+------+------+------+------+ 16 17 18 19 20 21 22 23 +------+------+------+------+------+------+------+------+ ... | DPS |NSAPA | SDB |RTRALT|ADDEXT| TR | EIP |IMITD | ... +------+------+------+------+------+------+------+------+ 24 25 26 27 28 29 30 31 +------+------+------+------+------+------+------+------+ | | EXP | to be assigned by IANA | QS | UMP | ... +------+------+------+------+------+------+------+------+ Type Option Bit Value Name Reference ---+-----+-------+------------------------------------ 0 7 RR Record Route, RFC 791 1 134 CIPSO Commercial Security 2 133 E-SEC Extended Security, RFC 1108 3 68 TS Time Stamp, RFC 791 4 131 LSR Loose Source Route, RFC791 5 130 SEC Security, RFC 1108 6 1 NOP No Operation, RFC 791 7 0 EOOL End of Options List, RFC 791 8 15 ENCODE 9 142 VISA Experimental Access Control 10 205 FINN Experimental Flow Control 11 12 MTUR (obsoleted) MTU Reply, RFC 1191 12 11 MTUP (obsoleted) MTU Probe, RFC 1191 13 10 ZSU Experimental Measurement 14 137 SSR Strict Source Route, RFC 791 15 136 SID Stream ID, RFC 791 16 151 DPS Dynamic Packet State 17 150 NSAPA NSAP Address 18 149 SDB Selective Directed Broadcast 19 147 ADDEXT Address Extension 20 148 RTRALT Router Alert, RFC 2113 21 82 TR Traceroute, RFC 3193 22 145 EIP Extended Internet Protocol, RFC 1385 23 144 IMITD IMI Traffic Descriptor 25 30 EXP RFC3692-style Experiment 25 94 EXP RFC3692-style Experiment 25 158 EXP RFC3692-style Experiment 25 222 EXP RFC3692-style Experiment 30 25 QS Quick-Start 31 152 UMP Upstream Multicast Pkt. ... ... ... Further options numbers may be assigned by IANA",,,"See [RFC791] for the definition of IPv4 options. See the list of IPv4 option numbers assigned by IANA at [IANA registry ip-parameters]. The diagram provided in [RFC5102] is incorrect. The diagram in this registry is taken from Errata 1737. See [RFC Errata 1737]",[RFC5102],0,2013-02-18 209,tcpOptions,unsigned64,flags,current,"TCP options in packets of this Flow. The information is encoded in a set of bit fields. For each TCP option, there is a bit in this set. The bit is set to 1 if any observed packet of this Flow contains the corresponding TCP option. Otherwise, if no observed packet of this Flow contained the respective TCP option, the value of the corresponding bit is 0. Options are mapped to bits according to their option numbers. Option number X is mapped to bit X. TCP option numbers are maintained by IANA. 0 1 2 3 4 5 6 7 +-----+-----+-----+-----+-----+-----+-----+-----+ | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | ... +-----+-----+-----+-----+-----+-----+-----+-----+ 8 9 10 11 12 13 14 15 +-----+-----+-----+-----+-----+-----+-----+-----+ ... | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 |... +-----+-----+-----+-----+-----+-----+-----+-----+ 16 17 18 19 20 21 22 23 +-----+-----+-----+-----+-----+-----+-----+-----+ ... | 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 |... +-----+-----+-----+-----+-----+-----+-----+-----+ . . . 56 57 58 59 60 61 62 63 +-----+-----+-----+-----+-----+-----+-----+-----+ ... | 63 | 62 | 61 | 60 | 59 | 58 | 57 | 56 | +-----+-----+-----+-----+-----+-----+-----+-----+",,,"See [RFC793] for the definition of TCP options. See the list of TCP option numbers assigned by IANA at [IANA registry tcp-parameters]. The diagram provided in [RFC5102] is incorrect. The diagram in this registry is taken from Errata 1739. See [RFC Errata 1739]",[RFC5102],0,2013-02-18 210,paddingOctets,octetArray,default,current,"The value of this Information Element is always a sequence of 0x00 values.",,,,[RFC5102],0,2013-02-18 211,collectorIPv4Address,ipv4Address,default,current,"An IPv4 address to which the Exporting Process sends Flow information.",,,,[RFC5102],1,2014-02-03 212,collectorIPv6Address,ipv6Address,default,current,"An IPv6 address to which the Exporting Process sends Flow information.",,,,[RFC5102],1,2014-02-03 213,exportInterface,unsigned32,identifier,current,"The index of the interface from which IPFIX Messages sent by the Exporting Process to a Collector leave the IPFIX Device. The value matches the value of managed object 'ifIndex' as defined in [RFC2863]. Note that ifIndex values are not assigned statically to an interface and that the interfaces may be renumbered every time the device's management system is re-initialized, as specified in [RFC2863].",,,"See [RFC2863] for the definition of the ifIndex object.",[RFC5102],0,2013-02-18 214,exportProtocolVersion,unsigned8,identifier,current,"The protocol version used by the Exporting Process for sending Flow information. The protocol version is given by the value of the Version Number field in the Message Header. The protocol version is 10 for IPFIX and 9 for NetFlow version 9. A value of 0 indicates that no export protocol is in use.",,,"See the [IPFIX protocol specification] for the definition of the IPFIX Message Header. See [RFC3954] for the definition of the NetFlow version 9 message header.",[RFC5102],0,2013-02-18 215,exportTransportProtocol,unsigned8,identifier,current,"The value of the protocol number used by the Exporting Process for sending Flow information. The protocol number identifies the IP packet payload type. Protocol numbers are defined in the IANA Protocol Numbers registry. In Internet Protocol version 4 (IPv4), this is carried in the Protocol field. In Internet Protocol version 6 (IPv6), this is carried in the Next Header field in the last extension header of the packet.",,,"See [RFC791] for the specification of the IPv4 protocol field. See [RFC2460] for the specification of the IPv6 protocol field. See the list of protocol numbers assigned by IANA at [IANA registry protocol-numbers].",[RFC5102],0,2013-02-18 216,collectorTransportPort,unsigned16,identifier,current,"The destination port identifier to which the Exporting Process sends Flow information. For the transport protocols UDP, TCP, and SCTP, this is the destination port number. This field MAY also be used for future transport protocols that have 16-bit source port identifiers.",,,"See [RFC768] for the definition of the UDP destination port field. See [RFC793] for the definition of the TCP destination port field. See [RFC4960] for the definition of SCTP. Additional information on defined UDP and TCP port numbers can be found at [IANA registry service-names-port-numbers].",[RFC5102],0,2013-02-18 217,exporterTransportPort,unsigned16,identifier,current,"The source port identifier from which the Exporting Process sends Flow information. For the transport protocols UDP, TCP, and SCTP, this is the source port number. This field MAY also be used for future transport protocols that have 16-bit source port identifiers. This field may be useful for distinguishing multiple Exporting Processes that use the same IP address.",,,"See [RFC768] for the definition of the UDP source port field. See [RFC793] for the definition of the TCP source port field. See [RFC4960] for the definition of SCTP. Additional information on defined UDP and TCP port numbers can be found at [IANA registry service-names-port-numbers].",[RFC5102],0,2013-02-18 218,tcpSynTotalCount,unsigned64,totalCounter,current,"The total number of packets of this Flow with TCP ""Synchronize sequence numbers"" (SYN) flag set.",packets,,"See [RFC793] for the definition of the TCP SYN flag.",[RFC5102],0,2013-02-18 219,tcpFinTotalCount,unsigned64,totalCounter,current,"The total number of packets of this Flow with TCP ""No more data from sender"" (FIN) flag set.",packets,,"See [RFC793] for the definition of the TCP FIN flag.",[RFC5102],0,2013-02-18 220,tcpRstTotalCount,unsigned64,totalCounter,current,"The total number of packets of this Flow with TCP ""Reset the connection"" (RST) flag set.",packets,,"See [RFC793] for the definition of the TCP RST flag.",[RFC5102],0,2013-02-18 221,tcpPshTotalCount,unsigned64,totalCounter,current,"The total number of packets of this Flow with TCP ""Push Function"" (PSH) flag set.",packets,,"See [RFC793] for the definition of the TCP PSH flag.",[RFC5102],0,2013-02-18 222,tcpAckTotalCount,unsigned64,totalCounter,current,"The total number of packets of this Flow with TCP ""Acknowledgment field significant"" (ACK) flag set.",packets,,"See [RFC793] for the definition of the TCP ACK flag.",[RFC5102],0,2013-02-18 223,tcpUrgTotalCount,unsigned64,totalCounter,current,"The total number of packets of this Flow with TCP ""Urgent Pointer field significant"" (URG) flag set.",packets,,"See [RFC793] for the definition of the TCP URG flag.",[RFC5102],0,2013-02-18 224,ipTotalLength,unsigned64,,current,The total length of the IP packet.,octets,,"See [RFC791] for the specification of the IPv4 total length. See [RFC2460] for the specification of the IPv6 payload length. See [RFC2675] for the specification of the IPv6 jumbo payload length.",[RFC5102],0,2013-02-18 225,postNATSourceIPv4Address,ipv4Address,default,current,"The definition of this Information Element is identical to the definition of Information Element 'sourceIPv4Address', except that it reports a modified value caused by a NAT middlebox function after the packet passed the Observation Point.",,,"See [RFC791] for the definition of the IPv4 source address field. See [RFC3022] for the definition of NAT. See [RFC3234] for the definition of middleboxes.",[ipfix-iana_at_cisco.com],1,2014-02-03 226,postNATDestinationIPv4Address,ipv4Address,default,current,"The definition of this Information Element is identical to the definition of Information Element 'destinationIPv4Address', except that it reports a modified value caused by a NAT middlebox function after the packet passed the Observation Point.",,,"See [RFC791] for the definition of the IPv4 destination address field. See [RFC3022] for the definition of NAT. See [RFC3234] for the definition of middleboxes.",[ipfix-iana_at_cisco.com],1,2014-02-03 227,postNAPTSourceTransportPort,unsigned16,identifier,current,"The definition of this Information Element is identical to the definition of Information Element 'sourceTransportPort', except that it reports a modified value caused by a Network Address Port Translation (NAPT) middlebox function after the packet passed the Observation Point.",,,"See [RFC768] for the definition of the UDP source port field. See [RFC793] for the definition of the TCP source port field. See [RFC4960] for the definition of SCTP. See [RFC3022] for the definition of NAPT. See [RFC3234] for the definition of middleboxes. Additional information on defined UDP and TCP port numbers can be found at http://www.iana.org/assignments/port-numbers.",[ipfix-iana_at_cisco.com],0,2013-02-18 228,postNAPTDestinationTransportPort,unsigned16,identifier,current,"The definition of this Information Element is identical to the definition of Information Element 'destinationTransportPort', except that it reports a modified value caused by a Network Address Port Translation (NAPT) middlebox function after the packet passed the Observation Point.",,,"See [RFC768] for the definition of the UDP source port field. See [RFC793] for the definition of the TCP source port field. See [RFC4960] for the definition of SCTP. See [RFC3022] for the definition of NAPT. See [RFC3234] for the definition of middleboxes. Additional information on defined UDP and TCP port numbers can be found at [IANA registry service-names-port-numbers].",[ipfix-iana_at_cisco.com],0,2013-02-18 229,natOriginatingAddressRealm,unsigned8,identifier,current,"Indicates whether the session was created because traffic originated in the private or public address realm. postNATSourceIPv4Address, postNATDestinationIPv4Address, postNAPTSourceTransportPort, and postNAPTDestinationTransportPort are qualified with the address realm in perspective. The allowed values are: Private: 1 Public: 2",,1-2,See [RFC3022] for the definition of NAT.,[ipfix-iana_at_cisco.com],1,2014-08-13 230,natEvent,unsigned8,identifier,current,"Indicates a NAT event. The allowed values are: 1 - Create event. 2 - Delete event. 3 - Pool exhausted. A Create event is generated when a NAT translation is created, whether dynamically or statically. A Delete event is generated when a NAT translation is deleted.",,1-2,See [RFC3022] for the definition of NAT.,[ipfix-iana_at_cisco.com],1,2014-08-13 231,initiatorOctets,unsigned64,deltaCounter,current,"The total number of layer 4 payload bytes in a flow from the initiator. The initiator is the device which triggered the session creation, and remains the same for the life of the session.",octets,,"See #298, initiatorPackets.",[ipfix-iana_at_cisco.com],1,2014-08-13 232,responderOctets,unsigned64,deltaCounter,current,"The total number of layer 4 payload bytes in a flow from the responder. The responder is the device which replies to the initiator, and remains the same for the life of the session.",octets,,"See #299, responderPackets.",[ipfix-iana_at_cisco.com],1,2014-08-13 233,firewallEvent,unsigned8,,current,"Indicates a firewall event. The allowed values are: 0 - Ignore (invalid) 1 - Flow Created 2 - Flow Deleted 3 - Flow Denied 4 - Flow Alert 5 - Flow Update",,,,[ipfix-iana_at_cisco.com],0,2013-02-18 234,ingressVRFID,unsigned32,,current,"An unique identifier of the VRFname where the packets of this flow are being received. This identifier is unique per Metering Process",,,,[ipfix-iana_at_cisco.com],0,2013-02-18 235,egressVRFID,unsigned32,,current,"An unique identifier of the VRFname where the packets of this flow are being sent. This identifier is unique per Metering Process",,,,[ipfix-iana_at_cisco.com],0,2013-02-18 236,VRFname,string,default,current,The name of a VPN Routing and Forwarding table (VRF).,,,See [RFC4364] for the definition of VRF.,[ipfix-iana_at_cisco.com],0,2013-02-18 237,postMplsTopLabelExp,unsigned8,flags,current,"The definition of this Information Element is identical to the definition of Information Element 'mplsTopLabelExp', except that it reports a potentially modified value caused by a middlebox function after the packet passed the Observation Point.",,,"See [RFC3032] for the specification of the Exp field. See [RFC3270] for usage of the Exp field.",[RFC5102],0,2013-02-18 238,tcpWindowScale,unsigned16,,current,The scale of the window field in the TCP header.,,,"See [RFC1323] for the definition of the TCP window scale.",[RFC5102],0,2013-02-18 239,biflowDirection,unsigned8,identifier,current,"A description of the direction assignment method used to assign the Biflow Source and Destination. This Information Element MAY be present in a Flow Data Record, or applied to all flows exported from an Exporting Process or Observation Domain using IPFIX Options. If this Information Element is not present in a Flow Record or associated with a Biflow via scope, it is assumed that the configuration of the direction assignment method is done out-of-band. Note that when using IPFIX Options to apply this Information Element to all flows within an Observation Domain or from an Exporting Process, the Option SHOULD be sent reliably. If reliable transport is not available (i.e., when using UDP), this Information Element SHOULD appear in each Flow Record. This field may take the following values: +-------+------------------+----------------------------------------+ | Value | Name | Description | +-------+------------------+----------------------------------------+ | 0x00 | arbitrary | Direction was assigned arbitrarily. | | 0x01 | initiator | The Biflow Source is the flow | | | | initiator, as determined by the | | | | Metering Process' best effort to | | | | detect the initiator. | | 0x02 | reverseInitiator | The Biflow Destination is the flow | | | | initiator, as determined by the | | | | Metering Process' best effort to | | | | detect the initiator. This value is | | | | provided for the convenience of | | | | Exporting Processes to revise an | | | | initiator estimate without re-encoding | | | | the Biflow Record. | | 0x03 | perimeter | The Biflow Source is the endpoint | | | | outside of a defined perimeter. The | | | | perimeter's definition is implicit in | | | | the set of Biflow Source and Biflow | | | | Destination addresses exported in the | | | | Biflow Records. | +-------+------------------+----------------------------------------+",,,,[RFC5103],0,2013-02-18 240,ethernetHeaderLength,unsigned8,quantity,current,"The difference between the length of an Ethernet frame (minus the FCS) and the length of its MAC Client Data section (including any padding) as defined in section 3.1 of [IEEE.802-3.2005]. It does not include the Preamble, SFD and Extension field lengths.",octets,,[IEEE.802-3.2005],[ipfix-iana_at_cisco.com],1,2014-08-13 241,ethernetPayloadLength,unsigned16,quantity,current,"The length of the MAC Client Data section (including any padding) of a frame as defined in section 3.1 of [IEEE.802-3.2005].",octets,,[IEEE.802-3.2005],[ipfix-iana_at_cisco.com],1,2014-08-13 242,ethernetTotalLength,unsigned16,quantity,current,"The total length of the Ethernet frame (excluding the Preamble, SFD, Extension and FCS fields) as described in section 3.1 of [IEEE.802-3.2005].",octets,,[IEEE.802-3.2005],[ipfix-iana_at_cisco.com],1,2014-08-13 243,dot1qVlanId,unsigned16,identifier,current,"The value of the 12-bit VLAN Identifier portion of the Tag Control Information field of an Ethernet frame. The structure and semantics within the Tag Control Information field are defined in [IEEE802.1Q]. In Provider Bridged Networks, it represents the Service VLAN identifier in the Service VLAN Tag (S-TAG) Tag Control Information (TCI) field or the Customer VLAN identifier in the Customer VLAN Tag (C-TAG) Tag Control Information (TCI) field as described in [IEEE802.1Q]. In Provider Backbone Bridged Networks, it represents the Backbone VLAN identifier in the Backbone VLAN Tag (B-TAG) Tag Control Information (TCI) field as described in [IEEE802.1Q]. In a virtual link between a host system and EVB bridge, it represents the Service VLAN identifier indicating S-channel as described in [IEEE802.1Qbg]. In the case of a multi-tagged frame, it represents the outer tag's VLAN identifier, except for I-TAG.",,,[IEEE802.1Q][IEEE802.1Qbg],[ipfix-iana_at_cisco.com][RFC7133],2,2014-01-11 244,dot1qPriority,unsigned8,identifier,current,"The value of the 3-bit User Priority portion of the Tag Control Information field of an Ethernet frame. The structure and semantics within the Tag Control Information field are defined in [IEEE802.1Q]. In the case of multi-tagged frame, it represents the 3-bit Priority Code Point (PCP) portion of the outer tag's Tag Control Information (TCI) field as described in [IEEE802.1Q], except for I-TAG.",,,[IEEE802.1Q],[ipfix-iana_at_cisco.com][RFC7133],1,2014-01-11 245,dot1qCustomerVlanId,unsigned16,identifier,current,"The value represents the Customer VLAN identifier in the Customer VLAN Tag (C-TAG) Tag Control Information (TCI) field as described in [IEEE802.1Q].",,,[IEEE802.1Q],[ipfix-iana_at_cisco.com][RFC7133],1,2014-01-11 246,dot1qCustomerPriority,unsigned8,identifier,current,"The value represents the 3-bit Priority Code Point (PCP) portion of the Customer VLAN Tag (C-TAG) Tag Control Information (TCI) field as described in [IEEE802.1Q].",,,[IEEE802.1Q],[ipfix-iana_at_cisco.com][RFC7133],1,2014-01-11 247,metroEvcId,string,default,current,"The EVC Service Attribute which uniquely identifies the Ethernet Virtual Connection (EVC) within a Metro Ethernet Network, as defined in section 6.2 of MEF 10.1. The MetroEVCID is encoded in a string of up to 100 characters.",,,"MEF 10.1 (Ethernet Services Attributes Phase 2) MEF16 (Ethernet Local Management Interface)",[ipfix-iana_at_cisco.com],1,2014-02-03 248,metroEvcType,unsigned8,identifier,current,"The 3-bit EVC Service Attribute which identifies the type of service provided by an EVC.",,,"MEF 10.1 (Ethernet Services Attributes Phase 2) MEF16 (Ethernet Local Management Interface)",[ipfix-iana_at_cisco.com],0,2013-02-18 249,pseudoWireId,unsigned32,identifier,current,"A 32-bit non-zero connection identifier, which together with the pseudoWireType, identifies the Pseudo Wire (PW) as defined in [RFC4447].",,,See [RFC4447] for pseudowire definitions.,[ipfix-iana_at_cisco.com],0,2013-02-18 250,pseudoWireType,unsigned16,identifier,current,"The value of this information element identifies the type of MPLS Pseudo Wire (PW) as defined in [RFC4446].",,,"See [RFC4446] for the pseudowire type definition, and http://www.iana.org/assignments/pwe3-parameters for the IANA Pseudowire Types Registry.",[ipfix-iana_at_cisco.com],0,2013-02-18 251,pseudoWireControlWord,unsigned32,identifier,current,"The 32-bit Preferred Pseudo Wire (PW) MPLS Control Word as defined in Section 3 of [RFC4385].",,,"See [RFC4385] for the Pseudo Wire Control Word definition.",[ipfix-iana_at_cisco.com],0,2013-02-18 252,ingressPhysicalInterface,unsigned32,identifier,current,"The index of a networking device's physical interface (example, a switch port) where packets of this flow are being received.",,,See [RFC2863] for the definition of the ifIndex object.,[ipfix-iana_at_cisco.com],0,2013-02-18 253,egressPhysicalInterface,unsigned32,identifier,current,"The index of a networking device's physical interface (example, a switch port) where packets of this flow are being sent.",,,See [RFC2863] for the definition of the ifIndex object.,[ipfix-iana_at_cisco.com],0,2013-02-18 254,postDot1qVlanId,unsigned16,identifier,current,"The definition of this Information Element is identical to the definition of Information Element 'dot1qVlanId', except that it reports a potentially modified value caused by a middlebox function after the packet passed the Observation Point.",,,"[IEEE.802-3.2005] [IEEE.802-1ad.2005]",[ipfix-iana_at_cisco.com],0,2013-02-18 255,postDot1qCustomerVlanId,unsigned16,identifier,current,"The definition of this Information Element is identical to the definition of Information Element 'dot1qCustomerVlanId', except that it reports a potentially modified value caused by a middlebox function after the packet passed the Observation Point.",,,"[IEEE.802-1ad.2005] [IEEE.802-1Q.2003]",[ipfix-iana_at_cisco.com],0,2013-02-18 256,ethernetType,unsigned16,identifier,current,"The Ethernet type field of an Ethernet frame that identifies the MAC client protocol carried in the payload as defined in paragraph 1.4.349 of [IEEE.802-3.2005].",,,"[IEEE.802-3.2005] Ethertype registry available at [http://standards.ieee.org/regauth/ethertype/eth.txt]",[ipfix-iana_at_cisco.com],0,2013-02-18 257,postIpPrecedence,unsigned8,identifier,current,"The definition of this Information Element is identical to the definition of Information Element 'ipPrecedence', except that it reports a potentially modified value caused by a middlebox function after the packet passed the Observation Point.",,0-7,"See [RFC1812] (Section 5.3.3) and [RFC791] for the definition of the IP Precedence. See [RFC1812] (Section 5.3.2) and [RFC791] for the definition of the IPv4 TOS field. See [RFC2460] for the definition of the IPv6 Traffic Class field.",[ipfix-iana_at_cisco.com],0,2013-02-18 258,collectionTimeMilliseconds,dateTimeMilliseconds,default,current,"The absolute timestamp at which the data within the scope containing this Information Element was received by a Collecting Process. This Information Element SHOULD be bound to its containing IPFIX Message via IPFIX Options and the messageScope Information Element, as defined below.",milliseconds,,,"[RFC5655][RFC Errata 3559]",1,2013-03-26 259,exportSctpStreamId,unsigned16,identifier,current,"The value of the SCTP Stream Identifier used by the Exporting Process for exporting IPFIX Message data. This is carried in the Stream Identifier field of the header of the SCTP DATA chunk containing the IPFIX Message(s).",,,,[RFC5655],0,2013-02-18 260,maxExportSeconds,dateTimeSeconds,default,current,"The absolute Export Time of the latest IPFIX Message within the scope containing this Information Element. This Information Element SHOULD be bound to its containing IPFIX Transport Session via IPFIX Options and the sessionScope Information Element.",seconds,,,[RFC5655],0,2013-02-18 261,maxFlowEndSeconds,dateTimeSeconds,default,current,"The latest absolute timestamp of the last packet within any Flow within the scope containing this Information Element, rounded up to the second if necessary. This Information Element SHOULD be bound to its containing IPFIX Transport Session via IPFIX Options and the sessionScope Information Element.",seconds,,,[RFC5655],0,2013-02-18 262,messageMD5Checksum,octetArray,default,current,"The MD5 checksum of the IPFIX Message containing this record. This Information Element SHOULD be bound to its containing IPFIX Message via an options record and the messageScope Information Element, as defined below, and SHOULD appear only once in a given IPFIX Message. To calculate the value of this Information Element, first buffer the containing IPFIX Message, setting the value of this Information Element to all zeroes. Then calculate the MD5 checksum of the resulting buffer as defined in [RFC1321], place the resulting value in this Information Element, and export the buffered message. This Information Element is intended as a simple checksum only; therefore collision resistance and algorithm agility are not required, and MD5 is an appropriate message digest. This Information Element has a fixed length of 16 octets.",,,,[RFC5655][RFC1321],0,2013-02-18 263,messageScope,unsigned8,,current,"The presence of this Information Element as scope in an Options Template signifies that the options described by the Template apply to the IPFIX Message that contains them. It is defined for general purpose message scoping of options, and proposed specifically to allow the attachment a checksum to a message via IPFIX Options. The value of this Information Element MUST be written as 0 by the File Writer or Exporting Process. The value of this Information Element MUST be ignored by the File Reader or the Collecting Process.",,0-0,,[RFC5655],0,2013-02-18 264,minExportSeconds,dateTimeSeconds,default,current,"The absolute Export Time of the earliest IPFIX Message within the scope containing this Information Element. This Information Element SHOULD be bound to its containing IPFIX Transport Session via an options record and the sessionScope Information Element.",seconds,,,[RFC5655],0,2013-02-18 265,minFlowStartSeconds,dateTimeSeconds,default,current,"The earliest absolute timestamp of the first packet within any Flow within the scope containing this Information Element, rounded down to the second if necessary. This Information Element SHOULD be bound to its containing IPFIX Transport Session via an options record and the sessionScope Information Element.",seconds,,,[RFC5655],0,2013-02-18 266,opaqueOctets,octetArray,default,current,"This Information Element is used to encapsulate non- IPFIX data into an IPFIX Message stream, for the purpose of allowing a non-IPFIX data processor to store a data stream inline within an IPFIX File. A Collecting Process or File Writer MUST NOT try to interpret this binary data. This Information Element differs from paddingOctets as its contents are meaningful in some non-IPFIX context, while the contents of paddingOctets MUST be 0x00 and are intended only for Information Element alignment.",,,,[RFC5655],0,2013-02-18 267,sessionScope,unsigned8,,current,"The presence of this Information Element as scope in an Options Template signifies that the options described by the Template apply to the IPFIX Transport Session that contains them. Note that as all options are implicitly scoped to Transport Session and Observation Domain, this Information Element is equivalent to a ""null"" scope. It is defined for general purpose session scoping of options, and proposed specifically to allow the attachment of time window to an IPFIX File via IPFIX Options. The value of this Information Element MUST be written as 0 by the File Writer or Exporting Process. The value of this Information Element MUST be ignored by the File Reader or the Collecting Process.",,0-0,,[RFC5655],0,2013-02-18 268,maxFlowEndMicroseconds,dateTimeMicroseconds,default,current,"The latest absolute timestamp of the last packet within any Flow within the scope containing this Information Element, rounded up to the microsecond if necessary. This Information Element SHOULD be bound to its containing IPFIX Transport Session via IPFIX Options and the sessionScope Information Element. This Information Element SHOULD be used only in Transport Sessions containing Flow Records with microsecond- precision (or better) timestamp Information Elements.",microseconds,,,[RFC5655],0,2013-02-18 269,maxFlowEndMilliseconds,dateTimeMilliseconds,default,current,"The latest absolute timestamp of the last packet within any Flow within the scope containing this Information Element, rounded up to the millisecond if necessary. This Information Element SHOULD be bound to its containing IPFIX Transport Session via IPFIX Options and the sessionScope Information Element. This Information Element SHOULD be used only in Transport Sessions containing Flow Records with millisecond- precision (or better) timestamp Information Elements.",milliseconds,,,[RFC5655],0,2013-02-18 270,maxFlowEndNanoseconds,dateTimeNanoseconds,default,current,"The latest absolute timestamp of the last packet within any Flow within the scope containing this Information Element. This Information Element SHOULD be bound to its containing IPFIX Transport Session via IPFIX Options and the sessionScope Information Element. This Information Element SHOULD be used only in Transport Sessions containing Flow Records with nanosecond-precision timestamp Information Elements.",nanoseconds,,,[RFC5655],0,2013-02-18 271,minFlowStartMicroseconds,dateTimeMicroseconds,default,current,"The earliest absolute timestamp of the first packet within any Flow within the scope containing this Information Element, rounded down to the microsecond if necessary. This Information Element SHOULD be bound to its containing IPFIX Transport Session via an options record and the sessionScope Information Element. This Information Element SHOULD be used only in Transport Sessions containing Flow Records with microsecond- precision (or better) timestamp Information Elements.",microseconds,,,[RFC5655],0,2013-02-18 272,minFlowStartMilliseconds,dateTimeMilliseconds,default,current,"The earliest absolute timestamp of the first packet within any Flow within the scope containing this Information Element, rounded down to the millisecond if necessary. This Information Element SHOULD be bound to its containing IPFIX Transport Session via an options record and the sessionScope Information Element. This Information Element SHOULD be used only in Transport Sessions containing Flow Records with millisecond- precision (or better) timestamp Information Elements.",milliseconds,,,[RFC5655],0,2013-02-18 273,minFlowStartNanoseconds,dateTimeNanoseconds,default,current,"The earliest absolute timestamp of the first packet within any Flow within the scope containing this Information Element. This Information Element SHOULD be bound to its containing IPFIX Transport Session via an options record and the sessionScope Information Element. This Information Element SHOULD be used only in Transport Sessions containing Flow Records with nanosecond-precision timestamp Information Elements.",nanoseconds,,,[RFC5655],0,2013-02-18 274,collectorCertificate,octetArray,default,current,"The full X.509 certificate, encoded in ASN.1 DER format, used by the Collector when IPFIX Messages were transmitted using TLS or DTLS. This Information Element SHOULD be bound to its containing IPFIX Transport Session via an options record and the sessionScope Information Element, or to its containing IPFIX Message via an options record and the messageScope Information Element.",,,,[RFC5655],0,2013-02-18 275,exporterCertificate,octetArray,default,current,"The full X.509 certificate, encoded in ASN.1 DER format, used by the Collector when IPFIX Messages were transmitted using TLS or DTLS. This Information Element SHOULD be bound to its containing IPFIX Transport Session via an options record and the sessionScope Information Element, or to its containing IPFIX Message via an options record and the messageScope Information Element.",,,,[RFC5655],0,2013-02-18 276,dataRecordsReliability,boolean,default,current,"The export reliability of Data Records, within this SCTP stream, for the element(s) in the Options Template scope. A typical example of an element for which the export reliability will be reported is the templateID, as specified in the Data Records Reliability Options Template. A value of 'True' means that the Exporting Process MUST send any Data Records associated with the element(s) reliably within this SCTP stream. A value of 'False' means that the Exporting Process MAY send any Data Records associated with the element(s) unreliably within this SCTP stream.",,,,[RFC6526],1,2014-02-03 277,observationPointType,unsigned8,identifier,current,"Type of observation point. Values assigned to date are: 1. Physical port 2. Port channel 3. Vlan.",,,,[ipfix-iana_at_cisco.com],0,2013-02-18 278,newConnectionDeltaCount,unsigned32,deltaCounter,current,"This information element counts the number of TCP or UDP connections which were opened during the observation period. The observation period may be specified by the flow start and end timestamps.",,,,[ipfix-iana_at_cisco.com],1,2014-08-13 279,connectionSumDurationSeconds,unsigned64,,current,"This information element aggregates the total time in seconds for all of the TCP or UDP connections which were in use during the observation period. For example if there are 5 concurrent connections each for 10 seconds, the value would be 50 s.",seconds,,,[ipfix-iana_at_cisco.com],1,2013-06-25 280,connectionTransactionId,unsigned64,identifier,current,"This information element identifies a transaction within a connection. A transaction is a meaningful exchange of application data between two network devices or a client and server. A transactionId is assigned the first time a flow is reported, so that later reports for the same flow will have the same transactionId. A different transactionId is used for each transaction within a TCP or UDP connection. The identifiers need not be sequential.",,,,[ipfix-iana_at_cisco.com],0,2013-02-18 281,postNATSourceIPv6Address,ipv6Address,default,current,"The definition of this Information Element is identical to the definition of Information Element 'sourceIPv6Address', except that it reports a modified value caused by a NAT64 middlebox function after the packet passed the Observation Point. See [RFC2460] for the definition of the Source Address field in the IPv6 header. See [RFC3234] for the definition of middleboxes. See [RFC6146] for nat64 specification.",,,,[ipfix-iana_at_cisco.com],0,2013-02-18 282,postNATDestinationIPv6Address,ipv6Address,default,current,"The definition of this Information Element is identical to the definition of Information Element 'destinationIPv6Address', except that it reports a modified value caused by a NAT64 middlebox function after the packet passed the Observation Point. See [RFC2460] for the definition of the Destination Address field in the IPv6 header. See [RFC3234] for the definition of middleboxes. See [RFC6146] for nat64 specification.",,,,[ipfix-iana_at_cisco.com],0,2013-02-18 283,natPoolId,unsigned32,identifier,current,Locally unique identifier of a NAT pool.,,,,[ipfix-iana_at_cisco.com],0,2013-02-18 284,natPoolName,string,default,current,The name of a NAT pool identified by a natPoolID.,,,,[ipfix-iana_at_cisco.com],0,2013-02-18 285,anonymizationFlags,unsigned16,flags,current,"A flag word describing specialized modifications to the anonymization policy in effect for the anonymization technique applied to a referenced Information Element within a referenced Template. When flags are clear (0), the normal policy (as described by anonymizationTechnique) applies without modification. MSB 14 13 12 11 10 9 8 7 6 5 4 3 2 1 LSB +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | Reserved |LOR|PmA| SC | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ anonymizationFlags IE +--------+----------+-----------------------------------------------+ | bit(s) | name | description | | (LSB = | | | | 0) | | | +--------+----------+-----------------------------------------------+ | 0-1 | SC | Stability Class: see the Stability Class | | | | table below, and section Section 5.1. | | 2 | PmA | Perimeter Anonymization: when set (1), | | | | source- Information Elements as described in | | | | [RFC5103] are interpreted as external | | | | addresses, and destination- Information | | | | Elements as described in [RFC5103] are | | | | interpreted as internal addresses, for the | | | | purposes of associating | | | | anonymizationTechnique to Information | | | | Elements only; see Section 7.2.2 for details. | | | | This bit MUST NOT be set when associated with | | | | a non-endpoint (i.e., source- or | | | | destination-) Information Element. SHOULD be | | | | consistent within a record (i.e., if a | | | | source- Information Element has this flag | | | | set, the corresponding destination- element | | | | SHOULD have this flag set, and vice-versa.) | | 3 | LOR | Low-Order Unchanged: when set (1), the | | | | low-order bits of the anonymized Information | | | | Element contain real data. This modification | | | | is intended for the anonymization of | | | | network-level addresses while leaving | | | | host-level addresses intact in order to | | | | preserve host level-structure, which could | | | | otherwise be used to reverse anonymization. | | | | MUST NOT be set when associated with a | | | | truncation-based anonymizationTechnique. | | 4-15 | Reserved | Reserved for future use: SHOULD be cleared | | | | (0) by the Exporting Process and MUST be | | | | ignored by the Collecting Process. | +--------+----------+-----------------------------------------------+ The Stability Class portion of this flags word describes the stability class of the anonymization technique applied to a referenced Information Element within a referenced Template. Stability classes refer to the stability of the parameters of the anonymization technique, and therefore the comparability of the mapping between the real and anonymized values over time. This determines which anonymized datasets may be compared with each other. Values are as follows: +-----+-----+-------------------------------------------------------+ | Bit | Bit | Description | | 1 | 0 | | +-----+-----+-------------------------------------------------------+ | 0 | 0 | Undefined: the Exporting Process makes no | | | | representation as to how stable the mapping is, or | | | | over what time period values of this field will | | | | remain comparable; while the Collecting Process MAY | | | | assume Session level stability, Session level | | | | stability is not guaranteed. Processes SHOULD assume | | | | this is the case in the absence of stability class | | | | information; this is the default stability class. | | 0 | 1 | Session: the Exporting Process will ensure that the | | | | parameters of the anonymization technique are stable | | | | during the Transport Session. All the values of the | | | | described Information Element for each Record | | | | described by the referenced Template within the | | | | Transport Session are comparable. The Exporting | | | | Process SHOULD endeavour to ensure at least this | | | | stability class. | | 1 | 0 | Exporter-Collector Pair: the Exporting Process will | | | | ensure that the parameters of the anonymization | | | | technique are stable across Transport Sessions over | | | | time with the given Collecting Process, but may use | | | | different parameters for different Collecting | | | | Processes. Data exported to different Collecting | | | | Processes are not comparable. | | 1 | 1 | Stable: the Exporting Process will ensure that the | | | | parameters of the anonymization technique are stable | | | | across Transport Sessions over time, regardless of | | | | the Collecting Process to which it is sent. | +-----+-----+-------------------------------------------------------+",,,,[RFC6235],0,2013-02-18 286,anonymizationTechnique,unsigned16,identifier,current,"A description of the anonymization technique applied to a referenced Information Element within a referenced Template. Each technique may be applicable only to certain Information Elements and recommended only for certain Infomation Elements; these restrictions are noted in the table below. +-------+---------------------------+-----------------+-------------+ | Value | Description | Applicable to | Recommended | | | | | for | +-------+---------------------------+-----------------+-------------+ | 0 | Undefined: the Exporting | all | all | | | Process makes no | | | | | representation as to | | | | | whether the defined field | | | | | is anonymized or not. | | | | | While the Collecting | | | | | Process MAY assume that | | | | | the field is not | | | | | anonymized, it is not | | | | | guaranteed not to be. | | | | | This is the default | | | | | anonymization technique. | | | | 1 | None: the values exported | all | all | | | are real. | | | | 2 | Precision | all | all | | | Degradation/Truncation: | | | | | the values exported are | | | | | anonymized using simple | | | | | precision degradation or | | | | | truncation. The new | | | | | precision or number of | | | | | truncated bits is | | | | | implicit in the exported | | | | | data, and can be deduced | | | | | by the Collecting | | | | | Process. | | | | 3 | Binning: the values | all | all | | | exported are anonymized | | | | | into bins. | | | | 4 | Enumeration: the values | all | timestamps | | | exported are anonymized | | | | | by enumeration. | | | | 5 | Permutation: the values | all | identifiers | | | exported are anonymized | | | | | by permutation. | | | | 6 | Structured Permutation: | addresses | | | | the values exported are | | | | | anonymized by | | | | | permutation, preserving | | | | | bit-level structure as | | | | | appropriate; this | | | | | represents | | | | | prefix-preserving IP | | | | | address anonymization or | | | | | structured MAC address | | | | | anonymization. | | | | 7 | Reverse Truncation: the | addresses | | | | values exported are | | | | | anonymized using reverse | | | | | truncation. The number | | | | | of truncated bits is | | | | | implicit in the exported | | | | | data, and can be deduced | | | | | by the Collecting | | | | | Process. | | | | 8 | Noise: the values | non-identifiers | counters | | | exported are anonymized | | | | | by adding random noise to | | | | | each value. | | | | 9 | Offset: the values | all | timestamps | | | exported are anonymized | | | | | by adding a single offset | | | | | to all values. | | | +-------+---------------------------+-----------------+-------------+",,,,[RFC6235],0,2013-02-18 287,informationElementIndex,unsigned16,identifier,current,"A zero-based index of an Information Element referenced by informationElementId within a Template referenced by templateId; used to disambiguate scope for templates containing multiple identical Information Elements.",,,,[RFC6235],0,2013-02-18 288,p2pTechnology,string,default,current,"Specifies if the Application ID is based on peer-to-peer technology. Possible values are: { ""yes"", ""y"", 1 }, { ""no"", ""n"", 2 } and { ""unassigned"", ""u"", 0 }.",,,,[RFC6759],0,2013-02-18 289,tunnelTechnology,string,default,current,"Specifies if the Application ID is used as a tunnel technology. Possible values are: { ""yes"", ""y"", 1 }, { ""no"", ""n"", 2 } and { ""unassigned"", ""u"", 0 }.",,,,[RFC6759],0,2013-02-18 290,encryptedTechnology,string,default,current,"Specifies if the Application ID is an encrypted networking protocol. Possible values are: { ""yes"", ""y"", 1 }, { ""no"", ""n"", 2 } and { ""unassigned"", ""u"", 0 }.",,,,[RFC6759],0,2013-02-18 291,basicList,basicList,list,current,"Specifies a generic Information Element with a basicList abstract data type. For example, a list of port numbers, a list of interface indexes, etc.",,,,[RFC6313],0,2013-02-18 292,subTemplateList,subTemplateList,list,current,"Specifies a generic Information Element with a subTemplateList abstract data type.",,,,[RFC6313],0,2013-02-18 293,subTemplateMultiList,subTemplateMultiList,list,current,"Specifies a generic Information Element with a subTemplateMultiList abstract data type.",,,,[RFC6313],0,2013-02-18 294,bgpValidityState,unsigned8,identifier,current,"This element describes the ""validity state"" of the BGP route correspondent source or destination IP address. If the ""validity state"" for this Flow is only available, then the value of this Information Element is 255.",,,"See [RFC4271] for a description of BGP-4, [RFC6811] for the definition of ""validity states"" and [draft-ietf-sidr-origin-validation-signaling] for the encoding of those ""validity states"".",[ipfix-iana_at_cisco.com],0,2013-02-18 295,IPSecSPI,unsigned32,identifier,current,IPSec Security Parameters Index (SPI).,,,See [RFC2401] for the definition of SPI.,[ipfix-iana_at_cisco.com],0,2013-02-18 296,greKey,unsigned32,identifier,current,"GRE key, which is used for identifying an individual traffic flow within a tunnel.",,,See [RFC1701] for the definition of GRE and the GRE Key.,[ipfix-iana_at_cisco.com],0,2013-02-18 297,natType,unsigned8,identifier,current,"The type of NAT treatment: 0 unknown 1 NAT44 translated 2 NAT64 translated 3 NAT46 translated 4 IPv4-->IPv4 (no NAT) 5 NAT66 translated 6 IPv6-->IPv6 (no NAT)",,,"See [RFC3022] for the definition of NAT. See [RFC1631] for the definition of NAT44. See [RFC6144] for the definition of NAT64. See [RFC6146] for the definition of NAT46. See [RFC6296] for the definition of NAT66. See [RFC791] for the definition of IPv4. See [RFC2460] for the definition of IPv6.",[ipfix-iana_at_cisco.com],0,2013-02-18 298,initiatorPackets,unsigned64,deltaCounter,current,"The total number of layer 4 packets in a flow from the initiator. The initiator is the device which triggered the session creation, and remains the same for the life of the session.",packets,,"See #231, initiatorOctets.",[ipfix-iana_at_cisco.com],1,2014-08-13 299,responderPackets,unsigned64,deltaCounter,current,"The total number of layer 4 packets in a flow from the responder. The responder is the device which replies to the initiator, and remains the same for the life of the session.",packets,,"See #232, responderOctets.",[ipfix-iana_at_cisco.com],1,2014-08-13 300,observationDomainName,string,default,current,"The name of an observation domain identified by an observationDomainId.",,,"See #149, observationDomainId.",[ipfix-iana_at_cisco.com],0,2013-02-18 301,selectionSequenceId,unsigned64,identifier,current,"From all the packets observed at an Observation Point, a subset of the packets is selected by a sequence of one or more Selectors. The selectionSequenceId is a unique value per Observation Domain, specifying the Observation Point and the sequence of Selectors through which the packets are selected.",,,,[RFC5477],0,2013-02-18 302,selectorId,unsigned64,identifier,current,"The Selector ID is the unique ID identifying a Primitive Selector. Each Primitive Selector must have a unique ID in the Observation Domain.",,,,"[RFC5477][RFC Errata 2052]",0,2013-02-18 303,informationElementId,unsigned16,identifier,current,"This Information Element contains the ID of another Information Element.",,,,[RFC5477],0,2013-02-18 304,selectorAlgorithm,unsigned16,identifier,current,"This Information Element identifies the packet selection methods (e.g., Filtering, Sampling) that are applied by the Selection Process. Most of these methods have parameters. Further Information Elements are needed to fully specify packet selection with these methods and all their parameters. The methods listed below are defined in [RFC5475]. For their parameters, Information Elements are defined in the information model document. The names of these Information Elements are listed for each method identifier. Further method identifiers may be added to the list below. It might be necessary to define new Information Elements to specify their parameters. The selectorAlgorithm registry is maintained by IANA. New assignments for the registry will be administered by IANA, and are subject to Expert Review [RFC5226]. The registry can be updated when specifications of the new method(s) and any new Information Elements are provided. The group of experts must double check the selectorAlgorithm definitions and Information Elements with already defined selectorAlgorithms and Information Elements for completeness, accuracy, and redundancy. Those experts will initially be drawn from the Working Group Chairs and document editors of the IPFIX and PSAMP Working Groups. The following packet selection methods identifiers are defined here: [IANA registry psamp-parameters] There is a broad variety of possible parameters that could be used for Property match Filtering (5) but currently there are no agreed parameters specified.",,,,[RFC5477],0,2013-02-18 305,samplingPacketInterval,unsigned32,quantity,current,"This Information Element specifies the number of packets that are consecutively sampled. A value of 100 means that 100 consecutive packets are sampled. For example, this Information Element may be used to describe the configuration of a systematic count-based Sampling Selector.",packets,,,[RFC5477],0,2013-02-18 306,samplingPacketSpace,unsigned32,quantity,current,"This Information Element specifies the number of packets between two ""samplingPacketInterval""s. A value of 100 means that the next interval starts 100 packets (which are not sampled) after the current ""samplingPacketInterval"" is over. For example, this Information Element may be used to describe the configuration of a systematic count-based Sampling Selector.",packets,,,[RFC5477],0,2013-02-18 307,samplingTimeInterval,unsigned32,quantity,current,"This Information Element specifies the time interval in microseconds during which all arriving packets are sampled. For example, this Information Element may be used to describe the configuration of a systematic time-based Sampling Selector.",microseconds,,,[RFC5477],0,2013-02-18 308,samplingTimeSpace,unsigned32,quantity,current,"This Information Element specifies the time interval in microseconds between two ""samplingTimeInterval""s. A value of 100 means that the next interval starts 100 microseconds (during which no packets are sampled) after the current ""samplingTimeInterval"" is over. For example, this Information Element may used to describe the configuration of a systematic time-based Sampling Selector.",microseconds,,,[RFC5477],0,2013-02-18 309,samplingSize,unsigned32,quantity,current,"This Information Element specifies the number of elements taken from the parent Population for random Sampling methods. For example, this Information Element may be used to describe the configuration of a random n-out-of-N Sampling Selector.",packets,,,[RFC5477],0,2013-02-18 310,samplingPopulation,unsigned32,quantity,current,"This Information Element specifies the number of elements in the parent Population for random Sampling methods. For example, this Information Element may be used to describe the configuration of a random n-out-of-N Sampling Selector.",packets,,,[RFC5477],0,2013-02-18 311,samplingProbability,float64,quantity,current,"This Information Element specifies the probability that a packet is sampled, expressed as a value between 0 and 1. The probability is equal for every packet. A value of 0 means no packet was sampled since the probability is 0. For example, this Information Element may be used to describe the configuration of a uniform probabilistic Sampling Selector.",,,,[RFC5477],0,2013-02-18 312,dataLinkFrameSize,unsigned16,quantity,current,"This Information Element specifies the length of the selected data link frame. The data link layer is defined in [ISO/IEC.7498-1:1994].",,,[ISO/IEC.7498-1:1994],[RFC7133],1,2014-01-11 313,ipHeaderPacketSection,octetArray,default,current,"This Information Element carries a series of n octets from the IP header of a sampled packet, starting sectionOffset octets into the IP header. However, if no sectionOffset field corresponding to this Information Element is present, then a sectionOffset of zero applies, and the octets MUST be from the start of the IP header. With sufficient length, this element also reports octets from the IP payload. However, full packet capture of arbitrary packet streams is explicitly out of scope per the Security Considerations sections of [RFC5477] and [RFC2804]. The sectionExportedOctets expresses how much data was exported, while the remainder is padding. When the sectionExportedOctets field corresponding to this Information Element exists, this Information Element MAY have a fixed length and MAY be padded, or it MAY have a variable length. When the sectionExportedOctets field corresponding to this Information Element does not exist, this Information Element SHOULD have a variable length and MUST NOT be padded. In this case, the size of the exported section may be constrained due to limitations in the IPFIX protocol.",,,"[RFC2804] [RFC5477]",[RFC5477][RFC7133],1,2014-01-11 314,ipPayloadPacketSection,octetArray,default,current,"This Information Element carries a series of n octets from the IP payload of a sampled packet, starting sectionOffset octets into the IP payload. However, if no sectionOffset field corresponding to this Information Element is present, then a sectionOffset of zero applies, and the octets MUST be from the start of the IP payload. The IPv4 payload is that part of the packet that follows the IPv4 header and any options, which [RFC791] refers to as ""data"" or ""data octets"". For example, see the examples in [RFC791], Appendix A. The IPv6 payload is the rest of the packet following the 40-octet IPv6 header. Note that any extension headers present are considered part of the payload. See [RFC2460] for the IPv6 specification. The sectionExportedOctets expresses how much data was observed, while the remainder is padding. When the sectionExportedOctets field corresponding to this Information Element exists, this Information Element MAY have a fixed length and MAY be padded, or MAY have a variable length. When the sectionExportedOctets field corresponding to this Information Element does not exist, this Information Element SHOULD have a variable length and MUST NOT be padded. In this case, the size of the exported section may be constrained due to limitations in the IPFIX protocol.",,,"[RFC791] [RFC2460]",[RFC5477][RFC7133],1,2014-01-11 315,dataLinkFrameSection,octetArray,default,current,"This Information Element carries n octets from the data link frame of a selected frame, starting sectionOffset octets into the frame. However, if no sectionOffset field corresponding to this Information Element is present, then a sectionOffset of zero applies, and the octets MUST be from the start of the data link frame. The sectionExportedOctets expresses how much data was observed, while the remainder is padding. When the sectionExportedOctets field corresponding to this Information Element exists, this Information Element MAY have a fixed length and MAY be padded, or MAY have a variable length. When the sectionExportedOctets field corresponding to this Information Element does not exist, this Information Element SHOULD have a variable length and MUST NOT be padded. In this case, the size of the exported section may be constrained due to limitations in the IPFIX protocol. Further Information Elements, i.e., dataLinkFrameType and dataLinkFrameSize, are needed to specify the data link type and the size of the data link frame of this Information Element. A set of these Information Elements MAY be contained in a structured data type, as expressed in [RFC6313]. Or a set of these Information Elements MAY be contained in one Flow Record as shown in Appendix B of [RFC7133]. The data link layer is defined in [ISO/IEC.7498-1:1994].",,,"[RFC6313] [RFC7133] [ISO/IEC.7498-1:1994]",[RFC7133],1,2014-01-11 316,mplsLabelStackSection,octetArray,default,current,"This Information Element carries a series of n octets from the MPLS label stack of a sampled packet, starting sectionOffset octets into the MPLS label stack. However, if no sectionOffset field corresponding to this Information Element is present, then a sectionOffset of zero applies, and the octets MUST be from the head of the MPLS label stack. With sufficient length, this element also reports octets from the MPLS payload. However, full packet capture of arbitrary packet streams is explicitly out of scope per the Security Considerations sections of [RFC5477] and [RFC2804]. See [RFC3031] for the specification of MPLS packets. See [RFC3032] for the specification of the MPLS label stack. The sectionExportedOctets expresses how much data was observed, while the remainder is padding. When the sectionExportedOctets field corresponding to this Information Element exists, this Information Element MAY have a fixed length and MAY be padded, or MAY have a variable length. When the sectionExportedOctets field corresponding to this Information Element does not exist, this Information Element SHOULD have a variable length and MUST NOT be padded. In this case, the size of the exported section may be constrained due to limitations in the IPFIX protocol.",,,"[RFC2804] [RFC3031] [RFC3032] [RFC5477]",[RFC5477][RFC7133],1,2014-01-11 317,mplsPayloadPacketSection,octetArray,default,current,"The mplsPayloadPacketSection carries a series of n octets from the MPLS payload of a sampled packet, starting sectionOffset octets into the MPLS payload, as it is data that follows immediately after the MPLS label stack. However, if no sectionOffset field corresponding to this Information Element is present, then a sectionOffset of zero applies, and the octets MUST be from the start of the MPLS payload. See [RFC3031] for the specification of MPLS packets. See [RFC3032] for the specification of the MPLS label stack. The sectionExportedOctets expresses how much data was observed, while the remainder is padding. When the sectionExportedOctets field corresponding to this Information Element exists, this Information Element MAY have a fixed length and MAY be padded, or it MAY have a variable length. When the sectionExportedOctets field corresponding to this Information Element does not exist, this Information Element SHOULD have a variable length and MUST NOT be padded. In this case, the size of the exported section may be constrained due to limitations in the IPFIX protocol.",,,"[RFC3031] [RFC3032]",[RFC5477][RFC7133],1,2014-01-11 318,selectorIdTotalPktsObserved,unsigned64,totalCounter,current,"This Information Element specifies the total number of packets observed by a Selector, for a specific value of SelectorId. This Information Element should be used in an Options Template scoped to the observation to which it refers. See Section 3.4.2.1 of the IPFIX protocol document [RFC7011].",packets,,,[RFC5477],0,2013-02-18 319,selectorIdTotalPktsSelected,unsigned64,totalCounter,current,"This Information Element specifies the total number of packets selected by a Selector, for a specific value of SelectorId. This Information Element should be used in an Options Template scoped to the observation to which it refers. See Section 3.4.2.1 of the IPFIX protocol document [RFC7011].",packets,,,[RFC5477],0,2013-02-18 320,absoluteError,float64,quantity,current,"This Information Element specifies the maximum possible measurement error of the reported value for a given Information Element. The absoluteError has the same unit as the Information Element with which it is associated. The real value of the metric can differ by absoluteError (positive or negative) from the measured value. This Information Element provides only the error for measured values. If an Information Element contains an estimated value (from Sampling), the confidence boundaries and confidence level have to be provided instead, using the upperCILimit, lowerCILimit, and confidenceLevel Information Elements. This Information Element should be used in an Options Template scoped to the observation to which it refers. See Section 3.4.2.1 of the IPFIX protocol document [RFC7011].",The units of the Information Element for which the error is specified.,,,[RFC5477],0,2013-02-18 321,relativeError,float64,quantity,current,"This Information Element specifies the maximum possible positive or negative error ratio for the reported value for a given Information Element as percentage of the measured value. The real value of the metric can differ by relativeError percent (positive or negative) from the measured value. This Information Element provides only the error for measured values. If an Information Element contains an estimated value (from Sampling), the confidence boundaries and confidence level have to be provided instead, using the upperCILimit, lowerCILimit, and confidenceLevel Information Elements. This Information Element should be used in an Options Template scoped to the observation to which it refers. See Section 3.4.2.1 of the IPFIX protocol document [RFC7011].",,,,[RFC5477],0,2013-02-18 322,observationTimeSeconds,dateTimeSeconds,default,current,"This Information Element specifies the absolute time in seconds of an observation.",seconds,,,[RFC5477],1,2014-02-03 323,observationTimeMilliseconds,dateTimeMilliseconds,default,current,"This Information Element specifies the absolute time in milliseconds of an observation.",milliseconds,,,[RFC5477],1,2014-02-03 324,observationTimeMicroseconds,dateTimeMicroseconds,default,current,"This Information Element specifies the absolute time in microseconds of an observation.",microseconds,,,[RFC5477],1,2014-02-03 325,observationTimeNanoseconds,dateTimeNanoseconds,default,current,"This Information Element specifies the absolute time in nanoseconds of an observation.",nanoseconds,,,[RFC5477],1,2014-02-03 326,digestHashValue,unsigned64,quantity,current,"This Information Element specifies the value from the digest hash function. See also Sections 6.2, 3.8 and 7.1 of [RFC5475].",,,,[RFC5477],0,2013-02-18 327,hashIPPayloadOffset,unsigned64,quantity,current,"This Information Element specifies the IP payload offset used by a Hash-based Selection Selector. See also Sections 6.2, 3.8 and 7.1 of [RFC5475].",,,,[RFC5477],0,2013-02-18 328,hashIPPayloadSize,unsigned64,quantity,current,"This Information Element specifies the IP payload size used by a Hash-based Selection Selector. See also Sections 6.2, 3.8 and 7.1 of [RFC5475].",,,,[RFC5477],0,2013-02-18 329,hashOutputRangeMin,unsigned64,quantity,current,"This Information Element specifies the value for the beginning of a hash function's potential output range. See also Sections 6.2, 3.8 and 7.1 of [RFC5475].",,,,[RFC5477],0,2013-02-18 330,hashOutputRangeMax,unsigned64,quantity,current,"This Information Element specifies the value for the end of a hash function's potential output range. See also Sections 6.2, 3.8 and 7.1 of [RFC5475].",,,,[RFC5477],0,2013-02-18 331,hashSelectedRangeMin,unsigned64,quantity,current,"This Information Element specifies the value for the beginning of a hash function's selected range. See also Sections 6.2, 3.8 and 7.1 of [RFC5475].",,,,[RFC5477],0,2013-02-18 332,hashSelectedRangeMax,unsigned64,quantity,current,"This Information Element specifies the value for the end of a hash function's selected range. See also Sections 6.2, 3.8 and 7.1 of [RFC5475].",,,,[RFC5477],0,2013-02-18 333,hashDigestOutput,boolean,default,current,"This Information Element contains a boolean value that is TRUE if the output from this hash Selector has been configured to be included in the packet report as a packet digest, else FALSE. See also Sections 6.2, 3.8 and 7.1 of [RFC5475].",,,,[RFC5477],1,2014-02-03 334,hashInitialiserValue,unsigned64,quantity,current,"This Information Element specifies the initialiser value to the hash function. See also Sections 6.2, 3.8 and 7.1 of [RFC5475].",,,,[RFC5477],0,2013-02-18 335,selectorName,string,default,current,"The name of a selector identified by a selectorID. Globally unique per Metering Process.",,,,[ipfix-iana_at_cisco.com],0,2013-02-18 336,upperCILimit,float64,quantity,current,"This Information Element specifies the upper limit of a confidence interval. It is used to provide an accuracy statement for an estimated value. The confidence limits define the range in which the real value is assumed to be with a certain probability p. Confidence limits always need to be associated with a confidence level that defines this probability p. Please note that a confidence interval only provides a probability that the real value lies within the limits. That means the real value can lie outside the confidence limits. The upperCILimit, lowerCILimit, and confidenceLevel Information Elements should all be used in an Options Template scoped to the observation to which they refer. See Section 3.4.2.1 of the IPFIX protocol document [RFC7011]. Note that the upperCILimit, lowerCILimit, and confidenceLevel are all required to specify confidence, and should be disregarded unless all three are specified together.",,,,[RFC5477],0,2013-02-18 337,lowerCILimit,float64,quantity,current,"This Information Element specifies the lower limit of a confidence interval. For further information, see the description of upperCILimit. The upperCILimit, lowerCILimit, and confidenceLevel Information Elements should all be used in an Options Template scoped to the observation to which they refer. See Section 3.4.2.1 of the IPFIX protocol document [RFC7011]. Note that the upperCILimit, lowerCILimit, and confidenceLevel are all required to specify confidence, and should be disregarded unless all three are specified together.",,,,[RFC5477],0,2013-02-18 338,confidenceLevel,float64,quantity,current,"This Information Element specifies the confidence level. It is used to provide an accuracy statement for estimated values. The confidence level provides the probability p with which the real value lies within a given range. A confidence level always needs to be associated with confidence limits that define the range in which the real value is assumed to be. The upperCILimit, lowerCILimit, and confidenceLevel Information Elements should all be used in an Options Template scoped to the observation to which they refer. See Section 3.4.2.1 of the IPFIX protocol document [RFC7011]. Note that the upperCILimit, lowerCILimit, and confidenceLevel are all required to specify confidence, and should be disregarded unless all three are specified together.",,,,[RFC5477],0,2013-02-18 339,informationElementDataType,unsigned8,,current,"A description of the abstract data type of an IPFIX information element.These are taken from the abstract data types defined in section 3.1 of the IPFIX Information Model [RFC5102]; see that section for more information on the types described in the informationElementDataType sub-registry. These types are registered in the IANA IPFIX Information Element Data Type subregistry. This subregistry is intended to assign numbers for type names, not to provide a mechanism for adding data types to the IPFIX Protocol, and as such requires a Standards Action [RFC5226] to modify.",,,,[RFC5610],0,2013-02-18 340,informationElementDescription,string,default,current,"A UTF-8 [RFC3629] encoded Unicode string containing a human-readable description of an Information Element. The content of the informationElementDescription MAY be annotated with one or more language tags [RFC4646], encoded in-line [RFC2482] within the UTF-8 string, in order to specify the language in which the description is written. Description text in multiple languages MAY tag each section with its own language tag; in this case, the description information in each language SHOULD have equivalent meaning. In the absence of any language tag, the ""i-default"" [RFC2277] language SHOULD be assumed. See the Security Considerations section for notes on string handling for Information Element type records.",,,,[RFC5610],0,2013-02-18 341,informationElementName,string,default,current,"A UTF-8 [RFC3629] encoded Unicode string containing the name of an Information Element, intended as a simple identifier. See the Security Considerations section for notes on string handling for Information Element type records",,,,[RFC5610],0,2013-02-18 342,informationElementRangeBegin,unsigned64,quantity,current,"Contains the inclusive low end of the range of acceptable values for an Information Element.",,,,[RFC5610],0,2013-02-18 343,informationElementRangeEnd,unsigned64,quantity,current,"Contains the inclusive high end of the range of acceptable values for an Information Element.",,,,[RFC5610],0,2013-02-18 344,informationElementSemantics,unsigned8,,current,"A description of the semantics of an IPFIX Information Element. These are taken from the data type semantics defined in section 3.2 of the IPFIX Information Model [RFC5102]; see that section for more information on the types defined in the informationElementSemantics sub-registry. This field may take the values in Table ; the special value 0x00 (default) is used to note that no semantics apply to the field; it cannot be manipulated by a Collecting Process or File Reader that does not understand it a priori. These semantics are registered in the IANA IPFIX Information Element Semantics subregistry. This subregistry is intended to assign numbers for semantics names, not to provide a mechanism for adding semantics to the IPFIX Protocol, and as such requires a Standards Action [RFC5226] to modify.",,,,[RFC5610],0,2013-02-18 345,informationElementUnits,unsigned16,,current,"A description of the units of an IPFIX Information Element. These correspond to the units implicitly defined in the Information Element definitions in section 5 of the IPFIX Information Model [RFC5102]; see that section for more information on the types described in the informationElementsUnits sub-registry. This field may take the values in Table 3 below; the special value 0x00 (none) is used to note that the field is unitless. These types are registered in the IANA IPFIX Information Element Units subregistry; new types may be added on a First Come First Served [RFC5226] basis.",,,,[RFC5610],0,2013-02-18 346,privateEnterpriseNumber,unsigned32,identifier,current,"A private enterprise number, as assigned by IANA. Within the context of an Information Element Type record, this element can be used along with the informationElementId element to scope properties to a specific Information Element. To export type information about an IANA-assigned Information Element, set the privateEnterpriseNumber to 0, or do not export the privateEnterpriseNumber in the type record. To export type information about an enterprise-specific Information Element, export the enterprise number in privateEnterpriseNumber, and export the Information Element number with the Enterprise bit cleared in informationElementId. The Enterprise bit in the associated informationElementId Information Element MUST be ignored by the Collecting Process.",,,,[RFC5610],0,2013-02-18 347,virtualStationInterfaceId,octetArray,default,current,"Instance Identifier of the interface to a Virtual Station. A Virtual Station is an end station instance: it can be a virtual machine or a physical host.",,,See IEEE 802.1Qbg for the definition of Virtual Station Interface ID.,[ipfix-iana_at_cisco.com],1,2014-02-03 348,virtualStationInterfaceName,string,default,current,"Name of the interface to a Virtual Station. A Virtual Station is an end station instance: it can be a virtual machine or a physical host.",,,See IEEE 802.1Qbg for the definition of Virtual Station Interface.,[ipfix-iana_at_cisco.com],1,2014-02-03 349,virtualStationUUID,octetArray,default,current,"Unique Identifier of a Virtual Station. A Virtual Station is an end station instance: it can be a virtual machine or a physical host.",,,See IEEE 802.1Qbg for the definition of Virtual Station.,[ipfix-iana_at_cisco.com],1,2014-02-03 350,virtualStationName,string,default,current,"Name of a Virtual Station. A Virtual Station is an end station instance: it can be a virtual machine or a physical host.",,,See IEEE 802.1Qbg for the definition of Virtual Station.,[ipfix-iana_at_cisco.com],0,2013-02-18 351,layer2SegmentId,unsigned64,identifier,current,"Identifier of a layer 2 network segment in an overlay network. The most significant byte identifies the layer 2 network overlay network encapsulation type: 0x00 reserved 0x01 VxLAN 0x02 NVGRE The three lowest significant bytes hold the value of the layer 2 overlay network segment identifier. For example: - a 24 bit segment ID VXLAN Network Identifier (VNI) - a 24 bit Tenant Network Identifier (TNI) for NVGRE",,,"See VxLAN RFC at [draft-mahalingam-dutt-dcops-vxlan] See NVGRE RFC at [draft-sridharan-virtualization-nvgre]",[ipfix-iana_at_cisco.com],0,2013-02-18 352,layer2OctetDeltaCount,unsigned64,deltaCounter,current,"The number of layer 2 octets since the previous report (if any) in incoming packets for this Flow at the Observation Point. The number of octets includes layer 2 header(s) and layer 2 payload. # memo: layer 2 version of octetDeltaCount (field #1)",octets,,,[ipfix-iana_at_cisco.com],1,2014-05-02 353,layer2OctetTotalCount,unsigned64,totalCounter,current,"The total number of layer 2 octets in incoming packets for this Flow at the Observation Point since the Metering Process (re-)initialization for this Observation Point. The number of octets includes layer 2 header(s) and layer 2 payload. # memo: layer 2 version of octetTotalCount (field #85)",octets,,,[ipfix-iana_at_cisco.com],1,2014-05-02 354,ingressUnicastPacketTotalCount,unsigned64,totalCounter,current,"The total number of incoming unicast packets metered at the Observation Point since the Metering Process (re-)initialization for this Observation Point.",packets,,,[ipfix-iana_at_cisco.com],0,2013-02-18 355,ingressMulticastPacketTotalCount,unsigned64,totalCounter,current,"The total number of incoming multicast packets metered at the Observation Point since the Metering Process (re-)initialization for this Observation Point.",packets,,,[ipfix-iana_at_cisco.com],0,2013-02-18 356,ingressBroadcastPacketTotalCount,unsigned64,totalCounter,current,"The total number of incoming broadcast packets metered at the Observation Point since the Metering Process (re-)initialization for this Observation Point.",packets,,,[ipfix-iana_at_cisco.com],0,2013-02-18 357,egressUnicastPacketTotalCount,unsigned64,totalCounter,current,"The total number of incoming unicast packets metered at the Observation Point since the Metering Process (re-)initialization for this Observation Point.",packets,,,[ipfix-iana_at_cisco.com],0,2013-02-18 358,egressBroadcastPacketTotalCount,unsigned64,totalCounter,current,"The total number of incoming broadcast packets metered at the Observation Point since the Metering Process (re-)initialization for this Observation Point.",packets,,,[ipfix-iana_at_cisco.com],0,2013-02-18 359,monitoringIntervalStartMilliSeconds,dateTimeMilliseconds,default,current,"The absolute timestamp at which the monitoring interval started. A Monitoring interval is the period of time during which the Metering Process is running.",milliseconds,,,[ipfix-iana_at_cisco.com],0,2013-02-18 360,monitoringIntervalEndMilliSeconds,dateTimeMilliseconds,default,current,"The absolute timestamp at which the monitoring interval ended. A Monitoring interval is the period of time during which the Metering Process is running.",milliseconds,,,[ipfix-iana_at_cisco.com],0,2013-02-18 361,portRangeStart,unsigned16,identifier,current,"The port number identifying the start of a range of ports. A value of zero indicates that the range start is not specified, ie the range is defined in some other way. Additional information on defined TCP port numbers can be found at [IANA registry service-names-port-numbers].",,,,[ipfix-iana_at_cisco.com],0,2013-02-18 362,portRangeEnd,unsigned16,identifier,current,"The port number identifying the end of a range of ports. A value of zero indicates that the range end is not specified, ie the range is defined in some other way. Additional information on defined TCP port numbers can be found at [IANA registry service-names-port-numbers].",,,,[ipfix-iana_at_cisco.com],0,2013-02-18 363,portRangeStepSize,unsigned16,identifier,current,"The step size in a port range. The default step size is 1, which indicates contiguous ports. A value of zero indicates that the step size is not specified, ie the range is defined in some other way.",,,,[ipfix-iana_at_cisco.com],0,2013-02-18 364,portRangeNumPorts,unsigned16,identifier,current,"The number of ports in a port range. A value of zero indicates that the number of ports is not specified, ie the range is defined in some other way.",,,,[ipfix-iana_at_cisco.com],0,2013-02-18 365,staMacAddress,macAddress,default,current,The IEEE 802 MAC address of a wireless station (STA).,,,See section 1.4 of [RFC5415] for the definition of STA.,[ipfix-iana_at_cisco.com],1,2014-02-03 366,staIPv4Address,ipv4Address,default,current,The IPv4 address of a wireless station (STA).,,,See section 1.4 of [RFC5415] for the definition of STA.,[ipfix-iana_at_cisco.com],1,2014-02-03 367,wtpMacAddress,macAddress,default,current,The IEEE 802 MAC address of a wireless access point (WTP).,,,See section 1.4 of [RFC5415] for the definition of WTP.,[ipfix-iana_at_cisco.com],1,2014-02-03 368,ingressInterfaceType,unsigned32,identifier,current,"The type of interface where packets of this Flow are being received. The value matches the value of managed object 'ifType' as defined in [IANA registry ianaiftype-mib].",,,[IANA registry ianaiftype-mib],[ipfix-iana_at_cisco.com],0,2013-02-18 369,egressInterfaceType,unsigned32,identifier,current,"The type of interface where packets of this Flow are being sent. The value matches the value of managed object 'ifType' as defined in [IANA registry ianaiftype-mib].",,,[IANA registry ianaiftype-mib],[ipfix-iana_at_cisco.com],0,2013-02-18 370,rtpSequenceNumber,unsigned16,,current,The RTP sequence number per [RFC3550].,,,[RFC3550],[ipfix-iana_at_cisco.com],0,2013-02-18 371,userName,string,default,current,User name associated with the flow.,,,,[ipfix-iana_at_cisco.com],0,2013-02-18 372,applicationCategoryName,string,default,current,"An attribute that provides a first level categorization for each Application ID.",,,,[RFC6759],0,2013-02-18 373,applicationSubCategoryName,string,default,current,"An attribute that provides a second level categorization for each Application ID.",,,,[RFC6759],0,2013-02-18 374,applicationGroupName,string,default,current,"An attribute that groups multiple Application IDs that belong to the same networking application.",,,,[RFC6759],0,2013-02-18 375,originalFlowsPresent,unsigned64,deltaCounter,current,"The non-conservative count of Original Flows contributing to this Aggregated Flow. Non-conservative counts need not sum to the original count on re-aggregation.",flows,,,[RFC7015],1,2013-06-25 376,originalFlowsInitiated,unsigned64,deltaCounter,current,"The conservative count of Original Flows whose first packet is represented within this Aggregated Flow. Conservative counts must sum to the original count on re-aggregation.",flows,,,[RFC7015],1,2013-06-25 377,originalFlowsCompleted,unsigned64,deltaCounter,current,"The conservative count of Original Flows whose last packet is represented within this Aggregated Flow. Conservative counts must sum to the original count on re-aggregation.",flows,,,[RFC7015],1,2013-06-25 378,distinctCountOfSourceIPAddress,unsigned64,totalCounter,current,"The count of distinct source IP address values for Original Flows contributing to this Aggregated Flow, without regard to IP version. This Information Element is preferred to the IP-version-specific counters, unless it is important to separate the counts by version.",,,,[RFC7015],0,2013-02-18 379,distinctCountOfDestinationIPAddress,unsigned64,totalCounter,current,"The count of distinct destination IP address values for Original Flows contributing to this Aggregated Flow, without regard to IP version. This Information Element is preferred to the version-specific counters below, unless it is important to separate the counts by version.",,,,[RFC7015],0,2013-02-18 380,distinctCountOfSourceIPv4Address,unsigned32,totalCounter,current,"The count of distinct source IPv4 address values for Original Flows contributing to this Aggregated Flow.",,,,[RFC7015],0,2013-02-18 381,distinctCountOfDestinationIPv4Address,unsigned32,totalCounter,current,"The count of distinct destination IPv4 address values for Original Flows contributing to this Aggregated Flow.",,,,[RFC7015],0,2013-02-18 382,distinctCountOfSourceIPv6Address,unsigned64,totalCounter,current,"The count of distinct source IPv6 address values for Original Flows contributing to this Aggregated Flow.",,,,[RFC7015],0,2013-02-18 383,distinctCountOfDestinationIPv6Address,unsigned64,totalCounter,current,"The count of distinct destination IPv6 address values for Original Flows contributing to this Aggregated Flow.",,,,[RFC7015],0,2013-02-18 384,valueDistributionMethod,unsigned8,,current,"A description of the method used to distribute the counters from Contributing Flows into the Aggregated Flow records described by an associated scope, generally a Template. The method is deemed to apply to all the non-key Information Elements in the referenced scope for which value distribution is a valid operation; if the originalFlowsInitiated and/or originalFlowsCompleted Information Elements appear in the Template, they are not subject to this distribution method, as they each infer their own distribution method. This is intended to be a complete set of possible value distribution methods; it is encoded as follows: +-------+-----------------------------------------------------------+ | Value | Description | +-------+-----------------------------------------------------------+ | 0 | Unspecified: The counters for an Original Flow are | | | explicitly not distributed according to any other method | | | defined for this Information Element; use for arbitrary | | | distribution, or distribution algorithms not described by | | | any other codepoint. | | | --------------------------------------------------------- | | | | | 1 | Start Interval: The counters for an Original Flow are | | | added to the counters of the appropriate Aggregated Flow | | | containing the start time of the Original Flow. This | | | should be assumed the default if value distribution | | | information is not available at a Collecting Process for | | | an Aggregated Flow. | | | --------------------------------------------------------- | | | | | 2 | End Interval: The counters for an Original Flow are added | | | to the counters of the appropriate Aggregated Flow | | | containing the end time of the Original Flow. | | | --------------------------------------------------------- | | | | | 3 | Mid Interval: The counters for an Original Flow are added | | | to the counters of a single appropriate Aggregated Flow | | | containing some timestamp between start and end time of | | | the Original Flow. | | | --------------------------------------------------------- | | | | | 4 | Simple Uniform Distribution: Each counter for an Original | | | Flow is divided by the number of time intervals the | | | Original Flow covers (i.e., of appropriate Aggregated | | | Flows sharing the same Flow Key), and this number is | | | added to each corresponding counter in each Aggregated | | | Flow. | | | --------------------------------------------------------- | | | | | 5 | Proportional Uniform Distribution: Each counter for an | | | Original Flow is divided by the number of time units the | | | Original Flow covers, to derive a mean count rate. This | | | mean count rate is then multiplied by the number of time | | | units in the intersection of the duration of the Original | | | Flow and the time interval of each Aggregated Flow. This | | | is like simple uniform distribution, but accounts for the | | | fractional portions of a time interval covered by an | | | Original Flow in the first and last time interval. | | | --------------------------------------------------------- | | | | | 6 | Simulated Process: Each counter of the Original Flow is | | | distributed among the intervals of the Aggregated Flows | | | according to some function the Intermediate Aggregation | | | Process uses based upon properties of Flows presumed to | | | be like the Original Flow. This is essentially an | | | assertion that the Intermediate Aggregation Process has | | | no direct packet timing information but is nevertheless | | | not using one of the other simpler distribution methods. | | | The Intermediate Aggregation Process specifically makes | | | no assertion as to the correctness of the simulation. | | | --------------------------------------------------------- | | | | | 7 | Direct: The Intermediate Aggregation Process has access | | | to the original packet timings from the packets making up | | | the Original Flow, and uses these to distribute or | | | recalculate the counters. | +-------+-----------------------------------------------------------+",,,,[RFC7015],0,2013-02-18 385,rfc3550JitterMilliseconds,unsigned32,quantity,current,"Interarrival jitter as defined in section 6.4.1 of [RFC3550], measured in milliseconds.",milliseconds,,[RFC3550],[ipfix-iana_at_cisco.com],0,2013-02-18 386,rfc3550JitterMicroseconds,unsigned32,quantity,current,"Interarrival jitter as defined in section 6.4.1 of [RFC3550], measured in microseconds.",microseconds,,[RFC3550],[ipfix-iana_at_cisco.com],0,2013-02-18 387,rfc3550JitterNanoseconds,unsigned32,quantity,current,"Interarrival jitter as defined in section 6.4.1 of [RFC3550], measured in nanoseconds.",nanoseconds,,[RFC3550],[ipfix-iana_at_cisco.com],0,2013-02-18 388,dot1qDEI,boolean,default,current,"The value of the 1-bit Drop Eligible Indicator (DEI) field of the VLAN tag as described in 802.1Q-2011 subclause 9.6. In case of a QinQ frame, it represents the outer tag's DEI field and in case of an IEEE 802.1ad frame it represents the DEI field of the S-TAG. Note: in earlier versions of 802.1Q the same bit field in the incoming packet is occupied by the Canonical Format Indicator (CFI) field, except for S-TAGs.",,,[802.1Q-2011 subclause 9.6],[Yaakov_J_Stein],1,2014-02-03 389,dot1qCustomerDEI,boolean,default,current,"In case of a QinQ frame, it represents the inner tag's Drop Eligible Indicator (DEI) field and in case of an IEEE 802.1ad frame it represents the DEI field of the C-TAG.",,,[802.1Q-2011 subclause 9.6],[Yaakov_J_Stein],1,2014-02-03 390,flowSelectorAlgorithm,unsigned16,identifier,current,"This Information Element identifies the Intermediate Flow Selection Process technique (e.g., Filtering, Sampling) that is applied by the Intermediate Flow Selection Process. Most of these techniques have parameters. Its configuration parameter(s) MUST be clearly specified. Further Information Elements are needed to fully specify packet selection with these methods and all their parameters. Further method identifiers may be added to the flowSelectorAlgorithm registry. It might be necessary to define new Information Elements to specify their parameters. The flowSelectorAlgorithm registry is maintained by IANA. New assignments for the registry will be administered by IANA, on a First Come First Served basis [RFC5226], subject to Expert Review [RFC5226]. Please note that the purpose of the flow selection techniques described in this document is the improvement of measurement functions as defined in the Scope (Section 1). Before adding new flow selector algorithms it should be checked what is their intended purpose and especially if those contradict with policies defined in [RFC2804]. The designated expert(s) should consult with the community if a request is received that runs counter to [RFC2804]. The registry can be updated when specifications of the new method(s) and any new Information Elements are provided. The group of experts must double check the flowSelectorAlgorithm definitions and Information Elements with already defined flowSelectorAlgorithm and Information Elements for completeness, accuracy, and redundancy. Those experts will initially be drawn from the Working Group Chairs and document editors of the IPFIX and PSAMP Working Groups. The Intermediate Flow Selection Process Techniques identifiers are defined at [http://www.iana.org/assignments/ipfix/ipfix.xml#ipfix-flowselectoralgorithm].",,,,[RFC7014],0,2013-06-07 391,flowSelectedOctetDeltaCount,unsigned64,deltaCounter,current,"This Information Element specifies the volume in octets of all Flows that are selected in the Intermediate Flow Selection Process since the previous report.",octets,,,[RFC7014],1,2014-08-13 392,flowSelectedPacketDeltaCount,unsigned64,deltaCounter,current,"This Information Element specifies the volume in packets of all Flows that were selected in the Intermediate Flow Selection Process since the previous report.",packets,,,[RFC7014],1,2014-08-13 393,flowSelectedFlowDeltaCount,unsigned64,deltaCounter,current,"This Information Element specifies the number of Flows that were selected in the Intermediate Flow Selection Process since the last report.",flows,,,[RFC7014],1,2014-08-13 394,selectorIDTotalFlowsObserved,unsigned64,,current,"This Information Element specifies the total number of Flows observed by a Selector, for a specific value of SelectorId. This Information Element should be used in an Options Template scoped to the observation to which it refers. See Section 3.4.2.1 of the IPFIX protocol document [RFC7011].",flows,,,[RFC7014],0,2013-06-07 395,selectorIDTotalFlowsSelected,unsigned64,,current,"This Information Element specifies the total number of Flows selected by a Selector, for a specific value of SelectorId. This Information Element should be used in an Options Template scoped to the observation to which it refers. See Section 3.4.2.1 of the IPFIX protocol document [RFC7011].",flows,,,[RFC7014],0,2013-06-07 396,samplingFlowInterval,unsigned64,,current,"This Information Element specifies the number of Flows that are consecutively sampled. A value of 100 means that 100 consecutive Flows are sampled. For example, this Information Element may be used to describe the configuration of a systematic count-based Sampling Selector.",flows,,,[RFC7014],0,2013-06-07 397,samplingFlowSpacing,unsigned64,,current,"This Information Element specifies the number of Flows between two ""samplingFlowInterval""s. A value of 100 means that the next interval starts 100 Flows (which are not sampled) after the current ""samplingFlowInterval"" is over. For example, this Information Element may be used to describe the configuration of a systematic count-based Sampling Selector.",flows,,,[RFC7014],0,2013-06-07 398,flowSamplingTimeInterval,unsigned64,,current,"This Information Element specifies the time interval in microseconds during which all arriving Flows are sampled. For example, this Information Element may be used to describe the configuration of a systematic time-based Sampling Selector.",microseconds,,,[RFC7014],0,2013-06-07 399,flowSamplingTimeSpacing,unsigned64,,current,"This Information Element specifies the time interval in microseconds between two ""flowSamplingTimeInterval""s. A value of 100 means that the next interval starts 100 microseconds (during which no Flows are sampled) after the current ""flowsamplingTimeInterval"" is over. For example, this Information Element may used to describe the configuration of a systematic time-based Sampling Selector.",microseconds,,,[RFC7014],0,2013-06-07 400,hashFlowDomain,unsigned16,identifier,current,"This Information Element specifies the Information Elements that are used by the Hash-based Flow Selector as the Hash Domain.",,,,[RFC7014],0,2013-06-07 401,transportOctetDeltaCount,unsigned64,deltaCounter,current,"The number of octets, excluding IP header(s) and Layer 4 transport protocol header(s), observed for this Flow at the Observation Point since the previous report (if any).",octets,,,[Brian_Trammell],0,2013-08-01 402,transportPacketDeltaCount,unsigned64,deltaCounter,current,"The number of packets containing at least one octet beyond the IP header(s) and Layer 4 transport protocol header(s), observed for this Flow at the Observation Point since the previous report (if any).",packets,,,[Brian_Trammell],0,2013-08-01 403,originalExporterIPv4Address,ipv4Address,,current,"The IPv4 address used by the Exporting Process on an Original Exporter, as seen by the Collecting Process on an IPFIX Mediator. Used to provide information about the Original Observation Points to a downstream Collector.",,,,[RFC7119],0,2013-12-24 404,originalExporterIPv6Address,ipv6Address,,current,"The IPv6 address used by the Exporting Process on an Original Exporter, as seen by the Collecting Process on an IPFIX Mediator. Used to provide information about the Original Observation Points to a downstream Collector.",,,,[RFC7119],0,2013-12-24 405,originalObservationDomainId,unsigned32,identifier,current,"The Observation Domain ID reported by the Exporting Process on an Original Exporter, as seen by the Collecting Process on an IPFIX Mediator. Used to provide information about the Original Observation Domain to a downstream Collector. When cascading through multiple Mediators, this identifies the initial Observation Domain in the cascade.",,,,[RFC7119],0,2013-12-24 406,intermediateProcessId,unsigned32,identifier,current,"Description: An identifier of an Intermediate Process that is unique per IPFIX Device. Typically, this Information Element is used for limiting the scope of other Information Elements. Note that process identifiers may be assigned dynamically; that is, an Intermediate Process may be restarted with a different ID.",,,,[RFC7119],0,2013-12-24 407,ignoredDataRecordTotalCount,unsigned64,totalCounter,current,"Description: The total number of received Data Records that the Intermediate Process did not process since the (re-)initialization of the Intermediate Process; includes only Data Records not examined or otherwise handled by the Intermediate Process due to resource constraints, not Data Records that were examined or otherwise handled by the Intermediate Process but those that merely do not contribute to any exported Data Record due to the operations performed by the Intermediate Process.",,,,[RFC7119],0,2013-12-24 408,dataLinkFrameType,unsigned16,flags,current,"This Information Element specifies the type of the selected data link frame. The following data link types are defined here: - 0x01 IEEE802.3 ETHERNET [IEEE802.3] - 0x02 IEEE802.11 MAC Frame format [IEEE802.11] Further values may be assigned by IANA. Note that the assigned values are bits so that multiple observations can be OR'd together. The data link layer is defined in [ISO/IEC.7498-1:1994].",,,[IEEE802.3][IEEE802.11][ISO/IEC.7498-1:1994],[RFC7133],0,2014-01-11 409,sectionOffset,unsigned16,quantity,current,"This Information Element specifies the offset of the packet section (e.g., dataLinkFrameSection, ipHeaderPacketSection, ipPayloadPacketSection, mplsLabelStackSection, and mplsPayloadPacketSection). If this Information Element is omitted, it defaults to zero (i.e., no offset). If multiple sectionOffset Information Elements are specified within a single Template, then they apply to the packet section Information Elements in order: the first sectionOffset applies to the first packet section, the second to the second, and so on. Note that the ""closest"" sectionOffset and packet section Information Elements within a given Template are not necessarily related. If there are fewer sectionOffset Information Elements than packet section Information Elements, then subsequent packet section Information Elements have no offset, i.e., a sectionOffset of zero applies to those packet section Information Elements. If there are more sectionOffset Information Elements than the number of packet section Information Elements, then the additional sectionOffset Information Elements are meaningless.",,,,[RFC7133],0,2014-01-11 410,sectionExportedOctets,unsigned16,quantity,current,"This Information Element specifies the observed length of the packet section (e.g., dataLinkFrameSection, ipHeaderPacketSection, ipPayloadPacketSection, mplsLabelStackSection, and mplsPayloadPacketSection) when padding is used. The packet section may be of a fixed size larger than the sectionExportedOctets. In this case, octets in the packet section beyond the sectionExportedOctets MUST follow the [RFC7011] rules for padding (i.e., be composed of zero (0) valued octets).",,,[RFC7011],[RFC7133],0,2014-01-11 411,dot1qServiceInstanceTag,octetArray,default,current,"This Information Element, which is 16 octets long, represents the Backbone Service Instance Tag (I-TAG) Tag Control Information (TCI) field of an Ethernet frame as described in [IEEE802.1Q]. It encodes the Backbone Service Instance Priority Code Point (I-PCP), Backbone Service Instance Drop Eligible Indicator (I-DEI), Use Customer Addresses (UCAs), Backbone Service Instance Identifier (I-SID), Encapsulated Customer Destination Address (C-DA), Encapsulated Customer Source Address (C-SA), and reserved fields. The structure and semantics within the Tag Control Information field are defined in [IEEE802.1Q].",,,[IEEE802.1Q],[RFC7133],1,2014-05-02 412,dot1qServiceInstanceId,unsigned32,identifier,current,"The value of the 24-bit Backbone Service Instance Identifier (I-SID) portion of the Backbone Service Instance Tag (I-TAG) Tag Control Information (TCI) field of an Ethernet frame as described in [IEEE802.1Q].",,"The valid range is 0 - 16777215 (i.e., 24 bits).",[IEEE802.1Q],[RFC7133],1,2014-05-02 413,dot1qServiceInstancePriority,unsigned8,identifier,current,"The value of the 3-bit Backbone Service Instance Priority Code Point (I-PCP) portion of the Backbone Service Instance Tag (I-TAG) Tag Control Information (TCI) field of an Ethernet frame as described in [IEEE802.1Q].",,The valid range is 0-7.,[IEEE802.1Q],[RFC7133],1,2014-05-02 414,dot1qCustomerSourceMacAddress,macAddress,default,current,"The value of the Encapsulated Customer Source Address (C-SA) portion of the Backbone Service Instance Tag (I-TAG) Tag Control Information (TCI) field of an Ethernet frame as described in [IEEE802.1Q].",,,[IEEE802.1Q],[RFC7133],1,2014-05-02 415,dot1qCustomerDestinationMacAddress,macAddress,default,current,"The value of the Encapsulated Customer Destination Address (C-DA) portion of the Backbone Service Instance Tag (I-TAG) Tag Control Information (TCI) field of an Ethernet frame as described in [IEEE802.1Q].",,,[IEEE802.1Q],[RFC7133],1,2014-05-02 416,,,,deprecated,"Duplicate of Information Element ID 352, layer2OctetDeltaCount.",,,[RFC5477],,2,2014-05-13 417,postLayer2OctetDeltaCount,unsigned64,deltaCounter,current,"The definition of this Information Element is identical to the definition of the layer2OctetDeltaCount Information Element, except that it reports a potentially modified value caused by a middlebox function after the packet passed the Observation Point. This Information Element is the layer 2 version of postOctetDeltaCount (ElementId #23).",octets,,[RFC5477],[RFC7133],1,2014-05-02 418,postMCastLayer2OctetDeltaCount,unsigned64,deltaCounter,current,"The number of layer 2 octets since the previous report (if any) in outgoing multicast packets sent for packets of this Flow by a multicast daemon within the Observation Domain. This property cannot necessarily be observed at the Observation Point but may be retrieved by other means. The number of octets includes layer 2 header(s) and layer 2 payload. This Information Element is the layer 2 version of postMCastOctetDeltaCount (ElementId #20).",octets,,[RFC5477],[RFC7133],1,2014-05-02 419,,,,deprecated,"Duplicate of Information Element ID 353, layer2OctetTotalCount.",,,[RFC5477],,2,2014-05-13 420,postLayer2OctetTotalCount,unsigned64,totalCounter,current,"The definition of this Information Element is identical to the definition of the layer2OctetTotalCount Information Element, except that it reports a potentially modified value caused by a middlebox function after the packet passed the Observation Point. This Information Element is the layer 2 version of postOctetTotalCount (ElementId #171).",octets,,[RFC5477],[RFC7133],1,2014-05-02 421,postMCastLayer2OctetTotalCount,unsigned64,totalCounter,current,"The total number of layer 2 octets in outgoing multicast packets sent for packets of this Flow by a multicast daemon in the Observation Domain since the Metering Process (re-)initialization. This property cannot necessarily be observed at the Observation Point but may be retrieved by other means. The number of octets includes layer 2 header(s) and layer 2 payload. This Information Element is the layer 2 version of postMCastOctetTotalCount (ElementId #175).",octets,,[RFC5477],[RFC7133],1,2014-05-02 422,minimumLayer2TotalLength,unsigned64,,current,"Layer 2 length of the smallest packet observed for this Flow. The packet length includes the length of the layer 2 header(s) and the length of the layer 2 payload. This Information Element is the layer 2 version of minimumIpTotalLength (ElementId #25).",octets,,[RFC5477],[RFC7133],1,2014-05-02 423,maximumLayer2TotalLength,unsigned64,,current,"Layer 2 length of the largest packet observed for this Flow. The packet length includes the length of the layer 2 header(s) and the length of the layer 2 payload. This Information Element is the layer 2 version of maximumIpTotalLength (ElementId #26).",octets,,[RFC5477],[RFC7133],1,2014-05-02 424,droppedLayer2OctetDeltaCount,unsigned64,deltaCounter,current,"The number of layer 2 octets since the previous report (if any) in packets of this Flow dropped by packet treatment. The number of octets includes layer 2 header(s) and layer 2 payload. This Information Element is the layer 2 version of droppedOctetDeltaCount (ElementId #132).",octets,,[RFC5477],[RFC7133],1,2014-05-02 425,droppedLayer2OctetTotalCount,unsigned64,totalCounter,current,"The total number of octets in observed layer 2 packets (including the layer 2 header) that were dropped by packet treatment since the (re-)initialization of the Metering Process. This Information Element is the layer 2 version of droppedOctetTotalCount (ElementId #134).",octets,,[RFC5477],[RFC7133],1,2014-05-02 426,ignoredLayer2OctetTotalCount,unsigned64,totalCounter,current,"The total number of octets in observed layer 2 packets (including the layer 2 header) that the Metering Process did not process since the (re-)initialization of the Metering Process. This Information Element is the layer 2 version of ignoredOctetTotalCount (ElementId #165).",octets,,[RFC5477],[RFC7133],1,2014-05-02 427,notSentLayer2OctetTotalCount,unsigned64,totalCounter,current,"The total number of octets in observed layer 2 packets (including the layer 2 header) that the Metering Process did not process since the (re-)initialization of the Metering Process. This Information Element is the layer 2 version of notSentOctetTotalCount (ElementId #168).",octets,,[RFC5477],[RFC7133],1,2014-05-02 428,layer2OctetDeltaSumOfSquares,unsigned64,deltaCounter,current,"The sum of the squared numbers of layer 2 octets per incoming packet since the previous report (if any) for this Flow at the Observation Point. The number of octets includes layer 2 header(s) and layer 2 payload. This Information Element is the layer 2 version of octetDeltaSumOfSquares (ElementId #198).",octets,,[RFC5477],[RFC7133],1,2014-05-02 429,layer2OctetTotalSumOfSquares,unsigned64,totalCounter,current,"The total sum of the squared numbers of layer 2 octets in incoming packets for this Flow at the Observation Point since the Metering Process (re-)initialization for this Observation Point. The number of octets includes layer 2 header(s) and layer 2 payload. This Information Element is the layer 2 version of octetTotalSumOfSquares (ElementId #199).",octets,,[RFC5477],[RFC7133],1,2014-05-02 430,layer2FrameDeltaCount,unsigned64,deltaCounter,current,"The number of incoming layer 2 frames since the previous report (if any) for this Flow at the Observation Point.",frames,,,[ipfix-iana_at_cisco.com],0,2014-05-02 431,layer2FrameTotalCount,unsigned64,totalCounter,current,"The total number of incoming layer 2 frames for this Flow at the Observation Point since the Metering Process (re-)initialization for this Observation Point.",frames,,,[ipfix-iana_at_cisco.com],0,2014-05-02 432,pseudoWireDestinationIPv4Address,ipv4Address,default,current,The destination IPv4 address of the PSN tunnel carrying the pseudowire.,,,[RFC3985],[ipfix-iana_at_cisco.com],0,2014-05-28 433,ignoredLayer2FrameTotalCount,unsigned64,totalCounter,current,"The total number of observed layer 2 frames that the Metering Process did not process since the (re-)initialization of the Metering Process. This Information Element is the layer 2 version of ignoredPacketTotalCount (ElementId #164).",frames,,,[ipfix-iana_at_cisco.com],0,2014-06-27 433-32767,Unassigned,,,,,,,,,, fastnetmon-1.1.4/src/ipfix_rfc.cpp000066400000000000000000000601411343111404700171370ustar00rootroot00000000000000#include #include #include "ipfix_rfc.h" /* This file is autogenerated with script ipfix_csv_processor.pl */ /* Please do not edit it directly */ std::string ipfix_information_element_t::get_name() { return this->name; } unsigned int ipfix_information_element_t::get_length() { return this->length; } ipfix_information_element_t::ipfix_information_element_t(std::string name, unsigned int length) { this->name = name; this->length = length; } ipfix_information_element_t::ipfix_information_element_t() { this->name = std::string(""); this->length = 0; } std::string ipfix_information_database::get_name_by_id(unsigned int field_id) { ipfix_database_t::iterator itr = database.find(field_id); if (itr == database.end()) { return std::string(""); } return itr->second.get_name(); } unsigned int ipfix_information_database::get_length_by_id(unsigned int field_id) { ipfix_database_t::iterator itr = database.find(field_id); if (itr == database.end()) { return 0; } return itr->second.get_length(); } bool ipfix_information_database::add_element(unsigned int field_id, std::string name, unsigned int length) { ipfix_database_t::iterator itr = database.find(field_id); // Duplicate ID's strictly prohibited if (itr != database.end()) { return false; } database[field_id] = ipfix_information_element_t(name, length); return true; } ipfix_information_database::ipfix_information_database() { this->add_element(0, "Reserved", 0); this->add_element(1, "octetDeltaCount", 8); this->add_element(2, "packetDeltaCount", 8); this->add_element(3, "deltaFlowCount", 8); this->add_element(4, "protocolIdentifier", 1); this->add_element(5, "ipClassOfService", 1); this->add_element(6, "tcpControlBits", 2); this->add_element(7, "sourceTransportPort", 2); this->add_element(8, "sourceIPv4Address", 4); this->add_element(9, "sourceIPv4PrefixLength", 1); this->add_element(10, "ingressInterface", 4); this->add_element(11, "destinationTransportPort", 2); this->add_element(12, "destinationIPv4Address", 4); this->add_element(13, "destinationIPv4PrefixLength", 1); this->add_element(14, "egressInterface", 4); this->add_element(15, "ipNextHopIPv4Address", 4); this->add_element(16, "bgpSourceAsNumber", 4); this->add_element(17, "bgpDestinationAsNumber", 4); this->add_element(18, "bgpNextHopIPv4Address", 4); this->add_element(19, "postMCastPacketDeltaCount", 8); this->add_element(20, "postMCastOctetDeltaCount", 8); this->add_element(21, "flowEndSysUpTime", 4); this->add_element(22, "flowStartSysUpTime", 4); this->add_element(23, "postOctetDeltaCount", 8); this->add_element(24, "postPacketDeltaCount", 8); this->add_element(25, "minimumIpTotalLength", 8); this->add_element(26, "maximumIpTotalLength", 8); this->add_element(27, "sourceIPv6Address", 16); this->add_element(28, "destinationIPv6Address", 16); this->add_element(29, "sourceIPv6PrefixLength", 1); this->add_element(30, "destinationIPv6PrefixLength", 1); this->add_element(31, "flowLabelIPv6", 4); this->add_element(32, "icmpTypeCodeIPv4", 2); this->add_element(33, "igmpType", 1); this->add_element(34, "samplingInterval", 4); this->add_element(35, "samplingAlgorithm", 1); this->add_element(36, "flowActiveTimeout", 2); this->add_element(37, "flowIdleTimeout", 2); this->add_element(38, "engineType", 1); this->add_element(39, "engineId", 1); this->add_element(40, "exportedOctetTotalCount", 8); this->add_element(41, "exportedMessageTotalCount", 8); this->add_element(42, "exportedFlowRecordTotalCount", 8); this->add_element(43, "ipv4RouterSc", 4); this->add_element(44, "sourceIPv4Prefix", 4); this->add_element(45, "destinationIPv4Prefix", 4); this->add_element(46, "mplsTopLabelType", 1); this->add_element(47, "mplsTopLabelIPv4Address", 4); this->add_element(48, "samplerId", 1); this->add_element(49, "samplerMode", 1); this->add_element(50, "samplerRandomInterval", 4); this->add_element(51, "classId", 1); this->add_element(52, "minimumTTL", 1); this->add_element(53, "maximumTTL", 1); this->add_element(54, "fragmentIdentification", 4); this->add_element(55, "postIpClassOfService", 1); this->add_element(56, "sourceMacAddress", 0); this->add_element(57, "postDestinationMacAddress", 0); this->add_element(58, "vlanId", 2); this->add_element(59, "postVlanId", 2); this->add_element(60, "ipVersion", 1); this->add_element(61, "flowDirection", 1); this->add_element(62, "ipNextHopIPv6Address", 16); this->add_element(63, "bgpNextHopIPv6Address", 16); this->add_element(64, "ipv6ExtensionHeaders", 4); this->add_element(65, "Reserved", 0); this->add_element(66, "Reserved", 0); this->add_element(67, "Reserved", 0); this->add_element(68, "Reserved", 0); this->add_element(69, "Reserved", 0); this->add_element(70, "mplsTopLabelStackSection", 0); this->add_element(71, "mplsLabelStackSection2", 0); this->add_element(72, "mplsLabelStackSection3", 0); this->add_element(73, "mplsLabelStackSection4", 0); this->add_element(74, "mplsLabelStackSection5", 0); this->add_element(75, "mplsLabelStackSection6", 0); this->add_element(76, "mplsLabelStackSection7", 0); this->add_element(77, "mplsLabelStackSection8", 0); this->add_element(78, "mplsLabelStackSection9", 0); this->add_element(79, "mplsLabelStackSection10", 0); this->add_element(80, "destinationMacAddress", 0); this->add_element(81, "postSourceMacAddress", 0); this->add_element(82, "interfaceName", 0); this->add_element(83, "interfaceDescription", 0); this->add_element(84, "samplerName", 0); this->add_element(85, "octetTotalCount", 8); this->add_element(86, "packetTotalCount", 8); this->add_element(87, "flagsAndSamplerId", 4); this->add_element(88, "fragmentOffset", 2); this->add_element(89, "forwardingStatus", 4); this->add_element(90, "mplsVpnRouteDistinguisher", 0); this->add_element(91, "mplsTopLabelPrefixLength", 1); this->add_element(92, "srcTrafficIndex", 4); this->add_element(93, "dstTrafficIndex", 4); this->add_element(94, "applicationDescription", 0); this->add_element(95, "applicationId", 0); this->add_element(96, "applicationName", 0); this->add_element(97, "Assigned for NetFlow v9 compatibility", 0); this->add_element(98, "postIpDiffServCodePoint", 1); this->add_element(99, "multicastReplicationFactor", 4); this->add_element(100, "className", 0); this->add_element(101, "classificationEngineId", 1); this->add_element(102, "layer2packetSectionOffset", 2); this->add_element(103, "layer2packetSectionSize", 2); this->add_element(104, "layer2packetSectionData", 0); this->add_element(105, "Reserved", 0); this->add_element(106, "Reserved", 0); this->add_element(107, "Reserved", 0); this->add_element(108, "Reserved", 0); this->add_element(109, "Reserved", 0); this->add_element(110, "Reserved", 0); this->add_element(111, "Reserved", 0); this->add_element(112, "Reserved", 0); this->add_element(113, "Reserved", 0); this->add_element(114, "Reserved", 0); this->add_element(115, "Reserved", 0); this->add_element(116, "Reserved", 0); this->add_element(117, "Reserved", 0); this->add_element(118, "Reserved", 0); this->add_element(119, "Reserved", 0); this->add_element(120, "Reserved", 0); this->add_element(121, "Reserved", 0); this->add_element(122, "Reserved", 0); this->add_element(123, "Reserved", 0); this->add_element(124, "Reserved", 0); this->add_element(125, "Reserved", 0); this->add_element(126, "Reserved", 0); this->add_element(127, "Reserved", 0); this->add_element(128, "bgpNextAdjacentAsNumber", 4); this->add_element(129, "bgpPrevAdjacentAsNumber", 4); this->add_element(130, "exporterIPv4Address", 4); this->add_element(131, "exporterIPv6Address", 16); this->add_element(132, "droppedOctetDeltaCount", 8); this->add_element(133, "droppedPacketDeltaCount", 8); this->add_element(134, "droppedOctetTotalCount", 8); this->add_element(135, "droppedPacketTotalCount", 8); this->add_element(136, "flowEndReason", 1); this->add_element(137, "commonPropertiesId", 8); this->add_element(138, "observationPointId", 8); this->add_element(139, "icmpTypeCodeIPv6", 2); this->add_element(140, "mplsTopLabelIPv6Address", 16); this->add_element(141, "lineCardId", 4); this->add_element(142, "portId", 4); this->add_element(143, "meteringProcessId", 4); this->add_element(144, "exportingProcessId", 4); this->add_element(145, "templateId", 2); this->add_element(146, "wlanChannelId", 1); this->add_element(147, "wlanSSID", 0); this->add_element(148, "flowId", 8); this->add_element(149, "observationDomainId", 4); this->add_element(150, "flowStartSeconds", 0); this->add_element(151, "flowEndSeconds", 0); this->add_element(152, "flowStartMilliseconds", 0); this->add_element(153, "flowEndMilliseconds", 0); this->add_element(154, "flowStartMicroseconds", 0); this->add_element(155, "flowEndMicroseconds", 0); this->add_element(156, "flowStartNanoseconds", 0); this->add_element(157, "flowEndNanoseconds", 0); this->add_element(158, "flowStartDeltaMicroseconds", 4); this->add_element(159, "flowEndDeltaMicroseconds", 4); this->add_element(160, "systemInitTimeMilliseconds", 0); this->add_element(161, "flowDurationMilliseconds", 4); this->add_element(162, "flowDurationMicroseconds", 4); this->add_element(163, "observedFlowTotalCount", 8); this->add_element(164, "ignoredPacketTotalCount", 8); this->add_element(165, "ignoredOctetTotalCount", 8); this->add_element(166, "notSentFlowTotalCount", 8); this->add_element(167, "notSentPacketTotalCount", 8); this->add_element(168, "notSentOctetTotalCount", 8); this->add_element(169, "destinationIPv6Prefix", 16); this->add_element(170, "sourceIPv6Prefix", 16); this->add_element(171, "postOctetTotalCount", 8); this->add_element(172, "postPacketTotalCount", 8); this->add_element(173, "flowKeyIndicator", 8); this->add_element(174, "postMCastPacketTotalCount", 8); this->add_element(175, "postMCastOctetTotalCount", 8); this->add_element(176, "icmpTypeIPv4", 1); this->add_element(177, "icmpCodeIPv4", 1); this->add_element(178, "icmpTypeIPv6", 1); this->add_element(179, "icmpCodeIPv6", 1); this->add_element(180, "udpSourcePort", 2); this->add_element(181, "udpDestinationPort", 2); this->add_element(182, "tcpSourcePort", 2); this->add_element(183, "tcpDestinationPort", 2); this->add_element(184, "tcpSequenceNumber", 4); this->add_element(185, "tcpAcknowledgementNumber", 4); this->add_element(186, "tcpWindowSize", 2); this->add_element(187, "tcpUrgentPointer", 2); this->add_element(188, "tcpHeaderLength", 1); this->add_element(189, "ipHeaderLength", 1); this->add_element(190, "totalLengthIPv4", 2); this->add_element(191, "payloadLengthIPv6", 2); this->add_element(192, "ipTTL", 1); this->add_element(193, "nextHeaderIPv6", 1); this->add_element(194, "mplsPayloadLength", 4); this->add_element(195, "ipDiffServCodePoint", 1); this->add_element(196, "ipPrecedence", 1); this->add_element(197, "fragmentFlags", 1); this->add_element(198, "octetDeltaSumOfSquares", 8); this->add_element(199, "octetTotalSumOfSquares", 8); this->add_element(200, "mplsTopLabelTTL", 1); this->add_element(201, "mplsLabelStackLength", 4); this->add_element(202, "mplsLabelStackDepth", 4); this->add_element(203, "mplsTopLabelExp", 1); this->add_element(204, "ipPayloadLength", 4); this->add_element(205, "udpMessageLength", 2); this->add_element(206, "isMulticast", 1); this->add_element(207, "ipv4IHL", 1); this->add_element(208, "ipv4Options", 4); this->add_element(209, "tcpOptions", 8); this->add_element(210, "paddingOctets", 0); this->add_element(211, "collectorIPv4Address", 4); this->add_element(212, "collectorIPv6Address", 16); this->add_element(213, "exportInterface", 4); this->add_element(214, "exportProtocolVersion", 1); this->add_element(215, "exportTransportProtocol", 1); this->add_element(216, "collectorTransportPort", 2); this->add_element(217, "exporterTransportPort", 2); this->add_element(218, "tcpSynTotalCount", 8); this->add_element(219, "tcpFinTotalCount", 8); this->add_element(220, "tcpRstTotalCount", 8); this->add_element(221, "tcpPshTotalCount", 8); this->add_element(222, "tcpAckTotalCount", 8); this->add_element(223, "tcpUrgTotalCount", 8); this->add_element(224, "ipTotalLength", 8); this->add_element(225, "postNATSourceIPv4Address", 4); this->add_element(226, "postNATDestinationIPv4Address", 4); this->add_element(227, "postNAPTSourceTransportPort", 2); this->add_element(228, "postNAPTDestinationTransportPort", 2); this->add_element(229, "natOriginatingAddressRealm", 1); this->add_element(230, "natEvent", 1); this->add_element(231, "initiatorOctets", 8); this->add_element(232, "responderOctets", 8); this->add_element(233, "firewallEvent", 1); this->add_element(234, "ingressVRFID", 4); this->add_element(235, "egressVRFID", 4); this->add_element(236, "VRFname", 0); this->add_element(237, "postMplsTopLabelExp", 1); this->add_element(238, "tcpWindowScale", 2); this->add_element(239, "biflowDirection", 1); this->add_element(240, "ethernetHeaderLength", 1); this->add_element(241, "ethernetPayloadLength", 2); this->add_element(242, "ethernetTotalLength", 2); this->add_element(243, "dot1qVlanId", 2); this->add_element(244, "dot1qPriority", 1); this->add_element(245, "dot1qCustomerVlanId", 2); this->add_element(246, "dot1qCustomerPriority", 1); this->add_element(247, "metroEvcId", 0); this->add_element(248, "metroEvcType", 1); this->add_element(249, "pseudoWireId", 4); this->add_element(250, "pseudoWireType", 2); this->add_element(251, "pseudoWireControlWord", 4); this->add_element(252, "ingressPhysicalInterface", 4); this->add_element(253, "egressPhysicalInterface", 4); this->add_element(254, "postDot1qVlanId", 2); this->add_element(255, "postDot1qCustomerVlanId", 2); this->add_element(256, "ethernetType", 2); this->add_element(257, "postIpPrecedence", 1); this->add_element(258, "collectionTimeMilliseconds", 0); this->add_element(259, "exportSctpStreamId", 2); this->add_element(260, "maxExportSeconds", 0); this->add_element(261, "maxFlowEndSeconds", 0); this->add_element(262, "messageMD5Checksum", 0); this->add_element(263, "messageScope", 1); this->add_element(264, "minExportSeconds", 0); this->add_element(265, "minFlowStartSeconds", 0); this->add_element(266, "opaqueOctets", 0); this->add_element(267, "sessionScope", 1); this->add_element(268, "maxFlowEndMicroseconds", 0); this->add_element(269, "maxFlowEndMilliseconds", 0); this->add_element(270, "maxFlowEndNanoseconds", 0); this->add_element(271, "minFlowStartMicroseconds", 0); this->add_element(272, "minFlowStartMilliseconds", 0); this->add_element(273, "minFlowStartNanoseconds", 0); this->add_element(274, "collectorCertificate", 0); this->add_element(275, "exporterCertificate", 0); this->add_element(276, "dataRecordsReliability", 0); this->add_element(277, "observationPointType", 1); this->add_element(278, "newConnectionDeltaCount", 4); this->add_element(279, "connectionSumDurationSeconds", 8); this->add_element(280, "connectionTransactionId", 8); this->add_element(281, "postNATSourceIPv6Address", 16); this->add_element(282, "postNATDestinationIPv6Address", 16); this->add_element(283, "natPoolId", 4); this->add_element(284, "natPoolName", 0); this->add_element(285, "anonymizationFlags", 2); this->add_element(286, "anonymizationTechnique", 2); this->add_element(287, "informationElementIndex", 2); this->add_element(288, "p2pTechnology", 0); this->add_element(289, "tunnelTechnology", 0); this->add_element(290, "encryptedTechnology", 0); this->add_element(291, "basicList", 0); this->add_element(292, "subTemplateList", 0); this->add_element(293, "subTemplateMultiList", 0); this->add_element(294, "bgpValidityState", 1); this->add_element(295, "IPSecSPI", 4); this->add_element(296, "greKey", 4); this->add_element(297, "natType", 1); this->add_element(298, "initiatorPackets", 8); this->add_element(299, "responderPackets", 8); this->add_element(300, "observationDomainName", 0); this->add_element(301, "selectionSequenceId", 8); this->add_element(302, "selectorId", 8); this->add_element(303, "informationElementId", 2); this->add_element(304, "selectorAlgorithm", 2); this->add_element(305, "samplingPacketInterval", 4); this->add_element(306, "samplingPacketSpace", 4); this->add_element(307, "samplingTimeInterval", 4); this->add_element(308, "samplingTimeSpace", 4); this->add_element(309, "samplingSize", 4); this->add_element(310, "samplingPopulation", 4); this->add_element(311, "samplingProbability", 0); this->add_element(312, "dataLinkFrameSize", 2); this->add_element(313, "ipHeaderPacketSection", 0); this->add_element(314, "ipPayloadPacketSection", 0); this->add_element(315, "dataLinkFrameSection", 0); this->add_element(316, "mplsLabelStackSection", 0); this->add_element(317, "mplsPayloadPacketSection", 0); this->add_element(318, "selectorIdTotalPktsObserved", 8); this->add_element(319, "selectorIdTotalPktsSelected", 8); this->add_element(320, "absoluteError", 0); this->add_element(321, "relativeError", 0); this->add_element(322, "observationTimeSeconds", 0); this->add_element(323, "observationTimeMilliseconds", 0); this->add_element(324, "observationTimeMicroseconds", 0); this->add_element(325, "observationTimeNanoseconds", 0); this->add_element(326, "digestHashValue", 8); this->add_element(327, "hashIPPayloadOffset", 8); this->add_element(328, "hashIPPayloadSize", 8); this->add_element(329, "hashOutputRangeMin", 8); this->add_element(330, "hashOutputRangeMax", 8); this->add_element(331, "hashSelectedRangeMin", 8); this->add_element(332, "hashSelectedRangeMax", 8); this->add_element(333, "hashDigestOutput", 0); this->add_element(334, "hashInitialiserValue", 8); this->add_element(335, "selectorName", 0); this->add_element(336, "upperCILimit", 0); this->add_element(337, "lowerCILimit", 0); this->add_element(338, "confidenceLevel", 0); this->add_element(339, "informationElementDataType", 1); this->add_element(340, "informationElementDescription", 0); this->add_element(341, "informationElementName", 0); this->add_element(342, "informationElementRangeBegin", 8); this->add_element(343, "informationElementRangeEnd", 8); this->add_element(344, "informationElementSemantics", 1); this->add_element(345, "informationElementUnits", 2); this->add_element(346, "privateEnterpriseNumber", 4); this->add_element(347, "virtualStationInterfaceId", 0); this->add_element(348, "virtualStationInterfaceName", 0); this->add_element(349, "virtualStationUUID", 0); this->add_element(350, "virtualStationName", 0); this->add_element(351, "layer2SegmentId", 8); this->add_element(352, "layer2OctetDeltaCount", 8); this->add_element(353, "layer2OctetTotalCount", 8); this->add_element(354, "ingressUnicastPacketTotalCount", 8); this->add_element(355, "ingressMulticastPacketTotalCount", 8); this->add_element(356, "ingressBroadcastPacketTotalCount", 8); this->add_element(357, "egressUnicastPacketTotalCount", 8); this->add_element(358, "egressBroadcastPacketTotalCount", 8); this->add_element(359, "monitoringIntervalStartMilliSeconds", 0); this->add_element(360, "monitoringIntervalEndMilliSeconds", 0); this->add_element(361, "portRangeStart", 2); this->add_element(362, "portRangeEnd", 2); this->add_element(363, "portRangeStepSize", 2); this->add_element(364, "portRangeNumPorts", 2); this->add_element(365, "staMacAddress", 0); this->add_element(366, "staIPv4Address", 4); this->add_element(367, "wtpMacAddress", 0); this->add_element(368, "ingressInterfaceType", 4); this->add_element(369, "egressInterfaceType", 4); this->add_element(370, "rtpSequenceNumber", 2); this->add_element(371, "userName", 0); this->add_element(372, "applicationCategoryName", 0); this->add_element(373, "applicationSubCategoryName", 0); this->add_element(374, "applicationGroupName", 0); this->add_element(375, "originalFlowsPresent", 8); this->add_element(376, "originalFlowsInitiated", 8); this->add_element(377, "originalFlowsCompleted", 8); this->add_element(378, "distinctCountOfSourceIPAddress", 8); this->add_element(379, "distinctCountOfDestinationIPAddress", 8); this->add_element(380, "distinctCountOfSourceIPv4Address", 4); this->add_element(381, "distinctCountOfDestinationIPv4Address", 4); this->add_element(382, "distinctCountOfSourceIPv6Address", 8); this->add_element(383, "distinctCountOfDestinationIPv6Address", 8); this->add_element(384, "valueDistributionMethod", 1); this->add_element(385, "rfc3550JitterMilliseconds", 4); this->add_element(386, "rfc3550JitterMicroseconds", 4); this->add_element(387, "rfc3550JitterNanoseconds", 4); this->add_element(388, "dot1qDEI", 0); this->add_element(389, "dot1qCustomerDEI", 0); this->add_element(390, "flowSelectorAlgorithm", 2); this->add_element(391, "flowSelectedOctetDeltaCount", 8); this->add_element(392, "flowSelectedPacketDeltaCount", 8); this->add_element(393, "flowSelectedFlowDeltaCount", 8); this->add_element(394, "selectorIDTotalFlowsObserved", 8); this->add_element(395, "selectorIDTotalFlowsSelected", 8); this->add_element(396, "samplingFlowInterval", 8); this->add_element(397, "samplingFlowSpacing", 8); this->add_element(398, "flowSamplingTimeInterval", 8); this->add_element(399, "flowSamplingTimeSpacing", 8); this->add_element(400, "hashFlowDomain", 2); this->add_element(401, "transportOctetDeltaCount", 8); this->add_element(402, "transportPacketDeltaCount", 8); this->add_element(403, "originalExporterIPv4Address", 4); this->add_element(404, "originalExporterIPv6Address", 16); this->add_element(405, "originalObservationDomainId", 4); this->add_element(406, "intermediateProcessId", 4); this->add_element(407, "ignoredDataRecordTotalCount", 8); this->add_element(408, "dataLinkFrameType", 2); this->add_element(409, "sectionOffset", 2); this->add_element(410, "sectionExportedOctets", 2); this->add_element(411, "dot1qServiceInstanceTag", 0); this->add_element(412, "dot1qServiceInstanceId", 4); this->add_element(413, "dot1qServiceInstancePriority", 1); this->add_element(414, "dot1qCustomerSourceMacAddress", 0); this->add_element(415, "dot1qCustomerDestinationMacAddress", 0); this->add_element(416, "reserved", 0); this->add_element(417, "postLayer2OctetDeltaCount", 8); this->add_element(418, "postMCastLayer2OctetDeltaCount", 8); this->add_element(419, "reserved", 0); this->add_element(420, "postLayer2OctetTotalCount", 8); this->add_element(421, "postMCastLayer2OctetTotalCount", 8); this->add_element(422, "minimumLayer2TotalLength", 8); this->add_element(423, "maximumLayer2TotalLength", 8); this->add_element(424, "droppedLayer2OctetDeltaCount", 8); this->add_element(425, "droppedLayer2OctetTotalCount", 8); this->add_element(426, "ignoredLayer2OctetTotalCount", 8); this->add_element(427, "notSentLayer2OctetTotalCount", 8); this->add_element(428, "layer2OctetDeltaSumOfSquares", 8); this->add_element(429, "layer2OctetTotalSumOfSquares", 8); this->add_element(430, "layer2FrameDeltaCount", 8); this->add_element(431, "layer2FrameTotalCount", 8); this->add_element(432, "pseudoWireDestinationIPv4Address", 4); this->add_element(433, "ignoredLayer2FrameTotalCount", 8); } fastnetmon-1.1.4/src/ipfix_rfc.h000066400000000000000000000015341343111404700166050ustar00rootroot00000000000000#ifndef IPFIX_RFC_H #define IPFIX_RFC_H /* This file is autogenerated with script ipfix_csv_processor.pl */ /* Please do not edit it directly */ #include #include class ipfix_information_element_t { public: ipfix_information_element_t(std::string name, unsigned int length); ipfix_information_element_t(); std::string get_name(); unsigned int get_length(); std::string name; unsigned int length; }; typedef std::map ipfix_database_t; class ipfix_information_database { public: ipfix_information_database(); bool add_element(unsigned int field_id, std::string name, unsigned int length); std::string get_name_by_id(unsigned int field_id); unsigned int get_length_by_id(unsigned int field_id); private: ipfix_database_t database; }; #endif fastnetmon-1.1.4/src/irq_balance_manually.sh000077500000000000000000000010641343111404700211620ustar00rootroot00000000000000#!/bin/bash # from http://habrahabr.ru/post/108240/ ncpus=`grep -ciw ^processor /proc/cpuinfo` test "$ncpus" -gt 1 || exit 1 n=0 for irq in `cat /proc/interrupts | grep eth | awk '{print $1}' | sed s/\://g` do f="/proc/irq/$irq/smp_affinity" test -r "$f" || continue cpu=$[$ncpus - ($n % $ncpus) - 1] if [ $cpu -ge 0 ] then mask=`printf %x $[2 ** $cpu]` echo "Assign SMP affinity: eth queue $n, irq $irq, cpu $cpu, mask 0x$mask" echo "$mask" > "$f" let n+=1 fi done fastnetmon-1.1.4/src/juniper_plugin/000077500000000000000000000000001343111404700175125ustar00rootroot00000000000000fastnetmon-1.1.4/src/juniper_plugin/README.md000066400000000000000000000030371343111404700207740ustar00rootroot00000000000000Juniper FastNetMon plug-in =========== Overview -------- Connects to a Juniper router and adds or removes a blackhole rule for an attack by IP address. The actions can be modified such as adding a firewall rule. This script uses the Juniper NETCONF PHP API. More information about this can be found at the following URL: * https://github.com/Juniper/netconf-php Installation ------------ #### Prerequisite You must have a user and netconf enabled on your Juniper to enable netconf go to your cli and type: ``` user@host> configure user@host# set netconf ssh ``` if you wish to change netconf port instead of ``` user@host# set netconf ssh ``` use ``` user@host# set netconf ssh port ``` Install php to your server: ``` sudo apt-get install php-cli php ``` #### Process 1. Configure the router in the ```fastnetmon_juniper.php``` file ``` $cfg['hostname'] = "10.0.0.1"; // Juniper IP $cfg['port'] = 880; //NETCONF Port $cfg['username'] = "user"; //user $cfg['password'] = "password"; //pass ``` 2. Change the ```notify_about_attack.sh``` with the new to run the PHP script This is the first buggy version, you are welcome to add more features. 3. Set executable bit ```sudo chmod +x /etc/fastnetmon/scripts/notify_about_attack.sh``` 4. For FastNetMon Advanced, please disable details: ``` sudo fcli set main notify_script_pass_details disable sudo fcli commit ``` Changelog --------- v1.0 - 5 Dec 18 - Initial version Author: Christian David Based on Mikrotik Plugin by Maximiliano Dobladez fastnetmon-1.1.4/src/juniper_plugin/fastnetmon_juniper.php000066400000000000000000000102371343111404700241400ustar00rootroot00000000000000#!/usr/bin/php * * Credits for the Netconf API By Juniper/netconf-php * Script based on Mikrotik Plugin by Maximiliano Dobladez * * Made based on a MX5 CLI and not tested yet, please feedback-us in Issues on github * * LICENSE: GPLv2 GNU GENERAL PUBLIC LICENSE * * * v1.0 - 5 Dec 18 - initial version ******************************/ define( "_VER", '1.0' ); $date = date("Y-m-d H:i:s", time()); // You need to enable NETCONF on your juniper // https://www.juniper.net/documentation/en_US/junos/topics/task/configuration/netconf-ssh-connection-establishing.html#task-netconf-service-over-ssh-enabling $cfg['hostname'] = "10.0.0.1"; // Juniper IP $cfg['port'] = 880; //NETCONF Port $cfg['username'] = "user"; //user $cfg['password'] = "password"; //pass /* PARAMS( $argv[1] = STRING (IP) $argv[2] = STRING (ATTACK DIRECTION) $argv[3] = STRING (PPS) $argv[4] = STRING (ACTION = BAN OR UNBAN) ) */ $IP_ATTACK = $argv[ 1 ]; $DIRECTION_ATTACK = $argv[ 2 ]; $POWER_ATTACK = $argv[ 3 ]; $ACTION_ATTACK = $argv[ 4 ]; if ( $argc <= 4 ) { $msg .= "Juniper API Integration for FastNetMon - Ver: " . _VER . "\n"; $msg .= "missing arguments"; $msg .= "php fastnetmon_juniper.php [IP] [data_direction] [pps_as_string] [action] \n"; echo $msg; exit( 1 ); } //NOTE help if ( $argv[ 1 ] == "help" ) { $msg = "Juniper API Integration for FastNetMon - Ver: " . _VER; echo $msg; exit( 1 ); } require_once "netconf/netconf/Device.php"; $conn = new Device($cfg); switch($ACTION_ATTACK){ case 'ban': try{ $desc = 'FastNetMon Guard: IP '. $IP_ATTACK .' unblocked because '. $DIRECTION_ATTACK .' attack with power '. $POWER_ATTACK .' pps | at '.$fecha_now; $conn->connect(); //Try conect or catch NetconfException (Wrong username, Timeout, Device not found, etc) $locked = $conn->lock_config(); //Equivalent of "configure exclusive" on Juniper CLI if($locked){ //Community 65535:666 = BLACKHOLE $conn->load_set_configuration("set routing-options static route {$IP_ATTACK} community 65535:666 discard"); $conn->commit(); } $conn->unlock(); //Unlock the CLI $conn->close(); //Close the connection _log($desc); } catch(NetconfException $e){ $msg = "Couldn't connect to " . $cfg['hostname'] . '\nLOG: '.$e; _log( $msg ); echo $msg; exit( 1 ); } break; case 'unban': try{ $desc = 'FastNetMon Guard: IP '. $IP_ATTACK .' remove from blacklist.'; $conn->connect(); //Try conect or catch NetconfException (Wrong username, Timeout, Device not found, etc) $locked = $conn->lock_config(); //Equivalent of "configure exclusive" on Juniper CLI if($locked){ $conn->load_set_configuration("delete routing-options static route {$IP_ATTACK}/32"); $conn->commit(); } $conn->unlock(); //Unlock the CLI $conn->close(); //Close the connection _log($desc); } catch(NetconfException $e){ $msg = "Couldn't connect to " . $cfg['hostname'] . '\nLOG: '.$e; _log( $msg ); echo $msg; exit( 1 ); } break; default: $msg = "Juniper API Integration for FastNetMon - Ver: " . _VER; echo $msg; exit( 1 ); break; } /** * [_log Write a log file] * @param [type] $msg [text to log] * @return [type] */ function _log( $msg ) { $FILE_LOG_TMP = "/tmp/fastnetmon_api_juniper.log"; if ( !file_exists( $FILE_LOG_TMP ) ) exec( "echo `date` \"- [FASTNETMON] - " . $msg . " \" > " . $FILE_LOG_TMP ); else exec( "echo `date` \"- [FASTNETMON] - " . $msg . " \" >> " . $FILE_LOG_TMP ); } ?> fastnetmon-1.1.4/src/juniper_plugin/netconf/000077500000000000000000000000001343111404700211465ustar00rootroot00000000000000fastnetmon-1.1.4/src/juniper_plugin/notify_about_attack.sh000077500000000000000000000006321343111404700241030ustar00rootroot00000000000000#!/usr/bin/env bash # # Fastnetmon: Juniper plugin # # Author: - info@mkesolutions.net - http://maxid.com.ar # Modified by Christian David for juniper implementation # # This script will get following params: # $1 client_ip_as_string # $2 data_direction # $3 pps_as_string # $4 action (ban or unban) php -f /opt/fastnetmon/fastnetmon_juniper.php $1 $2 $3 $4 exit 0 fastnetmon-1.1.4/src/libpatricia/000077500000000000000000000000001343111404700167435ustar00rootroot00000000000000fastnetmon-1.1.4/src/libpatricia/copyright000066400000000000000000000023451343111404700207020ustar00rootroot00000000000000$Id: COPYRIGHT,v 1.1.1.1 2013/08/15 18:46:09 labovit Exp $ Copyright (c) 1999-2013 The Regents of the University of Michigan ("The Regents") and Merit Network, Inc. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 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 HOLDER 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.fastnetmon-1.1.4/src/libpatricia/credits.txt000066400000000000000000000054351343111404700211500ustar00rootroot00000000000000 [newtool.gif] MRT Credits The Multi-Threaded Routing Toolkit _________________________________________________________________ MRT was developed by [1]Merit Network, Inc., under National Science Foundation grant NCR-9318902, "Experimentation with Routing Technology to be Used for Inter-Domain Routing in the Internet." Current MRT Staff * [2]Craig Labovitz * [3]Makaki Hirabaru * [4]Farnam Jahanian * Susan Hares * Susan R. Harris * Nathan Binkert * Gerald Winters Project Alumni * [5]Marc Unangst * John Scudder The BGP4+ extension was originally written by Francis Dupont . The public domain Struct C-library of linked list, hash table and memory allocation routines was developed by Jonathan Dekock . Susan Rebecca Harris provided help with the documentation. David Ward provided bug fixes and helpful suggestions. Some sections of code and architecture ideas were taken from the GateD routing daemon. The first port to Linux with IPv6 was done by Pedro Roque . Some interface routines to the Linux kernel were originally written by him. Alexey Kuznetsov made enhancements to 1.4.3a and fixed the Linux kernel intarface. Linux's netlink interface was written, referring to his code "iproute2". We would also like to thank our other colleagues in Japan, Portugal, the Netherlands, the UK, and the US for their many contributions to the MRT development effort. _________________________________________________________________ Cisco is a registered trademark of Cisco Systems Inc. _________________________________________________________________ Merit Network 4251 Plymouth Road Suite C Ann Arbor, MI 48105-2785 734-764-9430 info@merit.edu _________________________________________________________________ 1999 Merit Network, Inc. [6]www@merit.edu References 1. http://www.merit.edu/ 2. http://www.merit.edu/~labovit 3. http://www.merit.edu/~masaki 4. http://www.eecs.umich.edu/~farnam 5. http://www.contrib.andrew.cmu.edu/~mju/ 6. mailto:www@merit.edu fastnetmon-1.1.4/src/libpatricia/patricia.c000066400000000000000000000611351343111404700207110ustar00rootroot00000000000000/* * $Id: patricia.c,v 1.7 2005/12/07 20:46:41 dplonka Exp $ * Dave Plonka * * This product includes software developed by the University of Michigan, * Merit Network, Inc., and their contributors. * * This file had been called "radix.c" in the MRT sources. * * I renamed it to "patricia.c" since it's not an implementation of a general * radix trie. Also I pulled in various requirements from "prefix.c" and * "demo.c" so that it could be used as a standalone API. */ static char copyright[] = "This product includes software developed by the University of Michigan, Merit" "Network, Inc., and their contributors."; #include /* assert */ #include /* isdigit */ #include /* errno */ #include /* sin */ #include /* NULL */ #include /* sprintf, fprintf, stderr */ #include /* free, atol, calloc */ #include /* memcpy, strchr, strlen */ #include /* BSD: for inet_addr */ #include /* BSD, Linux: for inet_addr */ #include /* BSD, Linux: for inet_addr */ #include /* BSD, Linux, Solaris: for inet_addr */ #include "patricia.h" #define Delete free /* { from prefix.c */ /* prefix_tochar * convert prefix information to bytes */ u_char * prefix_tochar (prefix_t * prefix) { if (prefix == NULL) return (NULL); return ((u_char *) & prefix->add.sin); } int comp_with_mask (void *addr, void *dest, u_int mask) { if ( /* mask/8 == 0 || */ memcmp (addr, dest, mask / 8) == 0) { int n = mask / 8; int m = ((-1) << (8 - (mask % 8))); if (mask % 8 == 0 || (((u_char *)addr)[n] & m) == (((u_char *)dest)[n] & m)) return (1); } return (0); } /* this allows imcomplete prefix */ int my_inet_pton (int af, const char *src, void *dst) { if (af == AF_INET) { int i, c, val; u_char xp[sizeof(struct in_addr)] = {0, 0, 0, 0}; for (i = 0; ; i++) { c = *src++; if (!isdigit (c)) return (-1); val = 0; do { val = val * 10 + c - '0'; if (val > 255) return (0); c = *src++; } while (c && isdigit (c)); xp[i] = val; if (c == '\0') break; if (c != '.') return (0); if (i >= 3) return (0); } memcpy (dst, xp, sizeof(struct in_addr)); return (1); #ifdef HAVE_IPV6 } else if (af == AF_INET6) { return (inet_pton (af, src, dst)); #endif /* HAVE_IPV6 */ } else { #ifndef NT errno = EAFNOSUPPORT; #endif /* NT */ return -1; } } #define PATRICIA_MAX_THREADS 16 /* * convert prefix information to ascii string with length * thread safe and (almost) re-entrant implementation */ char * prefix_toa2x (prefix_t *prefix, char *buff, int with_len) { if (prefix == NULL) return ("(Null)"); assert (prefix->ref_count >= 0); if (buff == NULL) { struct buffer { char buffs[PATRICIA_MAX_THREADS][48+5]; u_int i; } *buffp; # if 0 THREAD_SPECIFIC_DATA (struct buffer, buffp, 1); # else { /* for scope only */ static struct buffer local_buff; buffp = &local_buff; } # endif if (buffp == NULL) { /* XXX should we report an error? */ return (NULL); } buff = buffp->buffs[buffp->i++%PATRICIA_MAX_THREADS]; } if (prefix->family == AF_INET) { u_char *a; assert (prefix->bitlen <= sizeof(struct in_addr) * 8); a = prefix_touchar (prefix); if (with_len) { sprintf (buff, "%d.%d.%d.%d/%d", a[0], a[1], a[2], a[3], prefix->bitlen); } else { sprintf (buff, "%d.%d.%d.%d", a[0], a[1], a[2], a[3]); } return (buff); } #ifdef HAVE_IPV6 else if (prefix->family == AF_INET6) { char *r; r = (char *) inet_ntop (AF_INET6, &prefix->add.sin6, buff, 48 /* a guess value */ ); if (r && with_len) { assert (prefix->bitlen <= sizeof(struct in6_addr) * 8); sprintf (buff + strlen (buff), "/%d", prefix->bitlen); } return (buff); } #endif /* HAVE_IPV6 */ else return (NULL); } /* prefix_toa2 * convert prefix information to ascii string */ char * prefix_toa2 (prefix_t *prefix, char *buff) { return (prefix_toa2x (prefix, buff, 0)); } /* prefix_toa */ char * prefix_toa (prefix_t * prefix) { return (prefix_toa2 (prefix, (char *) NULL)); } prefix_t * New_Prefix2 (int family, void *dest, int bitlen, prefix_t *prefix) { int dynamic_allocated = 0; int default_bitlen = sizeof(struct in_addr) * 8; #ifdef HAVE_IPV6 if (family == AF_INET6) { default_bitlen = sizeof(struct in6_addr) * 8; if (prefix == NULL) { prefix = calloc(1, sizeof (prefix_t)); dynamic_allocated++; } memcpy (&prefix->add.sin6, dest, sizeof(struct in6_addr)); } else #endif /* HAVE_IPV6 */ if (family == AF_INET) { if (prefix == NULL) { #ifndef NT prefix = calloc(1, sizeof (prefix4_t)); #else //for some reason, compiler is getting //prefix4_t size incorrect on NT prefix = calloc(1, sizeof (prefix_t)); #endif /* NT */ dynamic_allocated++; } memcpy (&prefix->add.sin, dest, sizeof(struct in_addr)); } else { return (NULL); } prefix->bitlen = (bitlen >= 0)? bitlen: default_bitlen; prefix->family = family; prefix->ref_count = 0; if (dynamic_allocated) { prefix->ref_count++; } /* fprintf(stderr, "[C %s, %d]\n", prefix_toa (prefix), prefix->ref_count); */ return (prefix); } prefix_t * New_Prefix (int family, void *dest, int bitlen) { return (New_Prefix2 (family, dest, bitlen, NULL)); } /* ascii2prefix */ prefix_t * ascii2prefix (int family, char *string) { u_long bitlen, maxbitlen = 0; char *cp; struct in_addr sin; #ifdef HAVE_IPV6 struct in6_addr sin6; #endif /* HAVE_IPV6 */ int result; char save[MAXLINE]; if (string == NULL) return (NULL); /* easy way to handle both families */ if (family == 0) { family = AF_INET; #ifdef HAVE_IPV6 if (strchr (string, ':')) family = AF_INET6; #endif /* HAVE_IPV6 */ } if (family == AF_INET) { maxbitlen = sizeof(struct in_addr) * 8; } #ifdef HAVE_IPV6 else if (family == AF_INET6) { maxbitlen = sizeof(struct in6_addr) * 8; } #endif /* HAVE_IPV6 */ if ((cp = strchr (string, '/')) != NULL) { bitlen = atol (cp + 1); /* *cp = '\0'; */ /* copy the string to save. Avoid destroying the string */ assert (cp - string < MAXLINE); memcpy (save, string, cp - string); save[cp - string] = '\0'; string = save; if (bitlen < 0 || bitlen > maxbitlen) bitlen = maxbitlen; } else { bitlen = maxbitlen; } if (family == AF_INET) { if ((result = my_inet_pton (AF_INET, string, &sin)) <= 0) return (NULL); return (New_Prefix (AF_INET, &sin, bitlen)); } #ifdef HAVE_IPV6 else if (family == AF_INET6) { // Get rid of this with next IPv6 upgrade #if defined(NT) && !defined(HAVE_INET_NTOP) inet6_addr(string, &sin6); return (New_Prefix (AF_INET6, &sin6, bitlen)); #else if ((result = inet_pton (AF_INET6, string, &sin6)) <= 0) return (NULL); #endif /* NT */ return (New_Prefix (AF_INET6, &sin6, bitlen)); } #endif /* HAVE_IPV6 */ else return (NULL); } prefix_t * Ref_Prefix (prefix_t * prefix) { if (prefix == NULL) return (NULL); if (prefix->ref_count == 0) { /* make a copy in case of a static prefix */ return (New_Prefix2 (prefix->family, &prefix->add, prefix->bitlen, NULL)); } prefix->ref_count++; /* fprintf(stderr, "[A %s, %d]\n", prefix_toa (prefix), prefix->ref_count); */ return (prefix); } void Deref_Prefix (prefix_t * prefix) { if (prefix == NULL) return; /* for secure programming, raise an assert. no static prefix can call this */ assert (prefix->ref_count > 0); prefix->ref_count--; assert (prefix->ref_count >= 0); if (prefix->ref_count <= 0) { Delete (prefix); return; } } /* } */ /* #define PATRICIA_DEBUG 1 */ static int num_active_patricia = 0; /* these routines support continuous mask only */ patricia_tree_t * New_Patricia (int maxbits) { patricia_tree_t *patricia = calloc(1, sizeof *patricia); patricia->maxbits = maxbits; patricia->head = NULL; patricia->num_active_node = 0; assert (maxbits <= PATRICIA_MAXBITS); /* XXX */ num_active_patricia++; return (patricia); } /* * if func is supplied, it will be called as func(node->data) * before deleting the node */ void Clear_Patricia (patricia_tree_t *patricia, void_fn_t func) { assert (patricia); if (patricia->head) { patricia_node_t *Xstack[PATRICIA_MAXBITS+1]; patricia_node_t **Xsp = Xstack; patricia_node_t *Xrn = patricia->head; while (Xrn) { patricia_node_t *l = Xrn->l; patricia_node_t *r = Xrn->r; if (Xrn->prefix) { Deref_Prefix (Xrn->prefix); if (Xrn->data && func) func (Xrn->data); } else { assert (Xrn->data == NULL); } Delete (Xrn); patricia->num_active_node--; if (l) { if (r) { *Xsp++ = r; } Xrn = l; } else if (r) { Xrn = r; } else if (Xsp != Xstack) { Xrn = *(--Xsp); } else { Xrn = NULL; } } } assert (patricia->num_active_node == 0); /* Delete (patricia); */ } void Destroy_Patricia (patricia_tree_t *patricia, void_fn_t func) { Clear_Patricia (patricia, func); Delete (patricia); num_active_patricia--; } /* * if func is supplied, it will be called as func(node->prefix, node->data) */ void patricia_process (patricia_tree_t *patricia, void_fn_t func) { patricia_node_t *node; assert (func); PATRICIA_WALK (patricia->head, node) { func (node->prefix, node->data); } PATRICIA_WALK_END; } size_t patricia_walk_inorder(patricia_node_t *node, void_fn_t func) { size_t n = 0; assert(func); if (node->l) { n += patricia_walk_inorder(node->l, func); } if (node->prefix) { func(node->prefix, node->data); n++; } if (node->r) { n += patricia_walk_inorder(node->r, func); } return n; } patricia_node_t * patricia_search_exact (patricia_tree_t *patricia, prefix_t *prefix) { patricia_node_t *node; u_char *addr; u_int bitlen; assert (patricia); assert (prefix); assert (prefix->bitlen <= patricia->maxbits); if (patricia->head == NULL) return (NULL); node = patricia->head; addr = prefix_touchar (prefix); bitlen = prefix->bitlen; while (node->bit < bitlen) { if (BIT_TEST (addr[node->bit >> 3], 0x80 >> (node->bit & 0x07))) { #ifdef PATRICIA_DEBUG if (node->prefix) fprintf (stderr, "patricia_search_exact: take right %s/%d\n", prefix_toa (node->prefix), node->prefix->bitlen); else fprintf (stderr, "patricia_search_exact: take right at %u\n", node->bit); #endif /* PATRICIA_DEBUG */ node = node->r; } else { #ifdef PATRICIA_DEBUG if (node->prefix) fprintf (stderr, "patricia_search_exact: take left %s/%d\n", prefix_toa (node->prefix), node->prefix->bitlen); else fprintf (stderr, "patricia_search_exact: take left at %u\n", node->bit); #endif /* PATRICIA_DEBUG */ node = node->l; } if (node == NULL) return (NULL); } #ifdef PATRICIA_DEBUG if (node->prefix) fprintf (stderr, "patricia_search_exact: stop at %s/%d\n", prefix_toa (node->prefix), node->prefix->bitlen); else fprintf (stderr, "patricia_search_exact: stop at %u\n", node->bit); #endif /* PATRICIA_DEBUG */ if (node->bit > bitlen || node->prefix == NULL) return (NULL); assert (node->bit == bitlen); assert (node->bit == node->prefix->bitlen); if (comp_with_mask (prefix_tochar (node->prefix), prefix_tochar (prefix), bitlen)) { #ifdef PATRICIA_DEBUG fprintf (stderr, "patricia_search_exact: found %s/%d\n", prefix_toa (node->prefix), node->prefix->bitlen); #endif /* PATRICIA_DEBUG */ return (node); } return (NULL); } /* if inclusive != 0, "best" may be the given prefix itself */ patricia_node_t * patricia_search_best2 (patricia_tree_t *patricia, prefix_t *prefix, int inclusive) { patricia_node_t *node; patricia_node_t *stack[PATRICIA_MAXBITS + 1]; u_char *addr; u_int bitlen; int cnt = 0; assert (patricia); assert (prefix); assert (prefix->bitlen <= patricia->maxbits); if (patricia->head == NULL) return (NULL); node = patricia->head; addr = prefix_touchar (prefix); bitlen = prefix->bitlen; while (node->bit < bitlen) { if (node->prefix) { #ifdef PATRICIA_DEBUG fprintf (stderr, "patricia_search_best: push %s/%d\n", prefix_toa (node->prefix), node->prefix->bitlen); #endif /* PATRICIA_DEBUG */ stack[cnt++] = node; } if (BIT_TEST (addr[node->bit >> 3], 0x80 >> (node->bit & 0x07))) { #ifdef PATRICIA_DEBUG if (node->prefix) fprintf (stderr, "patricia_search_best: take right %s/%d\n", prefix_toa (node->prefix), node->prefix->bitlen); else fprintf (stderr, "patricia_search_best: take right at %u\n", node->bit); #endif /* PATRICIA_DEBUG */ node = node->r; } else { #ifdef PATRICIA_DEBUG if (node->prefix) fprintf (stderr, "patricia_search_best: take left %s/%d\n", prefix_toa (node->prefix), node->prefix->bitlen); else fprintf (stderr, "patricia_search_best: take left at %u\n", node->bit); #endif /* PATRICIA_DEBUG */ node = node->l; } if (node == NULL) break; } if (inclusive && node && node->prefix) stack[cnt++] = node; #ifdef PATRICIA_DEBUG if (node == NULL) fprintf (stderr, "patricia_search_best: stop at null\n"); else if (node->prefix) fprintf (stderr, "patricia_search_best: stop at %s/%d\n", prefix_toa (node->prefix), node->prefix->bitlen); else fprintf (stderr, "patricia_search_best: stop at %u\n", node->bit); #endif /* PATRICIA_DEBUG */ if (cnt <= 0) return (NULL); while (--cnt >= 0) { node = stack[cnt]; #ifdef PATRICIA_DEBUG fprintf (stderr, "patricia_search_best: pop %s/%d\n", prefix_toa (node->prefix), node->prefix->bitlen); #endif /* PATRICIA_DEBUG */ if (comp_with_mask (prefix_tochar (node->prefix), prefix_tochar (prefix), node->prefix->bitlen) && node->prefix->bitlen <= bitlen) { #ifdef PATRICIA_DEBUG fprintf (stderr, "patricia_search_best: found %s/%d\n", prefix_toa (node->prefix), node->prefix->bitlen); #endif /* PATRICIA_DEBUG */ return (node); } } return (NULL); } patricia_node_t * patricia_search_best (patricia_tree_t *patricia, prefix_t *prefix) { return (patricia_search_best2 (patricia, prefix, 1)); } patricia_node_t * patricia_lookup (patricia_tree_t *patricia, prefix_t *prefix) { patricia_node_t *node, *new_node, *parent, *glue; u_char *addr, *test_addr; u_int bitlen, check_bit, differ_bit; int i, j, r; assert (patricia); assert (prefix); assert (prefix->bitlen <= patricia->maxbits); if (patricia->head == NULL) { node = calloc(1, sizeof *node); node->bit = prefix->bitlen; node->prefix = Ref_Prefix (prefix); node->parent = NULL; node->l = node->r = NULL; node->data = NULL; patricia->head = node; #ifdef PATRICIA_DEBUG fprintf (stderr, "patricia_lookup: new_node #0 %s/%d (head)\n", prefix_toa (prefix), prefix->bitlen); #endif /* PATRICIA_DEBUG */ patricia->num_active_node++; return (node); } addr = prefix_touchar (prefix); bitlen = prefix->bitlen; node = patricia->head; while (node->bit < bitlen || node->prefix == NULL) { if (node->bit < patricia->maxbits && BIT_TEST (addr[node->bit >> 3], 0x80 >> (node->bit & 0x07))) { if (node->r == NULL) break; #ifdef PATRICIA_DEBUG if (node->prefix) fprintf (stderr, "patricia_lookup: take right %s/%d\n", prefix_toa (node->prefix), node->prefix->bitlen); else fprintf (stderr, "patricia_lookup: take right at %u\n", node->bit); #endif /* PATRICIA_DEBUG */ node = node->r; } else { if (node->l == NULL) break; #ifdef PATRICIA_DEBUG if (node->prefix) fprintf (stderr, "patricia_lookup: take left %s/%d\n", prefix_toa (node->prefix), node->prefix->bitlen); else fprintf (stderr, "patricia_lookup: take left at %u\n", node->bit); #endif /* PATRICIA_DEBUG */ node = node->l; } assert (node); } assert (node->prefix); #ifdef PATRICIA_DEBUG fprintf (stderr, "patricia_lookup: stop at %s/%d\n", prefix_toa (node->prefix), node->prefix->bitlen); #endif /* PATRICIA_DEBUG */ test_addr = prefix_touchar (node->prefix); /* find the first bit different */ check_bit = (node->bit < bitlen)? node->bit: bitlen; differ_bit = 0; for (i = 0; i*8 < check_bit; i++) { if ((r = (addr[i] ^ test_addr[i])) == 0) { differ_bit = (i + 1) * 8; continue; } /* I know the better way, but for now */ for (j = 0; j < 8; j++) { if (BIT_TEST (r, (0x80 >> j))) break; } /* must be found */ assert (j < 8); differ_bit = i * 8 + j; break; } if (differ_bit > check_bit) differ_bit = check_bit; #ifdef PATRICIA_DEBUG fprintf (stderr, "patricia_lookup: differ_bit %d\n", differ_bit); #endif /* PATRICIA_DEBUG */ parent = node->parent; while (parent && parent->bit >= differ_bit) { node = parent; parent = node->parent; #ifdef PATRICIA_DEBUG if (node->prefix) fprintf (stderr, "patricia_lookup: up to %s/%d\n", prefix_toa (node->prefix), node->prefix->bitlen); else fprintf (stderr, "patricia_lookup: up to %u\n", node->bit); #endif /* PATRICIA_DEBUG */ } if (differ_bit == bitlen && node->bit == bitlen) { if (node->prefix) { #ifdef PATRICIA_DEBUG fprintf (stderr, "patricia_lookup: found %s/%d\n", prefix_toa (node->prefix), node->prefix->bitlen); #endif /* PATRICIA_DEBUG */ return (node); } node->prefix = Ref_Prefix (prefix); #ifdef PATRICIA_DEBUG fprintf (stderr, "patricia_lookup: new node #1 %s/%d (glue mod)\n", prefix_toa (prefix), prefix->bitlen); #endif /* PATRICIA_DEBUG */ assert (node->data == NULL); return (node); } new_node = calloc(1, sizeof *new_node); new_node->bit = prefix->bitlen; new_node->prefix = Ref_Prefix (prefix); new_node->parent = NULL; new_node->l = new_node->r = NULL; new_node->data = NULL; patricia->num_active_node++; if (node->bit == differ_bit) { new_node->parent = node; if (node->bit < patricia->maxbits && BIT_TEST (addr[node->bit >> 3], 0x80 >> (node->bit & 0x07))) { assert (node->r == NULL); node->r = new_node; } else { assert (node->l == NULL); node->l = new_node; } #ifdef PATRICIA_DEBUG fprintf (stderr, "patricia_lookup: new_node #2 %s/%d (child)\n", prefix_toa (prefix), prefix->bitlen); #endif /* PATRICIA_DEBUG */ return (new_node); } if (bitlen == differ_bit) { if (bitlen < patricia->maxbits && BIT_TEST (test_addr[bitlen >> 3], 0x80 >> (bitlen & 0x07))) { new_node->r = node; } else { new_node->l = node; } new_node->parent = node->parent; if (node->parent == NULL) { assert (patricia->head == node); patricia->head = new_node; } else if (node->parent->r == node) { node->parent->r = new_node; } else { node->parent->l = new_node; } node->parent = new_node; #ifdef PATRICIA_DEBUG fprintf (stderr, "patricia_lookup: new_node #3 %s/%d (parent)\n", prefix_toa (prefix), prefix->bitlen); #endif /* PATRICIA_DEBUG */ } else { glue = calloc(1, sizeof *glue); glue->bit = differ_bit; glue->prefix = NULL; glue->parent = node->parent; glue->data = NULL; patricia->num_active_node++; if (differ_bit < patricia->maxbits && BIT_TEST (addr[differ_bit >> 3], 0x80 >> (differ_bit & 0x07))) { glue->r = new_node; glue->l = node; } else { glue->r = node; glue->l = new_node; } new_node->parent = glue; if (node->parent == NULL) { assert (patricia->head == node); patricia->head = glue; } else if (node->parent->r == node) { node->parent->r = glue; } else { node->parent->l = glue; } node->parent = glue; #ifdef PATRICIA_DEBUG fprintf (stderr, "patricia_lookup: new_node #4 %s/%d (glue+node)\n", prefix_toa (prefix), prefix->bitlen); #endif /* PATRICIA_DEBUG */ } return (new_node); } void patricia_remove (patricia_tree_t *patricia, patricia_node_t *node) { patricia_node_t *parent, *child; assert (patricia); assert (node); if (node->r && node->l) { #ifdef PATRICIA_DEBUG fprintf (stderr, "patricia_remove: #0 %s/%d (r & l)\n", prefix_toa (node->prefix), node->prefix->bitlen); #endif /* PATRICIA_DEBUG */ /* this might be a placeholder node -- have to check and make sure * there is a prefix aossciated with it ! */ if (node->prefix != NULL) Deref_Prefix (node->prefix); node->prefix = NULL; /* Also I needed to clear data pointer -- masaki */ node->data = NULL; return; } if (node->r == NULL && node->l == NULL) { #ifdef PATRICIA_DEBUG fprintf (stderr, "patricia_remove: #1 %s/%d (!r & !l)\n", prefix_toa (node->prefix), node->prefix->bitlen); #endif /* PATRICIA_DEBUG */ parent = node->parent; Deref_Prefix (node->prefix); Delete (node); patricia->num_active_node--; if (parent == NULL) { assert (patricia->head == node); patricia->head = NULL; return; } if (parent->r == node) { parent->r = NULL; child = parent->l; } else { assert (parent->l == node); parent->l = NULL; child = parent->r; } if (parent->prefix) return; /* we need to remove parent too */ if (parent->parent == NULL) { assert (patricia->head == parent); patricia->head = child; } else if (parent->parent->r == parent) { parent->parent->r = child; } else { assert (parent->parent->l == parent); parent->parent->l = child; } child->parent = parent->parent; Delete (parent); patricia->num_active_node--; return; } #ifdef PATRICIA_DEBUG fprintf (stderr, "patricia_remove: #2 %s/%d (r ^ l)\n", prefix_toa (node->prefix), node->prefix->bitlen); #endif /* PATRICIA_DEBUG */ if (node->r) { child = node->r; } else { assert (node->l); child = node->l; } parent = node->parent; child->parent = parent; Deref_Prefix (node->prefix); Delete (node); patricia->num_active_node--; if (parent == NULL) { assert (patricia->head == node); patricia->head = child; return; } if (parent->r == node) { parent->r = child; } else { assert (parent->l == node); parent->l = child; } } /* { from demo.c */ patricia_node_t * make_and_lookup (patricia_tree_t *tree, char *string) { prefix_t *prefix; patricia_node_t *node; prefix = ascii2prefix (AF_INET, string); // printf ("make_and_lookup: %s/%d\n", prefix_toa (prefix), prefix->bitlen); node = patricia_lookup (tree, prefix); Deref_Prefix (prefix); return (node); } patricia_node_t * make_and_lookup_ipv6 (patricia_tree_t *tree, char *string) { prefix_t *prefix; patricia_node_t *node; prefix = ascii2prefix (AF_INET6, string); // printf ("make_and_lookup: %s/%d\n", prefix_toa (prefix), prefix->bitlen); node = patricia_lookup (tree, prefix); Deref_Prefix (prefix); return (node); } patricia_node_t * try_search_exact (patricia_tree_t *tree, char *string) { prefix_t *prefix; patricia_node_t *node; prefix = ascii2prefix (AF_INET, string); //printf ("try_search_exact: %s/%d\n", prefix_toa (prefix), prefix->bitlen); if ((node = patricia_search_exact (tree, prefix)) == NULL) { //printf ("try_search_exact: not found\n"); } else { //printf ("try_search_exact: %s/%d found\n", //prefix_toa (node->prefix), node->prefix->bitlen); } Deref_Prefix (prefix); return (node); } void lookup_then_remove (patricia_tree_t *tree, char *string) { patricia_node_t *node; if ((node = try_search_exact (tree, string))) patricia_remove (tree, node); } patricia_node_t * try_search_best (patricia_tree_t *tree, char *string) { prefix_t *prefix; patricia_node_t *node; prefix = ascii2prefix (AF_INET, string); //printf ("try_search_best: %s/%d\n", prefix_toa (prefix), prefix->bitlen); if ((node = patricia_search_best (tree, prefix)) == NULL) { //printf ("try_search_best: not found\n"); } else { //printf ("try_search_best: %s/%d found\n", //prefix_toa (node->prefix), node->prefix->bitlen); } Deref_Prefix (prefix); return (node); } /* } */ fastnetmon-1.1.4/src/libpatricia/patricia.h000066400000000000000000000107531343111404700207160ustar00rootroot00000000000000#ifdef __cplusplus extern "C" { #endif /* * $Id: patricia.h,v 1.6 2005/12/07 20:53:01 dplonka Exp $ * Dave Plonka * * This product includes software developed by the University of Michigan, * Merit Network, Inc., and their contributors. * * This file had been called "radix.h" in the MRT sources. * * I renamed it to "patricia.h" since it's not an implementation of a general * radix trie. Also, pulled in various requirements from "mrt.h" and added * some other things it could be used as a standalone API. */ #ifndef PATRICIA_H #define PATRICIA_H #define HAVE_IPV6 /* typedef unsigned int u_int; */ typedef void (*void_fn_t)(); /* { from defs.h */ #define prefix_touchar(prefix) ((u_char *)&(prefix)->add.sin) #define MAXLINE 1024 #define BIT_TEST(f, b) ((f) & (b)) /* } */ #define addroute make_and_lookup #include /* for u_* definitions (on FreeBSD 5) */ #include /* for EAFNOSUPPORT */ #ifndef EAFNOSUPPORT # defined EAFNOSUPPORT WSAEAFNOSUPPORT # include #else # include /* for struct in_addr */ #endif #include /* for AF_INET */ /* { from mrt.h */ typedef struct _prefix4_t { u_short family; /* AF_INET | AF_INET6 */ u_short bitlen; /* same as mask? */ int ref_count; /* reference count */ struct in_addr sin; } prefix4_t; typedef struct _prefix_t { u_short family; /* AF_INET | AF_INET6 */ u_short bitlen; /* same as mask? */ int ref_count; /* reference count */ union { struct in_addr sin; #ifdef HAVE_IPV6 struct in6_addr sin6; #endif /* IPV6 */ } add; } prefix_t; /* } */ typedef struct _patricia_node_t { u_int bit; /* flag if this node used */ prefix_t *prefix; /* who we are in patricia tree */ struct _patricia_node_t *l, *r; /* left and right children */ struct _patricia_node_t *parent;/* may be used */ void *data; /* pointer to data */ void *user1; /* pointer to usr data (ex. route flap info) */ } patricia_node_t; typedef struct _patricia_tree_t { patricia_node_t *head; u_int maxbits; /* for IP, 32 bit addresses */ int num_active_node; /* for debug purpose */ } patricia_tree_t; patricia_node_t *patricia_search_exact (patricia_tree_t *patricia, prefix_t *prefix); patricia_node_t *patricia_search_best (patricia_tree_t *patricia, prefix_t *prefix); patricia_node_t * patricia_search_best2 (patricia_tree_t *patricia, prefix_t *prefix, int inclusive); patricia_node_t *patricia_lookup (patricia_tree_t *patricia, prefix_t *prefix); void patricia_remove (patricia_tree_t *patricia, patricia_node_t *node); patricia_tree_t *New_Patricia (int maxbits); void Clear_Patricia (patricia_tree_t *patricia, void_fn_t func); void Destroy_Patricia (patricia_tree_t *patricia, void_fn_t func); void patricia_process (patricia_tree_t *patricia, void_fn_t func); char *prefix_toa (prefix_t * prefix); /* { from demo.c */ prefix_t * ascii2prefix (int family, char *string); patricia_node_t * make_and_lookup (patricia_tree_t *tree, char *string); patricia_node_t * make_and_lookup_ipv6 (patricia_tree_t *tree, char *string); /* } */ #define PATRICIA_MAXBITS (sizeof(struct in6_addr) * 8) #define PATRICIA_NBIT(x) (0x80 >> ((x) & 0x7f)) #define PATRICIA_NBYTE(x) ((x) >> 3) #define PATRICIA_DATA_GET(node, type) (type *)((node)->data) #define PATRICIA_DATA_SET(node, value) ((node)->data = (void *)(value)) #define PATRICIA_WALK(Xhead, Xnode) \ do { \ patricia_node_t *Xstack[PATRICIA_MAXBITS+1]; \ patricia_node_t **Xsp = Xstack; \ patricia_node_t *Xrn = (Xhead); \ while ((Xnode = Xrn)) { \ if (Xnode->prefix) #define PATRICIA_WALK_ALL(Xhead, Xnode) \ do { \ patricia_node_t *Xstack[PATRICIA_MAXBITS+1]; \ patricia_node_t **Xsp = Xstack; \ patricia_node_t *Xrn = (Xhead); \ while ((Xnode = Xrn)) { \ if (1) #define PATRICIA_WALK_BREAK { \ if (Xsp != Xstack) { \ Xrn = *(--Xsp); \ } else { \ Xrn = (patricia_node_t *) 0; \ } \ continue; } #define PATRICIA_WALK_END \ if (Xrn->l) { \ if (Xrn->r) { \ *Xsp++ = Xrn->r; \ } \ Xrn = Xrn->l; \ } else if (Xrn->r) { \ Xrn = Xrn->r; \ } else if (Xsp != Xstack) { \ Xrn = *(--Xsp); \ } else { \ Xrn = (patricia_node_t *) 0; \ } \ } \ } while (0) #endif /* PATRICIA_H */ #ifdef __cplusplus } #endif fastnetmon-1.1.4/src/log4cpp.spec000066400000000000000000000057441343111404700167160ustar00rootroot00000000000000%define RELEASE 4 %define rel %{?CUSTOM_RELEASE} %{!?CUSTOM_RELEASE:%RELEASE} %define lib_name log4cpp %define manualdir /var/www/html/manual/%{name} Name: log4cpp # Fixed by Pavel Odintsov Version: 1.1.1 Release: %rel Summary: Log for C++ License: LGPL Group: Development/Libraries Vendor: Bastiaan Bakker Packager: Cedric Le Goater Url: http://log4cpp.sourceforge.net/ Source: ftp://download.sourceforge.net/pub/sourceforge/log4cpp/%name-%version.tar.gz Prefix: %_prefix BuildRoot: %_tmppath/%name-%version-root %description Log for C++ is a library of classes for flexible logging to files, syslog, and other destinations. It is modeled after the Log for Java library and stays as close to its API as is reasonable. %package devel Summary: development tools for Log for C++ Group: Development/Libraries Requires: %name = %version %description devel The %name-devel package contains the static libraries and header files needed for development with %name. %package doc Summary: HTML formatted API documention for Log for C++ Group: Development/Libraries #can't set doc package to noarch without setting the others as well. #BuildArch: noarch %{!?_without_doxygenrpm:BuildRequires: doxygen} %description doc The %name-doc package contains HTML formatted API documention generated by the popular doxygen documentation generation tool. %prep %{__rm} -rf $RPM_BUILD_ROOT # Fixed by Pavel Odintsov %setup -n log4cpp # Doxygen disabled temporarly by Pavel Odintsov CC=%{__cc} CXX=%{__cxx} ./configure --prefix=%{prefix} --enable-doxygen %build %{__make} %install %{__rm} -rf $RPM_BUILD_ROOT # Space before docdir added by Pavel Odintsov %{__make} prefix=$RPM_BUILD_ROOT%{prefix} docdir=$RPM_BUILD_ROOT/%{manualdir} install %clean %{__rm} -rf $RPM_BUILD_ROOT %ifnos solaris2.8 solaris2.9 solaris2.10 %post -p /sbin/ldconfig %endif %post devel if test "x$RPM_INSTALL_PREFIX0" != "x" ; then %{__perl} -pi -e"s|^libdir='[^\']*'|libdir='$RPM_INSTALL_PREFIX0/lib'|" $RPM_INSTALL_PREFIX0/lib/liblog4cpp.la %{__perl} -pi -e"s|^prefix=\"[^\"]*\"|prefix=\"$RPM_INSTALL_PREFIX0\"|" $RPM_INSTALL_PREFIX0/bin/log4cpp-config fi %ifnos solaris2.8 solaris2.9 solaris2.10 %postun -p /sbin/ldconfig %endif %files %defattr(-,root,root,755) %attr(755,root,root) %prefix/lib/lib*.so.* %doc AUTHORS COPYING INSTALL NEWS README THANKS ChangeLog # Enable filtering of symlinks #%filter_requires_in %prefix/lib/liblog4cpp.so #%filter_setup %files devel %defattr(-,root,root,755) %prefix/include/* %prefix/share/man/* %attr(755,root,root) %prefix/bin/log4cpp-config # All * patterns replaced by Pavel Odintsov # It's symlink #%attr(755,root,root) %prefix/lib/liblog4cpp.so %attr(755,root,root) %prefix/lib/liblog4cpp.so.5.0.6 %attr(644,root,root) %prefix/lib/liblog4cpp.a %attr(644,root,root) %prefix/lib/liblog4cpp.la %attr(644,root,root) %prefix/lib/pkgconfig/log4cpp.pc %attr(644,root,root) %prefix/share/aclocal/*.m4 %files doc %defattr(-,root,root) %doc %{manualdir} fastnetmon-1.1.4/src/man/000077500000000000000000000000001343111404700152335ustar00rootroot00000000000000fastnetmon-1.1.4/src/man/fastnetmon.1000066400000000000000000000015671343111404700175040ustar00rootroot00000000000000.\" Manpage for fastnetmon. .\" Contact pavel.odintsov@gmail.com to correct errors or typos. .TH man 1 "04 Jun 2015" "1.1.2" "fastnetmon man page" .SH NAME FastNetMon \- a high performance DoS/DDoS load analyzer built on top of multiple packet capture engines .SH SYNOPSIS fastnetmon [--daemonize] .SH DESCRIPTION FastNetMon - a high performance DoS/DDoS load analyzer built on top of multiple packet capture engines (NetFlow, IPFIX, sFLOW, netmap, PF_RING, PCAP). For more information about configuration, please look at the comments in /etc/fastnetmon.conf and check the project GitHub page: https://github.com/pavel-odintsov/fastnetmon. .SH OPTIONS fastnetmon has only a single command line option --daemonize which is used for forking and detaching it from the terminal. .SH SEE ALSO fastnetmon_client(1) .SH BUGS No known bugs. .SH AUTHOR Pavel Odintsov (pavel.odintsov@gmail.com) fastnetmon-1.1.4/src/man/fastnetmon_client.1000066400000000000000000000007661343111404700210420ustar00rootroot00000000000000.\" Manpage for fastnetmon_client. .\" Contact pavel.odintsov@gmail.com to correct errors or typos. .TH man 1 "04 Jun 2015" "1.1.2" "fastnetmon_client man page" .SH NAME fastnetmon_client \- show information about top talkers and detected DDoS attacks .SH SYNOPSIS fastnetmon_client .SH DESCRIPTION Client interface for monitoring fastnetmon(1) DDoS detection toolkit. .SH OPTIONS No options here .SH SEE ALSO fastnetmon(1) .SH BUGS No known bugs. .SH AUTHOR Pavel Odintsov (pavel.odintsov@gmail.com) fastnetmon-1.1.4/src/mikrotik_plugin/000077500000000000000000000000001343111404700176675ustar00rootroot00000000000000fastnetmon-1.1.4/src/mikrotik_plugin/README.md000066400000000000000000000030531343111404700211470ustar00rootroot00000000000000MikroTik FastNetMon plug-in =========== Overview -------- Connects to a MikroTik router and adds or removes a blackhole rule for an attack by IP address. The actions can be modified such as adding a firewall rule. This script uses the MikroTik PHP API. More information about this can be found at the following URLs: * http://www.mikrotik.com * http://wiki.mikrotik.com/wiki/API_PHP_class Installation ------------ #### Prerequisite You must have a user with API access on the router Install php to your server: ``` sudo apt-get install php-cli php ``` #### Process 1. Configure the router in the ```fastnetmon_mikrotik.php``` file ``` $cfg[ ip_mikrotik ] = "192.168.10.1"; // MikroTik Router IP $cfg[ api_user ] = "api"; // username $cfg[ api_pass ] = "api123"; // password ``` 2. Change the ```notify_about_attack.sh``` with the new to run the PHP script This is the first version, you are welcome to add more features. 3. Set executable bit ```sudo chmod +x /etc/fastnetmon/scripts/notify_about_attack.sh``` 4. For FastNetMon Advanced, specify this script as callback: ``` sudo fcli set main notify_script_enabled enable sudo fcli set main notify_script_path /etc/fastnetmon/scripts/notify_about_attack.sh sudo fcli set main notify_script_format text sudo fcli commit ``` And disable passing details to this script: ``` sudo fcli set main notify_script_pass_details disable sudo fcli commit ``` Changelog --------- v1.0 - 4 Jul 16 - Initial version Author: Maximiliano Dobladez info@mkesolutions.net http://maxid.com.ar | http://www.mkesolutions.net fastnetmon-1.1.4/src/mikrotik_plugin/fastnetmon_mikrotik.php000066400000000000000000000072321343111404700244730ustar00rootroot00000000000000#!/usr/bin/php debug = true; if ( $API->connect( $cfg[ ip_mikrotik ], $cfg[ api_user ], $cfg[ api_pass ] ) ) { //add Blocking by route blackhole if ( $ACTION_ATTACK == "ban" ) { $comment_rule = 'FastNetMon Guard: IP ' . $IP_ATTACK . ' blocked because ' . $DIRECTION_ATTACK . ' attack with power ' . $POWER_ATTACK . ' pps | at '.$fecha_now; $API->write( '/ip/route/add', false ); $API->write( '=dst-address=' . $IP_ATTACK, false ); $API->write( '=type=blackhole', false ); $API->write( '=bgp-communities=65535:666', false ); $API->write( '=comment=' . $comment_rule ); // Log to router syslog. Useful for alerting and Graylog reporting $API->write( '/log/info', false ); $API->write( '=message=' . $comment_rule ); $ret = $API->read(); } if ( $ACTION_ATTACK == "unban" ) { // remove the blackhole rule $comment_rule = 'FastNetMon Guard: IP ' . $IP_ATTACK . ' remove from blacklist '; $API->write( '/ip/route/print', false ); $API->write( '?dst-address=' . $IP_ATTACK . "/32" ); $ID_ARRAY = $API->read(); $API->write( '/ip/route/remove', false ); $API->write( '=.id=' . $ID_ARRAY[ 0 ][ '.id' ] ); // Log to router syslog. Useful for alerting and Graylog reporting $API->write( '/log/info', false ); $API->write( '=message=' . $comment_rule ); $ret = $API->read(); } if ($ret) _log( $comment_rule ); } else { // can't connect $msg = "Couldn't connect to " . $cfg[ ip_mikrotik ]; _log( $msg ); echo $msg; exit( 1 ); } /** * [_log Write a log file] * @param [type] $msg [text to log] * @return [type] */ function _log( $msg ) { $FILE_LOG_TMP = "/tmp/fastnetmon_api_mikrotik.log"; if ( !file_exists( $FILE_LOG_TMP ) ) exec( "echo `date` \"- [FASTNETMON] - " . $msg . " \" > " . $FILE_LOG_TMP ); else exec( "echo `date` \"- [FASTNETMON] - " . $msg . " \" >> " . $FILE_LOG_TMP ); } ?> fastnetmon-1.1.4/src/mikrotik_plugin/notify_about_attack.sh000077500000000000000000000005451343111404700242630ustar00rootroot00000000000000#!/usr/bin/env bash # # Fastnetmon: MikroTik RouterOS plugin # # by Maximiliano Dobladez - info@mkesolutions.net - http://maxid.com.ar # # This script will get following params: # $1 client_ip_as_string # $2 data_direction # $3 pps_as_string # $4 action (ban or unban) php -f /opt/fastnetmon/fastnetmon_mikrotik.php $1 $2 $3 $4 exit 0 fastnetmon-1.1.4/src/mikrotik_plugin/routeros_api.php000066400000000000000000000342311343111404700231160ustar00rootroot00000000000000debug) { echo $text . "\n"; } } /** * * * @param string $length * * @return void */ public function encodeLength($length) { if ($length < 0x80) { $length = chr($length); } elseif ($length < 0x4000) { $length |= 0x8000; $length = chr(($length >> 8) & 0xFF) . chr($length & 0xFF); } elseif ($length < 0x200000) { $length |= 0xC00000; $length = chr(($length >> 16) & 0xFF) . chr(($length >> 8) & 0xFF) . chr($length & 0xFF); } elseif ($length < 0x10000000) { $length |= 0xE0000000; $length = chr(($length >> 24) & 0xFF) . chr(($length >> 16) & 0xFF) . chr(($length >> 8) & 0xFF) . chr($length & 0xFF); } elseif ($length >= 0x10000000) { $length = chr(0xF0) . chr(($length >> 24) & 0xFF) . chr(($length >> 16) & 0xFF) . chr(($length >> 8) & 0xFF) . chr($length & 0xFF); } return $length; } /** * Login to RouterOS * * @param string $ip Hostname (IP or domain) of the RouterOS server * @param string $login The RouterOS username * @param string $password The RouterOS password * * @return boolean If we are connected or not */ public function connect($ip, $login, $password) { for ($ATTEMPT = 1; $ATTEMPT <= $this->attempts; $ATTEMPT++) { $this->connected = false; $PROTOCOL = ($this->ssl ? 'ssl://' : '' ); $this->debug('Connection attempt #' . $ATTEMPT . ' to ' . $PROTOCOL . $ip . ':' . $this->port . '...'); $this->socket = @fsockopen($PROTOCOL . $ip, $this->port, $this->error_no, $this->error_str, $this->timeout); if ($this->socket) { socket_set_timeout($this->socket, $this->timeout); $this->write('/login'); $RESPONSE = $this->read(false); if (isset($RESPONSE[0]) && $RESPONSE[0] == '!done') { $MATCHES = array(); if (preg_match_all('/[^=]+/i', $RESPONSE[1], $MATCHES)) { if ($MATCHES[0][0] == 'ret' && strlen($MATCHES[0][1]) == 32) { $this->write('/login', false); $this->write('=name=' . $login, false); $this->write('=response=00' . md5(chr(0) . $password . pack('H*', $MATCHES[0][1]))); $RESPONSE = $this->read(false); if ($RESPONSE[0] == '!done') { $this->connected = true; break; } } } } fclose($this->socket); } sleep($this->delay); } if ($this->connected) { $this->debug('Connected...'); } else { $this->debug('Error...'); } return $this->connected; } /** * Disconnect from RouterOS * * @return void */ public function disconnect() { // let's make sure this socket is still valid. it may have been closed by something else if( is_resource($this->socket) ) { fclose($this->socket); } $this->connected = false; $this->debug('Disconnected...'); } /** * Parse response from Router OS * * @param array $response Response data * * @return array Array with parsed data */ public function parseResponse($response) { if (is_array($response)) { $PARSED = array(); $CURRENT = null; $singlevalue = null; foreach ($response as $x) { if (in_array($x, array('!fatal','!re','!trap'))) { if ($x == '!re') { $CURRENT =& $PARSED[]; } else { $CURRENT =& $PARSED[$x][]; } } elseif ($x != '!done') { $MATCHES = array(); if (preg_match_all('/[^=]+/i', $x, $MATCHES)) { if ($MATCHES[0][0] == 'ret') { $singlevalue = $MATCHES[0][1]; } $CURRENT[$MATCHES[0][0]] = (isset($MATCHES[0][1]) ? $MATCHES[0][1] : ''); } } } if (empty($PARSED) && !is_null($singlevalue)) { $PARSED = $singlevalue; } return $PARSED; } else { return array(); } } /** * Parse response from Router OS * * @param array $response Response data * * @return array Array with parsed data */ public function parseResponse4Smarty($response) { if (is_array($response)) { $PARSED = array(); $CURRENT = null; $singlevalue = null; foreach ($response as $x) { if (in_array($x, array('!fatal','!re','!trap'))) { if ($x == '!re') { $CURRENT =& $PARSED[]; } else { $CURRENT =& $PARSED[$x][]; } } elseif ($x != '!done') { $MATCHES = array(); if (preg_match_all('/[^=]+/i', $x, $MATCHES)) { if ($MATCHES[0][0] == 'ret') { $singlevalue = $MATCHES[0][1]; } $CURRENT[$MATCHES[0][0]] = (isset($MATCHES[0][1]) ? $MATCHES[0][1] : ''); } } } foreach ($PARSED as $key => $value) { $PARSED[$key] = $this->arrayChangeKeyName($value); } return $PARSED; if (empty($PARSED) && !is_null($singlevalue)) { $PARSED = $singlevalue; } } else { return array(); } } /** * Change "-" and "/" from array key to "_" * * @param array $array Input array * * @return array Array with changed key names */ public function arrayChangeKeyName(&$array) { if (is_array($array)) { foreach ($array as $k => $v) { $tmp = str_replace("-", "_", $k); $tmp = str_replace("/", "_", $tmp); if ($tmp) { $array_new[$tmp] = $v; } else { $array_new[$k] = $v; } } return $array_new; } else { return $array; } } /** * Read data from Router OS * * @param boolean $parse Parse the data? default: true * * @return array Array with parsed or unparsed data */ public function read($parse = true) { $RESPONSE = array(); $receiveddone = false; while (true) { // Read the first byte of input which gives us some or all of the length // of the remaining reply. $BYTE = ord(fread($this->socket, 1)); $LENGTH = 0; // If the first bit is set then we need to remove the first four bits, shift left 8 // and then read another byte in. // We repeat this for the second and third bits. // If the fourth bit is set, we need to remove anything left in the first byte // and then read in yet another byte. if ($BYTE & 128) { if (($BYTE & 192) == 128) { $LENGTH = (($BYTE & 63) << 8) + ord(fread($this->socket, 1)); } else { if (($BYTE & 224) == 192) { $LENGTH = (($BYTE & 31) << 8) + ord(fread($this->socket, 1)); $LENGTH = ($LENGTH << 8) + ord(fread($this->socket, 1)); } else { if (($BYTE & 240) == 224) { $LENGTH = (($BYTE & 15) << 8) + ord(fread($this->socket, 1)); $LENGTH = ($LENGTH << 8) + ord(fread($this->socket, 1)); $LENGTH = ($LENGTH << 8) + ord(fread($this->socket, 1)); } else { $LENGTH = ord(fread($this->socket, 1)); $LENGTH = ($LENGTH << 8) + ord(fread($this->socket, 1)); $LENGTH = ($LENGTH << 8) + ord(fread($this->socket, 1)); $LENGTH = ($LENGTH << 8) + ord(fread($this->socket, 1)); } } } } else { $LENGTH = $BYTE; } $_ = ""; // If we have got more characters to read, read them in. if ($LENGTH > 0) { $_ = ""; $retlen = 0; while ($retlen < $LENGTH) { $toread = $LENGTH - $retlen; $_ .= fread($this->socket, $toread); $retlen = strlen($_); } $RESPONSE[] = $_; $this->debug('>>> [' . $retlen . '/' . $LENGTH . '] bytes read.'); } // If we get a !done, make a note of it. if ($_ == "!done") { $receiveddone = true; } $STATUS = socket_get_status($this->socket); if ($LENGTH > 0) { $this->debug('>>> [' . $LENGTH . ', ' . $STATUS['unread_bytes'] . ']' . $_); } if ((!$this->connected && !$STATUS['unread_bytes']) || ($this->connected && !$STATUS['unread_bytes'] && $receiveddone)) { break; } } if ($parse) { $RESPONSE = $this->parseResponse($RESPONSE); } return $RESPONSE; } /** * Write (send) data to Router OS * * @param string $command A string with the command to send * @param mixed $param2 If we set an integer, the command will send this data as a "tag" * If we set it to boolean true, the funcion will send the comand and finish * If we set it to boolean false, the funcion will send the comand and wait for next command * Default: true * * @return boolean Return false if no command especified */ public function write($command, $param2 = true) { if ($command) { $data = explode("\n", $command); foreach ($data as $com) { $com = trim($com); fwrite($this->socket, $this->encodeLength(strlen($com)) . $com); $this->debug('<<< [' . strlen($com) . '] ' . $com); } if (gettype($param2) == 'integer') { fwrite($this->socket, $this->encodeLength(strlen('.tag=' . $param2)) . '.tag=' . $param2 . chr(0)); $this->debug('<<< [' . strlen('.tag=' . $param2) . '] .tag=' . $param2); } elseif (gettype($param2) == 'boolean') { fwrite($this->socket, ($param2 ? chr(0) : '')); } return true; } else { return false; } } /** * Write (send) data to Router OS * * @param string $com A string with the command to send * @param array $arr An array with arguments or queries * * @return array Array with parsed */ public function comm($com, $arr = array()) { $count = count($arr); $this->write($com, !$arr); $i = 0; if ($this->isIterable($arr)) { foreach ($arr as $k => $v) { switch ($k[0]) { case "?": $el = "$k=$v"; break; case "~": $el = "$k~$v"; break; default: $el = "=$k=$v"; break; } $last = ($i++ == $count - 1); $this->write($el, $last); } } return $this->read(); } /** * Standard destructor * * @return void */ public function __destruct() { $this->disconnect(); } }fastnetmon-1.1.4/src/netflow_hooks.lua000066400000000000000000000037741343111404700200570ustar00rootroot00000000000000package.path = package.path .. ";/usr/share/lua/5.1/?.lua" local json = require("json") -- We have this library bundled only in luajit: -- g++ lua_integration.cpp -lluajit-5.1 -- Before production use, please call your code with luajit CLI local ffi = require("ffi") -- Load declaration from the inside separate header file -- This code should be in sync with https://github.com/pavel-odintsov/fastnetmon/blob/master/src/netflow_plugin/netflow.h -- And we use uintXX_t instead u_intXX_t here ffi.cdef([[typedef struct __attribute__((packed)) NF5_FLOW { uint32_t src_ip, dest_ip, nexthop_ip; uint16_t if_index_in, if_index_out; uint32_t flow_packets, flow_octets; uint32_t flow_start, flow_finish; uint16_t src_port, dest_port; uint8_t pad1; uint8_t tcp_flags, protocol, tos; uint16_t src_as, dest_as; uint8_t src_mask, dst_mask; uint16_t pad2; } NF5_FLOW_t;]]) -- Load json file once local json_file = io.open("/usr/src/fastnetmon/src/tests/netflow_exclude.json", "r") local decoded = json.decode(json_file:read("*all")) --for k, v in pairs(decoded) do -- for kk, vv in pairs(v) do -- print(k, kk, vv) -- end --end function process_netflow(flow_agent_ip, flow) local netlflow5_t = ffi.typeof('NF5_FLOW_t*') local lua_flow = ffi.cast(netlflow5_t, flow) --print ("We got this packets from: ", flow_agent_ip) -- TODO: PLEASE BE AWARE! Thid code will read json file for every netflow packet --print ("Flow packets and bytes: ", lua_flow.flow_packets, lua_flow.flow_octets) --print ("In interface :", lua_flow.if_index_in, " out interface: ", lua_flow.if_index_out) for agent_ip, ports_table in pairs(decoded) do if agent_ip == flow_agent_ip then for port_number, port_description in pairs(ports_table) do if lua_flow.if_index_in == port_number then -- We found this port in ignore list return false end end end end return true end fastnetmon-1.1.4/src/netflow_plugin/000077500000000000000000000000001343111404700175145ustar00rootroot00000000000000fastnetmon-1.1.4/src/netflow_plugin/netflow.h000066400000000000000000000176361343111404700213600ustar00rootroot00000000000000/* $Id$ */ /* * Copyright (c) 2004,2005 Damien Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* NetFlow packet definitions */ #ifndef NETFLOW_H #define NETFLOW_H #include /* A record in a NetFlow v.9 template record */ struct peer_nf9_record { u_int type; u_int len; }; typedef std::vector netflow9_template_records_map; /* A NetFlow v.9 template record */ struct peer_nf9_template { u_int16_t template_id; u_int num_records; u_int total_len; netflow9_template_records_map records; }; // TODO: clean up!!! #if defined(__GNUC__) #ifndef __dead #define __dead __attribute__((__noreturn__)) #endif #ifndef __packed #define __packed __attribute__((__packed__)) #endif #endif /* * These are Cisco Netflow(tm) packet formats * Based on: * http://www.cisco.com/univercd/cc/td/doc/product/rtrmgmt/nfc/nfc_3_0/nfc_ug/nfcform.htm */ /* Common header fields */ struct NF_HEADER_COMMON { u_int16_t version, flows; } __packed; /* Netflow v.1 */ struct NF1_HEADER { struct NF_HEADER_COMMON c; u_int32_t uptime_ms, time_sec, time_nanosec; } __packed; struct NF1_FLOW { u_int32_t src_ip, dest_ip, nexthop_ip; u_int16_t if_index_in, if_index_out; u_int32_t flow_packets, flow_octets; u_int32_t flow_start, flow_finish; u_int16_t src_port, dest_port; u_int16_t pad1; u_int8_t protocol, tos, tcp_flags; u_int8_t pad2, pad3, pad4; u_int32_t reserved1; #if 0 u_int8_t reserved2; /* XXX: no longer used */ #endif } __packed; /* Maximum of 30 flows per packet */ #define NF1_MAXFLOWS 24 #define NF1_PACKET_SIZE(nflows) (sizeof(struct NF1_HEADER) + ((nflows) * sizeof(struct NF1_FLOW))) #define NF1_MAXPACKET_SIZE (NF1_PACKET_SIZE(NF1_MAXFLOWS)) /* Netflow v.5 */ struct NF5_HEADER { struct NF_HEADER_COMMON c; u_int32_t uptime_ms, time_sec, time_nanosec, flow_sequence; u_int8_t engine_type, engine_id; // "First two bits hold the sampling mode; remaining 14 bits hold value of sampling interval" // accoring to https://www.plixer.com/support/netflow_v5.html // http://www.cisco.com/c/en/us/td/docs/net_mgmt/netflow_collection_engine/3-6/user/guide/format.html u_int16_t sampling_rate; } __packed; struct NF5_FLOW { u_int32_t src_ip, dest_ip, nexthop_ip; u_int16_t if_index_in, if_index_out; u_int32_t flow_packets, flow_octets; u_int32_t flow_start, flow_finish; u_int16_t src_port, dest_port; u_int8_t pad1; u_int8_t tcp_flags, protocol, tos; u_int16_t src_as, dest_as; u_int8_t src_mask, dst_mask; u_int16_t pad2; } __packed; /* Maximum of 24 flows per packet */ #define NF5_MAXFLOWS 30 #define NF5_PACKET_SIZE(nflows) (sizeof(struct NF5_HEADER) + ((nflows) * sizeof(struct NF5_FLOW))) #define NF5_MAXPACKET_SIZE (NF5_PACKET_SIZE(NF5_MAXFLOWS)) /* Netflow v.7 */ struct NF7_HEADER { struct NF_HEADER_COMMON c; u_int32_t uptime_ms, time_sec, time_nanosec, flow_sequence; u_int32_t reserved1; } __packed; struct NF7_FLOW { u_int32_t src_ip, dest_ip, nexthop_ip; u_int16_t if_index_in, if_index_out; u_int32_t flow_packets, flow_octets; u_int32_t flow_start, flow_finish; u_int16_t src_port, dest_port; u_int8_t flags1; u_int8_t tcp_flags, protocol, tos; u_int16_t src_as, dest_as; u_int8_t src_mask, dst_mask; u_int16_t flags2; u_int32_t router_sc; } __packed; /* Maximum of 24 flows per packet */ #define NF7_MAXFLOWS 30 #define NF7_PACKET_SIZE(nflows) (sizeof(struct NF7_HEADER) + ((nflows) * sizeof(struct NF7_FLOW))) #define NF7_MAXPACKET_SIZE (NF7_PACKET_SIZE(NF7_MAXFLOWS)) /* Netflow v.9 */ struct NF9_HEADER { struct NF_HEADER_COMMON c; u_int32_t uptime_ms, time_sec; u_int32_t package_sequence, source_id; } __packed; struct NF9_FLOWSET_HEADER_COMMON { u_int16_t flowset_id, length; } __packed; struct NF9_TEMPLATE_FLOWSET_HEADER { u_int16_t template_id, count; } __packed; struct NF9_TEMPLATE_FLOWSET_RECORD { u_int16_t type, length; } __packed; struct NF9_DATA_FLOWSET_HEADER { struct NF9_FLOWSET_HEADER_COMMON c; } __packed; #define NF9_TEMPLATE_FLOWSET_ID 0 #define NF9_OPTIONS_FLOWSET_ID 1 #define NF9_MIN_RECORD_FLOWSET_ID 256 /* Flowset record types the we care about */ #define NF9_IN_BYTES 1 #define NF9_IN_PACKETS 2 /* ... */ #define NF9_IN_PROTOCOL 4 #define NF9_SRC_TOS 5 #define NF9_TCP_FLAGS 6 #define NF9_L4_SRC_PORT 7 #define NF9_IPV4_SRC_ADDR 8 #define NF9_SRC_MASK 9 #define NF9_INPUT_SNMP 10 #define NF9_L4_DST_PORT 11 #define NF9_IPV4_DST_ADDR 12 #define NF9_DST_MASK 13 #define NF9_OUTPUT_SNMP 14 #define NF9_IPV4_NEXT_HOP 15 #define NF9_SRC_AS 16 #define NF9_DST_AS 17 /* ... */ #define NF9_LAST_SWITCHED 21 #define NF9_FIRST_SWITCHED 22 /* ... */ #define NF9_IPV6_SRC_ADDR 27 #define NF9_IPV6_DST_ADDR 28 #define NF9_IPV6_SRC_MASK 29 #define NF9_IPV6_DST_MASK 30 /* Added by Odintsov Pavel */ #define NF9_SAMPLING_INTERVAL 34 /* ... */ #define NF9_ENGINE_TYPE 38 #define NF9_ENGINE_ID 39 /* ... */ #define NF9_IPV6_NEXT_HOP 62 /* Netflow v.10 */ struct NF10_HEADER { struct NF_HEADER_COMMON c; u_int32_t time_sec; u_int32_t package_sequence, source_id; } __packed; struct NF10_FLOWSET_HEADER_COMMON { u_int16_t flowset_id, length; } __packed; struct NF10_TEMPLATE_FLOWSET_HEADER { u_int16_t template_id, count; } __packed; struct NF10_TEMPLATE_FLOWSET_RECORD { u_int16_t type, length; } __packed; struct NF10_DATA_FLOWSET_HEADER { struct NF10_FLOWSET_HEADER_COMMON c; } __packed; #define NF10_TEMPLATE_FLOWSET_ID 2 #define NF10_OPTIONS_FLOWSET_ID 3 #define NF10_MIN_RECORD_FLOWSET_ID 256 #define NF10_ENTERPRISE (1 << 15) /* Flowset record types the we care about */ #define NF10_IN_BYTES 1 #define NF10_IN_PACKETS 2 /* ... */ #define NF10_IN_PROTOCOL 4 #define NF10_SRC_TOS 5 #define NF10_TCP_FLAGS 6 #define NF10_L4_SRC_PORT 7 #define NF10_IPV4_SRC_ADDR 8 #define NF10_SRC_MASK 9 #define NF10_INPUT_SNMP 10 #define NF10_L4_DST_PORT 11 #define NF10_IPV4_DST_ADDR 12 #define NF10_DST_MASK 13 #define NF10_OUTPUT_SNMP 14 #define NF10_IPV4_NEXT_HOP 15 #define NF10_SRC_AS 16 #define NF10_DST_AS 17 /* ... */ #define NF10_LAST_SWITCHED 21 #define NF10_FIRST_SWITCHED 22 /* ... */ #define NF10_IPV6_SRC_ADDR 27 #define NF10_IPV6_DST_ADDR 28 #define NF10_IPV6_SRC_MASK 29 #define NF10_IPV6_DST_MASK 30 /* ... */ #define NF10_ENGINE_TYPE 38 #define NF10_ENGINE_ID 39 /* ... */ #define NF10_IPV6_NEXT_HOP 62 // copy & paste from store.h /* * Optional flow fields, specify what is stored for the flow * NB - the flow records appear in this order on disk */ #define STORE_FIELD_TAG (1U) #define STORE_FIELD_RECV_TIME (1U << 1) #define STORE_FIELD_PROTO_FLAGS_TOS (1U << 2) #define STORE_FIELD_AGENT_ADDR4 (1U << 3) #define STORE_FIELD_AGENT_ADDR6 (1U << 4) #define STORE_FIELD_SRC_ADDR4 (1U << 5) #define STORE_FIELD_SRC_ADDR6 (1U << 6) #define STORE_FIELD_DST_ADDR4 (1U << 7) #define STORE_FIELD_DST_ADDR6 (1U << 8) #define STORE_FIELD_GATEWAY_ADDR4 (1U << 9) #define STORE_FIELD_GATEWAY_ADDR6 (1U << 10) #define STORE_FIELD_SRCDST_PORT (1U << 11) #define STORE_FIELD_PACKETS (1U << 12) #define STORE_FIELD_OCTETS (1U << 13) #define STORE_FIELD_IF_INDICES (1U << 14) #define STORE_FIELD_AGENT_INFO (1U << 15) #define STORE_FIELD_FLOW_TIMES (1U << 16) #define STORE_FIELD_AS_INFO (1U << 17) #define STORE_FIELD_FLOW_ENGINE_INFO (1U << 18) #endif /* _NETFLOW_H */ fastnetmon-1.1.4/src/netflow_plugin/netflow_collector.cpp000066400000000000000000001311661343111404700237540ustar00rootroot00000000000000/* netflow plugin body */ #include #include #include #include #include #include #include #include #include #include #include #include #include "../fast_library.h" #include "../ipfix_rfc.h" // log4cpp logging facility #include "log4cpp/Category.hh" #include "log4cpp/Appender.hh" #include "log4cpp/FileAppender.hh" #include "log4cpp/OstreamAppender.hh" #include "log4cpp/Layout.hh" #include "log4cpp/BasicLayout.hh" #include "log4cpp/PatternLayout.hh" #include "log4cpp/Priority.hh" #ifdef ENABLE_LUA_HOOKS #include #endif #ifdef ENABLE_LUA_HOOKS lua_State* netflow_lua_state = NULL; bool lua_hooks_enabled = false; std::string lua_hooks_path = "/usr/src/fastnetmon/src/netflow_hooks.lua"; #endif // Get it from main program extern log4cpp::Category& logger; // Global configuration map extern std::map configuration_map; // Sampling rate for all netflow agents unsigned int sampling_rate = 1; std::string netflow_plugin_name = "netflow"; std::string netflow_plugin_log_prefix = netflow_plugin_name + ": "; // Divide packets and bytes counters from the flow by interval length for more 'smoother' data bool netflow_divide_counters_on_interval_length = false; ipfix_information_database ipfix_db_instance; #include "netflow_collector.h" #include "netflow.h" // If we wan't listen on IPv4 and IPv6 i nsame time we need listen multiple sockets. Not good, // right. // TODO: add per source uniq templates support process_packet_pointer netflow_process_func_ptr = NULL; typedef std::map template_storage_t; typedef std::map global_template_storage_t; global_template_storage_t global_netflow9_templates; global_template_storage_t global_netflow10_templates; /* Prototypes */ void add_peer_template(global_template_storage_t& table_for_add, u_int32_t source_id, u_int template_id, std::string client_addres_in_string_format, struct peer_nf9_template& field_template); int nf9_rec_to_flow(u_int record_type, u_int record_length, u_int8_t* data, simple_packet& packet, netflow9_template_records_map& template_records); struct peer_nf9_template* peer_find_template(global_template_storage_t& table_for_lookup, u_int32_t source_id, u_int template_id, std::string client_addres_in_string_format) { // We use source_id for distinguish multiple netflow agents with same IP std::string key = client_addres_in_string_format + "_" + convert_int_to_string(source_id); global_template_storage_t::iterator itr = table_for_lookup.find(key); if (itr == table_for_lookup.end()) { return NULL; } // Well, we find it! if (itr->second.count(template_id) > 0) { return &itr->second[template_id]; } else { return NULL; } } // Wrapper functions struct peer_nf9_template* peer_nf9_find_template(u_int32_t source_id, u_int template_id, std::string client_addres_in_string_format) { return peer_find_template(global_netflow9_templates, source_id, template_id, client_addres_in_string_format); } struct peer_nf9_template* peer_nf10_find_template(u_int32_t source_id, u_int template_id, std::string client_addres_in_string_format) { return peer_find_template(global_netflow10_templates, source_id, template_id, client_addres_in_string_format); } std::string print_peer_nf9_template(struct peer_nf9_template& field_template) { std::stringstream buffer; buffer << "template_id: " << field_template.template_id << "\n" << "num records: " << field_template.num_records << "\n" << "total len: " << field_template.total_len << "\n"; for (netflow9_template_records_map::iterator itr = field_template.records.begin(); itr != field_template.records.end(); ++itr) { buffer << "Records\n"; unsigned int length_from_database = ipfix_db_instance.get_length_by_id(itr->type); buffer << "type: " << itr->type << "\n"; buffer << "len: " << itr->len << "\n"; buffer << "name from database: " << ipfix_db_instance.get_name_by_id(itr->type) << "\n"; buffer << "length from database: " << length_from_database << "\n"; if (length_from_database != itr->len) { buffer << "ATTENTION!!!! Length from database is not equal to length from received " "from the device\n"; } buffer << "\n"; } return buffer.str(); } struct NF10_OPTIONS_HEADER_COMMON { u_int16_t flowset_id; u_int16_t length; }; struct NF10_OPTIONS_HEADER { u_int16_t template_id; u_int16_t field_count; u_int16_t scope_field_count; }; // https://tools.ietf.org/html/rfc5101#page-18 int process_netflow_v10_options_template(u_int8_t* pkt, size_t len, u_int32_t source_id) { struct NF10_OPTIONS_HEADER_COMMON* options_template_header = (struct NF10_OPTIONS_HEADER_COMMON*)pkt; if (len < sizeof(*options_template_header)) { logger << log4cpp::Priority::ERROR << "Short netflow ipfix options template header"; return 1; } if (ntohs(options_template_header->flowset_id) != NF10_OPTIONS_FLOWSET_ID) { logger << log4cpp::Priority::ERROR << "Function process_netflow_v10_options_template " "expects only NF10_OPTIONS_FLOWSET_ID but got " "another id: " << ntohs(options_template_header->flowset_id); return 1; } struct NF10_OPTIONS_HEADER* options_nested_header = (struct NF10_OPTIONS_HEADER*)(pkt + sizeof(struct NF10_OPTIONS_HEADER_COMMON*)); // Yes, I should convert it to host byter order but it broke it! // WTF?? u_int16_t template_id = options_nested_header->template_id; if (template_id <= 255) { logger << log4cpp::Priority::ERROR << "Template ID for options template should be bigger than 255"; return 1; } u_int16_t field_count = ntohs(options_nested_header->field_count); u_int16_t scope_field_count = ntohs(options_nested_header->scope_field_count); logger << log4cpp::Priority::INFO << "Options template id: " << template_id << " field_count: " << field_count << " scope_field_count: " << scope_field_count; return 0; } int process_netflow_v10_template(u_int8_t* pkt, size_t len, u_int32_t source_id, std::string client_addres_in_string_format) { struct NF10_FLOWSET_HEADER_COMMON* template_header = (struct NF10_FLOWSET_HEADER_COMMON*)pkt; // We use same struct as netflow v9 because netflow v9 and v10 (ipfix) is compatible struct peer_nf9_template field_template; if (len < sizeof(*template_header)) { logger << log4cpp::Priority::ERROR << "Short netflow ipfix flowset template header"; return 1; } if (ntohs(template_header->flowset_id) != NF10_TEMPLATE_FLOWSET_ID) { logger << log4cpp::Priority::ERROR << "Function process_netflow_v10_template expects only NF10_TEMPLATE_FLOWSET_ID but " "got another id: " << ntohs(template_header->flowset_id); return 1; } for (u_int offset = sizeof(*template_header); offset < len;) { struct NF10_TEMPLATE_FLOWSET_HEADER* tmplh = (struct NF10_TEMPLATE_FLOWSET_HEADER*)(pkt + offset); u_int template_id = ntohs(tmplh->template_id); u_int count = ntohs(tmplh->count); offset += sizeof(*tmplh); netflow9_template_records_map template_records_map; u_int total_size = 0; for (u_int i = 0; i < count; i++) { if (offset >= len) { logger << log4cpp::Priority::ERROR << "short netflow v.10 flowset template"; return 1; } struct NF10_TEMPLATE_FLOWSET_RECORD* tmplr = (struct NF10_TEMPLATE_FLOWSET_RECORD*)(pkt + offset); u_int record_type = ntohs(tmplr->type); u_int record_length = ntohs(tmplr->length); struct peer_nf9_record current_record; current_record.type = record_type; current_record.len = record_length; template_records_map.push_back(current_record); offset += sizeof(*tmplr); if (record_type & NF10_ENTERPRISE) { offset += sizeof(u_int32_t); /* XXX -- ? */ } total_size += record_length; // add check: if (total_size > peers->max_template_len) } field_template.num_records = count; field_template.total_len = total_size; field_template.records = template_records_map; add_peer_template(global_netflow10_templates, source_id, template_id, client_addres_in_string_format, field_template); } return 0; } int process_netflow_v9_template(u_int8_t* pkt, size_t len, u_int32_t source_id, std::string client_addres_in_string_format) { struct NF9_FLOWSET_HEADER_COMMON* template_header = (struct NF9_FLOWSET_HEADER_COMMON*)pkt; struct peer_nf9_template field_template; if (len < sizeof(*template_header)) { logger << log4cpp::Priority::ERROR << "Short netflow v9 flowset template header"; return 1; } if (ntohs(template_header->flowset_id) != NF9_TEMPLATE_FLOWSET_ID) { logger << log4cpp::Priority::ERROR << "Function process_netflow_v9_template expects only NF9_TEMPLATE_FLOWSET_ID but " "got another id: " << ntohs(template_header->flowset_id); return 1; } for (u_int offset = sizeof(*template_header); offset < len;) { struct NF9_TEMPLATE_FLOWSET_HEADER* tmplh = (struct NF9_TEMPLATE_FLOWSET_HEADER*)(pkt + offset); u_int template_id = ntohs(tmplh->template_id); u_int count = ntohs(tmplh->count); offset += sizeof(*tmplh); // logger<< log4cpp::Priority::INFO<<"Template template_id is:"<= len) { logger << log4cpp::Priority::ERROR << "short netflow v.9 flowset template"; return 1; } struct NF9_TEMPLATE_FLOWSET_RECORD* tmplr = (struct NF9_TEMPLATE_FLOWSET_RECORD*)(pkt + offset); u_int record_type = ntohs(tmplr->type); u_int record_length = ntohs(tmplr->length); struct peer_nf9_record current_record; current_record.type = record_type; current_record.len = record_length; template_records_map.push_back(current_record); // logger<< log4cpp::Priority::INFO<<"Learn new template type: "<type)<<" // length:"<length); offset += sizeof(*tmplr); total_size += record_length; // TODO: introduce nf9_check_rec_len } field_template.num_records = count; field_template.total_len = total_size; field_template.records = template_records_map; // Add/update template add_peer_template(global_netflow9_templates, source_id, template_id, client_addres_in_string_format, field_template); } return 0; } void add_peer_template(global_template_storage_t& table_for_add, u_int32_t source_id, u_int template_id, std::string client_addres_in_string_format, struct peer_nf9_template& field_template) { std::string key = client_addres_in_string_format + "_" + convert_int_to_string(source_id); // logger<< log4cpp::Priority::INFO<<"It's new option template "<second.count(template_id) > 0) { // logger<< log4cpp::Priority::INFO<<"We already have information about this template // with id:" // <second[template_id] = field_template; } else { // logger<< log4cpp::Priority::INFO<<"It's new option template "<second[template_id] = field_template; } } else { template_storage_t temp_template_storage; temp_template_storage[template_id] = field_template; table_for_add[key] = temp_template_storage; } return; } /* Copy an int (possibly shorter than the target) keeping their LSBs aligned */ #define BE_COPY(a) memcpy((u_char*)&a + (sizeof(a) - record_length), data, record_length); #define V9_FIELD(v9_field, store_field, flow_field) \ case v9_field: \ BE_COPY(packet.flow_field); \ break #define V9_FIELD_ADDR(v9_field, store_field, flow_field) \ case v9_field: \ memcpy(&packet.flow_field, data, record_length); \ break int nf9_rec_to_flow(u_int record_type, u_int record_length, u_int8_t* data, simple_packet& packet) { /* XXX: use a table-based interpreter */ switch (record_type) { V9_FIELD(NF9_IN_BYTES, OCTETS, length); V9_FIELD(NF9_IN_PACKETS, PACKETS, number_of_packets); V9_FIELD(NF9_IN_PROTOCOL, PROTO_FLAGS_TOS, protocol); V9_FIELD(NF9_TCP_FLAGS, PROTO_FLAGS_TOS, flags); V9_FIELD(NF9_L4_SRC_PORT, SRCDST_PORT, source_port); V9_FIELD(NF9_L4_DST_PORT, SRCDST_PORT, destination_port); case NF9_IPV4_SRC_ADDR: memcpy(&packet.src_ip, data, record_length); break; case NF9_IPV4_DST_ADDR: memcpy(&packet.dst_ip, data, record_length); break; case NF9_INPUT_SNMP: { // TODO: port number could be 4 byte (Juniper MX) and we should rewrite BE_COPY for correct handling uint16_t input_port = 0; if (record_length > sizeof(input_port)) { //logger << log4cpp::Priority::ERROR << "Received very big packet for NF9_INPUT_SNMP!"; //return 0; } else { BE_COPY(input_port); input_port = fast_ntoh(input_port); // logger << log4cpp::Priority::INFO << "NF9_INPUT_SNMP is: " << input_port; } } break; case NF9_OUTPUT_SNMP: { uint16_t output_port = 0; if (record_length > sizeof(output_port)) { //logger << log4cpp::Priority::ERROR << "Received very big packet for NF9_OUTPUT_SNMP!"; //return 0; } else { BE_COPY(output_port); output_port = fast_ntoh(output_port); // logger << log4cpp::Priority::INFO << "NF9_OUTPUT_SNMP is: " << output_port; } } break; //V9_FIELD_ADDR(NF9_IPV4_SRC_ADDR, SRC_ADDR4, src_ip); //V9_FIELD_ADDR(NF9_IPV4_DST_ADDR, DST_ADDR4, dst_ip); // Sampling rate // We use NULL as second argument because it's suelles for us // It did not help us because looks like sampling rate implemented with OPTIONS flowset // V9_FIELD(NF9_SAMPLING_INTERVAL, NULL, sample_ratio); // V9_FIELD(NF9_SRC_TOS, PROTO_FLAGS_TOS, pft.tos); // V9_FIELD(NF9_SRC_MASK, AS_INFO, asinf.src_mask); // V9_FIELD(NF9_INPUT_SNMP, IF_INDICES, ifndx.if_index_in); // V9_FIELD(NF9_DST_MASK, AS_INFO, asinf.dst_mask); // V9_FIELD(NF9_OUTPUT_SNMP, IF_INDICES, ifndx.if_index_out); // V9_FIELD(NF9_SRC_AS, AS_INFO, asinf.src_as); // V9_FIELD(NF9_DST_AS, AS_INFO, asinf.dst_as); // V9_FIELD(NF9_LAST_SWITCHED, FLOW_TIMES, ftimes.flow_finish); // V9_FIELD(NF9_FIRST_SWITCHED, FLOW_TIMES, ftimes.flow_start); // V9_FIELD(NF9_IPV6_SRC_MASK, AS_INFO, asinf.src_mask); // V9_FIELD(NF9_IPV6_DST_MASK, AS_INFO, asinf.dst_mask); // V9_FIELD(NF9_ENGINE_TYPE, FLOW_ENGINE_INFO, finf.engine_type); // V9_FIELD(NF9_ENGINE_ID, FLOW_ENGINE_INFO, finf.engine_id); // V9_FIELD_ADDR(NF9_IPV4_NEXT_HOP, GATEWAY_ADDR4, gateway_addr, 4, INET); // V9_FIELD_ADDR(NF9_IPV6_SRC_ADDR, SRC_ADDR6, src_addr, 6, INET6); // V9_FIELD_ADDR(NF9_IPV6_DST_ADDR, DST_ADDR6, dst_addr, 6, INET6); // V9_FIELD_ADDR(NF9_IPV6_NEXT_HOP, GATEWAY_ADDR6, gateway_addr, 6, INET6); //#undef V9_FIELD //#undef V9_FIELD_ADDR //#undef BE_COPY } return 0; } int nf10_rec_to_flow(u_int record_type, u_int record_length, u_int8_t* data, simple_packet& packet) { /* XXX: use a table-based interpreter */ switch (record_type) { V9_FIELD(NF10_IN_BYTES, OCTETS, length); V9_FIELD(NF10_IN_PACKETS, PACKETS, number_of_packets); V9_FIELD(NF10_IN_PROTOCOL, PROTO_FLAGS_TOS, protocol); V9_FIELD(NF10_TCP_FLAGS, PROTO_FLAGS_TOS, flags); V9_FIELD(NF10_L4_SRC_PORT, SRCDST_PORT, source_port); V9_FIELD(NF10_L4_DST_PORT, SRCDST_PORT, destination_port); V9_FIELD_ADDR(NF10_IPV4_SRC_ADDR, SRC_ADDR4, src_ip); V9_FIELD_ADDR(NF10_IPV4_DST_ADDR, DST_ADDR4, dst_ip); } return 0; } // We use maximum possible variable langth // But devices can send shoretr data to us typedef struct netflow_ipfix_struct { uint32_t sourceIPv4Address; uint32_t destinationIPv4Address; uint16_t sourceTransportPort; uint16_t destinationTransportPort; uint16_t tcpControlBits; uint8_t protocolIdentifier; uint64_t octetDeltaCount; uint64_t packetDeltaCount; } netflow_ipfix_struct; // We should rewrite nf9_flowset_to_store accroding to fixes here void nf10_flowset_to_store(u_int8_t* pkt, size_t len, struct NF10_HEADER* nf10_hdr, struct peer_nf9_template* field_template) { u_int offset = 0; if (len < field_template->total_len) { logger << log4cpp::Priority::ERROR << "Total len from template bigger than packet len"; return; } simple_packet packet; // We use shifted values and should process only zeroed values // because we are working with little and big endian data in same time packet.number_of_packets = 0; packet.ts.tv_sec = ntohl(nf10_hdr->time_sec); packet.sample_ratio = sampling_rate; netflow_ipfix_struct data_in_ipfix_format; memset(&data_in_ipfix_format, 0, sizeof(netflow_ipfix_struct)); for (netflow9_template_records_map::iterator iter = field_template->records.begin(); iter != field_template->records.end(); iter++) { u_int record_type = iter->type; u_int record_length = iter->len; nf10_rec_to_flow(record_type, record_length, pkt + offset, packet); // New code /* unsigned int field_id = record_type; std::string field_name = ipfix_db_instance.get_name_by_id(field_id); if (field_name == "octetDeltaCount") { unsigned int reference_field_length = sizeof(data_in_ipfix_format.octetDeltaCount); if (reference_field_length == record_length) { // We use standard copy memcpy(&data_in_ipfix_format.octetDeltaCount, pkt + offset, record_length); // Convert to host byte order data_in_ipfix_format.octetDeltaCount = fast_ntoh(data_in_ipfix_format.octetDeltaCount); } else if (record_length < reference_field_length) { logger<< log4cpp::Priority::ERROR<<"We can't copy data because magic memcpy is not implemented yet"; // We use copy memcpy for netfowrk byte order } else { // Holy cow! It's impossible! logger<< log4cpp::Priority::ERROR<<"We can't copy data because receiver data is bigger than our storage."; return; } logger<< log4cpp::Priority::INFO<<"We received packet size with new parser: "<total_len > len) // return 1; u_int offset = 0; simple_packet packet; // We use shifted values and should process only zeroed values // because we are working with little and big endian data in same time packet.number_of_packets = 0; packet.ts.tv_sec = ntohl(nf9_hdr->time_sec); packet.sample_ratio = sampling_rate; // We should iterate over all available template fields for (netflow9_template_records_map::iterator iter = template_records.begin(); iter != template_records.end(); iter++) { u_int record_type = iter->type; u_int record_length = iter->len; int nf9_rec_to_flow_result = nf9_rec_to_flow(record_type, record_length, pkt + offset, packet); // logger<< log4cpp::Priority::INFO<<"Read data with type: "<c.flowset_id); struct peer_nf9_template* flowset_template = peer_nf10_find_template(source_id, flowset_id, client_addres_in_string_format); if (flowset_template == NULL) { logger << log4cpp::Priority::INFO << "We don't have a template for flowset_id: " << flowset_id << " but it's not an error if this message disappears in 5-10 seconds. We need some " "time to learn it!"; return 1; } if (flowset_template->records.empty()) { logger << log4cpp::Priority::ERROR << "Blank records in template"; return 1; } u_int offset = sizeof(*dath); u_int num_flowsets = (len - offset) / flowset_template->total_len; if (num_flowsets == 0 || num_flowsets > 0x4000) { logger << log4cpp::Priority::ERROR << "Invalid number of data flowset, strange number of flows: " << num_flowsets; return 1; } for (u_int i = 0; i < num_flowsets; i++) { // process whole flowset nf10_flowset_to_store(pkt + offset, flowset_template->total_len, nf10_hdr, flowset_template); offset += flowset_template->total_len; } return 0; } int process_netflow_v9_data(u_int8_t* pkt, size_t len, struct NF9_HEADER* nf9_hdr, u_int32_t source_id, std::string client_addres_in_string_format) { struct NF9_DATA_FLOWSET_HEADER* dath = (struct NF9_DATA_FLOWSET_HEADER*)pkt; if (len < sizeof(*dath)) { logger << log4cpp::Priority::INFO << "Short netflow v9 data flowset header"; return 1; } u_int flowset_id = ntohs(dath->c.flowset_id); // logger<< log4cpp::Priority::INFO<<"We have data with flowset_id: "<records.empty()) { logger << log4cpp::Priority::ERROR << "Blank records in template"; return 1; } u_int offset = sizeof(*dath); u_int num_flowsets = (len - offset) / flowset_template->total_len; if (num_flowsets == 0 || num_flowsets > 0x4000) { logger << log4cpp::Priority::ERROR << "Invalid number of data flowsets, strange number of flows: " << num_flowsets; return 1; } for (u_int i = 0; i < num_flowsets; i++) { // process whole flowset nf9_flowset_to_store(pkt + offset, flowset_template->total_len, nf9_hdr, flowset_template->records); offset += flowset_template->total_len; } return 0; } void process_netflow_packet_v10(u_int8_t* packet, u_int len, std::string client_addres_in_string_format) { struct NF10_HEADER* nf10_hdr = (struct NF10_HEADER*)packet; struct NF10_FLOWSET_HEADER_COMMON* flowset; u_int32_t i, pktlen, flowset_id, flowset_len, flowset_flows; u_int32_t offset, source_id, total_flows; if (len < sizeof(*nf10_hdr)) { logger << log4cpp::Priority::ERROR << "Short netflow v10 header"; return; } /* v10 uses pkt length, not # of flows */ pktlen = ntohs(nf10_hdr->c.flows); source_id = ntohl(nf10_hdr->source_id); offset = sizeof(*nf10_hdr); total_flows = 0; for (i = 0;; i++) { if (offset >= len) { logger << log4cpp::Priority::ERROR << "We tried to read from address outside netflow packet"; return; } flowset = (struct NF10_FLOWSET_HEADER_COMMON*)(packet + offset); flowset_id = ntohs(flowset->flowset_id); flowset_len = ntohs(flowset->length); /* * Yes, this is a near duplicate of the short packet check * above, but this one validates the flowset length from in * the packet before we pass it to the flowset-specific * handlers below. */ if (offset + flowset_len > len) { logger << log4cpp::Priority::ERROR << "We tried to read from address outside netflow's packet flowset"; return; } switch (flowset_id) { case NF10_TEMPLATE_FLOWSET_ID: if (process_netflow_v10_template(packet + offset, flowset_len, source_id, client_addres_in_string_format) != 0) { logger << log4cpp::Priority::ERROR << "Function process_netflow_v10_template executed with errors"; break; } break; case NF10_OPTIONS_FLOWSET_ID: // process_netflow_v10_options_template(packet + offset, flowset_len, source_id); //logger << log4cpp::Priority::INFO // << "Received ipfix options flowset id, which is not supported"; /* Not implemented yet */ break; default: if (flowset_id < NF10_MIN_RECORD_FLOWSET_ID) { logger << log4cpp::Priority::ERROR << "Received unknown netflow v10 reserved flowset type " << flowset_id; break; } if (process_netflow_v10_data(packet + offset, flowset_len, nf10_hdr, source_id, client_addres_in_string_format) != 0) { // logger<< log4cpp::Priority::ERROR<<"Can't process function // process_netflow_v10_data correctly"; return; } break; } offset += flowset_len; if (offset == len) { break; } } } void process_netflow_packet_v9(u_int8_t* packet, u_int len, std::string client_addres_in_string_format) { // logger<< log4cpp::Priority::INFO<<"We get v9 netflow packet!"; struct NF9_HEADER* nf9_hdr = (struct NF9_HEADER*)packet; struct NF9_FLOWSET_HEADER_COMMON* flowset; u_int32_t count, flowset_id, flowset_len, flowset_flows; u_int32_t offset, source_id, total_flows; if (len < sizeof(*nf9_hdr)) { logger << log4cpp::Priority::ERROR << "Short netflow v9 header"; return; } count = ntohs(nf9_hdr->c.flows); source_id = ntohl(nf9_hdr->source_id); // logger<< log4cpp::Priority::INFO<<"Template source id: "<= len) { logger << log4cpp::Priority::ERROR << "We tried to read from address outside netflow packet"; return; } flowset = (struct NF9_FLOWSET_HEADER_COMMON*)(packet + offset); flowset_id = ntohs(flowset->flowset_id); flowset_len = ntohs(flowset->length); /* * Yes, this is a near duplicate of the short packet check * above, but this one validates the flowset length from in * the packet before we pass it to the flowset-specific * handlers below. */ if (offset + flowset_len > len) { logger << log4cpp::Priority::ERROR << "We tried to read from address outside netflow's packet flowset"; return; } switch (flowset_id) { case NF9_TEMPLATE_FLOWSET_ID: // logger<< log4cpp::Priority::INFO<<"We read template"; if (process_netflow_v9_template(packet + offset, flowset_len, source_id, client_addres_in_string_format) != 0) { logger << log4cpp::Priority::ERROR << "Function process_netflow_v9_template executed with errors"; break; } break; case NF9_OPTIONS_FLOWSET_ID: logger << log4cpp::Priority::INFO << "I received netflow v9 options flowset id but I haven't support for it"; /* Not implemented yet */ break; default: if (flowset_id < NF9_MIN_RECORD_FLOWSET_ID) { logger << log4cpp::Priority::ERROR << "Received unknown netflow v9 reserved flowset type " << flowset_id; break; } // logger<< log4cpp::Priority::INFO<<"We read data"; if (process_netflow_v9_data(packet + offset, flowset_len, nf9_hdr, source_id, client_addres_in_string_format) != 0) { // logger<< log4cpp::Priority::ERROR<<"Can't process function // process_netflow_v9_data correctly"; return; } break; } offset += flowset_len; if (offset == len) { break; } } } void process_netflow_packet_v5(u_int8_t* packet, u_int len, std::string client_addres_in_string_format) { // logger<< log4cpp::Priority::INFO<<"We get v5 netflow packet!"; struct NF5_HEADER* nf5_hdr = (struct NF5_HEADER*)packet; if (len < sizeof(*nf5_hdr)) { logger << log4cpp::Priority::ERROR << "Short netflow v5 packet " << len; return; } u_int nflows = ntohs(nf5_hdr->c.flows); if (nflows == 0 || nflows > NF5_MAXFLOWS) { logger << log4cpp::Priority::ERROR << "Invalid number of flows in netflow " << nflows; return; } uint16_t netflow5_sampling_ratio = fast_ntoh(nf5_hdr->sampling_rate); // In first two bits we store sampling type. // We are not interested in it and should zeroify it for getting correct value of sampling rate clear_bit_value(netflow5_sampling_ratio, 15); clear_bit_value(netflow5_sampling_ratio, 16); // Sampling not enabled on device if (netflow5_sampling_ratio == 0) { netflow5_sampling_ratio = 1; } for (u_int i = 0; i < nflows; i++) { size_t offset = NF5_PACKET_SIZE(i); struct NF5_FLOW* nf5_flow = (struct NF5_FLOW*)(packet + offset); /* Check packet bounds */ if (offset + sizeof(struct NF5_FLOW) > len) { logger << log4cpp::Priority::ERROR << "Error! You will try to read outside the packet"; } /* Decode to host encoding */ // TODO: move to separate function nf5_flow->flow_octets = fast_ntoh(nf5_flow->flow_octets); nf5_flow->flow_packets = fast_ntoh(nf5_flow->flow_packets); nf5_flow->if_index_in = fast_ntoh(nf5_flow->if_index_in); nf5_flow->if_index_out = fast_ntoh(nf5_flow->if_index_out); // convert netflow to simple packet form simple_packet current_packet; current_packet.src_ip = nf5_flow->src_ip; current_packet.dst_ip = nf5_flow->dest_ip; current_packet.ts.tv_sec = ntohl(nf5_hdr->time_sec); current_packet.ts.tv_usec = ntohl(nf5_hdr->time_nanosec); current_packet.flags = 0; current_packet.source_port = 0; current_packet.destination_port = 0; // TODO: we should pass data about "flow" structure of this data current_packet.length = nf5_flow->flow_octets; current_packet.number_of_packets = nf5_flow->flow_packets; if (netflow_divide_counters_on_interval_length) { // This interval in milliseconds, convert it to seconds int64_t interval_length = (fast_ntoh(nf5_flow->flow_finish) - fast_ntoh(nf5_flow->flow_start)) / 1000; /* if (interval_length > 0) { logger << log4cpp::Priority::INFO << "NetFlow v5 from: " << client_addres_in_string_format << " start: " << fast_ntoh(nf5_flow->flow_start) << " finish: " << fast_ntoh(nf5_flow->flow_finish) << " interval length:" << interval_length << "\n"; } */ if (interval_length == 0) { // it's OK } else if (interval_length < 0) { // it's internal error logger << log4cpp::Priority::ERROR << "We got negative interval length from netflow agent, something goes wrong!"; } else { // OK, let's divide // We will get integer result for this operation current_packet.length = current_packet.length / interval_length; current_packet.number_of_packets = current_packet.number_of_packets / interval_length; } } // TODO: use sampling data from packet, disable customization here // Wireshark dump approves this idea current_packet.sample_ratio = netflow5_sampling_ratio; current_packet.source_port = fast_ntoh(nf5_flow->src_port); current_packet.destination_port = fast_ntoh(nf5_flow->dest_port); // We do not support IPv6 in NetFlow v5 at all current_packet.ip_protocol_version = 4; switch (nf5_flow->protocol) { case 1: { // ICMP current_packet.protocol = IPPROTO_ICMP; } break; case 6: { // TCP current_packet.protocol = IPPROTO_TCP; // TODO: flags can be in another format! current_packet.flags = nf5_flow->tcp_flags; } break; case 17: { // UDP current_packet.protocol = IPPROTO_UDP; } break; } #ifdef ENABLE_LUA_HOOKS if (lua_hooks_enabled) { // This code could be used only for tests with pcap_reader //if (lua_state == NULL) { // init_lua_jit(); //} if (call_lua_function("process_netflow", netflow_lua_state, client_addres_in_string_format, (void*)nf5_flow)) { // We will process this packet } else { logger << log4cpp::Priority::INFO << "We will drop this packets because LUA script decided to do it"; return; } } #endif // Call processing function for every flow in packet netflow_process_func_ptr(current_packet); } } void process_netflow_packet(u_int8_t* packet, u_int len, std::string client_addres_in_string_format) { struct NF_HEADER_COMMON* hdr = (struct NF_HEADER_COMMON*)packet; switch (ntohs(hdr->version)) { case 5: process_netflow_packet_v5(packet, len, client_addres_in_string_format); break; case 9: process_netflow_packet_v9(packet, len, client_addres_in_string_format); break; case 10: process_netflow_packet_v10(packet, len, client_addres_in_string_format); break; default: logger << log4cpp::Priority::ERROR << "We do not support this version of netflow " << ntohs(hdr->version); break; } } void start_netflow_collector(std::string netflow_host, unsigned int netflow_port); // #include void start_netflow_collection(process_packet_pointer func_ptr) { logger << log4cpp::Priority::INFO << "netflow plugin started"; #ifdef ENABLE_LUA_HOOKS if (lua_hooks_enabled) { netflow_lua_state = init_lua_jit(lua_hooks_path); if (netflow_lua_state == NULL) { lua_hooks_enabled = false; } } #endif // prctl(PR_SET_NAME,"fastnetmon_netflow", 0, 0, 0); netflow_process_func_ptr = func_ptr; // By default we listen on IPv4 std::string netflow_host = "0.0.0.0"; std::string netflow_ports = ""; if (configuration_map.count("netflow_port") != 0) { netflow_ports = configuration_map["netflow_port"]; } if (configuration_map.count("netflow_host") != 0) { netflow_host = configuration_map["netflow_host"]; } if (configuration_map.count("netflow_sampling_ratio") != 0) { sampling_rate = convert_string_to_integer(configuration_map["netflow_sampling_ratio"]); logger << log4cpp::Priority::INFO << "Using custom sampling ratio for netflow: " << sampling_rate; } if (configuration_map.count("netflow_divide_counters_on_interval_length") != 0) { netflow_divide_counters_on_interval_length = configuration_map["netflow_divide_counters_on_interval_length"] == "on" ? true : false; } #ifdef ENABLE_LUA_HOOKS if (configuration_map.count("netflow_lua_hooks_path") != 0) { lua_hooks_path = configuration_map["netflow_lua_hooks_path"]; lua_hooks_enabled = true; } #endif boost::thread_group netflow_collector_threads; std::vector ports_for_listen; boost::split(ports_for_listen, netflow_ports, boost::is_any_of(","), boost::token_compress_on); logger << log4cpp::Priority::INFO << netflow_plugin_log_prefix << "We will listen on " << ports_for_listen.size() << " ports"; for (std::vector::iterator port = ports_for_listen.begin(); port != ports_for_listen.end(); ++port) { unsigned int netflow_port = convert_string_to_integer(*port); if (netflow_port == 0) { netflow_port = 2055; } netflow_collector_threads.add_thread( new boost::thread(start_netflow_collector, netflow_host, convert_string_to_integer(*port) )); } netflow_collector_threads.join_all(); } void start_netflow_collector(std::string netflow_host, unsigned int netflow_port) { logger << log4cpp::Priority::INFO << "netflow plugin will listen on " << netflow_host << ":" << netflow_port << " udp port"; unsigned int udp_buffer_size = 65536; char udp_buffer[udp_buffer_size]; struct addrinfo hints; memset(&hints, 0, sizeof hints); // Could be AF_INET6 or AF_INET hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_DGRAM; // This flag will generate wildcard IP address if we not specified certain IP address for // binding hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST; struct addrinfo* servinfo = NULL; const char* address_for_binding = NULL; if (!netflow_host.empty()) { address_for_binding = netflow_host.c_str(); } char port_as_string[16]; sprintf(port_as_string, "%d", netflow_port); int getaddrinfo_result = getaddrinfo(address_for_binding, port_as_string, &hints, &servinfo); if (getaddrinfo_result != 0) { logger << log4cpp::Priority::ERROR << "Netflow getaddrinfo function failed with code: " << getaddrinfo_result << " please check netflow_host"; exit(1); } int sockfd = socket(servinfo->ai_family, servinfo->ai_socktype, servinfo->ai_protocol); int bind_result = bind(sockfd, servinfo->ai_addr, servinfo->ai_addrlen); if (bind_result) { logger << log4cpp::Priority::ERROR << "Can't listen on port: " << netflow_port << " on host " << netflow_host << " errno:" << errno << " error: " << strerror(errno); return; } struct sockaddr_in6 peer; memset(&peer, 0, sizeof(peer)); /* We should specify timeout there for correct toolkit shutdown */ /* Because otherwise recvfrom will stay in blocked mode forever */ struct timeval tv; tv.tv_sec = 5; /* X Secs Timeout */ tv.tv_usec = 0; // Not init'ing this can cause strange errors setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(struct timeval)); while (true) { // This approach provide ability to store both IPv4 and IPv6 client's addresses struct sockaddr_storage client_address; // It's MUST memset(&client_address, 0, sizeof(struct sockaddr_storage)); socklen_t address_len = sizeof(struct sockaddr_storage); int received_bytes = recvfrom(sockfd, udp_buffer, udp_buffer_size, 0, (struct sockaddr*)&client_address, &address_len); if (received_bytes > 0) { // Pass host and port as numbers without any conversion int getnameinfo_flags = NI_NUMERICSERV | NI_NUMERICHOST; char host[NI_MAXHOST]; char service[NI_MAXSERV]; int result = getnameinfo((struct sockaddr*)&client_address, address_len, host, NI_MAXHOST, service, NI_MAXSERV, getnameinfo_flags); // We sill store client's IP address as string for allowing IPv4 and IPv6 processing in // same time std::string client_addres_in_string_format = std::string(host); // logger<< log4cpp::Priority::INFO<<"We receive packet from IP: // "< #include #include "../fast_library.h" // For support uint32_t, uint16_t #include // For config map operations #include #include #include #include #include #define NETMAP_WITH_LIBS // Disable debug messages from Netmap #define NETMAP_NO_DEBUG #include #include #if defined(__FreeBSD__) // On FreeBSD function pthread_attr_setaffinity_np declared here #include // Also we have different type name for cpu set's store typedef cpuset_t cpu_set_t; #endif #include "../fastnetmon_packet_parser.h" #include "../unified_parser.hpp" // For pooling operations #include // For support: IPPROTO_TCP, IPPROTO_ICMP, IPPROTO_UDP #include #include #include #include "netmap_collector.h" // By default we read packet size from link layer // But in case of Juniper we could crop first X bytes from packet: // maximum-packet-length 110; // And this option become mandatory if we want correct bps speed in toolkit bool netmap_read_packet_length_from_ip_header = false; uint32_t netmap_sampling_ratio = 1; /* prototypes */ void netmap_thread(struct nm_desc* netmap_descriptor, int netmap_thread); void consume_pkt(u_char* buffer, int len, int thread_number); // Get log4cpp logger from main program extern log4cpp::Category& logger; // Pass unparsed packets number to main program extern uint64_t total_unparsed_packets; // Global configuration map extern std::map configuration_map; u_int num_cpus = 0; // This variable name should be uniq for every plugin! process_packet_pointer netmap_process_func_ptr = NULL; bool execute_strict_cpu_affinity = true; int receive_packets(struct netmap_ring* ring, int thread_number) { u_int cur, rx, n; cur = ring->cur; n = nm_ring_space(ring); for (rx = 0; rx < n; rx++) { struct netmap_slot* slot = &ring->slot[cur]; char* p = NETMAP_BUF(ring, slot->buf_idx); // process data consume_pkt((u_char*)p, slot->len, thread_number); cur = nm_ring_next(ring, cur); } ring->head = ring->cur = cur; return (rx); } void consume_pkt(u_char* buffer, int len, int thread_number) { // We should fill this structure for passing to FastNetMon simple_packet packet; packet.sample_ratio = netmap_sampling_ratio; if (!parse_raw_packet_to_simple_packet(buffer, len, packet, netmap_read_packet_length_from_ip_header)) { total_unparsed_packets++; return; } netmap_process_func_ptr(packet); } void receiver(std::string interface_for_listening) { struct nm_desc* netmap_descriptor; struct nmreq base_nmd; bzero(&base_nmd, sizeof(base_nmd)); // Magic from pkt-gen.c base_nmd.nr_tx_rings = base_nmd.nr_rx_rings = 0; base_nmd.nr_tx_slots = base_nmd.nr_rx_slots = 0; std::string interface = ""; std::string system_interface_name = ""; // If we haven't netmap: prefix in interface name we will append it if (interface_for_listening.find("netmap:") == std::string::npos) { system_interface_name = interface_for_listening; interface = "netmap:" + interface_for_listening; } else { // We should skip netmap prefix system_interface_name = boost::replace_all_copy(interface_for_listening, "netmap:", ""); interface = interface_for_listening; } #ifdef __linux__ manage_interface_promisc_mode(system_interface_name, true); logger.warn("Please disable all types of offload for this NIC manually: ethtool -K %s gro off gso off tso off lro off", system_interface_name.c_str()); #endif netmap_descriptor = nm_open(interface.c_str(), &base_nmd, 0, NULL); if (netmap_descriptor == NULL) { logger.error("Can't open netmap device %s", interface.c_str()); return; } logger.info("Mapped %dKB memory at %p", netmap_descriptor->req.nr_memsize >> 10, netmap_descriptor->mem); logger.info("We have %d tx and %d rx rings", netmap_descriptor->req.nr_tx_rings, netmap_descriptor->req.nr_rx_rings); if (num_cpus > netmap_descriptor->req.nr_rx_rings) { num_cpus = netmap_descriptor->req.nr_rx_rings; logger.info("We have number of CPUs bigger than number of NIC RX queues. Set number of " "CPU's to number of threads"); } /* protocol stack and may cause a reset of the card, which in turn may take some time for the PHY to reconfigure. We do the open here to have time to reset. */ int wait_link = 2; logger.info("Wait %d seconds for NIC reset", wait_link); sleep(wait_link); boost::thread_group packet_receiver_thread_group; for (int i = 0; i < num_cpus; i++) { struct nm_desc nmd = *netmap_descriptor; // This operation is VERY important! nmd.self = &nmd; uint64_t nmd_flags = 0; if (nmd.req.nr_flags != NR_REG_ALL_NIC) { logger.error("Ooops, main descriptor should be with NR_REG_ALL_NIC flag"); } nmd.req.nr_flags = NR_REG_ONE_NIC; nmd.req.nr_ringid = i; /* Only touch one of the rings (rx is already ok) */ nmd_flags |= NETMAP_NO_TX_POLL; struct nm_desc* new_nmd = nm_open(interface.c_str(), NULL, nmd_flags | NM_OPEN_IFNAME | NM_OPEN_NO_MMAP, &nmd); if (new_nmd == NULL) { logger.error("Can't open netmap descriptor for netmap per hardware queue thread"); return; } logger.info("My first ring is %d and last ring id is %d I'm thread %d", new_nmd->first_rx_ring, new_nmd->last_rx_ring, i); /* logger<< log4cpp::Priority::INFO<< "We are using Boost " << BOOST_VERSION / 100000 << "." // major version << BOOST_VERSION / 100 % 1000 << "." // minior version << BOOST_VERSION % 100; */ logger.info("Start new netmap thread %d", i); // Well, we have thread attributes from Boost 1.50 #if defined(BOOST_THREAD_PLATFORM_PTHREAD) && BOOST_VERSION / 100 % 1000 >= 50 && !defined(__APPLE__) && defined(__GLIBC__) /* Bind to certain core */ boost::thread::attributes thread_attrs; if (execute_strict_cpu_affinity) { cpu_set_t current_cpu_set; int cpu_to_bind = i % num_cpus; CPU_ZERO(¤t_cpu_set); // We count cpus from zero CPU_SET(cpu_to_bind, ¤t_cpu_set); logger.info("I will bind this thread to logical CPU: %d", cpu_to_bind); int set_affinity_result = pthread_attr_setaffinity_np(thread_attrs.native_handle(), sizeof(cpu_set_t), ¤t_cpu_set); if (set_affinity_result != 0) { logger.error("Can't specify CPU affinity for netmap thread"); } } // Start thread and pass netmap descriptor to it packet_receiver_thread_group.add_thread( new boost::thread(thread_attrs, boost::bind(netmap_thread, new_nmd, i))); #else logger.error("Sorry but CPU affinity did not supported for your platform"); packet_receiver_thread_group.add_thread(new boost::thread(netmap_thread, new_nmd, i)); #endif } // Wait all threads for completion packet_receiver_thread_group.join_all(); } void netmap_thread(struct nm_desc* netmap_descriptor, int thread_number) { struct nm_pkthdr h; u_char* buf; struct pollfd fds; fds.fd = netmap_descriptor->fd; // NETMAP_FD(netmap_descriptor); fds.events = POLLIN; struct netmap_ring* rxring = NULL; struct netmap_if* nifp = netmap_descriptor->nifp; // printf("Reading from fd %d thread id: %d", netmap_descriptor->fd, thread_number); for (;;) { // We will wait 1000 microseconds for retry, for infinite timeout please use -1 int poll_result = poll(&fds, 1, 1000); if (poll_result == 0) { // printf("poll return 0 return code"); continue; } if (poll_result == -1) { logger.error("Netmap plugin: poll failed with return code -1"); } for (int i = netmap_descriptor->first_rx_ring; i <= netmap_descriptor->last_rx_ring; i++) { // printf("Check ring %d from thread %d", i, thread_number); rxring = NETMAP_RXRING(nifp, i); if (nm_ring_empty(rxring)) { continue; } receive_packets(rxring, thread_number); } // TODO: this code could add performance degradation // Add interruption point for correct toolkit shutdown // boost::this_thread::interruption_point(); } // nm_close(netmap_descriptor); } void start_netmap_collection(process_packet_pointer func_ptr) { logger << log4cpp::Priority::INFO << "Netmap plugin started"; netmap_process_func_ptr = func_ptr; num_cpus = sysconf(_SC_NPROCESSORS_ONLN); logger.info("We have %d cpus", num_cpus); std::string interfaces_list = ""; if (configuration_map.count("interfaces") != 0) { interfaces_list = configuration_map["interfaces"]; } if (configuration_map.count("netmap_sampling_ratio") != 0) { netmap_sampling_ratio = convert_string_to_integer(configuration_map["netmap_sampling_ratio"]); } if (configuration_map.count("netmap_read_packet_length_from_ip_header") != 0) { netmap_read_packet_length_from_ip_header = configuration_map["netmap_read_packet_length_from_ip_header"] == "on"; } std::vector interfaces_for_listen; boost::split(interfaces_for_listen, interfaces_list, boost::is_any_of(","), boost::token_compress_on); logger << log4cpp::Priority::INFO << "netmap will listen on " << interfaces_for_listen.size() << " interfaces"; // Thread group for all "master" processes boost::thread_group netmap_main_threads; for (std::vector::iterator interface = interfaces_for_listen.begin(); interface != interfaces_for_listen.end(); ++interface) { logger << log4cpp::Priority::INFO << "netmap will sniff interface: " << *interface; netmap_main_threads.add_thread( new boost::thread(receiver, *interface) ); } netmap_main_threads.join_all(); } fastnetmon-1.1.4/src/netmap_plugin/netmap_collector.h000066400000000000000000000002321343111404700230220ustar00rootroot00000000000000#ifndef NETMAP_PLUGIN_H #define NETMAP_PLUGIN_H #include "../fastnetmon_types.h" void start_netmap_collection(process_packet_pointer func_ptr); #endif fastnetmon-1.1.4/src/netmap_plugin/netmap_includes/000077500000000000000000000000001343111404700224745ustar00rootroot00000000000000fastnetmon-1.1.4/src/netmap_plugin/netmap_includes/net/000077500000000000000000000000001343111404700232625ustar00rootroot00000000000000fastnetmon-1.1.4/src/netmap_plugin/netmap_includes/net/netmap.h000066400000000000000000000500671343111404700247270ustar00rootroot00000000000000/* * Copyright (C) 2011-2014 Matteo Landi, Luigi Rizzo. 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``S 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 AUTHOR 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. */ /* * $FreeBSD: head/sys/net/netmap.h 251139 2013-05-30 14:07:14Z luigi $ * * Definitions of constants and the structures used by the netmap * framework, for the part visible to both kernel and userspace. * Detailed info on netmap is available with "man netmap" or at * * http://info.iet.unipi.it/~luigi/netmap/ * * This API is also used to communicate with the VALE software switch */ #ifndef _NET_NETMAP_H_ #define _NET_NETMAP_H_ #define NETMAP_API 11 /* current API version */ #define NETMAP_MIN_API 11 /* min and max versions accepted */ #define NETMAP_MAX_API 15 /* * Some fields should be cache-aligned to reduce contention. * The alignment is architecture and OS dependent, but rather than * digging into OS headers to find the exact value we use an estimate * that should cover most architectures. */ #define NM_CACHE_ALIGN 128 /* * --- Netmap data structures --- * * The userspace data structures used by netmap are shown below. * They are allocated by the kernel and mmap()ed by userspace threads. * Pointers are implemented as memory offsets or indexes, * so that they can be easily dereferenced in kernel and userspace. KERNEL (opaque, obviously) ==================================================================== | USERSPACE | struct netmap_ring +---->+---------------+ / | head,cur,tail | struct netmap_if (nifp, 1 per fd) / | buf_ofs | +---------------+ / | other fields | | ni_tx_rings | / +===============+ | ni_rx_rings | / | buf_idx, len | slot[0] | | / | flags, ptr | | | / +---------------+ +===============+ / | buf_idx, len | slot[1] | txring_ofs[0] | (rel.to nifp)--' | flags, ptr | | txring_ofs[1] | +---------------+ (tx+1 entries) (num_slots entries) | txring_ofs[t] | | buf_idx, len | slot[n-1] +---------------+ | flags, ptr | | rxring_ofs[0] | +---------------+ | rxring_ofs[1] | (rx+1 entries) | rxring_ofs[r] | +---------------+ * For each "interface" (NIC, host stack, PIPE, VALE switch port) bound to * a file descriptor, the mmap()ed region contains a (logically readonly) * struct netmap_if pointing to struct netmap_ring's. * * There is one netmap_ring per physical NIC ring, plus one tx/rx ring * pair attached to the host stack (this pair is unused for non-NIC ports). * * All physical/host stack ports share the same memory region, * so that zero-copy can be implemented between them. * VALE switch ports instead have separate memory regions. * * The netmap_ring is the userspace-visible replica of the NIC ring. * Each slot has the index of a buffer (MTU-sized and residing in the * mmapped region), its length and some flags. An extra 64-bit pointer * is provided for user-supplied buffers in the tx path. * * In user space, the buffer address is computed as * (char *)ring + buf_ofs + index * NETMAP_BUF_SIZE * * Added in NETMAP_API 11: * * + NIOCREGIF can request the allocation of extra spare buffers from * the same memory pool. The desired number of buffers must be in * nr_arg3. The ioctl may return fewer buffers, depending on memory * availability. nr_arg3 will return the actual value, and, once * mapped, nifp->ni_bufs_head will be the index of the first buffer. * * The buffers are linked to each other using the first uint32_t * as the index. On close, ni_bufs_head must point to the list of * buffers to be released. * * + NIOCREGIF can request space for extra rings (and buffers) * allocated in the same memory space. The number of extra rings * is in nr_arg1, and is advisory. This is a no-op on NICs where * the size of the memory space is fixed. * * + NIOCREGIF can attach to PIPE rings sharing the same memory * space with a parent device. The ifname indicates the parent device, * which must already exist. Flags in nr_flags indicate if we want to * bind the master or slave side, the index (from nr_ringid) * is just a cookie and does not need to be sequential. * * + NIOCREGIF can also attach to 'monitor' rings that replicate * the content of specific rings, also from the same memory space. * * Extra flags in nr_flags support the above functions. * Application libraries may use the following naming scheme: * netmap:foo all NIC ring pairs * netmap:foo^ only host ring pair * netmap:foo+ all NIC ring + host ring pairs * netmap:foo-k the k-th NIC ring pair * netmap:foo{k PIPE ring pair k, master side * netmap:foo}k PIPE ring pair k, slave side */ /* * struct netmap_slot is a buffer descriptor */ struct netmap_slot { uint32_t buf_idx; /* buffer index */ uint16_t len; /* length for this slot */ uint16_t flags; /* buf changed, etc. */ uint64_t ptr; /* pointer for indirect buffers */ }; /* * The following flags control how the slot is used */ #define NS_BUF_CHANGED 0x0001 /* buf_idx changed */ /* * must be set whenever buf_idx is changed (as it might be * necessary to recompute the physical address and mapping) * * It is also set by the kernel whenever the buf_idx is * changed internally (e.g., by pipes). Applications may * use this information to know when they can reuse the * contents of previously prepared buffers. */ #define NS_REPORT 0x0002 /* ask the hardware to report results */ /* * Request notification when slot is used by the hardware. * Normally transmit completions are handled lazily and * may be unreported. This flag lets us know when a slot * has been sent (e.g. to terminate the sender). */ #define NS_FORWARD 0x0004 /* pass packet 'forward' */ /* * (Only for physical ports, rx rings with NR_FORWARD set). * Slot released to the kernel (i.e. before ring->head) with * this flag set are passed to the peer ring (host/NIC), * thus restoring the host-NIC connection for these slots. * This supports efficient traffic monitoring or firewalling. */ #define NS_NO_LEARN 0x0008 /* disable bridge learning */ /* * On a VALE switch, do not 'learn' the source port for * this buffer. */ #define NS_INDIRECT 0x0010 /* userspace buffer */ /* * (VALE tx rings only) data is in a userspace buffer, * whose address is in the 'ptr' field in the slot. */ #define NS_MOREFRAG 0x0020 /* packet has more fragments */ /* * (VALE ports only) * Set on all but the last slot of a multi-segment packet. * The 'len' field refers to the individual fragment. */ #define NS_PORT_SHIFT 8 #define NS_PORT_MASK (0xff << NS_PORT_SHIFT) /* * The high 8 bits of the flag, if not zero, indicate the * destination port for the VALE switch, overriding * the lookup table. */ #define NS_RFRAGS(_slot) ( ((_slot)->flags >> 8) & 0xff) /* * (VALE rx rings only) the high 8 bits * are the number of fragments. */ /* * struct netmap_ring * * Netmap representation of a TX or RX ring (also known as "queue"). * This is a queue implemented as a fixed-size circular array. * At the software level the important fields are: head, cur, tail. * * In TX rings: * * head first slot available for transmission. * cur wakeup point. select() and poll() will unblock * when 'tail' moves past 'cur' * tail (readonly) first slot reserved to the kernel * * [head .. tail-1] can be used for new packets to send; * 'head' and 'cur' must be incremented as slots are filled * with new packets to be sent; * 'cur' can be moved further ahead if we need more space * for new transmissions. XXX todo (2014-03-12) * * In RX rings: * * head first valid received packet * cur wakeup point. select() and poll() will unblock * when 'tail' moves past 'cur' * tail (readonly) first slot reserved to the kernel * * [head .. tail-1] contain received packets; * 'head' and 'cur' must be incremented as slots are consumed * and can be returned to the kernel; * 'cur' can be moved further ahead if we want to wait for * new packets without returning the previous ones. * * DATA OWNERSHIP/LOCKING: * The netmap_ring, and all slots and buffers in the range * [head .. tail-1] are owned by the user program; * the kernel only accesses them during a netmap system call * and in the user thread context. * * Other slots and buffers are reserved for use by the kernel */ struct netmap_ring { /* * buf_ofs is meant to be used through macros. * It contains the offset of the buffer region from this * descriptor. */ const int64_t buf_ofs; const uint32_t num_slots; /* number of slots in the ring. */ const uint32_t nr_buf_size; const uint16_t ringid; const uint16_t dir; /* 0: tx, 1: rx */ uint32_t head; /* (u) first user slot */ uint32_t cur; /* (u) wakeup point */ uint32_t tail; /* (k) first kernel slot */ uint32_t flags; struct timeval ts; /* (k) time of last *sync() */ /* opaque room for a mutex or similar object */ uint8_t sem[128] __attribute__((__aligned__(NM_CACHE_ALIGN))); /* the slots follow. This struct has variable size */ struct netmap_slot slot[0]; /* array of slots. */ }; /* * RING FLAGS */ #define NR_TIMESTAMP 0x0002 /* set timestamp on *sync() */ /* * updates the 'ts' field on each netmap syscall. This saves * saves a separate gettimeofday(), and is not much worse than * software timestamps generated in the interrupt handler. */ #define NR_FORWARD 0x0004 /* enable NS_FORWARD for ring */ /* * Enables the NS_FORWARD slot flag for the ring. */ /* * Netmap representation of an interface and its queue(s). * This is initialized by the kernel when binding a file * descriptor to a port, and should be considered as readonly * by user programs. The kernel never uses it. * * There is one netmap_if for each file descriptor on which we want * to select/poll. * select/poll operates on one or all pairs depending on the value of * nmr_queueid passed on the ioctl. */ struct netmap_if { char ni_name[IFNAMSIZ]; /* name of the interface. */ const uint32_t ni_version; /* API version, currently unused */ const uint32_t ni_flags; /* properties */ #define NI_PRIV_MEM 0x1 /* private memory region */ /* * The number of packet rings available in netmap mode. * Physical NICs can have different numbers of tx and rx rings. * Physical NICs also have a 'host' ring pair. * Additionally, clients can request additional ring pairs to * be used for internal communication. */ const uint32_t ni_tx_rings; /* number of HW tx rings */ const uint32_t ni_rx_rings; /* number of HW rx rings */ uint32_t ni_bufs_head; /* head index for extra bufs */ uint32_t ni_spare1[5]; /* * The following array contains the offset of each netmap ring * from this structure, in the following order: * NIC tx rings (ni_tx_rings); host tx ring (1); extra tx rings; * NIC rx rings (ni_rx_rings); host tx ring (1); extra rx rings. * * The area is filled up by the kernel on NIOCREGIF, * and then only read by userspace code. */ const ssize_t ring_ofs[0]; }; #ifndef NIOCREGIF /* * ioctl names and related fields * * NIOCTXSYNC, NIOCRXSYNC synchronize tx or rx queues, * whose identity is set in NIOCREGIF through nr_ringid. * These are non blocking and take no argument. * * NIOCGINFO takes a struct ifreq, the interface name is the input, * the outputs are number of queues and number of descriptor * for each queue (useful to set number of threads etc.). * The info returned is only advisory and may change before * the interface is bound to a file descriptor. * * NIOCREGIF takes an interface name within a struct nmre, * and activates netmap mode on the interface (if possible). * * The argument to NIOCGINFO/NIOCREGIF overlays struct ifreq so we * can pass it down to other NIC-related ioctls. * * The actual argument (struct nmreq) has a number of options to request * different functions. * The following are used in NIOCREGIF when nr_cmd == 0: * * nr_name (in) * The name of the port (em0, valeXXX:YYY, etc.) * limited to IFNAMSIZ for backward compatibility. * * nr_version (in/out) * Must match NETMAP_API as used in the kernel, error otherwise. * Always returns the desired value on output. * * nr_tx_slots, nr_tx_slots, nr_tx_rings, nr_rx_rings (in/out) * On input, non-zero values may be used to reconfigure the port * according to the requested values, but this is not guaranteed. * On output the actual values in use are reported. * * nr_ringid (in) * Indicates how rings should be bound to the file descriptors. * If nr_flags != 0, then the low bits (in NETMAP_RING_MASK) * are used to indicate the ring number, and nr_flags specifies * the actual rings to bind. NETMAP_NO_TX_POLL is unaffected. * * NOTE: THE FOLLOWING (nr_flags == 0) IS DEPRECATED: * If nr_flags == 0, NETMAP_HW_RING and NETMAP_SW_RING control * the binding as follows: * 0 (default) binds all physical rings * NETMAP_HW_RING | ring number binds a single ring pair * NETMAP_SW_RING binds only the host tx/rx rings * * NETMAP_NO_TX_POLL can be OR-ed to make select()/poll() push * packets on tx rings only if POLLOUT is set. * The default is to push any pending packet. * * NETMAP_DO_RX_POLL can be OR-ed to make select()/poll() release * packets on rx rings also when POLLIN is NOT set. * The default is to touch the rx ring only with POLLIN. * Note that this is the opposite of TX because it * reflects the common usage. * * NOTE: NETMAP_PRIV_MEM IS DEPRECATED, use nr_arg2 instead. * NETMAP_PRIV_MEM is set on return for ports that do not use * the global memory allocator. * This information is not significant and applications * should look at the region id in nr_arg2 * * nr_flags is the recommended mode to indicate which rings should * be bound to a file descriptor. Values are NR_REG_* * * nr_arg1 (in) The number of extra rings to be reserved. * Especially when allocating a VALE port the system only * allocates the amount of memory needed for the port. * If more shared memory rings are desired (e.g. for pipes), * the first invocation for the same basename/allocator * should specify a suitable number. Memory cannot be * extended after the first allocation without closing * all ports on the same region. * * nr_arg2 (in/out) The identity of the memory region used. * On input, 0 means the system decides autonomously, * other values may try to select a specific region. * On return the actual value is reported. * Region '1' is the global allocator, normally shared * by all interfaces. Other values are private regions. * If two ports the same region zero-copy is possible. * * nr_arg3 (in/out) number of extra buffers to be allocated. * * * * nr_cmd (in) if non-zero indicates a special command: * NETMAP_BDG_ATTACH and nr_name = vale*:ifname * attaches the NIC to the switch; nr_ringid specifies * which rings to use. Used by vale-ctl -a ... * nr_arg1 = NETMAP_BDG_HOST also attaches the host port * as in vale-ctl -h ... * * NETMAP_BDG_DETACH and nr_name = vale*:ifname * disconnects a previously attached NIC. * Used by vale-ctl -d ... * * NETMAP_BDG_LIST * list the configuration of VALE switches. * * NETMAP_BDG_VNET_HDR * Set the virtio-net header length used by the client * of a VALE switch port. * * NETMAP_BDG_NEWIF * create a persistent VALE port with name nr_name. * Used by vale-ctl -n ... * * NETMAP_BDG_DELIF * delete a persistent VALE port. Used by vale-ctl -d ... * * nr_arg1, nr_arg2, nr_arg3 (in/out) command specific * * * */ /* * struct nmreq overlays a struct ifreq (just the name) */ struct nmreq { char nr_name[IFNAMSIZ]; uint32_t nr_version; /* API version */ uint32_t nr_offset; /* nifp offset in the shared region */ uint32_t nr_memsize; /* size of the shared region */ uint32_t nr_tx_slots; /* slots in tx rings */ uint32_t nr_rx_slots; /* slots in rx rings */ uint16_t nr_tx_rings; /* number of tx rings */ uint16_t nr_rx_rings; /* number of rx rings */ uint16_t nr_ringid; /* ring(s) we care about */ #define NETMAP_HW_RING 0x4000 /* single NIC ring pair */ #define NETMAP_SW_RING 0x2000 /* only host ring pair */ #define NETMAP_RING_MASK 0x0fff /* the ring number */ #define NETMAP_NO_TX_POLL 0x1000 /* no automatic txsync on poll */ #define NETMAP_DO_RX_POLL 0x8000 /* DO automatic rxsync on poll */ uint16_t nr_cmd; #define NETMAP_BDG_ATTACH 1 /* attach the NIC */ #define NETMAP_BDG_DETACH 2 /* detach the NIC */ #define NETMAP_BDG_REGOPS 3 /* register bridge callbacks */ #define NETMAP_BDG_LIST 4 /* get bridge's info */ #define NETMAP_BDG_VNET_HDR 5 /* set the port virtio-net-hdr length */ #define NETMAP_BDG_OFFSET NETMAP_BDG_VNET_HDR /* deprecated alias */ #define NETMAP_BDG_NEWIF 6 /* create a virtual port */ #define NETMAP_BDG_DELIF 7 /* destroy a virtual port */ uint16_t nr_arg1; /* reserve extra rings in NIOCREGIF */ #define NETMAP_BDG_HOST 1 /* attach the host stack on ATTACH */ uint16_t nr_arg2; uint32_t nr_arg3; /* req. extra buffers in NIOCREGIF */ uint32_t nr_flags; /* various modes, extends nr_ringid */ uint32_t spare2[1]; }; #define NR_REG_MASK 0xf /* values for nr_flags */ enum { NR_REG_DEFAULT = 0, /* backward compat, should not be used. */ NR_REG_ALL_NIC = 1, NR_REG_SW = 2, NR_REG_NIC_SW = 3, NR_REG_ONE_NIC = 4, NR_REG_PIPE_MASTER = 5, NR_REG_PIPE_SLAVE = 6, }; /* monitor uses the NR_REG to select the rings to monitor */ #define NR_MONITOR_TX 0x100 #define NR_MONITOR_RX 0x200 #define NR_ZCOPY_MON 0x400 /* request exclusive access to the selected rings */ #define NR_EXCLUSIVE 0x800 /* * FreeBSD uses the size value embedded in the _IOWR to determine * how much to copy in/out. So we need it to match the actual * data structure we pass. We put some spares in the structure * to ease compatibility with other versions */ #define NIOCGINFO _IOWR('i', 145, struct nmreq) /* return IF info */ #define NIOCREGIF _IOWR('i', 146, struct nmreq) /* interface register */ #define NIOCTXSYNC _IO('i', 148) /* sync tx queues */ #define NIOCRXSYNC _IO('i', 149) /* sync rx queues */ #define NIOCCONFIG _IOWR('i',150, struct nm_ifreq) /* for ext. modules */ #endif /* !NIOCREGIF */ /* * Helper functions for kernel and userspace */ /* * check if space is available in the ring. */ static inline int nm_ring_empty(struct netmap_ring *ring) { return (ring->cur == ring->tail); } /* * Opaque structure that is passed to an external kernel * module via ioctl(fd, NIOCCONFIG, req) for a user-owned * bridge port (at this point ephemeral VALE interface). */ #define NM_IFRDATA_LEN 256 struct nm_ifreq { char nifr_name[IFNAMSIZ]; char data[NM_IFRDATA_LEN]; }; #endif /* _NET_NETMAP_H_ */ fastnetmon-1.1.4/src/netmap_plugin/netmap_includes/net/netmap_user.h000066400000000000000000000524641343111404700257700ustar00rootroot00000000000000/* * Copyright (C) 2011-2014 Universita` di Pisa. 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. */ /* * $FreeBSD$ * * Functions and macros to manipulate netmap structures and packets * in userspace. See netmap(4) for more information. * * The address of the struct netmap_if, say nifp, is computed from the * value returned from ioctl(.., NIOCREG, ...) and the mmap region: * ioctl(fd, NIOCREG, &req); * mem = mmap(0, ... ); * nifp = NETMAP_IF(mem, req.nr_nifp); * (so simple, we could just do it manually) * * From there: * struct netmap_ring *NETMAP_TXRING(nifp, index) * struct netmap_ring *NETMAP_RXRING(nifp, index) * we can access ring->cur, ring->head, ring->tail, etc. * * ring->slot[i] gives us the i-th slot (we can access * directly len, flags, buf_idx) * * char *buf = NETMAP_BUF(ring, x) returns a pointer to * the buffer numbered x * * All ring indexes (head, cur, tail) should always move forward. * To compute the next index in a circular ring you can use * i = nm_ring_next(ring, i); * * To ease porting apps from pcap to netmap we supply a few fuctions * that can be called to open, close, read and write on netmap in a way * similar to libpcap. Note that the read/write function depend on * an ioctl()/select()/poll() being issued to refill rings or push * packets out. * * In order to use these, include #define NETMAP_WITH_LIBS * in the source file that invokes these functions. */ #ifndef _NET_NETMAP_USER_H_ #define _NET_NETMAP_USER_H_ #include #include /* apple needs sockaddr */ #include /* IFNAMSIZ */ #ifndef likely #define likely(x) __builtin_expect(!!(x), 1) #define unlikely(x) __builtin_expect(!!(x), 0) #endif /* likely and unlikely */ #include /* helper macro */ #define _NETMAP_OFFSET(type, ptr, offset) \ ((type)(void *)((char *)(ptr) + (offset))) #define NETMAP_IF(_base, _ofs) _NETMAP_OFFSET(struct netmap_if *, _base, _ofs) #define NETMAP_TXRING(nifp, index) _NETMAP_OFFSET(struct netmap_ring *, \ nifp, (nifp)->ring_ofs[index] ) #define NETMAP_RXRING(nifp, index) _NETMAP_OFFSET(struct netmap_ring *, \ nifp, (nifp)->ring_ofs[index + (nifp)->ni_tx_rings + 1] ) #define NETMAP_BUF(ring, index) \ ((char *)(ring) + (ring)->buf_ofs + ((index)*(ring)->nr_buf_size)) #define NETMAP_BUF_IDX(ring, buf) \ ( ((char *)(buf) - ((char *)(ring) + (ring)->buf_ofs) ) / \ (ring)->nr_buf_size ) static inline uint32_t nm_ring_next(struct netmap_ring *r, uint32_t i) { return ( unlikely(i + 1 == r->num_slots) ? 0 : i + 1); } /* * Return 1 if we have pending transmissions in the tx ring. * When everything is complete ring->head = ring->tail + 1 (modulo ring size) */ static inline int nm_tx_pending(struct netmap_ring *r) { return nm_ring_next(r, r->tail) != r->head; } static inline uint32_t nm_ring_space(struct netmap_ring *ring) { int ret = ring->tail - ring->cur; if (ret < 0) ret += ring->num_slots; return ret; } #ifdef NETMAP_WITH_LIBS /* * Support for simple I/O libraries. * Include other system headers required for compiling this. */ #ifndef HAVE_NETMAP_WITH_LIBS #define HAVE_NETMAP_WITH_LIBS #include #include #include #include /* memset */ #include #include /* EINVAL */ #include /* O_RDWR */ #include /* close() */ #include #include #ifdef NETMAP_NO_DEBUG #define ND(_fmt, ...) do {} while(0) #define D(_fmt, ...) do {} while(0) #define RD(lps, format, ...) do {} while(0) #else #ifndef ND /* debug macros */ /* debug support */ #define ND(_fmt, ...) do {} while(0) #define D(_fmt, ...) \ do { \ struct timeval _t0; \ gettimeofday(&_t0, NULL); \ fprintf(stderr, "%03d.%06d %s [%d] " _fmt "\n", \ (int)(_t0.tv_sec % 1000), (int)_t0.tv_usec, \ __FUNCTION__, __LINE__, ##__VA_ARGS__); \ } while (0) /* Rate limited version of "D", lps indicates how many per second */ #define RD(lps, format, ...) \ do { \ static int __t0, __cnt; \ struct timeval __xxts; \ gettimeofday(&__xxts, NULL); \ if (__t0 != __xxts.tv_sec) { \ __t0 = __xxts.tv_sec; \ __cnt = 0; \ } \ if (__cnt++ < lps) { \ D(format, ##__VA_ARGS__); \ } \ } while (0) #endif #endif struct nm_pkthdr { /* same as pcap_pkthdr */ struct timeval ts; uint32_t caplen; uint32_t len; }; struct nm_stat { /* same as pcap_stat */ u_int ps_recv; u_int ps_drop; u_int ps_ifdrop; #ifdef WIN32 u_int bs_capt; #endif /* WIN32 */ }; #define NM_ERRBUF_SIZE 512 struct nm_desc { struct nm_desc *self; /* point to self if netmap. */ int fd; void *mem; uint32_t memsize; int done_mmap; /* set if mem is the result of mmap */ struct netmap_if * const nifp; uint16_t first_tx_ring, last_tx_ring, cur_tx_ring; uint16_t first_rx_ring, last_rx_ring, cur_rx_ring; struct nmreq req; /* also contains the nr_name = ifname */ struct nm_pkthdr hdr; /* * The memory contains netmap_if, rings and then buffers. * Given a pointer (e.g. to nm_inject) we can compare with * mem/buf_start/buf_end to tell if it is a buffer or * some other descriptor in our region. * We also store a pointer to some ring as it helps in the * translation from buffer indexes to addresses. */ struct netmap_ring * const some_ring; void * const buf_start; void * const buf_end; /* parameters from pcap_open_live */ int snaplen; int promisc; int to_ms; char *errbuf; /* save flags so we can restore them on close */ uint32_t if_flags; uint32_t if_reqcap; uint32_t if_curcap; struct nm_stat st; char msg[NM_ERRBUF_SIZE]; }; /* * when the descriptor is open correctly, d->self == d * Eventually we should also use some magic number. */ #define P2NMD(p) ((struct nm_desc *)(p)) #define IS_NETMAP_DESC(d) ((d) && P2NMD(d)->self == P2NMD(d)) #define NETMAP_FD(d) (P2NMD(d)->fd) /* * this is a slightly optimized copy routine which rounds * to multiple of 64 bytes and is often faster than dealing * with other odd sizes. We assume there is enough room * in the source and destination buffers. * * XXX only for multiples of 64 bytes, non overlapped. */ static inline void nm_pkt_copy(const void *_src, void *_dst, int l) { const uint64_t *src = (const uint64_t *)_src; uint64_t *dst = (uint64_t *)_dst; if (unlikely(l >= 1024)) { memcpy(dst, src, l); return; } for (; likely(l > 0); l-=64) { *dst++ = *src++; *dst++ = *src++; *dst++ = *src++; *dst++ = *src++; *dst++ = *src++; *dst++ = *src++; *dst++ = *src++; *dst++ = *src++; } } /* * The callback, invoked on each received packet. Same as libpcap */ typedef void (*nm_cb_t)(u_char *, const struct nm_pkthdr *, const u_char *d); /* *--- the pcap-like API --- * * nm_open() opens a file descriptor, binds to a port and maps memory. * * ifname (netmap:foo or vale:foo) is the port name * a suffix can indicate the follwing: * ^ bind the host (sw) ring pair * * bind host and NIC ring pairs (transparent) * -NN bind individual NIC ring pair * {NN bind master side of pipe NN * }NN bind slave side of pipe NN * a suffix starting with + and the following flags, * in any order: * x exclusive access * z zero copy monitor * t monitor tx side * r monitor rx side * * req provides the initial values of nmreq before parsing ifname. * Remember that the ifname parsing will override the ring * number in nm_ringid, and part of nm_flags; * flags special functions, normally 0 * indicates which fields of *arg are significant * arg special functions, normally NULL * if passed a netmap_desc with mem != NULL, * use that memory instead of mmap. */ static struct nm_desc *nm_open(const char *ifname, const struct nmreq *req, uint64_t flags, const struct nm_desc *arg); /* * nm_open can import some fields from the parent descriptor. * These flags control which ones. * Also in flags you can specify NETMAP_NO_TX_POLL and NETMAP_DO_RX_POLL, * which set the initial value for these flags. * Note that the 16 low bits of the flags are reserved for data * that may go into the nmreq. */ enum { NM_OPEN_NO_MMAP = 0x040000, /* reuse mmap from parent */ NM_OPEN_IFNAME = 0x080000, /* nr_name, nr_ringid, nr_flags */ NM_OPEN_ARG1 = 0x100000, NM_OPEN_ARG2 = 0x200000, NM_OPEN_ARG3 = 0x400000, NM_OPEN_RING_CFG = 0x800000, /* tx|rx rings|slots */ }; /* * nm_close() closes and restores the port to its previous state */ static int nm_close(struct nm_desc *); /* * nm_inject() is the same as pcap_inject() * nm_dispatch() is the same as pcap_dispatch() * nm_nextpkt() is the same as pcap_next() */ static int nm_inject(struct nm_desc *, const void *, size_t); static int nm_dispatch(struct nm_desc *, int, nm_cb_t, u_char *); static u_char *nm_nextpkt(struct nm_desc *, struct nm_pkthdr *); /* * Try to open, return descriptor if successful, NULL otherwise. * An invalid netmap name will return errno = 0; * You can pass a pointer to a pre-filled nm_desc to add special * parameters. Flags is used as follows * NM_OPEN_NO_MMAP use the memory from arg, only * if the nr_arg2 (memory block) matches. * NM_OPEN_ARG1 use req.nr_arg1 from arg * NM_OPEN_ARG2 use req.nr_arg2 from arg * NM_OPEN_RING_CFG user ring config from arg */ static struct nm_desc * nm_open(const char *ifname, const struct nmreq *req, uint64_t new_flags, const struct nm_desc *arg) { struct nm_desc *d = NULL; const struct nm_desc *parent = arg; u_int namelen; uint32_t nr_ringid = 0, nr_flags, nr_reg; const char *port = NULL; #define MAXERRMSG 80 char errmsg[MAXERRMSG] = ""; enum { P_START, P_RNGSFXOK, P_GETNUM, P_FLAGS, P_FLAGSOK } p_state; long num; if (strncmp(ifname, "netmap:", 7) && strncmp(ifname, "vale", 4)) { errno = 0; /* name not recognised, not an error */ return NULL; } if (ifname[0] == 'n') ifname += 7; /* scan for a separator */ for (port = ifname; *port && !index("-*^{}/", *port); port++) ; namelen = port - ifname; if (namelen >= sizeof(d->req.nr_name)) { snprintf(errmsg, MAXERRMSG, "name too long"); goto fail; } p_state = P_START; nr_flags = NR_REG_ALL_NIC; /* default for no suffix */ while (*port) { switch (p_state) { case P_START: switch (*port) { case '^': /* only SW ring */ nr_flags = NR_REG_SW; p_state = P_RNGSFXOK; break; case '*': /* NIC and SW */ nr_flags = NR_REG_NIC_SW; p_state = P_RNGSFXOK; break; case '-': /* one NIC ring pair */ nr_flags = NR_REG_ONE_NIC; p_state = P_GETNUM; break; case '{': /* pipe (master endpoint) */ nr_flags = NR_REG_PIPE_MASTER; p_state = P_GETNUM; break; case '}': /* pipe (slave endoint) */ nr_flags = NR_REG_PIPE_SLAVE; p_state = P_GETNUM; break; case '/': /* start of flags */ p_state = P_FLAGS; break; default: snprintf(errmsg, MAXERRMSG, "unknown modifier: '%c'", *port); goto fail; } port++; break; case P_RNGSFXOK: switch (*port) { case '/': p_state = P_FLAGS; break; default: snprintf(errmsg, MAXERRMSG, "unexpected character: '%c'", *port); goto fail; } port++; break; case P_GETNUM: num = strtol(port, (char **)&port, 10); if (num < 0 || num >= NETMAP_RING_MASK) { snprintf(errmsg, MAXERRMSG, "'%ld' out of range [0, %d)", num, NETMAP_RING_MASK); goto fail; } nr_ringid = num & NETMAP_RING_MASK; p_state = P_RNGSFXOK; break; case P_FLAGS: case P_FLAGSOK: switch (*port) { case 'x': nr_flags |= NR_EXCLUSIVE; break; case 'z': nr_flags |= NR_ZCOPY_MON; break; case 't': nr_flags |= NR_MONITOR_TX; break; case 'r': nr_flags |= NR_MONITOR_RX; break; default: snprintf(errmsg, MAXERRMSG, "unrecognized flag: '%c'", *port); goto fail; } port++; p_state = P_FLAGSOK; break; } } if (p_state != P_START && p_state != P_RNGSFXOK && p_state != P_FLAGSOK) { snprintf(errmsg, MAXERRMSG, "unexpected end of port name"); goto fail; } ND("flags: %s %s %s %s", (nr_flags & NR_EXCLUSIVE) ? "EXCLUSIVE" : "", (nr_flags & NR_ZCOPY_MON) ? "ZCOPY_MON" : "", (nr_flags & NR_MONITOR_TX) ? "MONITOR_TX" : "", (nr_flags & NR_MONITOR_RX) ? "MONITOR_RX" : ""); d = (struct nm_desc *)calloc(1, sizeof(*d)); if (d == NULL) { snprintf(errmsg, MAXERRMSG, "nm_desc alloc failure"); errno = ENOMEM; return NULL; } d->self = d; /* set this early so nm_close() works */ d->fd = open("/dev/netmap", O_RDWR); if (d->fd < 0) { snprintf(errmsg, MAXERRMSG, "cannot open /dev/netmap: %s", strerror(errno)); goto fail; } if (req) d->req = *req; d->req.nr_version = NETMAP_API; d->req.nr_ringid &= ~NETMAP_RING_MASK; /* these fields are overridden by ifname and flags processing */ d->req.nr_ringid |= nr_ringid; d->req.nr_flags = nr_flags; memcpy(d->req.nr_name, ifname, namelen); d->req.nr_name[namelen] = '\0'; /* optionally import info from parent */ if (IS_NETMAP_DESC(parent) && new_flags) { if (new_flags & NM_OPEN_ARG1) D("overriding ARG1 %d", parent->req.nr_arg1); d->req.nr_arg1 = new_flags & NM_OPEN_ARG1 ? parent->req.nr_arg1 : 4; if (new_flags & NM_OPEN_ARG2) D("overriding ARG2 %d", parent->req.nr_arg2); d->req.nr_arg2 = new_flags & NM_OPEN_ARG2 ? parent->req.nr_arg2 : 0; if (new_flags & NM_OPEN_ARG3) D("overriding ARG3 %d", parent->req.nr_arg3); d->req.nr_arg3 = new_flags & NM_OPEN_ARG3 ? parent->req.nr_arg3 : 0; if (new_flags & NM_OPEN_RING_CFG) { D("overriding RING_CFG"); d->req.nr_tx_slots = parent->req.nr_tx_slots; d->req.nr_rx_slots = parent->req.nr_rx_slots; d->req.nr_tx_rings = parent->req.nr_tx_rings; d->req.nr_rx_rings = parent->req.nr_rx_rings; } if (new_flags & NM_OPEN_IFNAME) { D("overriding ifname %s ringid 0x%x flags 0x%x", parent->req.nr_name, parent->req.nr_ringid, parent->req.nr_flags); memcpy(d->req.nr_name, parent->req.nr_name, sizeof(d->req.nr_name)); d->req.nr_ringid = parent->req.nr_ringid; d->req.nr_flags = parent->req.nr_flags; } } /* add the *XPOLL flags */ d->req.nr_ringid |= new_flags & (NETMAP_NO_TX_POLL | NETMAP_DO_RX_POLL); if (ioctl(d->fd, NIOCREGIF, &d->req)) { snprintf(errmsg, MAXERRMSG, "NIOCREGIF failed: %s", strerror(errno)); goto fail; } if (IS_NETMAP_DESC(parent) && parent->mem && parent->req.nr_arg2 == d->req.nr_arg2) { /* do not mmap, inherit from parent */ d->memsize = parent->memsize; d->mem = parent->mem; } else { /* XXX TODO: check if memsize is too large (or there is overflow) */ d->memsize = d->req.nr_memsize; d->mem = mmap(0, d->memsize, PROT_WRITE | PROT_READ, MAP_SHARED, d->fd, 0); if (d->mem == MAP_FAILED) { snprintf(errmsg, MAXERRMSG, "mmap failed: %s", strerror(errno)); goto fail; } d->done_mmap = 1; } { struct netmap_if *nifp = NETMAP_IF(d->mem, d->req.nr_offset); struct netmap_ring *r = NETMAP_RXRING(nifp, ); *(struct netmap_if **)(uintptr_t)&(d->nifp) = nifp; *(struct netmap_ring **)(uintptr_t)&d->some_ring = r; *(void **)(uintptr_t)&d->buf_start = NETMAP_BUF(r, 0); *(void **)(uintptr_t)&d->buf_end = (char *)d->mem + d->memsize; } nr_reg = d->req.nr_flags & NR_REG_MASK; if (nr_reg == NR_REG_SW) { /* host stack */ d->first_tx_ring = d->last_tx_ring = d->req.nr_tx_rings; d->first_rx_ring = d->last_rx_ring = d->req.nr_rx_rings; } else if (nr_reg == NR_REG_ALL_NIC) { /* only nic */ d->first_tx_ring = 0; d->first_rx_ring = 0; d->last_tx_ring = d->req.nr_tx_rings - 1; d->last_rx_ring = d->req.nr_rx_rings - 1; } else if (nr_reg == NR_REG_NIC_SW) { d->first_tx_ring = 0; d->first_rx_ring = 0; d->last_tx_ring = d->req.nr_tx_rings; d->last_rx_ring = d->req.nr_rx_rings; } else if (nr_reg == NR_REG_ONE_NIC) { /* XXX check validity */ d->first_tx_ring = d->last_tx_ring = d->first_rx_ring = d->last_rx_ring = d->req.nr_ringid & NETMAP_RING_MASK; } else { /* pipes */ d->first_tx_ring = d->last_tx_ring = 0; d->first_rx_ring = d->last_rx_ring = 0; } #ifdef DEBUG_NETMAP_USER { /* debugging code */ int i; D("%s tx %d .. %d %d rx %d .. %d %d", ifname, d->first_tx_ring, d->last_tx_ring, d->req.nr_tx_rings, d->first_rx_ring, d->last_rx_ring, d->req.nr_rx_rings); for (i = 0; i <= d->req.nr_tx_rings; i++) { struct netmap_ring *r = NETMAP_TXRING(d->nifp, i); D("TX%d %p h %d c %d t %d", i, r, r->head, r->cur, r->tail); } for (i = 0; i <= d->req.nr_rx_rings; i++) { struct netmap_ring *r = NETMAP_RXRING(d->nifp, i); D("RX%d %p h %d c %d t %d", i, r, r->head, r->cur, r->tail); } } #endif /* debugging */ d->cur_tx_ring = d->first_tx_ring; d->cur_rx_ring = d->first_rx_ring; return d; fail: nm_close(d); if (errmsg[0]) D("%s %s", errmsg, ifname); if (errno == 0) errno = EINVAL; return NULL; } static int nm_close(struct nm_desc *d) { /* * ugly trick to avoid unused warnings */ static void *__xxzt[] __attribute__ ((unused)) = { (void *)nm_open, (void *)nm_inject, (void *)nm_dispatch, (void *)nm_nextpkt } ; if (d == NULL || d->self != d) return EINVAL; if (d->done_mmap && d->mem) munmap(d->mem, d->memsize); if (d->fd != -1) close(d->fd); bzero(d, sizeof(*d)); free(d); return 0; } /* * Same prototype as pcap_inject(), only need to cast. */ static int nm_inject(struct nm_desc *d, const void *buf, size_t size) { u_int c, n = d->last_tx_ring - d->first_tx_ring + 1; for (c = 0; c < n ; c++) { /* compute current ring to use */ struct netmap_ring *ring; uint32_t i, idx; uint32_t ri = d->cur_tx_ring + c; if (ri > d->last_tx_ring) ri = d->first_tx_ring; ring = NETMAP_TXRING(d->nifp, ri); if (nm_ring_empty(ring)) { continue; } i = ring->cur; idx = ring->slot[i].buf_idx; ring->slot[i].len = size; nm_pkt_copy(buf, NETMAP_BUF(ring, idx), size); d->cur_tx_ring = ri; ring->head = ring->cur = nm_ring_next(ring, i); return size; } return 0; /* fail */ } /* * Same prototype as pcap_dispatch(), only need to cast. */ static int nm_dispatch(struct nm_desc *d, int cnt, nm_cb_t cb, u_char *arg) { int n = d->last_rx_ring - d->first_rx_ring + 1; int c, got = 0, ri = d->cur_rx_ring; if (cnt == 0) cnt = -1; /* cnt == -1 means infinite, but rings have a finite amount * of buffers and the int is large enough that we never wrap, * so we can omit checking for -1 */ for (c=0; c < n && cnt != got; c++) { /* compute current ring to use */ struct netmap_ring *ring; ri = d->cur_rx_ring + c; if (ri > d->last_rx_ring) ri = d->first_rx_ring; ring = NETMAP_RXRING(d->nifp, ri); for ( ; !nm_ring_empty(ring) && cnt != got; got++) { u_int i = ring->cur; u_int idx = ring->slot[i].buf_idx; u_char *buf = (u_char *)NETMAP_BUF(ring, idx); // __builtin_prefetch(buf); d->hdr.len = d->hdr.caplen = ring->slot[i].len; d->hdr.ts = ring->ts; cb(arg, &d->hdr, buf); ring->head = ring->cur = nm_ring_next(ring, i); } } d->cur_rx_ring = ri; return got; } static u_char * nm_nextpkt(struct nm_desc *d, struct nm_pkthdr *hdr) { int ri = d->cur_rx_ring; do { /* compute current ring to use */ struct netmap_ring *ring = NETMAP_RXRING(d->nifp, ri); if (!nm_ring_empty(ring)) { u_int i = ring->cur; u_int idx = ring->slot[i].buf_idx; u_char *buf = (u_char *)NETMAP_BUF(ring, idx); // __builtin_prefetch(buf); hdr->ts = ring->ts; hdr->len = hdr->caplen = ring->slot[i].len; ring->cur = nm_ring_next(ring, i); /* we could postpone advancing head if we want * to hold the buffer. This can be supported in * the future. */ ring->head = ring->cur; d->cur_rx_ring = ri; return buf; } ri++; if (ri > d->last_rx_ring) ri = d->first_rx_ring; } while (ri != d->cur_rx_ring); return NULL; /* nothing found */ } #endif /* !HAVE_NETMAP_WITH_LIBS */ #endif /* NETMAP_WITH_LIBS */ #endif /* _NET_NETMAP_USER_H_ */ fastnetmon-1.1.4/src/networks_list000066400000000000000000000000001343111404700173000ustar00rootroot00000000000000fastnetmon-1.1.4/src/networks_whitelist000066400000000000000000000000001343111404700203410ustar00rootroot00000000000000fastnetmon-1.1.4/src/notify_about_attack.sh000077500000000000000000000023411343111404700210500ustar00rootroot00000000000000#!/usr/bin/env bash # # Hello, lovely FastNetMon customer! I'm really happy to see you here! # Pavel Odintsov, author # # This script will get following params: # $1 client_ip_as_string # $2 data_direction # $3 pps_as_string # $4 action (ban or unban) email_notify="root,please_fix_this_email@domain.ru" # # Please be carefult! You should not remove cat > # if [ "$4" = "unban" ]; then # No details arrived to stdin here # Unban actions if used exit 0 fi # # For ban and attack_details actions we will receive attack details to stdin # if option notify_script_pass_details enabled in FastNetMon's configuration file # # If you do not need this details, please set option notify_script_pass_details to "no". # # Please do not remove "cat" command if you have notify_script_pass_details enabled, because # FastNetMon will crash in this case (it expect read of data from script side). # if [ "$4" = "ban" ]; then cat | mail -s "FastNetMon Guard: IP $1 blocked because $2 attack with power $3 pps" $email_notify; # You can add ban code here! exit 0 fi if [ "$4" == "attack_details" ]; then cat | mail -s "FastNetMon Guard: IP $1 blocked because $2 attack with power $3 pps" $email_notify; exit 0 fi fastnetmon-1.1.4/src/packet_storage.h000066400000000000000000000077321343111404700176350ustar00rootroot00000000000000#ifndef PACKET_STORAGE_H #define PACKET_STORAGE_H #include #include #include "fastnetmon_pcap_format.h" class packet_storage_t { public: packet_storage_t() { memory_pointer = NULL; memory_pos = NULL; buffer_size = 0; // TODO: fix hardcoded mtu size this!!! max_packet_size = 1500; } bool allocate_buffer(unsigned int buffer_size_in_packets) { unsigned int memory_size_in_bytes = buffer_size_in_packets * (max_packet_size + sizeof(fastnetmon_pcap_pkthdr)) + sizeof(fastnetmon_pcap_file_header); // std::cout << "We will allocate " << memory_size_in_bytes << std::endl; memory_pointer = (unsigned char*)malloc( memory_size_in_bytes ); if (memory_pointer != NULL) { this->buffer_size = memory_size_in_bytes; memory_pos = memory_pointer; // Add header to newely allocated memory block return this->write_header(); } else { return false; } } bool write_binary_data(void* data_pointer, unsigned int length) { if (we_have_free_space_for_x_bytes(length)) { memcpy(memory_pos, data_pointer, length); memory_pos += length; return true; } else { return false; } } bool write_packet(void* payload_pointer, unsigned int length) { // TODO: performance killer! Check it! bool we_do_timestamps = true; struct timeval current_time; current_time.tv_sec = 0; current_time.tv_usec = 0; if (we_do_timestamps) { gettimeofday(¤t_time, NULL); } struct fastnetmon_pcap_pkthdr pcap_packet_header; pcap_packet_header.ts_sec = current_time.tv_sec; pcap_packet_header.ts_usec = current_time.tv_usec; // Store full length of packet pcap_packet_header.orig_len = length; if (length > max_packet_size) { // We whould crop packet because it's too big pcap_packet_header.incl_len = max_packet_size; } else { pcap_packet_header.incl_len = length; } if (!this->write_binary_data(&pcap_packet_header, sizeof(pcap_packet_header))) { return false; } return (this->write_binary_data(payload_pointer, pcap_packet_header.incl_len)); } bool we_have_free_space_for_x_bytes(unsigned int length) { if (this->get_used_memory() + length <= this->buffer_size) { return true; } else { return false; } } bool write_header() { struct fastnetmon_pcap_file_header pcap_header; fill_pcap_header(&pcap_header, max_packet_size); return this->write_binary_data(&pcap_header, sizeof(pcap_header)); } int64_t get_used_memory() { return memory_pos - memory_pointer; } bool deallocate_buffer() { if (memory_pointer == NULL or buffer_size == 0) { return true; } free(this->memory_pointer); this->memory_pointer = NULL; this->memory_pos = NULL; this->buffer_size = 0; return true; } void* get_buffer_pointer() { return memory_pointer; } unsigned int get_max_packet_size() { return this->max_packet_size; } void set_max_packet_size(unsigned int new_max_packet_size) { this->max_packet_size = new_max_packet_size; } private: unsigned char* memory_pointer; unsigned char* memory_pos; unsigned int buffer_size; unsigned int max_packet_size; }; #endif fastnetmon-1.1.4/src/patches/000077500000000000000000000000001343111404700161075ustar00rootroot00000000000000fastnetmon-1.1.4/src/patches/0001-Fix-netmap-code-for-compatibility-with-C-boost.patch000066400000000000000000000074661343111404700303530ustar00rootroot00000000000000From 463481e630a1f9717bdb6a1eb8c993464393d7e7 Mon Sep 17 00:00:00 2001 From: Pavel Odintsov Date: Mon, 2 Mar 2015 15:53:11 +0300 Subject: [PATCH] Fix netmap code for compatibility with C++ boost --- tests/netmap_includes/net/netmap_user.h | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/tests/netmap_includes/net/netmap_user.h b/tests/netmap_includes/net/netmap_user.h index aab6c35..17c2308 100644 --- a/tests/netmap_includes/net/netmap_user.h +++ b/tests/netmap_includes/net/netmap_user.h @@ -147,7 +147,7 @@ nm_ring_space(struct netmap_ring *ring) #ifndef ND /* debug macros */ /* debug support */ #define ND(_fmt, ...) do {} while(0) -#define D(_fmt, ...) \ +#define NETMAP_DEBUG(_fmt, ...) \ do { \ struct timeval _t0; \ gettimeofday(&_t0, NULL); \ @@ -167,7 +167,7 @@ nm_ring_space(struct netmap_ring *ring) __cnt = 0; \ } \ if (__cnt++ < lps) { \ - D(format, ##__VA_ARGS__); \ + NETMAP_DEBUG(format, ##__VA_ARGS__); \ } \ } while (0) #endif @@ -432,26 +432,26 @@ nm_open(const char *ifname, const struct nmreq *req, /* optionally import info from parent */ if (IS_NETMAP_DESC(parent) && new_flags) { if (new_flags & NM_OPEN_ARG1) - D("overriding ARG1 %d", parent->req.nr_arg1); + NETMAP_DEBUG("overriding ARG1 %d", parent->req.nr_arg1); d->req.nr_arg1 = new_flags & NM_OPEN_ARG1 ? parent->req.nr_arg1 : 4; if (new_flags & NM_OPEN_ARG2) - D("overriding ARG2 %d", parent->req.nr_arg2); + NETMAP_DEBUG("overriding ARG2 %d", parent->req.nr_arg2); d->req.nr_arg2 = new_flags & NM_OPEN_ARG2 ? parent->req.nr_arg2 : 0; if (new_flags & NM_OPEN_ARG3) - D("overriding ARG3 %d", parent->req.nr_arg3); + NETMAP_DEBUG("overriding ARG3 %d", parent->req.nr_arg3); d->req.nr_arg3 = new_flags & NM_OPEN_ARG3 ? parent->req.nr_arg3 : 0; if (new_flags & NM_OPEN_RING_CFG) { - D("overriding RING_CFG"); + NETMAP_DEBUG("overriding RING_CFG"); d->req.nr_tx_slots = parent->req.nr_tx_slots; d->req.nr_rx_slots = parent->req.nr_rx_slots; d->req.nr_tx_rings = parent->req.nr_tx_rings; d->req.nr_rx_rings = parent->req.nr_rx_rings; } if (new_flags & NM_OPEN_IFNAME) { - D("overriding ifname %s ringid 0x%x flags 0x%x", + NETMAP_DEBUG("overriding ifname %s ringid 0x%x flags 0x%x", parent->req.nr_name, parent->req.nr_ringid, parent->req.nr_flags); memcpy(d->req.nr_name, parent->req.nr_name, @@ -521,16 +521,16 @@ nm_open(const char *ifname, const struct nmreq *req, { /* debugging code */ int i; - D("%s tx %d .. %d %d rx %d .. %d %d", ifname, + NETMAP_DEBUG("%s tx %d .. %d %d rx %d .. %d %d", ifname, d->first_tx_ring, d->last_tx_ring, d->req.nr_tx_rings, d->first_rx_ring, d->last_rx_ring, d->req.nr_rx_rings); for (i = 0; i <= d->req.nr_tx_rings; i++) { struct netmap_ring *r = NETMAP_TXRING(d->nifp, i); - D("TX%d %p h %d c %d t %d", i, r, r->head, r->cur, r->tail); + NETMAP_DEBUG("TX%d %p h %d c %d t %d", i, r, r->head, r->cur, r->tail); } for (i = 0; i <= d->req.nr_rx_rings; i++) { struct netmap_ring *r = NETMAP_RXRING(d->nifp, i); - D("RX%d %p h %d c %d t %d", i, r, r->head, r->cur, r->tail); + NETMAP_DEBUG("RX%d %p h %d c %d t %d", i, r, r->head, r->cur, r->tail); } } #endif /* debugging */ @@ -542,7 +542,7 @@ nm_open(const char *ifname, const struct nmreq *req, fail: nm_close(d); if (errmsg) - D("%s %s", errmsg, ifname); + NETMAP_DEBUG("%s %s", errmsg, ifname); if (errno == 0) errno = EINVAL; return NULL; -- 1.7.10.4 fastnetmon-1.1.4/src/pcap_plugin/000077500000000000000000000000001343111404700167615ustar00rootroot00000000000000fastnetmon-1.1.4/src/pcap_plugin/pcap_collector.cpp000066400000000000000000000175401343111404700224650ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include // struct arphdr #include #include #include #include #include // log4cpp logging facility #include "log4cpp/Category.hh" #include "log4cpp/Appender.hh" #include "log4cpp/FileAppender.hh" #include "log4cpp/OstreamAppender.hh" #include "log4cpp/Layout.hh" #include "log4cpp/BasicLayout.hh" #include "log4cpp/PatternLayout.hh" #include "log4cpp/Priority.hh" #include "pcap_collector.h" // Standard shift for type DLT_EN10MB, Ethernet unsigned int DATA_SHIFT_VALUE = 14; /* Complete list of ethertypes: http://en.wikipedia.org/wiki/EtherType */ /* This is the decimal equivalent of the VLAN tag's ether frame type */ #define VLAN_ETHERTYPE 0x8100 #define IP_ETHERTYPE 0x0800 #define IP6_ETHERTYPE 0x86dd #define ARP_ETHERTYPE 0x0806 /* 802.1Q VLAN tags are 4 bytes long. */ #define VLAN_HDRLEN 4 #ifndef DLT_LINUX_SLL #define DLT_LINUX_SLL 113 #endif extern log4cpp::Category& logger; extern std::map configuration_map; // This variable name should be uniq for every plugin! process_packet_pointer pcap_process_func_ptr = NULL; // Enlarge receive buffer for PCAP for minimize packet drops unsigned int pcap_buffer_size_mbytes = 10; // pcap handler, we want it as global variable beacuse it used in singnal handler pcap_t* descr = NULL; char errbuf[PCAP_ERRBUF_SIZE]; struct pcap_pkthdr hdr; // Prototypes void parse_packet(u_char* user, struct pcap_pkthdr* packethdr, const u_char* packetptr); void pcap_main_loop(const char* dev); void start_pcap_collection(process_packet_pointer func_ptr) { logger << log4cpp::Priority::INFO << "Pcap plugin started"; pcap_process_func_ptr = func_ptr; std::string interface_for_listening = ""; if (configuration_map.count("interfaces") != 0) { interface_for_listening = configuration_map["interfaces"]; } logger << log4cpp::Priority::INFO << "Pcap will sniff interface: " << interface_for_listening; pcap_main_loop(interface_for_listening.c_str()); } void stop_pcap_collection() { // stop pcap loop pcap_breakloop(descr); } // We do not use this function now! It's buggy! void parse_packet(u_char* user, struct pcap_pkthdr* packethdr, const u_char* packetptr) { struct ip* iphdr; struct tcphdr* tcphdr; struct udphdr* udphdr; struct ether_header* eptr; /* net/ethernet.h */ eptr = (struct ether_header*)packetptr; if (ntohs(eptr->ether_type) == VLAN_ETHERTYPE) { // It's tagged traffic we should sjoft for 4 bytes for getting the data packetptr += DATA_SHIFT_VALUE + VLAN_HDRLEN; } else if (ntohs(eptr->ether_type) == IP_ETHERTYPE) { // Skip the datalink layer header and get the IP header fields. packetptr += DATA_SHIFT_VALUE; } else if (ntohs(eptr->ether_type) == IP6_ETHERTYPE or ntohs(eptr->ether_type) == ARP_ETHERTYPE) { // we know about it but does't not care now } else { // printf("Packet with non standard ethertype found: 0x%x\n", ntohs(eptr->ether_type)); } iphdr = (struct ip*)packetptr; // src/dst UO is an in_addr, http://man7.org/linux/man-pages/man7/ip.7.html uint32_t src_ip = iphdr->ip_src.s_addr; uint32_t dst_ip = iphdr->ip_dst.s_addr; // The ntohs() function converts the unsigned short integer netshort from network byte order to // host byte order unsigned int packet_length = ntohs(iphdr->ip_len); simple_packet current_packet; // Advance to the transport layer header then parse and display // the fields based on the type of hearder: tcp, udp or icmp packetptr += 4 * iphdr->ip_hl; switch (iphdr->ip_p) { case IPPROTO_TCP: tcphdr = (struct tcphdr*)packetptr; #if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__) || defined(__OpenBSD__) current_packet.source_port = ntohs(tcphdr->th_sport); #else current_packet.source_port = ntohs(tcphdr->source); #endif #if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__) || defined(__OpenBSD__) current_packet.destination_port = ntohs(tcphdr->th_dport); #else current_packet.destination_port = ntohs(tcphdr->dest); #endif break; case IPPROTO_UDP: udphdr = (struct udphdr*)packetptr; #if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__) || defined(__OpenBSD__) current_packet.source_port = ntohs(udphdr->uh_sport); #else current_packet.source_port = ntohs(udphdr->source); #endif #if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__) || defined(__OpenBSD__) current_packet.destination_port = ntohs(udphdr->uh_dport); #else current_packet.destination_port = ntohs(udphdr->dest); #endif break; case IPPROTO_ICMP: // there are no port for ICMP current_packet.source_port = 0; current_packet.destination_port = 0; break; } current_packet.protocol = iphdr->ip_p; current_packet.src_ip = src_ip; current_packet.dst_ip = dst_ip; current_packet.length = packet_length; // Do packet processing pcap_process_func_ptr(current_packet); } void pcap_main_loop(const char* dev) { char errbuf[PCAP_ERRBUF_SIZE]; /* open device for reading in promiscuous mode */ int promisc = 1; bpf_u_int32 maskp; /* subnet mask */ bpf_u_int32 netp; /* ip */ logger << log4cpp::Priority::INFO << "Start listening on " << dev; /* Get the network address and mask */ pcap_lookupnet(dev, &netp, &maskp, errbuf); descr = pcap_create(dev, errbuf); if (descr == NULL) { logger << log4cpp::Priority::ERROR << "pcap_create was failed with error: " << errbuf; exit(0); } // Setting up 1MB buffer int set_buffer_size_res = pcap_set_buffer_size(descr, pcap_buffer_size_mbytes * 1024 * 1024); if (set_buffer_size_res != 0) { if (set_buffer_size_res == PCAP_ERROR_ACTIVATED) { logger << log4cpp::Priority::ERROR << "Can't set buffer size because pcap already activated\n"; exit(1); } else { logger << log4cpp::Priority::ERROR << "Can't set buffer size due to error: " << set_buffer_size_res; exit(1); } } if (pcap_set_promisc(descr, promisc) != 0) { logger << log4cpp::Priority::ERROR << "Can't activate promisc mode for interface: " << dev; exit(1); } if (pcap_activate(descr) != 0) { logger << log4cpp::Priority::ERROR << "Call pcap_activate was failed: " << pcap_geterr(descr); exit(1); } // man pcap-linktype int link_layer_header_type = pcap_datalink(descr); if (link_layer_header_type == DLT_EN10MB) { DATA_SHIFT_VALUE = 14; } else if (link_layer_header_type == DLT_LINUX_SLL) { DATA_SHIFT_VALUE = 16; } else { logger << log4cpp::Priority::INFO << "We did not support link type:" << link_layer_header_type; exit(0); } pcap_loop(descr, -1, (pcap_handler)parse_packet, NULL); } std::string get_pcap_stats() { std::stringstream output_buffer; struct pcap_stat current_pcap_stats; if (pcap_stats(descr, ¤t_pcap_stats) == 0) { output_buffer << "PCAP statistics" << "\n" << "Received packets: " << current_pcap_stats.ps_recv << "\n" << "Dropped packets: " << current_pcap_stats.ps_drop << " (" << int((double)current_pcap_stats.ps_drop / current_pcap_stats.ps_recv * 100) << "%)" << "\n" << "Dropped by driver or interface: " << current_pcap_stats.ps_ifdrop << "\n"; } return output_buffer.str(); } fastnetmon-1.1.4/src/pcap_plugin/pcap_collector.h000066400000000000000000000003431343111404700221230ustar00rootroot00000000000000#ifndef PCAP_PLUGIN_H #define PCAP_PLUGIN_H #include "../fastnetmon_types.h" #include void start_pcap_collection(process_packet_pointer func_ptr); void stop_pcap_collection(); std::string get_pcap_stats(); #endif fastnetmon-1.1.4/src/pcap_reader.cpp000066400000000000000000000222011343111404700174260ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include "fastnetmon_pcap_format.h" #ifdef ENABLE_DPI #include "fast_dpi.h" #endif #include #include #include #include "netflow_plugin/netflow_collector.h" #include "sflow_plugin/sflow_collector.h" #include "sflow_plugin/sflow_data.h" #include "sflow_plugin/sflow.h" #include "fastnetmon_packet_parser.h" #include "fastnetmon_types.h" #include "fast_library.h" #include "log4cpp/Category.hh" #include "log4cpp/Appender.hh" #include "log4cpp/FileAppender.hh" #include "log4cpp/OstreamAppender.hh" #include "log4cpp/Layout.hh" #include "log4cpp/BasicLayout.hh" #include "log4cpp/PatternLayout.hh" #include "log4cpp/Priority.hh" #include "unified_parser.hpp" // Fake config std::map configuration_map; std::string log_file_path = "/tmp/fastnetmon_pcap_reader.log"; log4cpp::Category& logger = log4cpp::Category::getRoot(); uint64_t total_unparsed_packets = 0; uint64_t dns_amplification_packets = 0; uint64_t ntp_amplification_packets = 0; uint64_t ssdp_amplification_packets = 0; uint64_t raw_parsed_packets = 0; uint64_t raw_unparsed_packets = 0; /* It's prototype for moc testing of FastNetMon, it's very useful for netflow or direct packet * parsers debug */ void init_logging() { log4cpp::PatternLayout* layout = new log4cpp::PatternLayout(); layout->setConversionPattern("%d [%p] %m%n"); log4cpp::Appender* appender = new log4cpp::FileAppender("default", log_file_path); appender->setLayout(layout); logger.setPriority(log4cpp::Priority::INFO); logger.addAppender(appender); logger.info("Logger initialized!"); } void pcap_parse_packet(const char* flow_type, char* buffer, uint32_t len); void my_fastnetmon_packet_handler(simple_packet& current_packet) { std::cout << print_simple_packet(current_packet); } extern process_packet_pointer netflow_process_func_ptr; extern process_packet_pointer sflow_process_func_ptr; char* flow_type = NULL; #ifdef ENABLE_DPI struct ndpi_detection_module_struct* my_ndpi_struct = NULL; u_int32_t ndpi_size_flow_struct = 0; u_int32_t ndpi_size_id_struct = 0; #endif void pcap_parse_packet(char* buffer, uint32_t len, uint32_t snap_len) { struct pfring_pkthdr packet_header; memset(&packet_header, 0, sizeof(packet_header)); packet_header.len = len; packet_header.caplen = snap_len; fastnetmon_parse_pkt((u_char*)buffer, &packet_header, 4, 1, 0); // char print_buffer[512]; // fastnetmon_print_parsed_pkt(print_buffer, 512, (u_char*)buffer, &packet_header); // logger.info("%s", print_buffer); char* payload_ptr = packet_header.extended_hdr.parsed_pkt.offset.payload_offset + buffer; if (packet_header.len < packet_header.extended_hdr.parsed_pkt.offset.payload_offset) { printf("Something goes wrong! Offset %u is bigger than total packet length %u\n", packet_header.extended_hdr.parsed_pkt.offset.payload_offset, packet_header.len); return; } unsigned int payload_length = packet_header.len - packet_header.extended_hdr.parsed_pkt.offset.payload_offset; if (strcmp(flow_type, "netflow") == 0) { netflow_process_func_ptr = my_fastnetmon_packet_handler; std::string fake_peer_ip = "10.0.1.2"; process_netflow_packet((u_int8_t*)payload_ptr, payload_length, fake_peer_ip); } else if (strcmp(flow_type, "sflow") == 0) { sflow_process_func_ptr = my_fastnetmon_packet_handler; SFSample sample; memset(&sample, 0, sizeof(sample)); sample.rawSample = (uint8_t*)payload_ptr; sample.rawSampleLen = payload_length; sample.sourceIP.type = SFLADDRESSTYPE_IP_V4; read_sflow_datagram(&sample); } else if (strcmp(flow_type, "raw") == 0) { // We do not need parsed data here struct pfring_pkthdr raw_packet_header; memset(&raw_packet_header, 0, sizeof(raw_packet_header)); raw_packet_header.len = len; raw_packet_header.caplen = snap_len; int parser_return_code = fastnetmon_parse_pkt((u_char*)buffer, &raw_packet_header, 4, 1, 0); // We are not interested so much in l2 data and we interested only in l3 data here and more if (parser_return_code < 3) { printf("Parser failed for with code %d following packet with number %llu\n", parser_return_code, raw_unparsed_packets + raw_parsed_packets); raw_unparsed_packets++; } else { raw_parsed_packets++; } char print_buffer[512]; fastnetmon_print_parsed_pkt(print_buffer, 512, (u_char*)buffer, &raw_packet_header); printf("Raw parser: %s", print_buffer); simple_packet packet; // TODO: add support for caplen here! if (parse_raw_packet_to_simple_packet((u_char*)buffer, len, packet, false)) { std::cout << "High level parser: " << print_simple_packet(packet) << std::endl; } else { printf("High level parser failed\n"); } } else if (strcmp(flow_type, "dpi") == 0) { #ifdef ENABLE_DPI struct ndpi_id_struct *src = NULL; struct ndpi_id_struct *dst = NULL; struct ndpi_flow_struct *flow = NULL; src = (struct ndpi_id_struct*)malloc(ndpi_size_id_struct); memset(src, 0, ndpi_size_id_struct); dst = (struct ndpi_id_struct*)malloc(ndpi_size_id_struct); memset(dst, 0, ndpi_size_id_struct); flow = (struct ndpi_flow_struct *)malloc(ndpi_size_flow_struct); memset(flow, 0, ndpi_size_flow_struct); uint32_t current_tickt = 0; uint8_t* iph = (uint8_t*)(&buffer[packet_header.extended_hdr.parsed_pkt.offset.l3_offset]); unsigned int ipsize = packet_header.len; ndpi_protocol detected_protocol = ndpi_detection_process_packet(my_ndpi_struct, flow, iph, ipsize, current_tickt, src, dst); char* protocol_name = ndpi_get_proto_name(my_ndpi_struct, detected_protocol.protocol); char* master_protocol_name = ndpi_get_proto_name(my_ndpi_struct, detected_protocol.master_protocol); printf("Protocol: %s master protocol: %s\n", protocol_name, master_protocol_name); if (detected_protocol.protocol == NDPI_PROTOCOL_DNS) { // It's answer for ANY request with so much if (flow->protos.dns.query_type == 255 && flow->protos.dns.num_queries < flow->protos.dns.num_answers) { dns_amplification_packets++; } printf("It's DNS, we could check packet type. query_type: %d query_class: %d rsp_code: %d num answers: %d, num queries: %d\n", flow->protos.dns.query_type, flow->protos.dns.query_class, flow->protos.dns.rsp_type, flow->protos.dns.num_answers, flow->protos.dns.num_queries ); /* struct { u_int8_t num_queries, num_answers, ret_code; u_int8_t bad_packet // the received packet looks bad u_int16_t query_type, query_class, rsp_type; } dns; */ } else if (detected_protocol.protocol == NDPI_PROTOCOL_NTP) { printf("Request type field: %d version: %d\n", flow->protos.ntp.request_code, flow->protos.ntp.version); // Detect packets with type MON_GETLIST_1 if (flow->protos.ntp.version == 2 && flow->protos.ntp.request_code == 42) { ntp_amplification_packets++; } } else if (detected_protocol.protocol == NDPI_PROTOCOL_SSDP) { ssdp_amplification_packets++; } ndpi_free_flow(flow); free(dst); free(src); #endif } else { printf("We do not support this flow type: %s\n", flow_type); } } int main(int argc, char** argv) { init_logging(); if (argc != 3) { printf("Please provide flow type: sflow, netflow, raw or dpi and path to pcap dump\n"); exit(1); } flow_type = argv[1]; printf("We will process file: %s as %s dump\n", argv[2], argv[1]); #ifdef ENABLE_DPI if (strcmp(flow_type, "dpi") == 0) { my_ndpi_struct = init_ndpi(); if (my_ndpi_struct == NULL) { printf("Can't load nDPI\n"); exit(0); } ndpi_size_id_struct = ndpi_detection_get_sizeof_ndpi_id_struct(); ndpi_size_flow_struct = ndpi_detection_get_sizeof_ndpi_flow_struct(); } #endif pcap_reader(argv[2], pcap_parse_packet); if (strcmp(flow_type, "raw") == 0) { printf("Parsed packets: %llu\n", raw_parsed_packets); printf("Unparsed packets: %llu\n", raw_unparsed_packets); printf("Total packets: %llu\n", raw_parsed_packets + raw_unparsed_packets); } #ifdef ENABLE_DPI if (strcmp(flow_type, "dpi") == 0) { printf("DNS amplification packets: %lld\n", dns_amplification_packets); printf("NTP amplification packets: %lld\n", ntp_amplification_packets); printf("SSDP amplification packets: %lld\n", ssdp_amplification_packets); } #endif } fastnetmon-1.1.4/src/pfring_plugin/000077500000000000000000000000001343111404700173235ustar00rootroot00000000000000fastnetmon-1.1.4/src/pfring_plugin/pfring_collector.cpp000066400000000000000000000614111343111404700233650ustar00rootroot00000000000000// log4cpp logging facility #include "log4cpp/Category.hh" #include "log4cpp/Appender.hh" #include "log4cpp/FileAppender.hh" #include "log4cpp/OstreamAppender.hh" #include "log4cpp/Layout.hh" #include "log4cpp/BasicLayout.hh" #include "log4cpp/PatternLayout.hh" #include "log4cpp/Priority.hh" #include "../fast_library.h" // For support uint32_t, uint16_t #include #include #include // For config map operations #include #include // For support: IPPROTO_TCP, IPPROTO_ICMP, IPPROTO_UDP #include #include #include #include "pfring_collector.h" #include "pfring.h" #ifdef PF_RING_ZC #include "pfring_zc.h" #endif #include uint32_t pfring_sampling_ratio = 1; // Get log4cpp logger from main program extern log4cpp::Category& logger; extern uint64_t total_unparsed_packets; // Global configuration map extern std::map configuration_map; // Interface name or interface list (delimitered by comma) std::string work_on_interfaces = ""; // This variable name should be uniq for every plugin! process_packet_pointer pfring_process_func_ptr = NULL; // We can look inside L2TP packets with IP encapsulation // And do it by default bool do_unpack_l2tp_over_ip = true; // Variable from PF_RING multi channel mode int num_pfring_channels = 0; // We can use software or hardware (in kernel module) packet parser bool we_use_pf_ring_in_kernel_parser = true; // By default we pool PF_RING on one thread bool enable_pfring_multi_channel_mode = false; struct thread_stats { u_int64_t __padding_0[8]; u_int64_t numPkts; u_int64_t numBytes; pfring* ring; pthread_t pd_thread; int core_affinity; volatile u_int64_t do_shutdown; u_int64_t __padding_1[3]; }; struct thread_stats* threads; pfring* pf_ring_descr = NULL; // We can use ZC api bool pf_ring_zc_api_mode = false; #ifdef PF_RING_ZC u_int32_t zc_num_threads = 0; pthread_t* zc_threads; pfring_zc_cluster* zc; pfring_zc_worker* zw; pfring_zc_queue** inzq; pfring_zc_queue** outzq; pfring_zc_multi_queue* outzmq; /* fanout */ pfring_zc_buffer_pool* wsp; pfring_zc_pkt_buff** buffers; #endif // Prototypes #ifdef PF_RING_ZC bool zc_main_loop(const char* device); #endif bool pf_ring_main_loop(const char* dev); bool pf_ring_main_loop_multi_channel(const char* dev); void* pf_ring_packet_consumer_thread(void* _id); void pfring_main_packet_process_task(); void start_pfring_collection(process_packet_pointer func_ptr) { logger << log4cpp::Priority::INFO << "PF_RING plugin started"; pfring_process_func_ptr = func_ptr; #ifdef PF_RING_ZC if (configuration_map.count("enable_pf_ring_zc_mode")) { if (configuration_map["enable_pf_ring_zc_mode"] == "on") { pf_ring_zc_api_mode = true; } else { pf_ring_zc_api_mode = false; } } #endif if (configuration_map.count("interfaces") != 0) { work_on_interfaces = configuration_map["interfaces"]; // We should check all interfaces and check zc flag for all if (work_on_interfaces.find("zc:") != std::string::npos) { we_use_pf_ring_in_kernel_parser = false; logger << log4cpp::Priority::INFO << "We detect run in PF_RING Zero Copy or DNA mode and we enable packet parser!"; } logger << log4cpp::Priority::INFO << "We selected interface:" << work_on_interfaces; } if (configuration_map.count("pfring_sampling_ratio") != 0) { pfring_sampling_ratio = convert_string_to_integer(configuration_map["pfring_sampling_ratio"]); } if (work_on_interfaces == "") { logger << log4cpp::Priority::ERROR << "Please specify interface"; exit(1); } pfring_main_packet_process_task(); } void stop_pfring_collection() { pfring_breakloop(pf_ring_descr); } void parse_packet_pf_ring(const struct pfring_pkthdr* h, const u_char* p, const u_char* user_bytes) { // Description of all fields: http://www.ntop.org/pfring_api/structpkt__parsing__info.html simple_packet packet; // We pass only one packet to processing packet.number_of_packets = 1; // Now we support only non sampled input from PF_RING packet.sample_ratio = pfring_sampling_ratio; if (!pf_ring_zc_api_mode) { if (!we_use_pf_ring_in_kernel_parser) { // In ZC (zc:eth0) mode you should manually add packet parsing here // Because it disabled by default: "parsing already disabled in zero-copy" // http://www.ntop.org/pfring_api/pfring_8h.html // Parse up to L3, no timestamp, no hashing // 1 - add timestamp, 0 - disable hash // We should zeroify packet header because PFRING ZC did not do this! memset((void*)&h->extended_hdr.parsed_pkt, 0, sizeof(h->extended_hdr.parsed_pkt)); // We do not calculate timestamps here because it's useless and consumes so much cpu // https://github.com/ntop/PF_RING/issues/9 u_int8_t timestamp = 0; u_int8_t add_hash = 0; pfring_parse_pkt((u_char*)p, (struct pfring_pkthdr*)h, 4, timestamp, add_hash); } } if (do_unpack_l2tp_over_ip) { // 2014-12-08 13:36:53,537 [INFO] [00:1F:12:84:E2:E7 -> 90:E2:BA:49:85:C8] // [IPv4][5.254.105.102:0 -> 159.253.17.251:0] // [l3_proto=115][hash=2784721876][tos=32][tcp_seq_num=0] // [caplen=128][len=873][parsed_header_len=0][eth_offset=-14][l3_offset=14][l4_offset=34][payload_offset=0] // L2TP has an proto number 115 if (h->extended_hdr.parsed_pkt.l3_proto == 115) { // pfring_parse_pkt expects that the hdr memory is either zeroed or contains valid // values // for the current packet, in order to avoid parsing twice the same packet headers. struct pfring_pkthdr l2tp_header; memset(&l2tp_header, 0, sizeof(l2tp_header)); int16_t l4_offset = h->extended_hdr.parsed_pkt.offset.l4_offset; // L2TP has two headers: L2TP and default L2-Specific Sublayer: every header for 4bytes int16_t l2tp_header_size = 8; l2tp_header.len = h->len - (l4_offset + l2tp_header_size); l2tp_header.caplen = h->caplen - (l4_offset + l2tp_header_size); const u_char* l2tp_tunnel_payload = p + l4_offset + l2tp_header_size; // 1 - add timestamp, 0 - disable hash pfring_parse_pkt((u_char*)l2tp_tunnel_payload, &l2tp_header, 4, 1, 0); // Copy data back // TODO: it's not fine solution and I should redesign this code memcpy((struct pfring_pkthdr*)h, &l2tp_header, sizeof(l2tp_header)); // TODO: Global pfring_print_parsed_pkt can fail because we did not shift 'p' pointer // Uncomment this line for deep inspection of all packets /* char buffer[512]; pfring_print_parsed_pkt(buffer, 512, l2tp_tunnel_payload, h); logger<extended_hdr.parsed_pkt.ip_version != 4 && h->extended_hdr.parsed_pkt.ip_version != 6) { total_unparsed_packets++; return; } packet.ip_protocol_version = h->extended_hdr.parsed_pkt.ip_version; if (packet.ip_protocol_version == 4) { // IPv4 /* PF_RING stores data in host byte order but we use network byte order */ packet.src_ip = htonl(h->extended_hdr.parsed_pkt.ip_src.v4); packet.dst_ip = htonl(h->extended_hdr.parsed_pkt.ip_dst.v4); } else { // IPv6 memcpy(packet.src_ipv6.s6_addr, h->extended_hdr.parsed_pkt.ip_src.v6.s6_addr, 16); memcpy(packet.dst_ipv6.s6_addr, h->extended_hdr.parsed_pkt.ip_dst.v6.s6_addr, 16); } packet.source_port = h->extended_hdr.parsed_pkt.l4_src_port; packet.destination_port = h->extended_hdr.parsed_pkt.l4_dst_port; // We need this for deep packet inspection packet.packet_payload_length = h->len; packet.packet_payload_pointer = (void*)p; packet.length = h->len; packet.protocol = h->extended_hdr.parsed_pkt.l3_proto; packet.ts = h->ts; // Copy flags from PF_RING header to our pseudo header if (packet.protocol == IPPROTO_TCP) { packet.flags = h->extended_hdr.parsed_pkt.tcp.flags; } else { packet.flags = 0; } pfring_process_func_ptr(packet); } // Main worker thread for packet handling void pfring_main_packet_process_task() { const char* device_name = work_on_interfaces.c_str(); bool pf_ring_init_result = false; if (pf_ring_zc_api_mode) { #ifdef PF_RING_ZC pf_ring_init_result = zc_main_loop((char*)device_name); #else logger << log4cpp::Priority::ERROR << "PF_RING library hasn't ZC support, please try SVN version"; #endif } else { if (enable_pfring_multi_channel_mode) { pf_ring_init_result = pf_ring_main_loop_multi_channel(device_name); } else { pf_ring_init_result = pf_ring_main_loop(device_name); } } if (!pf_ring_init_result) { // Internal error in PF_RING logger << log4cpp::Priority::ERROR << "PF_RING initilization failed, exit from program"; exit(1); } } std::string get_pf_ring_stats() { std::stringstream output_buffer; if (pf_ring_zc_api_mode) { #ifdef PF_RING_ZC pfring_zc_stat stats; // We have elements in insq for every hardware device! We shoulw add ability to configure ot int stats_res = pfring_zc_stats(inzq[0], &stats); if (stats_res) { logger << log4cpp::Priority::ERROR << "Can't get PF_RING ZC stats for in queue"; } else { double dropped_percent = 0; if (stats.recv + stats.sent > 0) { dropped_percent = (double)stats.drop / ((double)stats.recv + (double)stats.sent) * 100; } output_buffer << "\n"; output_buffer << "PF_RING ZC in queue statistics\n"; output_buffer << "Received:\t" << stats.recv << "\n"; output_buffer << "Sent:\t\t" << stats.sent << "\n"; output_buffer << "Dropped:\t" << stats.drop << "\n"; output_buffer << "Dropped:\t" << std::fixed << std::setprecision(2) << dropped_percent << " %\n"; } output_buffer << "\n"; output_buffer << "PF_RING ZC out queue statistics\n"; u_int64_t total_recv = 0; u_int64_t total_sent = 0; u_int64_t total_drop = 0; for (int i = 0; i < zc_num_threads; i++) { pfring_zc_stat outq_stats; int outq_stats_res = pfring_zc_stats(outzq[0], &outq_stats); if (stats_res) { logger << log4cpp::Priority::ERROR << "Can't get PF_RING ZC stats for out queue"; } else { total_recv += outq_stats.recv; total_sent += outq_stats.sent; total_drop += outq_stats.drop; } } double total_drop_percent = 0; if (total_recv + total_sent > 0) { total_drop_percent = (double)total_drop / ((double)total_recv + (double)total_sent) * 100; } output_buffer << "Received:\t" << total_recv << "\n"; output_buffer << "Sent:\t\t" << total_sent << "\n"; output_buffer << "Dropped:\t" << total_drop << "\n"; output_buffer << "Dropped:\t" << std::fixed << std::setprecision(2) << total_drop_percent << " %\n"; #endif } // Getting stats for multi channel mode is so complex task if (!enable_pfring_multi_channel_mode && !pf_ring_zc_api_mode) { pfring_stat pfring_status_data; if (pfring_stats(pf_ring_descr, &pfring_status_data) >= 0) { char stats_buffer[256]; double packets_dropped_percent = 0; if (pfring_status_data.recv > 0) { packets_dropped_percent = (double)pfring_status_data.drop / pfring_status_data.recv * 100; } sprintf(stats_buffer, "Packets received:\t%lu\n" "Packets dropped:\t%lu\n" "Packets dropped:\t%.1f %%\n", (long unsigned int)pfring_status_data.recv, (long unsigned int)pfring_status_data.drop, packets_dropped_percent); output_buffer << stats_buffer; } else { logger << log4cpp::Priority::ERROR << "Can't get PF_RING stats"; } } return output_buffer.str(); } bool pf_ring_main_loop_multi_channel(const char* dev) { int MAX_NUM_THREADS = 64; if ((threads = (struct thread_stats*)calloc(MAX_NUM_THREADS, sizeof(struct thread_stats))) == NULL) { logger << log4cpp::Priority::ERROR << "Can't allocate memory for threads structure"; return false; } u_int32_t flags = 0; flags |= PF_RING_PROMISC; /* hardcode: promisc=1 */ flags |= PF_RING_DNA_SYMMETRIC_RSS; /* Note that symmetric RSS is ignored by non-DNA drivers */ flags |= PF_RING_LONG_HEADER; packet_direction direction = rx_only_direction; pfring* ring_array[MAX_NUM_RX_CHANNELS]; unsigned int snaplen = 128; num_pfring_channels = pfring_open_multichannel(dev, snaplen, flags, ring_array); if (num_pfring_channels <= 0) { logger << log4cpp::Priority::INFO << "pfring_open_multichannel returned: " << num_pfring_channels << " and error:" << strerror(errno); return false; } u_int num_cpus = sysconf(_SC_NPROCESSORS_ONLN); logger << log4cpp::Priority::INFO << "We have: " << num_cpus << " logical cpus in this server"; logger << log4cpp::Priority::INFO << "We have: " << num_pfring_channels << " channels from pf_ring NIC"; // We should not start more processes then we have kernel cores // if (num_pfring_channels > num_cpus) { // num_pfring_channels = num_cpus; //} for (int i = 0; i < num_pfring_channels; i++) { // char buf[32]; threads[i].ring = ring_array[i]; // threads[i].core_affinity = threads_core_affinity[i]; int rc = 0; if ((rc = pfring_set_direction(threads[i].ring, direction)) != 0) { logger << log4cpp::Priority::INFO << "pfring_set_direction returned: " << rc; } if ((rc = pfring_set_socket_mode(threads[i].ring, recv_only_mode)) != 0) { logger << log4cpp::Priority::INFO << "pfring_set_socket_mode returned: " << rc; } int rehash_rss = 0; if (rehash_rss) pfring_enable_rss_rehash(threads[i].ring); int poll_duration = 0; if (poll_duration > 0) pfring_set_poll_duration(threads[i].ring, poll_duration); pfring_enable_ring(threads[i].ring); unsigned long thread_id = i; pthread_create(&threads[i].pd_thread, NULL, pf_ring_packet_consumer_thread, (void*)thread_id); } for (int i = 0; i < num_pfring_channels; i++) { pthread_join(threads[i].pd_thread, NULL); pfring_close(threads[i].ring); } return true; } void* pf_ring_packet_consumer_thread(void* _id) { long thread_id = (long)_id; int wait_for_packet = 1; // TODO: fix it bool do_shutdown = false; while (!do_shutdown) { u_char* buffer = NULL; struct pfring_pkthdr hdr; if (pfring_recv(threads[thread_id].ring, &buffer, 0, &hdr, wait_for_packet) > 0) { // TODO: pass (u_char*)thread_id) parse_packet_pf_ring(&hdr, buffer, 0); } else { if (wait_for_packet == 0) { usleep(1); // sched_yield(); } } } return NULL; } #ifdef PF_RING_ZC int rr = -1; int32_t rr_distribution_func(pfring_zc_pkt_buff* pkt_handle, pfring_zc_queue* in_queue, void* user) { long num_out_queues = (long)user; if (++rr == num_out_queues) { rr = 0; } return rr; } #endif #ifdef PF_RING_ZC int bind2core(int core_id) { cpu_set_t cpuset; int s; if (core_id < 0) return -1; CPU_ZERO(&cpuset); CPU_SET(core_id, &cpuset); if ((s = pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset)) != 0) { logger << log4cpp::Priority::INFO << "Error while binding to core:" << core_id; return -1; } else { return 0; } } #endif #ifdef PF_RING_ZC void* zc_packet_consumer_thread(void* _id) { long id = (long)_id; pfring_zc_pkt_buff* b = buffers[id]; // Bind to core with thread number bind2core(id); u_int8_t wait_for_packet = 1; struct pfring_pkthdr zc_header; memset(&zc_header, 0, sizeof(zc_header)); while (true) { if (pfring_zc_recv_pkt(outzq[id], &b, wait_for_packet) > 0) { u_char* pkt_data = pfring_zc_pkt_buff_data(b, outzq[id]); memset(&zc_header, 0, sizeof(zc_header)); zc_header.len = b->len; zc_header.caplen = b->len; pfring_parse_pkt(pkt_data, (struct pfring_pkthdr*)&zc_header, 4, 1, 0); parse_packet_pf_ring(&zc_header, pkt_data, 0); } } pfring_zc_sync_queue(outzq[id], rx_only); return NULL; } #endif int max_packet_len(const char* device) { int max_len = 0; pfring* ring = pfring_open(device, 1536, PF_RING_PROMISC); if (ring == NULL) return 1536; // pfring_get_card_settings have added in 6.0.3 // We should not use 6.0.3 API for PF_RING library from ntop because it announces "6.0.3" but lack // of many 6.0.3 features #if RING_VERSION_NUM >= 0x060003 and !defined(WE_USE_PFRING_FROM_NTOP) pfring_card_settings settings; pfring_get_card_settings(ring, &settings); max_len = settings.max_packet_size; #else if (ring->dna.dna_mapped_device) { max_len = ring->dna.dna_dev.mem_info.rx.packet_memory_slot_len; } else { max_len = pfring_get_mtu_size(ring); if (max_len == 0) max_len = 9000 /* Jumbo */; max_len += 14 /* Eth */ + 4 /* VLAN */; } #endif pfring_close(ring); return max_len; } #define MAX_CARD_SLOTS 32768 #define PREFETCH_BUFFERS 8 #define QUEUE_LEN 8192 #ifdef PF_RING_ZC bool zc_main_loop(const char* device) { u_int32_t cluster_id = 0; int bind_core = -1; u_int num_cpus = sysconf(_SC_NPROCESSORS_ONLN); logger << log4cpp::Priority::INFO << "We have: " << num_cpus << " logical cpus in this server"; // TODO: add support for multiple devices! u_int32_t num_devices = 1; zc_num_threads = num_cpus - 1; logger << log4cpp::Priority::INFO << "We will start " << zc_num_threads << " worker threads"; u_int32_t tot_num_buffers = (num_devices * MAX_CARD_SLOTS) + (zc_num_threads * QUEUE_LEN) + zc_num_threads + PREFETCH_BUFFERS; u_int32_t buffer_len = max_packet_len(device); logger << log4cpp::Priority::INFO << "We got max packet len from device: " << buffer_len; logger << log4cpp::Priority::INFO << "We will use total number of ZC buffers: " << tot_num_buffers; zc = pfring_zc_create_cluster(cluster_id, buffer_len, 0, tot_num_buffers, numa_node_of_cpu(bind_core), NULL /* auto hugetlb mountpoint */ ); if (zc == NULL) { logger << log4cpp::Priority::INFO << "pfring_zc_create_cluster error: " << strerror(errno) << " Please check that pf_ring.ko is loaded and hugetlb fs is mounted"; return false; } zc_threads = (pthread_t*)calloc(zc_num_threads, sizeof(pthread_t)); buffers = (pfring_zc_pkt_buff**)calloc(zc_num_threads, sizeof(pfring_zc_pkt_buff*)); inzq = (pfring_zc_queue**)calloc(num_devices, sizeof(pfring_zc_queue*)); outzq = (pfring_zc_queue**)calloc(zc_num_threads, sizeof(pfring_zc_queue*)); for (int i = 0; i < zc_num_threads; i++) { buffers[i] = pfring_zc_get_packet_handle(zc); if (buffers[i] == NULL) { logger << log4cpp::Priority::ERROR << "pfring_zc_get_packet_handle failed"; return false; } } for (int i = 0; i < num_devices; i++) { u_int32_t zc_flags = 0; inzq[i] = pfring_zc_open_device(zc, device, rx_only, zc_flags); if (inzq[i] == NULL) { logger << log4cpp::Priority::ERROR << "pfring_zc_open_device error " << strerror(errno) << " Please check that device is up and not already used"; return false; } #if RING_VERSION_NUM >= 0x060003 int pf_ring_license_state = pfring_zc_check_license(); if (!pf_ring_license_state) { logger << log4cpp::Priority::WARN << "PF_RING ZC haven't license for device" << device << " and running in trial mode and will work only 5 minutes! Please buy license " "or switch to vanilla PF_RING"; } #endif } for (int i = 0; i < zc_num_threads; i++) { outzq[i] = pfring_zc_create_queue(zc, QUEUE_LEN); if (outzq[i] == NULL) { logger << log4cpp::Priority::ERROR << "pfring_zc_create_queue error: " << strerror(errno); return false; } } wsp = pfring_zc_create_buffer_pool(zc, PREFETCH_BUFFERS); if (wsp == NULL) { logger << log4cpp::Priority::ERROR << "pfring_zc_create_buffer_pool error"; return false; } logger << log4cpp::Priority::INFO << "We are starting balancer with: " << zc_num_threads << " threads"; pfring_zc_distribution_func func = rr_distribution_func; u_int8_t wait_for_packet = 1; // We run balancer at last thread int32_t bind_worker_core = zc_num_threads; logger << log4cpp::Priority::INFO << "We will run balancer on core: " << bind_worker_core; zw = pfring_zc_run_balancer(inzq, outzq, num_devices, zc_num_threads, wsp, round_robin_bursts_policy, NULL /* idle callback */, func, (void*)((long)zc_num_threads), !wait_for_packet, bind_worker_core); if (zw == NULL) { logger << log4cpp::Priority::ERROR << "pfring_zc_run_balancer error:" << strerror(errno); return false; } for (int i = 0; i < zc_num_threads; i++) { pthread_create(&zc_threads[i], NULL, zc_packet_consumer_thread, (void*)(long)i); } for (int i = 0; i < zc_num_threads; i++) { pthread_join(zc_threads[i], NULL); } pfring_zc_kill_worker(zw); pfring_zc_destroy_cluster(zc); return true; } #endif bool pf_ring_main_loop(const char* dev) { // We could pool device in multiple threads unsigned int num_threads = 1; bool promisc = true; /* This flag manages packet parser for extended_hdr */ bool use_extended_pkt_header = true; bool enable_hw_timestamp = false; bool dont_strip_timestamps = false; u_int32_t flags = 0; if (num_threads > 1) flags |= PF_RING_REENTRANT; if (use_extended_pkt_header) flags |= PF_RING_LONG_HEADER; if (promisc) flags |= PF_RING_PROMISC; if (enable_hw_timestamp) flags |= PF_RING_HW_TIMESTAMP; if (!dont_strip_timestamps) flags |= PF_RING_STRIP_HW_TIMESTAMP; if (!we_use_pf_ring_in_kernel_parser) { flags |= PF_RING_DO_NOT_PARSE; } flags |= PF_RING_DNA_SYMMETRIC_RSS; /* Note that symmetric RSS is ignored by non-DNA drivers */ // use default value from pfcount.c unsigned int snaplen = 128; pf_ring_descr = pfring_open(dev, snaplen, flags); if (pf_ring_descr == NULL) { logger << log4cpp::Priority::INFO << "pfring_open error: " << strerror(errno) << " (pf_ring not loaded or perhaps you use quick mode and have already a socket bound to: " << dev << ")"; return false; } logger << log4cpp::Priority::INFO << "Successully binded to: " << dev; // We need cast to int because in other way it will be interpreted as char :( logger << log4cpp::Priority::INFO << "Device RX channels number: " << int(pfring_get_num_rx_channels(pf_ring_descr)); u_int32_t version; // Set spplication name in /proc int pfring_set_application_name_result = pfring_set_application_name(pf_ring_descr, (char*)"fastnetmon"); if (pfring_set_application_name_result != 0) { logger << log4cpp::Priority::ERROR << "Can't set program name for PF_RING: pfring_set_application_name"; } pfring_version(pf_ring_descr, &version); logger.info("Using PF_RING v.%d.%d.%d", (version & 0xFFFF0000) >> 16, (version & 0x0000FF00) >> 8, version & 0x000000FF); int pfring_set_socket_mode_result = pfring_set_socket_mode(pf_ring_descr, recv_only_mode); if (pfring_set_socket_mode_result != 0) { logger.info("pfring_set_socket_mode returned [rc=%d]\n", pfring_set_socket_mode_result); } // enable ring if (pfring_enable_ring(pf_ring_descr) != 0) { logger << log4cpp::Priority::INFO << "Unable to enable ring :-("; pfring_close(pf_ring_descr); return false; } // Active wait wor packets. But I did not know what is mean.. u_int8_t wait_for_packet = 1; pfring_loop(pf_ring_descr, parse_packet_pf_ring, (u_char*)NULL, wait_for_packet); return true; } fastnetmon-1.1.4/src/pfring_plugin/pfring_collector.h000066400000000000000000000004121343111404700230240ustar00rootroot00000000000000#ifndef PFRING_PLUGIN_H #define PFRING_PLUGIN_H #include "../fastnetmon_types.h" // This function should be implemented in plugin void start_pfring_collection(process_packet_pointer func_ptr); void stop_pfring_collection(); std::string get_pf_ring_stats(); #endif fastnetmon-1.1.4/src/plugin_runner.cpp000066400000000000000000000121051343111404700200520ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include "libpatricia/patricia.h" #include "fastnetmon_types.h" #include "fast_library.h" #include "netflow_plugin/netflow_collector.h" #include "sflow_plugin/sflow_collector.h" #include "pcap_plugin/pcap_collector.h" #ifdef PF_RING #include "pfring_plugin/pfring_collector.h" #endif #ifdef FASTNETMON_ENABLE_AFPACKET #include "afpacket_plugin/afpacket_collector.h" #endif #ifdef SNABB_SWITCH #include "snabbswitch_plugin/snabbswitch_collector.h" #endif #ifdef NETMAP_PLUGIN #include "netmap_plugin/netmap_collector.h" #endif // log4cpp logging facility #include "log4cpp/Category.hh" #include "log4cpp/Appender.hh" #include "log4cpp/FileAppender.hh" #include "log4cpp/OstreamAppender.hh" #include "log4cpp/Layout.hh" #include "log4cpp/BasicLayout.hh" #include "log4cpp/PatternLayout.hh" #include "log4cpp/Priority.hh" #include using namespace std; uint64_t total_unparsed_packets = 0; std::string log_file_path = "/tmp/fastnetmon_plugin_tester.log"; log4cpp::Category& logger = log4cpp::Category::getRoot(); // #define DO_SUBNET_LOOKUP #ifdef DO_SUBNET_LOOKUP patricia_tree_t* lookup_tree; #endif // Global map with parsed config file std::map configuration_map; void init_logging() { log4cpp::PatternLayout* layout = new log4cpp::PatternLayout(); layout->setConversionPattern("%d [%p] %m%n"); log4cpp::Appender* appender = new log4cpp::FileAppender("default", log_file_path); appender->setLayout(layout); logger.setPriority(log4cpp::Priority::INFO); logger.addAppender(appender); logger.info("Logger initialized!"); } void process_packet(simple_packet& current_packet) { std::cout << print_simple_packet(current_packet); #ifdef DO_SUBNET_LOOKUP unsigned long subnet = 0; unsigned int subnet_cidr_mask = 0; direction packet_direction = get_packet_direction(lookup_tree, current_packet.src_ip, current_packet.dst_ip, subnet, subnet_cidr_mask); std::cout << "direction: " << get_direction_name(packet_direction) << std::endl; #endif } // Copy & paste from fastnetmon.cpp std::vector read_file_to_vector(std::string file_name) { std::vector data; std::string line; std::ifstream reading_file; reading_file.open(file_name.c_str(), std::ifstream::in); if (reading_file.is_open()) { while (getline(reading_file, line)) { data.push_back(line); } } else { logger << log4cpp::Priority::ERROR << "Can't open file: " << file_name; } return data; } int main(int argc, char* argv[]) { init_logging(); if (argc < 2) { std::cout << "Please specify sflow, netflow, raw, dpi, snabbswitch, afpacket as param" << std::endl; return 1; } #ifdef DO_SUBNET_LOOKUP lookup_tree = New_Patricia(32); std::vector network_list_from_config = read_file_to_vector("/etc/networks_list"); for (std::vector::iterator ii = network_list_from_config.begin(); ii != network_list_from_config.end(); ++ii) { std::string network_address_in_cidr_form = *ii; make_and_lookup(lookup_tree, const_cast(network_address_in_cidr_form.c_str())); } #endif // Required by Netmap and PF_RING plugins // We use fake interface name here because netmap could make server unreachable :) configuration_map["interfaces"] = "ethXXX"; configuration_map["sflow_lua_hooks_path"] = "/usr/src/fastnetmon_lua/src/sflow_hooks.lua"; if (strstr(argv[1], "sflow") != NULL) { std::cout << "Starting sflow" << std::endl; start_sflow_collection(process_packet); } else if (strstr(argv[1], "netflow") != NULL) { std::cout << "Starting netflow" << std::endl; start_netflow_collection(process_packet); } else if (strstr(argv[1], "pcap") != NULL) { std::cout << "Starting pcap" << std::endl; start_pcap_collection(process_packet); } else if (strstr(argv[1], "snabbswitch") != NULL) { std::cout << "Starting snabbswitch" << std::endl; #ifdef SNABB_SWITCH start_snabbswitch_collection(process_packet); #else printf("SnabbSwitch support is not compiled here\n"); #endif } else if (strstr(argv[1], "pfring") != NULL) { #ifdef PF_RING std::cout << "Starting pf_ring" << std::endl; start_pfring_collection(process_packet); #else std::cout << "PF_RING support disabled here" << std::endl; #endif } else if (strstr(argv[1], "afpacket") != NULL) { #ifdef FASTNETMON_ENABLE_AFPACKET std::cout << "Starting afpacket" << std::endl; start_afpacket_collection(process_packet); #else printf("AF_PACKET is not supported here"); #endif } else if (strstr(argv[1], "netmap") != NULL) { std::cout << "Starting netmap" << std::endl; start_netmap_collection(process_packet); } else { std::cout << "Bad plugin name!" << std::endl; } } fastnetmon-1.1.4/src/scripts/000077500000000000000000000000001343111404700161475ustar00rootroot00000000000000fastnetmon-1.1.4/src/scripts/README.md000066400000000000000000000001511343111404700174230ustar00rootroot00000000000000We moved documentation to our [site](https://fastnetmon.com/subnet-collection-from-bgp-peering-session/) fastnetmon-1.1.4/src/scripts/bgp_network_collector.py000077500000000000000000000037041343111404700231170ustar00rootroot00000000000000#!/usr/bin/env python import os import sys import time import json from StringIO import StringIO import pprint import shelve import shelve # # Add withdrawal option # database = shelve.open("/var/lib/bgp_network_collector.db") while True: try: line = sys.stdin.readline().strip() # print >> sys.stderr, "GOT A LINE: ", line sys.stdout.flush() io = StringIO(line) decoded_update = json.load(io) pp = pprint.PrettyPrinter(indent=4, stream=sys.stderr) #pp.pprint(decoded_update) try: current_announce = decoded_update["neighbor"]["message"]["update"]["announce"]["ipv4 unicast"] #pp.pprint(current_announce) for next_hop in current_announce: current_announce_for_certain_next_hop = current_announce[next_hop] for prefix_announce in current_announce_for_certain_next_hop: #pp.pprint(current_announce_for_certain_next_hop[prefix_announce]) # drop default gateway if prefix_announce == "0.0.0.0/0": continue print >> sys.stderr, "We learned prefix:", prefix_announce if type(prefix_announce) != str: prefix_announce = prefix_announce.encode('utf8') if not database.has_key(prefix_announce): #print >> sys.stderr, "New data" database[prefix_announce] = 1; else: pass #print >> sys.stderr, "I already have this subnet" # call sync for each data portion database.sync() except KeyError: pass except KeyboardInterrupt: database.close() sys.exit(0) except IOError: # most likely a signal during readline database.close() sys.exit(0) fastnetmon-1.1.4/src/scripts/bgp_network_retriever.py000077500000000000000000000002141343111404700231310ustar00rootroot00000000000000#!/usr/bin/env python import shelve database = shelve.open("/var/lib/bgp_network_collector.db") for key in sorted(database): print key fastnetmon-1.1.4/src/scripts/build_any_package.pl000077500000000000000000000336671343111404700221470ustar00rootroot00000000000000#!/usr/bin/perl use strict; use warnings; unless (scalar @ARGV == 2) { die "Please specify type and original binary file name: rpm fastnetmon-binary-git-0cfdfd5e2062ad94de24f2f383576ea48e6f3a07-debian-6.0.10-x86_64"; } my $package_type = $ARGV[0]; my $archive_name = $ARGV[1]; if ($package_type eq 'rpm') { build_rpm_package(); } elsif ($package_type eq 'deb') { build_deb_package(); } sub build_rpm_package { print "Install packages for crafting rpm packages\n"; `yum install -y rpmdevtools yum-utils`; mkdir '/root/rpmbuild'; mkdir '/root/rpmbuild/SOURCES'; my $system_v_init_script = <<'DOC'; #!/bin/bash # # fastnetmon Startup script for FastNetMon # # chkconfig: - 85 15 # description: FastNetMon - high performance DoS/DDoS analyzer with sflow/netflow/mirror support # processname: fastnemon # config: /etc/fastnetmon.conf # pidfile: /var/run/fastnetmon.pid # ### BEGIN INIT INFO # Provides: fastnetmon # Required-Start: $local_fs $remote_fs $network # Required-Stop: $local_fs $remote_fs $network # Should-Start: # Short-Description: start and stop FastNetMon # Description: high performance DoS/DDoS analyzer with sflow/netflow/mirror support ### END INIT INFO # Source function library. . /etc/rc.d/init.d/functions # We do not use this configs #if [ -f /etc/sysconfig/fastnetmon ]; then # . /etc/sysconfig/fastnetmon #fi FASTNETMON=/opt/fastnetmon/fastnetmon PROGNAME="fastnetmon" PIDFILE=/var/run/fastnetmon.pid RETVAL=0 ARGS="--daemonize" start() { echo -n $"Starting $PROGNAME: " $FASTNETMON $ARGS > /dev/null 2>&1 && echo_success || echo_failure RETVAL=$? echo "" return $RETVAL } stop() { echo -n $"Stopping $PROGNAME: " killproc -p $PIDFILE $FASTNETMON RETVAL=$? echo "" rm -f $PIDFILE } reload() { echo "Reloading is not supported now, sorry" #echo -n $"Reloading $PROGNAME: " #kill -HUP `cat $PIDFILE` } # See how we were called. case "$1" in start) start ;; stop) stop ;; status) status -p ${PIDFILE} $PROGNAME RETVAL=$? ;; restart) stop sleep 1 start ;; reload) reload ;; *) echo $"Usage: $prog {start|stop|restart|reload|status}" RETVAL=2 esac exit $RETVAL DOC my $systemd_init_script = <<'DOC'; [Unit] Description=FastNetMon - DoS/DDoS analyzer with sflow/netflow/mirror support After=syslog.target network.target remote-fs.target [Service] Type=forking ExecStart=/opt/fastnetmon/fastnetmon --daemonize PIDFile=/run/fastnetmon.pid #ExecReload=/bin/kill -s HUP $MAINPID #ExecStop=/bin/kill -s QUIT $MAINPID [Install] WantedBy=multi-user.target DOC my $rpm_sources_path = '/root/rpmbuild/SOURCES'; # Copy bundle to build tree `cp $archive_name $rpm_sources_path/archive.tar.gz`; `wget --no-check-certificate https://raw.githubusercontent.com/pavel-odintsov/fastnetmon/master/src/fastnetmon.conf -O$rpm_sources_path/fastnetmon.conf`; open my $system_v_init_fl, ">", "$rpm_sources_path/system_v_init"; print {$system_v_init_fl} $system_v_init_script; close $system_v_init_fl; open my $systemd_init_fl, ">", "$rpm_sources_path/systemd_init"; print {$systemd_init_fl} $systemd_init_script; close $systemd_init_fl; # Create files list from archive # ./luajit_2.0.4/ my @files_list = `tar -tf /root/rpmbuild/SOURCES/archive.tar.gz`; chomp @files_list; # Replace path @files_list = map { s#^\.#/opt#; $_ } @files_list; # Filter out folders @files_list = grep { ! m#/$# } @files_list; my $systemd_spec_file = <<'DOC'; # # Pre/post params: https://fedoraproject.org/wiki/Packaging:ScriptletSnippets # %global fastnetmon_attackdir %{_localstatedir}/log/fastnetmon_attacks %global fastnetmon_user root %global fastnetmon_group %{fastnetmon_user} %global fastnetmon_config_path %{_sysconfdir}/fastnetmon.conf Name: fastnetmon Version: 1.1.3 Release: 1%{?dist} Summary: A high performance DoS/DDoS load analyzer built on top of multiple packet capture engines (NetFlow, IPFIX, sFLOW, netmap, PF_RING, PCAP). Group: System Environment/Daemons License: GPLv2 URL: https://fastnetmon.com # Top level fodler inside archive should be named as "fastnetmon-1.1.1" Source0: http://178.62.227.110/fastnetmon_binary_repository/test_binary_builds/this_fake_path_do_not_check_it/archive.tar.gz # Disable any sort of dynamic dependency detection for our own custom bunch of binaries AutoReq: no AutoProv: no Requires: libpcap, numactl, libicu Requires(pre): shadow-utils Requires(post): systemd Requires(preun): systemd Requires(postun): systemd Provides: fastnetmon %description A high performance DoS/DDoS load analyzer built on top of multiple packet capture engines (NetFlow, IPFIX, sFLOW, netmap, PF_RING, PCAP). %prep rm -rf fastnetmon-tree mkdir fastnetmon-tree mkdir fastnetmon-tree/opt tar -xvvf /root/rpmbuild/SOURCES/archive.tar.gz -C fastnetmon-tree/opt # Copy service scripts mkdir fastnetmon-tree/etc cp /root/rpmbuild/SOURCES/systemd_init fastnetmon-tree/etc cp /root/rpmbuild/SOURCES/fastnetmon.conf fastnetmon-tree/etc %build # We do not build anything exit 0 %install mkdir %{buildroot}/opt cp -R fastnetmon-tree/opt/* %{buildroot}/opt chmod 755 %{buildroot}/opt/fastnetmon/fastnetmon chmod 755 %{buildroot}/opt/fastnetmon/fastnetmon_client # install init script install -p -D -m 0755 fastnetmon-tree/etc/systemd_init %{buildroot}%{_sysconfdir}/systemd/system/fastnetmon.service # install config install -p -D -m 0644 fastnetmon-tree/etc/fastnetmon.conf %{buildroot}%{fastnetmon_config_path} # Create log folder install -p -d -m 0700 %{buildroot}%{fastnetmon_attackdir} exit 0 %pre exit 0 %post %systemd_post fastnetmon.service if [ $1 -eq 1 ]; then # It's install # Enable autostart /usr/bin/systemctl enable fastnetmon.service /usr/bin/systemctl start fastnetmon.service fi #if [ $1 -eq 2 ]; then # upgrade #/sbin/service %{name} restart >/dev/null 2>&1 #fi %preun %systemd_preun fastnetmon.service %postun %systemd_postun_with_restart fastnetmon.service %files #%doc LICENSE CHANGES README {files_list} %{_sysconfdir}/systemd/system %config(noreplace) %{_sysconfdir}/fastnetmon.conf %attr(700,%{fastnetmon_user},%{fastnetmon_group}) %dir %{fastnetmon_attackdir} %changelog * Mon Mar 23 2015 Pavel Odintsov - 1.1.1-1 - First RPM package release DOC my $spec_file = <<'DOC'; # # Pre/post params: https://fedoraproject.org/wiki/Packaging:ScriptletSnippets # %global fastnetmon_attackdir %{_localstatedir}/log/fastnetmon_attacks %global fastnetmon_user root %global fastnetmon_group %{fastnetmon_user} %global fastnetmon_config_path %{_sysconfdir}/fastnetmon.conf Name: fastnetmon Version: 1.1.3 Release: 1%{?dist} Summary: A high performance DoS/DDoS load analyzer built on top of multiple packet capture engines (NetFlow, IPFIX, sFLOW, netmap, PF_RING, PCAP). Group: System Environment/Daemons License: GPLv2 URL: https://fastnetmon.com # Top level fodler inside archive should be named as "fastnetmon-1.1.1" Source0: http://178.62.227.110/fastnetmon_binary_repository/test_binary_builds/this_fake_path_do_not_check_it/archive.tar.gz # Disable any sort of dynamic dependency detection for our own custom bunch of binaries AutoReq: no AutoProv: no Requires: libpcap, numactl, libicu Requires(pre): shadow-utils Requires(post): chkconfig Requires(preun): chkconfig, initscripts Requires(postun): initscripts Provides: fastnetmon %description A high performance DoS/DDoS load analyzer built on top of multiple packet capture engines (NetFlow, IPFIX, sFLOW, netmap, PF_RING, PCAP). %prep rm -rf fastnetmon-tree mkdir fastnetmon-tree mkdir fastnetmon-tree/opt tar -xvvf /root/rpmbuild/SOURCES/archive.tar.gz -C fastnetmon-tree/opt # Copy service scripts mkdir fastnetmon-tree/etc cp /root/rpmbuild/SOURCES/system_v_init fastnetmon-tree/etc cp /root/rpmbuild/SOURCES/fastnetmon.conf fastnetmon-tree/etc %build # We do not build anything exit 0 %install mkdir %{buildroot}/opt cp -R fastnetmon-tree/opt/* %{buildroot}/opt chmod 755 %{buildroot}/opt/fastnetmon/fastnetmon chmod 755 %{buildroot}/opt/fastnetmon/fastnetmon_client # install init script install -p -D -m 0755 fastnetmon-tree/etc/system_v_init %{buildroot}%{_initrddir}/fastnetmon # install config install -p -D -m 0644 fastnetmon-tree/etc/fastnetmon.conf %{buildroot}%{fastnetmon_config_path} # Create log folder install -p -d -m 0700 %{buildroot}%{fastnetmon_attackdir} exit 0 %pre exit 0 %post if [ $1 -eq 1 ]; then # It's install /sbin/chkconfig --add %{name} /sbin/chkconfig %{name} on /sbin/service %{name} start fi #if [ $1 -eq 2 ]; then # upgrade #/sbin/service %{name} restart >/dev/null 2>&1 #fi %preun # Pre remove if [ $1 -eq 0 ]; then # Uninstall # Stops fastnetmon and disable it loading at startup /sbin/service %{name} stop >/dev/null 2>&1 /sbin/chkconfig --del %{name} fi %postun # Post remove %files #%doc LICENSE CHANGES README {files_list} %{_initrddir}/fastnetmon %config(noreplace) %{_sysconfdir}/fastnetmon.conf %attr(700,%{fastnetmon_user},%{fastnetmon_group}) %dir %{fastnetmon_attackdir} %changelog * Mon Mar 23 2015 Pavel Odintsov - 1.1.1-1 - First RPM package release DOC my $selected_spec_file = $spec_file; # For CentOS we use systemd if ($archive_name =~ m/centos-7/) { $selected_spec_file = $systemd_spec_file; } my $joined_file_list = join "\n", @files_list; $selected_spec_file =~ s/\{files_list\}/$joined_file_list/; open my $fl, ">", "generated_spec_file.spec" or die "Can't create spec file\n"; print {$fl} $selected_spec_file; system("rpmbuild -bb generated_spec_file.spec"); mkdir "/tmp/result_data"; `cp /root/rpmbuild/RPMS/x86_64/* /tmp/result_data`; } sub build_deb_package { print "We will build deb from $archive_name\n"; my $fastnetmon_systemd_unit = <<'DOC'; [Unit] Description=FastNetMon - DoS/DDoS analyzer with sflow/netflow/mirror support After=network.target remote-fs.target [Service] Type=forking ExecStart=/opt/fastnetmon/fastnetmon --daemonize PIDFile=/run/fastnetmon.pid #ExecReload=/bin/kill -s HUP $MAINPID #ExecStop=/bin/kill -s QUIT $MAINPID [Install] WantedBy=multi-user.target DOC my $fastnetmon_systemv_init = <<'DOC'; #!/bin/sh ### BEGIN INIT INFO # Provides: fastnetmon # Required-Start: $local_fs $remote_fs $network $syslog # Required-Stop: $local_fs $remote_fs $network $syslog # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: Fast DDoS detection toolkit. # Description: Fast DDoS detection toolkit with sFLOW/Netflow/netmap/pf_ring support. ### END INIT INFO # test -r /etc/default/fastnetmon && . /etc/default/fastnetmon NAME="fastnetmon" . /lib/lsb/init-functions PIDFILE="/var/run/${NAME}.pid" DAEMON="/opt/fastnetmon/fastnetmon" DAEMON_OPTS="--daemonize" START_OPTS="--start --background --exec ${DAEMON} -- ${DAEMON_OPTS}" STOP_OPTS="--stop --pidfile ${PIDFILE}" STATUS_OPTS="--status --pidfile ${PIDFILE}" case "$1" in start) echo -n "Starting $NAME: " start-stop-daemon $START_OPTS echo "$NAME." ;; stop) echo -n "Stopping $NAME: " start-stop-daemon $STOP_OPTS rm -f $PIDFILE echo "$NAME." ;; restart) $0 stop sleep 2 $0 start ;; force-reload) $0 restart ;; # no support of status on Debian squeeze # status) # start-stop-daemon $STATUS_OPTS # ;; *) N=/etc/init.d/$NAME echo "Usage: $N {start|stop|restart}" >&2 exit 1 ;; esac exit 0 DOC # dpkg-deb: warning: '/tmp/tmp.gbd1VXGPQB/DEBIAN/control' contains user-defined field '#Standards-Version' my $fastnetmon_control_file = <<'DOC'; Package: fastnetmon Maintainer: Pavel Odintsov Section: misc Priority: optional Architecture: amd64 Version: 1.1.3 Depends: libpcap0.8, libnuma1 Description: Very fast DDoS analyzer with sflow/netflow/mirror support FastNetMon - A high performance DoS/DDoS attack sensor. DOC my $folder_for_build = `mktemp -d`; chomp $folder_for_build; unless (-e $folder_for_build) { die "Can't create temp folder\n"; } chdir $folder_for_build; mkdir "$folder_for_build/DEBIAN"; put_text_to_file("$folder_for_build/DEBIAN/control", $fastnetmon_control_file); # Create init files for different versions of Debian like OS mkdir "$folder_for_build/etc"; mkdir "$folder_for_build/etc/init.d"; put_text_to_file("$folder_for_build/etc/init.d/fastnetmon", $fastnetmon_systemv_init); chmod 0755, "$folder_for_build/etc/init.d/fastnetmon"; # systemd mkdir "$folder_for_build/lib"; mkdir "$folder_for_build/lib/systemd"; mkdir "$folder_for_build/lib/systemd/system"; put_text_to_file("$folder_for_build/lib/systemd/system/fastnetmon.service", $fastnetmon_systemd_unit); # Configuration file put_text_to_file("$folder_for_build/DEBIAN/conffiles", "etc/fastnetmon.conf\n"); # Create folder for config mkdir("$folder_for_build/etc"); print `wget --no-check-certificate https://raw.githubusercontent.com/pavel-odintsov/fastnetmon/master/src/fastnetmon.conf -O$folder_for_build/etc/fastnetmon.conf`; `cp $archive_name $folder_for_build/archive.tar.gz`; mkdir "$folder_for_build/opt"; print `tar -xf $folder_for_build/archive.tar.gz -C $folder_for_build/opt`; unlink("$folder_for_build/archive.tar.gz"); mkdir "/tmp/result_data"; system("dpkg-deb --build $folder_for_build /tmp/result_data/fastnetmon_package.deb"); } sub put_text_to_file { my ($path, $text) = @_; open my $fl, ">", $path or die "Can't open $! for writing\n"; print {$fl} $text; close $fl; } fastnetmon-1.1.4/src/scripts/build_libary_bundle.pl000077500000000000000000000052671343111404700225130ustar00rootroot00000000000000#!/usr/bin/perl use strict; use warnings; use File::Copy; use File::Basename; use File::Path qw(make_path); # # This script will produce binary archive withh all libraries which required for FastNetMon # my $archive_bundle_name = ''; if (scalar @ARGV == 1 && $ARGV[0]) { $archive_bundle_name = $ARGV[0]; } else { $archive_bundle_name = '/tmp/fastnetmon_bundle.tar.gz'; } if (-e $archive_bundle_name) { print "Bundle file is already exists, remove it\n"; unlink $archive_bundle_name; } my $global_path = '/opt'; my $target_path = `mktemp -d`; chomp $target_path; unless (-e $target_path && -d $target_path) { die "Can't create target path\n"; } my @our_libraries = ( 'boost_1_58_0', 'gcc520', 'json-c-0.12', 'libhiredis_0_13', 'log4cpp1.1.1', 'luajit_2.0.4', 'ndpi', 'pf_ring_6.0.3' ); for my $library (@our_libraries) { my $library_path = "$global_path/$library"; unless (-e $library_path) { die "Can't find library $library please check\n"; } print "Library: $library\n"; my @files = `find $library_path`; for my $file_full_path (@files) { chomp $file_full_path; if ($file_full_path =~ /\.so[\.\d]*$/) { my $dir_name = dirname($file_full_path); my $file_name = basename($file_full_path); print "$dir_name $file_name\n"; my $target_full_path = $file_full_path; $target_full_path =~ s/^$global_path/$target_path/; # Create target folder my $target_full_folder_path = $dir_name; $target_full_folder_path =~ s/^$global_path/$target_path/; unless (-e $target_full_folder_path) { print "Create folder $target_full_folder_path\n"; make_path( $target_full_folder_path ); } if (-l $file_full_path) { my $symlink_target_name = readlink($file_full_path); print "We have symlink which aims to $symlink_target_name\n"; # This way we copy symlinks symlink($symlink_target_name, $target_full_path); } else { copy($file_full_path, $target_full_folder_path); } } } } # manually handle toolkit itself mkdir "$target_path/fastnetmon"; copy("$global_path/fastnetmon/fastnetmon", "$target_path/fastnetmon/fastnetmon"); copy("$global_path/fastnetmon/fastnetmon_client", "$target_path/fastnetmon/fastnetmon_client"); # Set exec flag chmod 0755, "$target_path/fastnetmon/fastnetmon"; chmod 0755, "$target_path/fastnetmon/fastnetmon_client"; `tar -cpzf $archive_bundle_name -C $target_path ./`; print "We have created bundle $archive_bundle_name\n"; fastnetmon-1.1.4/src/scripts/enable_passthrough_for_pcie_nic_to_kvm_vm.pl000066400000000000000000000063201343111404700271420ustar00rootroot00000000000000#!/usr/bin/perl use strict; use warnings; use Data::Dumper; my $vm_name = "fastnetmonvm.fastvps.ru"; execute_precheck(); execute_detach(); sub execute_precheck { my $cmdline = `cat /proc/cmdline`; chomp $cmdline; unless ($cmdline =~ /intel_iommu=on/) { print "Please add intel_iommu=on to kernel params\n"; print "You could do it in file /etc/default/grub\n"; print 'With param: GRUB_CMDLINE_LINUX_DEFAULT="intel_iommu=on"', "\n"; print "update-grub\n"; print "reboot\n"; exit(1); } my $conf_path = '/etc/modprobe.d/vfio_iommu_type1.conf'; # Debian Jessie, 3.16, Intel(R) Core(TM) i7-3820 CPU @ 3.60GHz desktop # Could be fixed in runtime: # echo 1 > /sys/module/vfio_iommu_type1/parameters/allow_unsafe_interrupts unless (-e $conf_path) { print "Please apply work around for error\n"; print "vfio_iommu_type1_attach_group: No interrupt remapping support.\n\n"; print "echo \"options vfio_iommu_type1 allow_unsafe_interrupts=1\" > /etc/modprobe.d/vfio_iommu_type1.conf"; print "\n"; print "And reboot server \n"; exit(0); } } sub execute_detach { # 03:00.0 Ethernet controller: Intel Corporation 82599ES 10-Gigabit SFI/SFP+ Network Connection (rev 01) my @lspci = `lspci`; chomp @lspci; # We process only Ethernet devices @lspci = grep {/Ethernet/} @lspci; @lspci = grep {/82599/} @lspci; my @nic_addresses = (); for my $nic (@lspci) { if ($nic =~ /(\d+\:\d+\.\d+)/) { push @nic_addresses, $1; } } my @virsh_full_addresses = (); for my $nic_address (@nic_addresses) { my $nic_address_in_virsh_format = $nic_address; $nic_address_in_virsh_format =~ s/[\:\.]/_/; my $virsh_address_full_format = `virsh nodedev-list | grep '$nic_address_in_virsh_format'`; chomp $virsh_address_full_format; push @virsh_full_addresses, $virsh_address_full_format; } # We use hash because multy port NICs could produce multiple equal address groups my $xml_blocks = {}; print "Detach NICs from the system\n"; for my $virsh_address (@virsh_full_addresses) { my $output = `virsh nodedev-dettach $virsh_address 2>&1`; chomp $output; if ($? != 0) { die "virsh nodedev-dettach failed with output: $output\n"; } #
my @xml_address_data = `virsh nodedev-dumpxml $virsh_address | grep address`; chomp @xml_address_data; for my $xml_line (@xml_address_data) { # cleanup $xml_line =~ s/^\s+//g; $xml_line =~ s/\s+$//g; $xml_blocks->{ $xml_line } = 1; } } my $target_xml = ''; for my $address_block (keys %$xml_blocks) { $target_xml .= "$address_block\n"; } print "Please run virsh edit $vm_name and insert this xml to devices block\n\n"; print $target_xml, "\n"; print "After this please execute virsh destroy $vm_name and virsh start $vm_name for applying changes\n"; } fastnetmon-1.1.4/src/scripts/exabgp_network_collector.conf000066400000000000000000000010751343111404700241060ustar00rootroot00000000000000group Core_v4 { hold-time 180; local-as 65000; peer-as 65000; router-id 10.0.129.2; #graceful-restart 1200; neighbor 10.0.131.2 { local-address 10.0.129.2; description "ExaBGP route server client"; # Do not establish outgoing connection # passive; process stdout { neighbor-changes; receive { parsed; update; } encoder json; run /usr/local/bin/bgp_network_collector.py; } } } fastnetmon-1.1.4/src/scripts/fastnetmon_notify.py000077500000000000000000000044621343111404700223000ustar00rootroot00000000000000#!/usr/bin/python import smtplib import sys from sys import stdin import optparse import sys import logging LOG_FILE = "/var/log/fastnetmon-notify.log" MAIL_HOSTNAME="localhost" MAIL_FROM="infra@example.com" MAIL_TO="infra@example.com" logger = logging.getLogger("DaemonLog") logger.setLevel(logging.INFO) formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s") handler = logging.FileHandler(LOG_FILE) handler.setFormatter(formatter) logger.addHandler(handler) client_ip_as_string=sys.argv[1] data_direction=sys.argv[2] pps_as_string=int(sys.argv[3]) action=sys.argv[4] logger.info(" - " . join(sys.argv)) def mail(subject, body): fromaddr = MAIL_FROM toaddrs = [MAIL_TO] # Add the From: and To: headers at the start! headers = ("From: %s\r\nTo: %s\r\nSubject: %s\r\n\r\n" % ( fromaddr, ", ".join(toaddrs), subject ) ) msg = headers + body server = smtplib.SMTP(MAIL_HOSTNAME) #server.set_debuglevel(1) server.sendmail(fromaddr, toaddrs, msg) server.quit() if action == "unban": subject = "Fastnetmon Guard: IP %(client_ip_as_string)s unblocked because %(data_direction)s attack with power %(pps_as_string)d pps" % { 'client_ip_as_string': client_ip_as_string, 'data_direction': data_direction, 'pps_as_string' : pps_as_string, 'action' : action } mail(subject, "unban") sys.exit(0) elif action == "ban": subject = "Fastnetmon Guard: IP %(client_ip_as_string)s blocked because %(data_direction)s attack with power %(pps_as_string)d pps" % { 'client_ip_as_string': client_ip_as_string, 'data_direction': data_direction, 'pps_as_string' : pps_as_string, 'action' : action } body = "".join(sys.stdin.readlines()) mail(subject, body) sys.exit(0) elif action == "attack_details": subject = "Fastnetmon Guard: IP %(client_ip_as_string)s blocked because %(data_direction)s attack with power %(pps_as_string)d pps" % { 'client_ip_as_string': client_ip_as_string, 'data_direction': data_direction, 'pps_as_string' : pps_as_string, 'action' : action } body = "".join(sys.stdin.readlines()) mail(subject, body) sys.exit(0) else: sys.exit(0) fastnetmon-1.1.4/src/scripts/install_binary.pl000077500000000000000000000203201343111404700215160ustar00rootroot00000000000000#!/usr/bin/perl use strict; use warnings; use Getopt::Long; my $start_time = time(); my $install_log_path = '/tmp/fastnetmon_install.log'; my $distro_type = ''; my $distro_version = ''; my $distro_architecture = ''; # Used for VyOS and different appliances based on rpm/deb my $appliance_name = ''; # So, you could disable this option but without this feature we could not improve FastNetMon for your distribution my $do_not_track_me = ''; my $cpus_number = get_logical_cpus_number(); main(); sub get_logical_cpus_number { my @cpuinfo = `cat /proc/cpuinfo`; chomp @cpuinfo; my $cpus_number = scalar grep {/processor/} @cpuinfo; return $cpus_number; } ### Functions start here sub main { detect_distribution(); # Refresh information about packages init_package_manager(); send_tracking_information('started'); install_fastnetmon(); my $install_time = time() - $start_time; my $pretty_install_time_in_minutes = sprintf("%.2f", $install_time / 60); print "We have installed project in $pretty_install_time_in_minutes minutes\n"; } sub send_tracking_information { my $step = shift; unless ($do_not_track_me) { my $stats_url = "http://178.62.227.110/new_fastnetmon_installation"; my $post_data = "distro_type=$distro_type&distro_version=$distro_version&distro_architecture=$distro_architecture&step=$step"; my $user_agent = 'FastNetMon install tracker v1'; `wget --post-data="$post_data" --user-agent="$user_agent" -q '$stats_url'`; } } sub exec_command { my $command = shift; open my $fl, ">>", $install_log_path; print {$fl} "We are calling command: $command\n\n"; my $output = `$command 2>&1 >> $install_log_path`; print {$fl} "Command finished with code $?\n\n"; if ($? == 0) { return 1; } else { return ''; } } sub get_sha1_sum { my $path = shift; my $output = `sha1sum $path`; chomp $output; my ($sha1) = ($output =~ m/^(\w+)\s+/); return $sha1; } sub download_file { my ($url, $path, $expected_sha1_checksumm) = @_; `wget --quiet '$url' -O$path`; if ($? != 0) { print "We can't download archive $url correctly\n"; return ''; } if ($expected_sha1_checksumm) { if (get_sha1_sum($path) eq $expected_sha1_checksumm) { return 1; } else { print "Downloaded archive has incorrect sha1\n"; return ''; } } else { return 1; } } sub init_package_manager { print "Update package manager cache\n"; if ($distro_type eq 'debian' or $distro_type eq 'ubuntu') { exec_command("apt-get update"); } } sub read_file { my $file_name = shift; my $res = open my $fl, "<", $file_name; unless ($res) { return ""; } my $content = join '', <$fl>; chomp $content; return $content; } # Detect operating system of this machine sub detect_distribution { # We use following global variables here: # $distro_type, $distro_version, $appliance_name # x86_64 or i686 $distro_architecture = `uname -m`; chomp $distro_architecture; if (-e "/etc/debian_version") { # Well, on this step it could be Ubuntu or Debian # We need check issue for more details my @issue = `cat /etc/issue`; chomp @issue; my $issue_first_line = $issue[0]; # Possible /etc/issue contents: # Debian GNU/Linux 8 \n \l # Ubuntu 14.04.2 LTS \n \l # Welcome to VyOS - \n \l if ($issue_first_line =~ m/Debian/) { $distro_type = 'debian'; $distro_version = `cat /etc/debian_version`; chomp $distro_version; # Debian 6 example: 6.0.10 # We will try transform it to decimal number if ($distro_version =~ /^(\d+\.\d+)\.\d+$/) { $distro_version = $1; } } elsif ($issue_first_line =~ m/Ubuntu (\d+(?:\.\d+)?)/) { $distro_type = 'ubuntu'; $distro_version = $1; } elsif ($issue_first_line =~ m/VyOS/) { # Yes, VyOS is a Debian $distro_type = 'debian'; $appliance_name = 'vyos'; my $vyos_distro_version = `cat /etc/debian_version`; chomp $vyos_distro_version; # VyOS have strange version and we should fix it if ($vyos_distro_version =~ /^(\d+)\.\d+\.\d+$/) { $distro_version = $1; } } } if (-e "/etc/redhat-release") { $distro_type = 'centos'; my $distro_version_raw = `cat /etc/redhat-release`; chomp $distro_version_raw; # CentOS 6: # CentOS release 6.6 (Final) # CentOS 7: # CentOS Linux release 7.0.1406 (Core) # Fedora release 21 (Twenty One) if ($distro_version_raw =~ /(\d+)/) { $distro_version = $1; } } if (-e "/etc/gentoo-release") { $distro_type = 'gentoo'; my $distro_version_raw = `cat /etc/gentoo-release`; chomp $distro_version_raw; } unless ($distro_type) { die "This distro is unsupported, please do manual install"; } } sub apt_get { my @packages_list = @_; # We install one package per apt-get call because installing multiple packages in one time could fail of one package is broken for my $package (@packages_list) { exec_command("apt-get install -y --force-yes $package"); if ($? != 0) { print "Package '$package' install failed with code $?\n" } } } sub yum { my @packages_list = @_; for my $package (@packages_list) { exec_command("yum install -y $package"); if ($? != 0) { print "Package '$package' install failed with code $?\n"; } } } sub install_fastnetmon { print "Install FastNetMon dependency list\n"; my $repository_address = 'http://178.62.227.110/fastnetmon_binary_repository/test_package_build'; my $file_name = ''; if ($distro_type eq 'ubuntu') { $file_name = "ubuntu-$distro_version-x86_64.deb"; } elsif ($distro_type eq 'debian') { my $our_own_debian_version = $distro_version; # Convert 6.x to 6.0 $our_own_debian_version =~ s/\.\d+/.0/; $file_name = "debian-$our_own_debian_version-x86_64.deb"; } elsif ($distro_type eq 'centos') { my $our_own_centos_version = int($distro_version); $file_name = "centos-$our_own_centos_version-x86_64.rpm"; } else { die "Sorry, we haven't binary packages for your distribution\n"; } # http://178.62.227.110/fastnetmon_binary_repository/test_package_build/fastnetmon-git-447aa5b86bb5a248e310c15a4d5945e72594d6cf-centos-6-x86_64_x86_64.rpm my $git_version = '447aa5b86bb5a248e310c15a4d5945e72594d6cf'; my $bundle_file_name = "fastnetmon-git-$git_version-$file_name"; my $full_url = "$repository_address/$bundle_file_name"; print "I will try to download file from $full_url\n"; my $fastnetmon_download_result = download_file($full_url, "/tmp/$bundle_file_name"); unless ($fastnetmon_download_result) { die "Can't download FastNetMon distribution\n"; } if ($distro_type eq 'debian') { if (int($distro_version) == 6) { apt_get('libpcap0.8', 'libnuma1', 'libicu44'); } if (int($distro_version) == 7) { apt_get('libpcap0.8', 'libnuma1', 'libicu48'); } if (int($distro_version) == 8) { apt_get('libpcap0.8', 'libnuma1', 'libicu52'); } } if ($distro_type eq 'ubuntu') { if ($distro_version eq '14.04') { apt_get('libicu52', 'libpcap0.8', 'libnuma1'); } } if ($distro_type eq 'centos') { # For CentOS 6 and 7 we are installind all deps with yum } if ($distro_type eq 'centos') { yum("/tmp/$bundle_file_name"); } elsif ($distro_type eq 'debian' or $distro_type eq 'ubuntu') { exec_command("dpkg -i /tmp/$bundle_file_name"); } print "If you have any issues, please check /var/log/fastnetmon.log file contents\n"; print "Please add your subnets in /etc/networks_list in CIDR format one subnet per line\n"; } fastnetmon-1.1.4/src/scripts/notify_with_slack.sh000077500000000000000000000054671343111404700222420ustar00rootroot00000000000000#!/usr/bin/env bash # # Hello, lovely FastNetMon customer! I'm really happy to see you here! # Pavel Odintsov, author # # # Instructions: # # Copy this script to /usr/local/bin/ # Edit /etc/fastnetmon.conf and set: # notify_script_path = /usr/local/bin/notify_with_slack.sh # # Add your email address to email_notify. # # Add your Slack incoming webhook to slack_url. # slack_url="https://hooks.slack.com/services/TXXXXXXXX/BXXXXXXXXX/LXXXXXXXXX" # # Notes: # hostname lookup requires the dig command. # Debian: apt-get install dnsutils # Redhat: yum install bind-utils # # For ban and attack_details actions we will receive attack details to stdin # if option notify_script_pass_details enabled in FastNetMon's configuration file # # If you do not need this details, please set option notify_script_pass_details to "no". # # Please do not remove the following command if you have notify_script_pass_details enabled, because # FastNetMon will crash in this case (it expect read of data from script side). # if [ "$4" = "ban" ] || [ "$4" = "attack_details" ]; then fastnetmon_output=$( 1 = n - 1 retransmissions */ uint32_t packet_duration_us; /* amount of time that the successfully transmitted packet occupied the RF medium */ uint32_t retrans_duration_us; /* amount of time that failed transmission attempts occupied the RF medium */ uint32_t channel; /* channel number */ uint64_t speed; uint32_t power_mw; /* transmit power in mW. */ } SFLExtended_wifi_tx; /* Extended 802.11 Aggregation Data */ /* A flow_sample of an aggregated frame would consist of a packet header for the whole frame + any other extended structures that apply (e.g. 80211_tx/rx etc.) + an extended_wifi_aggregation structure which would contain an array of pdu structures (one for each PDU in the aggregate). A pdu is simply an array of flow records, in the simplest case a packet header for each PDU, but extended structures could be included as well. */ /* opaque = flow_data; enterprise = 0; format = 1016 */ struct _SFLFlow_Pdu; /* forward decl */ typedef struct _SFLExtended_aggregation { uint32_t num_pdus; struct _SFFlow_Pdu* pdus; } SFLExtended_aggregation; /* Extended socket information, Must be filled in for all application transactions associated with a network socket Omit if transaction associated with non-network IPC */ /* IPv4 Socket */ /* opaque = flow_data; enterprise = 0; format = 2100 */ typedef struct _SFLExtended_socket_ipv4 { uint32_t protocol; /* IP Protocol (e.g. TCP = 6, UDP = 17) */ SFLIPv4 local_ip; /* local IP address */ SFLIPv4 remote_ip; /* remote IP address */ uint32_t local_port; /* TCP/UDP local port number or equivalent */ uint32_t remote_port; /* TCP/UDP remote port number of equivalent */ } SFLExtended_socket_ipv4; #define XDRSIZ_SFLEXTENDED_SOCKET4 20 /* IPv6 Socket */ /* opaque = flow_data; enterprise = 0; format = 2101 */ typedef struct _SFLExtended_socket_ipv6 { uint32_t protocol; /* IP Protocol (e.g. TCP = 6, UDP = 17) */ SFLIPv6 local_ip; /* local IP address */ SFLIPv6 remote_ip; /* remote IP address */ uint32_t local_port; /* TCP/UDP local port number or equivalent */ uint32_t remote_port; /* TCP/UDP remote port number of equivalent */ } SFLExtended_socket_ipv6; #define XDRSIZ_SFLEXTENDED_SOCKET6 44 typedef enum { MEMCACHE_PROT_OTHER = 0, MEMCACHE_PROT_ASCII = 1, MEMCACHE_PROT_BINARY = 2, } SFLMemcache_prot; typedef enum { MEMCACHE_CMD_OTHER = 0, MEMCACHE_CMD_SET = 1, MEMCACHE_CMD_ADD = 2, MEMCACHE_CMD_REPLACE = 3, MEMCACHE_CMD_APPEND = 4, MEMCACHE_CMD_PREPEND = 5, MEMCACHE_CMD_CAS = 6, MEMCACHE_CMD_GET = 7, MEMCACHE_CMD_GETS = 8, MEMCACHE_CMD_INCR = 9, MEMCACHE_CMD_DECR = 10, MEMCACHE_CMD_DELETE = 11, MEMCACHE_CMD_STATS = 12, MEMCACHE_CMD_FLUSH = 13, MEMCACHE_CMD_VERSION = 14, MEMCACHE_CMD_QUIT = 15, MEMCACHE_CMD_TOUCH = 16, } SFLMemcache_cmd; enum SFLMemcache_operation_status { MEMCACHE_OP_UNKNOWN = 0, MEMCACHE_OP_OK = 1, MEMCACHE_OP_ERROR = 2, MEMCACHE_OP_CLIENT_ERROR = 3, MEMCACHE_OP_SERVER_ERROR = 4, MEMCACHE_OP_STORED = 5, MEMCACHE_OP_NOT_STORED = 6, MEMCACHE_OP_EXISTS = 7, MEMCACHE_OP_NOT_FOUND = 8, MEMCACHE_OP_DELETED = 9, }; #define SFL_MAX_MEMCACHE_KEY 255 typedef struct _SFLSampled_memcache { uint32_t protocol; /* SFLMemcache_prot */ uint32_t command; /* SFLMemcache_cmd */ SFLString key; /* up to 255 chars */ uint32_t nkeys; uint32_t value_bytes; uint32_t duration_uS; uint32_t status; /* SFLMemcache_operation_status */ } SFLSampled_memcache; typedef enum { SFHTTP_OTHER = 0, SFHTTP_OPTIONS = 1, SFHTTP_GET = 2, SFHTTP_HEAD = 3, SFHTTP_POST = 4, SFHTTP_PUT = 5, SFHTTP_DELETE = 6, SFHTTP_TRACE = 7, SFHTTP_CONNECT = 8, } SFLHTTP_method; #define SFL_MAX_HTTP_URI 255 #define SFL_MAX_HTTP_HOST 64 #define SFL_MAX_HTTP_REFERRER 255 #define SFL_MAX_HTTP_USERAGENT 128 #define SFL_MAX_HTTP_XFF 64 #define SFL_MAX_HTTP_AUTHUSER 32 #define SFL_MAX_HTTP_MIMETYPE 64 typedef struct _SFLSampled_http { SFLHTTP_method method; uint32_t protocol; /* 1.1=1001 */ SFLString uri; /* URI exactly as it came from the client (up to 255 bytes) */ SFLString host; /* Host value from request header (<= 64 bytes) */ SFLString referrer; /* Referer value from request header (<=255 bytes) */ SFLString useragent; /* User-Agent value from request header (<= 128 bytes)*/ SFLString xff; /* X-Forwarded-For value from request header (<= 64 bytes)*/ SFLString authuser; /* RFC 1413 identity of user (<=32 bytes)*/ SFLString mimetype; /* Mime-Type (<=64 bytes) */ uint64_t req_bytes; /* Content-Length of request */ uint64_t resp_bytes; /* Content-Length of response */ uint32_t uS; /* duration of the operation (microseconds) */ uint32_t status; /* HTTP status code */ } SFLSampled_http; typedef enum { SFLAPP_SUCCESS = 0, SFLAPP_OTHER = 1, SFLAPP_TIMEOUT = 2, SFLAPP_INTERNAL_ERROR = 3, SFLAPP_BAD_REQUEST = 4, SFLAPP_FORBIDDEN = 5, SFLAPP_TOO_LARGE = 6, SFLAPP_NOT_IMPLEMENTED = 7, SFLAPP_NOT_FOUND = 8, SFLAPP_UNAVAILABLE = 9, SFLAPP_UNAUTHORIZED = 10, } EnumSFLAPPStatus; static const char* SFL_APP_STATUS_names[] = { "SUCCESS", "OTHER", "TIMEOUT", "INTERNAL_ERROR", "BAD_REQUEST", "FORBIDDEN", "TOO_LARGE", "NOT_IMPLEMENTED", "NOT_FOUND", "UNAVAILABLE", "UNATHORIZED" }; /* Operation context */ typedef struct { SFLString application; SFLString operation; /* type of operation (e.g. authorization, payment) */ SFLString attributes; /* specific attributes associated operation */ } SFLSampled_APP_CTXT; #define SFLAPP_MAX_APPLICATION_LEN 32 #define SFLAPP_MAX_OPERATION_LEN 32 #define SFLAPP_MAX_ATTRIBUTES_LEN 255 /* Sampled Enterprise Operation */ /* opaque = flow_data; enterprise = 0; format = 2202 */ typedef struct { SFLSampled_APP_CTXT context; /* attributes describing the operation */ SFLString status_descr; /* additional text describing status (e.g. "unknown client") */ uint64_t req_bytes; /* size of request body (exclude headers) */ uint64_t resp_bytes; /* size of response body (exclude headers) */ uint32_t duration_uS; /* duration of the operation (microseconds) */ EnumSFLAPPStatus status; /* status code */ } SFLSampled_APP; #define SFLAPP_MAX_STATUS_LEN 32 typedef struct { SFLString actor; } SFLSampled_APP_ACTOR; #define SFLAPP_MAX_ACTOR_LEN 64 typedef struct _SFLExtended_vni { uint32_t vni; /* virtual network identifier */ } SFLExtended_vni; typedef struct _SFLExtended_decap { uint32_t innerHeaderOffset; } SFLExtended_decap; enum SFLFlow_type_tag { /* enterprise = 0, format = ... */ SFLFLOW_HEADER = 1, /* Packet headers are sampled */ SFLFLOW_ETHERNET = 2, /* MAC layer information */ SFLFLOW_IPV4 = 3, /* IP version 4 data */ SFLFLOW_IPV6 = 4, /* IP version 6 data */ SFLFLOW_EX_SWITCH = 1001, /* Extended switch information */ SFLFLOW_EX_ROUTER = 1002, /* Extended router information */ SFLFLOW_EX_GATEWAY = 1003, /* Extended gateway router information */ SFLFLOW_EX_USER = 1004, /* Extended TACAS/RADIUS user information */ SFLFLOW_EX_URL = 1005, /* Extended URL information */ SFLFLOW_EX_MPLS = 1006, /* Extended MPLS information */ SFLFLOW_EX_NAT = 1007, /* Extended NAT information */ SFLFLOW_EX_MPLS_TUNNEL = 1008, /* additional MPLS information */ SFLFLOW_EX_MPLS_VC = 1009, SFLFLOW_EX_MPLS_FTN = 1010, SFLFLOW_EX_MPLS_LDP_FEC = 1011, SFLFLOW_EX_VLAN_TUNNEL = 1012, /* VLAN stack */ SFLFLOW_EX_80211_PAYLOAD = 1013, SFLFLOW_EX_80211_RX = 1014, SFLFLOW_EX_80211_TX = 1015, SFLFLOW_EX_AGGREGATION = 1016, SFLFLOW_EX_NAT_PORT = 1020, /* Extended NAT port information */ SFLFLOW_EX_L2_TUNNEL_OUT = 1021, /* http://sflow.org/sflow_tunnels.txt */ SFLFLOW_EX_L2_TUNNEL_IN = 1022, SFLFLOW_EX_IPV4_TUNNEL_OUT = 1023, SFLFLOW_EX_IPV4_TUNNEL_IN = 1024, SFLFLOW_EX_IPV6_TUNNEL_OUT = 1025, SFLFLOW_EX_IPV6_TUNNEL_IN = 1026, SFLFLOW_EX_DECAP_OUT = 1027, SFLFLOW_EX_DECAP_IN = 1028, SFLFLOW_EX_VNI_OUT = 1029, SFLFLOW_EX_VNI_IN = 1030, SFLFLOW_EX_SOCKET4 = 2100, SFLFLOW_EX_SOCKET6 = 2101, SFLFLOW_EX_PROXYSOCKET4 = 2102, SFLFLOW_EX_PROXYSOCKET6 = 2103, SFLFLOW_MEMCACHE = 2200, SFLFLOW_HTTP = 2201, SFLFLOW_APP = 2202, /* transaction sample */ SFLFLOW_APP_CTXT = 2203, /* enclosing server context */ SFLFLOW_APP_ACTOR_INIT = 2204, /* initiator */ SFLFLOW_APP_ACTOR_TGT = 2205, /* target */ SFLFLOW_HTTP2 = 2206, }; typedef union _SFLFlow_type { SFLSampled_header header; SFLSampled_ethernet ethernet; SFLSampled_ipv4 ipv4; SFLSampled_ipv6 ipv6; SFLSampled_memcache memcache; SFLSampled_http http; SFLSampled_APP app; SFLSampled_APP_CTXT appCtxt; SFLSampled_APP_ACTOR appActor; SFLExtended_switch sw; SFLExtended_router router; SFLExtended_gateway gateway; SFLExtended_user user; SFLExtended_url url; SFLExtended_mpls mpls; SFLExtended_nat nat; SFLExtended_nat_port nat_port; SFLExtended_mpls_tunnel mpls_tunnel; SFLExtended_mpls_vc mpls_vc; SFLExtended_mpls_FTN mpls_ftn; SFLExtended_mpls_LDP_FEC mpls_ldp_fec; SFLExtended_vlan_tunnel vlan_tunnel; SFLExtended_wifi_payload wifi_payload; SFLExtended_wifi_rx wifi_rx; SFLExtended_wifi_tx wifi_tx; SFLExtended_aggregation aggregation; SFLExtended_socket_ipv4 socket4; SFLExtended_socket_ipv6 socket6; SFLExtended_vni tunnel_vni; SFLExtended_decap tunnel_decap; } SFLFlow_type; typedef struct _SFLFlow_sample_element { struct _SFLFlow_sample_element* nxt; uint32_t tag; /* SFLFlow_type_tag */ uint32_t length; SFLFlow_type flowType; } SFLFlow_sample_element; enum SFL_sample_tag { SFLFLOW_SAMPLE = 1, /* enterprise = 0 : format = 1 */ SFLCOUNTERS_SAMPLE = 2, /* enterprise = 0 : format = 2 */ SFLFLOW_SAMPLE_EXPANDED = 3, /* enterprise = 0 : format = 3 */ SFLCOUNTERS_SAMPLE_EXPANDED = 4 /* enterprise = 0 : format = 4 */ }; typedef struct _SFLFlow_Pdu { struct _SFLFlow_Pdu* nxt; uint32_t num_elements; SFLFlow_sample_element* elements; } SFLFlow_Pdu; /* Format of a single flow sample */ typedef struct _SFLFlow_sample { /* uint32_t tag; */ /* SFL_sample_tag -- enterprise = 0 : format = 1 */ /* uint32_t length; */ uint32_t sequence_number; /* Incremented with each flow sample generated */ uint32_t source_id; /* fsSourceId */ uint32_t sampling_rate; /* fsPacketSamplingRate */ uint32_t sample_pool; /* Total number of packets that could have been sampled (i.e. packets skipped by sampling process + total number of samples) */ uint32_t drops; /* Number of times a packet was dropped due to lack of resources */ uint32_t input; /* SNMP ifIndex of input interface. 0 if interface is not known. */ uint32_t output; /* SNMP ifIndex of output interface, 0 if interface is not known. Set most significant bit to indicate multiple destination interfaces (i.e. in case of broadcast or multicast) and set lower order bits to indicate number of destination interfaces. Examples: 0x00000002 indicates ifIndex = 2 0x00000000 ifIndex unknown. 0x80000007 indicates a packet sent to 7 interfaces. 0x80000000 indicates a packet sent to an unknown number of interfaces greater than 1.*/ uint32_t num_elements; SFLFlow_sample_element* elements; } SFLFlow_sample; /* same thing, but the expanded version (for full 32-bit ifIndex numbers) */ typedef struct _SFLFlow_sample_expanded { /* uint32_t tag; */ /* SFL_sample_tag -- enterprise = 0 : format = 1 */ /* uint32_t length; */ uint32_t sequence_number; /* Incremented with each flow sample generated */ uint32_t ds_class; /* EXPANDED */ uint32_t ds_index; /* EXPANDED */ uint32_t sampling_rate; /* fsPacketSamplingRate */ uint32_t sample_pool; /* Total number of packets that could have been sampled (i.e. packets skipped by sampling process + total number of samples) */ uint32_t drops; /* Number of times a packet was dropped due to lack of resources */ uint32_t inputFormat; /* EXPANDED */ uint32_t input; /* SNMP ifIndex of input interface. 0 if interface is not known. */ uint32_t outputFormat; /* EXPANDED */ uint32_t output; /* SNMP ifIndex of output interface, 0 if interface is not known. */ uint32_t num_elements; SFLFlow_sample_element* elements; } SFLFlow_sample_expanded; /* Counter types */ /* Generic interface counters - see RFC 1573, 2233 */ typedef struct _SFLIf_counters { uint32_t ifIndex; uint32_t ifType; uint64_t ifSpeed; uint32_t ifDirection; /* Derived from MAU MIB (RFC 2668) 0 = unknown, 1 = full-duplex, 2 = half-duplex, 3 = in, 4 = out */ uint32_t ifStatus; /* bit field with the following bits assigned: bit 0 = ifAdminStatus (0 = down, 1 = up) bit 1 = ifOperStatus (0 = down, 1 = up) */ uint64_t ifInOctets; uint32_t ifInUcastPkts; uint32_t ifInMulticastPkts; uint32_t ifInBroadcastPkts; uint32_t ifInDiscards; uint32_t ifInErrors; uint32_t ifInUnknownProtos; uint64_t ifOutOctets; uint32_t ifOutUcastPkts; uint32_t ifOutMulticastPkts; uint32_t ifOutBroadcastPkts; uint32_t ifOutDiscards; uint32_t ifOutErrors; uint32_t ifPromiscuousMode; } SFLIf_counters; /* Ethernet interface counters - see RFC 2358 */ typedef struct _SFLEthernet_counters { uint32_t dot3StatsAlignmentErrors; uint32_t dot3StatsFCSErrors; uint32_t dot3StatsSingleCollisionFrames; uint32_t dot3StatsMultipleCollisionFrames; uint32_t dot3StatsSQETestErrors; uint32_t dot3StatsDeferredTransmissions; uint32_t dot3StatsLateCollisions; uint32_t dot3StatsExcessiveCollisions; uint32_t dot3StatsInternalMacTransmitErrors; uint32_t dot3StatsCarrierSenseErrors; uint32_t dot3StatsFrameTooLongs; uint32_t dot3StatsInternalMacReceiveErrors; uint32_t dot3StatsSymbolErrors; } SFLEthernet_counters; /* Token ring counters - see RFC 1748 */ typedef struct _SFLTokenring_counters { uint32_t dot5StatsLineErrors; uint32_t dot5StatsBurstErrors; uint32_t dot5StatsACErrors; uint32_t dot5StatsAbortTransErrors; uint32_t dot5StatsInternalErrors; uint32_t dot5StatsLostFrameErrors; uint32_t dot5StatsReceiveCongestions; uint32_t dot5StatsFrameCopiedErrors; uint32_t dot5StatsTokenErrors; uint32_t dot5StatsSoftErrors; uint32_t dot5StatsHardErrors; uint32_t dot5StatsSignalLoss; uint32_t dot5StatsTransmitBeacons; uint32_t dot5StatsRecoverys; uint32_t dot5StatsLobeWires; uint32_t dot5StatsRemoves; uint32_t dot5StatsSingles; uint32_t dot5StatsFreqErrors; } SFLTokenring_counters; /* 100 BaseVG interface counters - see RFC 2020 */ typedef struct _SFLVg_counters { uint32_t dot12InHighPriorityFrames; uint64_t dot12InHighPriorityOctets; uint32_t dot12InNormPriorityFrames; uint64_t dot12InNormPriorityOctets; uint32_t dot12InIPMErrors; uint32_t dot12InOversizeFrameErrors; uint32_t dot12InDataErrors; uint32_t dot12InNullAddressedFrames; uint32_t dot12OutHighPriorityFrames; uint64_t dot12OutHighPriorityOctets; uint32_t dot12TransitionIntoTrainings; uint64_t dot12HCInHighPriorityOctets; uint64_t dot12HCInNormPriorityOctets; uint64_t dot12HCOutHighPriorityOctets; } SFLVg_counters; typedef struct _SFLVlan_counters { uint32_t vlan_id; uint64_t octets; uint32_t ucastPkts; uint32_t multicastPkts; uint32_t broadcastPkts; uint32_t discards; } SFLVlan_counters; typedef struct _SFLWifi_counters { uint32_t dot11TransmittedFragmentCount; uint32_t dot11MulticastTransmittedFrameCount; uint32_t dot11FailedCount; uint32_t dot11RetryCount; uint32_t dot11MultipleRetryCount; uint32_t dot11FrameDuplicateCount; uint32_t dot11RTSSuccessCount; uint32_t dot11RTSFailureCount; uint32_t dot11ACKFailureCount; uint32_t dot11ReceivedFragmentCount; uint32_t dot11MulticastReceivedFrameCount; uint32_t dot11FCSErrorCount; uint32_t dot11TransmittedFrameCount; uint32_t dot11WEPUndecryptableCount; uint32_t dot11QoSDiscardedFragmentCount; uint32_t dot11AssociatedStationCount; uint32_t dot11QoSCFPollsReceivedCount; uint32_t dot11QoSCFPollsUnusedCount; uint32_t dot11QoSCFPollsUnusableCount; uint32_t dot11QoSCFPollsLostCount; } SFLWifi_counters; /* Processor Information */ /* opaque = counter_data; enterprise = 0; format = 1001 */ typedef struct _SFLProcessor_counters { uint32_t five_sec_cpu; /* 5 second average CPU utilization */ uint32_t one_min_cpu; /* 1 minute average CPU utilization */ uint32_t five_min_cpu; /* 5 minute average CPU utilization */ uint64_t total_memory; /* total memory (in bytes) */ uint64_t free_memory; /* free memory (in bytes) */ } SFLProcessor_counters; typedef struct _SFLRadio_counters { uint32_t elapsed_time; /* elapsed time in ms */ uint32_t on_channel_time; /* time in ms spent on channel */ uint32_t on_channel_busy_time; /* time in ms spent on channel and busy */ } SFLRadio_counters; /* host sflow */ enum SFLMachine_type { SFLMT_unknown = 0, SFLMT_other = 1, SFLMT_x86 = 2, SFLMT_x86_64 = 3, SFLMT_ia64 = 4, SFLMT_sparc = 5, SFLMT_alpha = 6, SFLMT_powerpc = 7, SFLMT_m68k = 8, SFLMT_mips = 9, SFLMT_arm = 10, SFLMT_hppa = 11, SFLMT_s390 = 12 }; enum SFLOS_name { SFLOS_unknown = 0, SFLOS_other = 1, SFLOS_linux = 2, SFLOS_windows = 3, SFLOS_darwin = 4, SFLOS_hpux = 5, SFLOS_aix = 6, SFLOS_dragonfly = 7, SFLOS_freebsd = 8, SFLOS_netbsd = 9, SFLOS_openbsd = 10, SFLOS_osf = 11, SFLOS_solaris = 12 }; typedef struct _SFLMacAddress { uint8_t mac[8]; } SFLMacAddress; typedef struct _SFLAdaptor { uint32_t ifIndex; uint32_t num_macs; SFLMacAddress macs[1]; } SFLAdaptor; typedef struct _SFLAdaptorList { uint32_t capacity; uint32_t num_adaptors; SFLAdaptor** adaptors; } SFLAdaptorList; typedef struct _SFLHost_parent { uint32_t dsClass; /* sFlowDataSource class */ uint32_t dsIndex; /* sFlowDataSource index */ } SFLHost_parent; #define SFL_MAX_HOSTNAME_LEN 64 #define SFL_MAX_OSRELEASE_LEN 32 typedef struct _SFLHostId { SFLString hostname; u_char uuid[16]; uint32_t machine_type; /* enum SFLMachine_type */ uint32_t os_name; /* enum SFLOS_name */ SFLString os_release; /* max len 32 bytes */ } SFLHostId; typedef struct _SFLHost_nio_counters { uint64_t bytes_in; uint32_t pkts_in; uint32_t errs_in; uint32_t drops_in; uint64_t bytes_out; uint32_t pkts_out; uint32_t errs_out; uint32_t drops_out; } SFLHost_nio_counters; typedef struct _SFLHost_cpu_counters { float load_one; /* 1 minute load avg. */ float load_five; /* 5 minute load avg. */ float load_fifteen; /* 15 minute load avg. */ uint32_t proc_run; /* running threads */ uint32_t proc_total; /* total threads */ uint32_t cpu_num; /* # CPU cores */ uint32_t cpu_speed; /* speed in MHz of CPU */ uint32_t uptime; /* seconds since last reboot */ uint32_t cpu_user; /* time executing in user mode processes (ms) */ uint32_t cpu_nice; /* time executing niced processs (ms) */ uint32_t cpu_system; /* time executing kernel mode processes (ms) */ uint32_t cpu_idle; /* idle time (ms) */ uint32_t cpu_wio; /* time waiting for I/O to complete (ms) */ uint32_t cpu_intr; /* time servicing interrupts (ms) */ uint32_t cpu_sintr; /* time servicing softirqs (ms) */ uint32_t interrupts; /* interrupt count */ uint32_t contexts; /* context switch count */ } SFLHost_cpu_counters; typedef struct _SFLHost_mem_counters { uint64_t mem_total; /* total bytes */ uint64_t mem_free; /* free bytes */ uint64_t mem_shared; /* shared bytes */ uint64_t mem_buffers; /* buffers bytes */ uint64_t mem_cached; /* cached bytes */ uint64_t swap_total; /* swap total bytes */ uint64_t swap_free; /* swap free bytes */ uint32_t page_in; /* page in count */ uint32_t page_out; /* page out count */ uint32_t swap_in; /* swap in count */ uint32_t swap_out; /* swap out count */ } SFLHost_mem_counters; typedef struct _SFLHost_dsk_counters { uint64_t disk_total; uint64_t disk_free; uint32_t part_max_used; /* as percent * 100, so 100==1% */ uint32_t reads; /* reads issued */ uint64_t bytes_read; /* bytes read */ uint32_t read_time; /* read time (ms) */ uint32_t writes; /* writes completed */ uint64_t bytes_written; /* bytes written */ uint32_t write_time; /* write time (ms) */ } SFLHost_dsk_counters; /* Virtual Node Statistics */ /* opaque = counter_data; enterprise = 0; format = 2100 */ typedef struct _SFLHost_vrt_node_counters { uint32_t mhz; /* expected CPU frequency */ uint32_t cpus; /* the number of active CPUs */ uint64_t memory; /* memory size in bytes */ uint64_t memory_free; /* unassigned memory in bytes */ uint32_t num_domains; /* number of active domains */ } SFLHost_vrt_node_counters; /* Virtual Domain Statistics */ /* opaque = counter_data; enterprise = 0; format = 2101 */ /* virDomainState imported from libvirt.h */ enum SFLVirDomainState { SFL_VIR_DOMAIN_NOSTATE = 0, /* no state */ SFL_VIR_DOMAIN_RUNNING = 1, /* the domain is running */ SFL_VIR_DOMAIN_BLOCKED = 2, /* the domain is blocked on resource */ SFL_VIR_DOMAIN_PAUSED = 3, /* the domain is paused by user */ SFL_VIR_DOMAIN_SHUTDOWN = 4, /* the domain is being shut down */ SFL_VIR_DOMAIN_SHUTOFF = 5, /* the domain is shut off */ SFL_VIR_DOMAIN_CRASHED = 6 /* the domain is crashed */ }; typedef struct _SFLHost_vrt_cpu_counters { uint32_t state; /* virtDomainState */ uint32_t cpuTime; /* the CPU time used in mS */ uint32_t cpuCount; /* number of virtual CPUs for the domain */ } SFLHost_vrt_cpu_counters; /* Virtual Domain Memory statistics */ /* opaque = counter_data; enterprise = 0; format = 2102 */ typedef struct _SFLHost_vrt_mem_counters { uint64_t memory; /* memory in bytes used by domain */ uint64_t maxMemory; /* memory in bytes allowed */ } SFLHost_vrt_mem_counters; /* Virtual Domain Disk statistics */ /* opaque = counter_data; enterprise = 0; format = 2103 */ typedef struct _SFLHost_vrt_dsk_counters { uint64_t capacity; /* logical size in bytes */ uint64_t allocation; /* current allocation in bytes */ uint64_t available; /* remaining free bytes */ uint32_t rd_req; /* number of read requests */ uint64_t rd_bytes; /* number of read bytes */ uint32_t wr_req; /* number of write requests */ uint64_t wr_bytes; /* number of written bytes */ uint32_t errs; /* read/write errors */ } SFLHost_vrt_dsk_counters; /* Virtual Domain Network statistics */ /* opaque = counter_data; enterprise = 0; format = 2104 */ typedef struct _SFLHost_vrt_nio_counters { uint64_t bytes_in; uint32_t pkts_in; uint32_t errs_in; uint32_t drops_in; uint64_t bytes_out; uint32_t pkts_out; uint32_t errs_out; uint32_t drops_out; } SFLHost_vrt_nio_counters; /* NVML statistics */ /* opaque = counter_data; enterprise = 5703, format=1 */ typedef struct _SFLHost_gpu_nvml { uint32_t device_count; /* see nvmlGetDeviceCount */ uint32_t processes; /* see nvmlDeviceGetComputeRunningProcesses */ uint32_t gpu_time; /* total milliseconds in which one or more kernels was executing on GPU */ uint32_t mem_time; /* total milliseconds during which global device memory was being read/written */ uint64_t mem_total; /* bytes. see nvmlDeviceGetMemoryInfo */ uint64_t mem_free; /* bytes. see nvmlDeviceGetMemoryInfo */ uint32_t ecc_errors; /* see nvmlDeviceGetTotalEccErrors */ uint32_t energy; /* mJ. see nvmlDeviceGetPowerUsage */ uint32_t temperature; /* C. maximum across devices - see nvmlDeviceGetTemperature */ uint32_t fan_speed; /* %. maximum across devices - see nvmlDeviceGetFanSpeed */ } SFLHost_gpu_nvml; typedef struct _SFLMemcache_counters { uint32_t uptime; /* not in 2204 */ uint32_t rusage_user; /* not in 2204 */ uint32_t rusage_system; /* not in 2204 */ uint32_t cmd_get; /* not in 2204 */ uint32_t accepting_conns; /* not in 2204 */ uint32_t cmd_set; uint32_t cmd_touch; /* added for 2204 */ uint32_t cmd_flush; uint32_t get_hits; uint32_t get_misses; uint32_t delete_hits; uint32_t delete_misses; uint32_t incr_hits; uint32_t incr_misses; uint32_t decr_hits; uint32_t decr_misses; uint32_t cas_hits; uint32_t cas_misses; uint32_t cas_badval; uint32_t auth_cmds; uint32_t auth_errors; uint32_t threads; uint32_t conn_yields; uint32_t listen_disabled_num; uint32_t curr_connections; uint32_t rejected_connections; /* added for 2204 */ uint32_t total_connections; uint32_t connection_structures; uint32_t evictions; uint32_t reclaimed; /* added for 2204 */ uint32_t curr_items; uint32_t total_items; uint64_t bytes_read; uint64_t bytes_written; uint64_t bytes; uint64_t limit_maxbytes; /* converted to 64-bit for structure 2204 */ } SFLMemcache_counters; typedef struct _SFLHTTP_counters { uint32_t method_option_count; uint32_t method_get_count; uint32_t method_head_count; uint32_t method_post_count; uint32_t method_put_count; uint32_t method_delete_count; uint32_t method_trace_count; uint32_t methd_connect_count; uint32_t method_other_count; uint32_t status_1XX_count; uint32_t status_2XX_count; uint32_t status_3XX_count; uint32_t status_4XX_count; uint32_t status_5XX_count; uint32_t status_other_count; } SFLHTTP_counters; /* Enterprise counters */ /* opaque = counter_data; enterprise = 0; format = 2202 */ typedef struct _SFLAPP_counters { SFLString application; uint32_t status_OK; uint32_t errors_OTHER; uint32_t errors_TIMEOUT; uint32_t errors_INTERNAL_ERROR; uint32_t errors_BAD_REQUEST; uint32_t errors_FORBIDDEN; uint32_t errors_TOO_LARGE; uint32_t errors_NOT_IMPLEMENTED; uint32_t errors_NOT_FOUND; uint32_t errors_UNAVAILABLE; uint32_t errors_UNAUTHORIZED; } SFLAPP_counters; /* Enterprise resource counters */ /* opaque = counter_data; enterprise = 0; format = 2203 */ typedef struct { uint32_t user_time; /* in milliseconds */ uint32_t system_time; /* in milliseconds */ uint64_t mem_used; uint64_t mem_max; uint32_t fd_open; uint32_t fd_max; uint32_t conn_open; uint32_t conn_max; } SFLAPP_resources; /* Enterprise application workers */ /* opaque = counter_data; enterprise = 0; format = 2206 */ typedef struct { uint32_t workers_active; uint32_t workers_idle; uint32_t workers_max; uint32_t req_delayed; uint32_t req_dropped; } SFLAPP_workers; typedef struct _SFLJVM_ID { SFLString vm_name; SFLString vm_vendor; SFLString vm_version; } SFLJVM_ID; #define SFLJVM_MAX_VMNAME_LEN 64 #define SFLJVM_MAX_VENDOR_LEN 32 #define SFLJVM_MAX_VERSION_LEN 32 typedef struct _SFLJMX_counters { uint64_t hmem_initial; uint64_t hmem_used; uint64_t hmem_committed; uint64_t hmem_max; uint64_t nhmem_initial; uint64_t nhmem_used; uint64_t nhmem_committed; uint64_t nhmem_max; uint32_t gc_count; uint32_t gc_ms; uint32_t cls_loaded; uint32_t cls_total; uint32_t cls_unloaded; uint32_t comp_ms; uint32_t thread_live; uint32_t thread_daemon; uint32_t thread_started; uint32_t fds_open; uint32_t fds_max; } SFLJMX_counters; #define XDRSIZ_JMX_COUNTERS 108 typedef struct _SFLVdi_counters { uint32_t sessions_current; /* number of current sessions */ uint32_t sessions_total; /* total sessions started */ uint32_t sessions_duration; /* cumulative session time (in seconds) across all sessions, such that average session duration = sessions_duration / sessions_total */ uint32_t rx_bytes; /* total bytes received */ uint32_t tx_bytes; /* total bytes sent */ uint32_t rx_packets; /* total packet received */ uint32_t tx_packets; /* total packets sent */ uint32_t rx_packets_lost; /* total received packets lost */ uint32_t tx_packets_lost; /* total sent packets lost */ uint32_t rtt_min_ms; /* minimum round trip latency with client across all current sessions measured in milliseconds */ uint32_t rtt_max_ms; /* maximum round trip latency with client across all current sessions measured in millisecond */ uint32_t rtt_avg_ms; /* average round trip latency with client across all current sessions measured in milliseconds */ uint32_t audio_rx_bytes; /* total bytes of audio data received */ uint32_t audio_tx_bytes; /* total bytes of audio data sent */ uint32_t audio_tx_limit; /* administrative limit on audio transmission bandwidth (in bits per second) */ uint32_t img_rx_bytes; /* total bytes of imaging data recieved */ uint32_t img_tx_bytes; /* total bytes of imaging data sent */ uint32_t img_frames; /* total image frames encoded */ uint32_t img_qual_min; /* minimum image encoding quality across current sessions, on a scale of 0 to 100 */ uint32_t img_qual_max; /* best image encoding quality across current sessions, on a scale of 0 to 100 */ uint32_t img_qual_avg; /* average image encoding quality across current sessions, on a scale of 0 to 100 */ uint32_t usb_rx_bytes; /* total bytes of usb data received */ uint32_t usb_tx_bytes; /* total bytes of usb data sent */ } SFLVdi_counters; /* LAG Port Statistics - see IEEE8023-LAG-MIB */ /* opaque = counter_data; enterprise = 0; format = 7 */ typedef union _SFLLACP_portState { uint32_t all; struct { uint8_t actorAdmin; uint8_t actorOper; uint8_t partnerAdmin; uint8_t partnerOper; } v; } SFLLACP_portState; typedef struct _SFLLACP_counters { uint8_t actorSystemID[8]; /* 6 bytes + 2 pad */ uint8_t partnerSystemID[8]; /* 6 bytes + 2 pad */ uint32_t attachedAggID; SFLLACP_portState portState; uint32_t LACPDUsRx; uint32_t markerPDUsRx; uint32_t markerResponsePDUsRx; uint32_t unknownRx; uint32_t illegalRx; uint32_t LACPDUsTx; uint32_t markerPDUsTx; uint32_t markerResponsePDUsTx; } SFLLACP_counters; #define XDRSIZ_LACP_COUNTERS 56 /* port name */ /* opaque = counter_data; enterprise = 0; format = 1005 */ typedef struct { SFLString portName; } SFLPortName; #define SFL_MAX_PORTNAME_LEN 255 /* Counters data */ enum SFLCounters_type_tag { /* enterprise = 0, format = ... */ SFLCOUNTERS_GENERIC = 1, SFLCOUNTERS_ETHERNET = 2, SFLCOUNTERS_TOKENRING = 3, SFLCOUNTERS_VG = 4, SFLCOUNTERS_VLAN = 5, SFLCOUNTERS_80211 = 6, SFLCOUNTERS_LACP = 7, SFLCOUNTERS_PROCESSOR = 1001, SFLCOUNTERS_RADIO = 1002, SFLCOUNTERS_PORTNAME = 1005, SFLCOUNTERS_HOST_HID = 2000, /* host id */ SFLCOUNTERS_ADAPTORS = 2001, /* host adaptors */ SFLCOUNTERS_HOST_PAR = 2002, /* host parent */ SFLCOUNTERS_HOST_CPU = 2003, /* host cpu */ SFLCOUNTERS_HOST_MEM = 2004, /* host memory */ SFLCOUNTERS_HOST_DSK = 2005, /* host storage I/O */ SFLCOUNTERS_HOST_NIO = 2006, /* host network I/O */ SFLCOUNTERS_HOST_VRT_NODE = 2100, /* host virt node */ SFLCOUNTERS_HOST_VRT_CPU = 2101, /* host virt cpu */ SFLCOUNTERS_HOST_VRT_MEM = 2102, /* host virt mem */ SFLCOUNTERS_HOST_VRT_DSK = 2103, /* host virt storage */ SFLCOUNTERS_HOST_VRT_NIO = 2104, /* host virt network I/O */ SFLCOUNTERS_JVM = 2105, /* java runtime */ SFLCOUNTERS_JMX = 2106, /* java JMX stats */ SFLCOUNTERS_MEMCACHE = 2200, /* memcached (deprecated) */ SFLCOUNTERS_HTTP = 2201, /* http */ SFLCOUNTERS_APP = 2202, SFLCOUNTERS_APP_RESOURCE = 2203, SFLCOUNTERS_MEMCACHE2 = 2204, /* memcached */ SFLCOUNTERS_VDI = 2205, SFLCOUNTERS_APP_WORKERS = 2206, SFLCOUNTERS_HOST_GPU_NVML = (5703 << 12) + 1, /* = 23359489 */ }; typedef union _SFLCounters_type { SFLIf_counters generic; SFLEthernet_counters ethernet; SFLTokenring_counters tokenring; SFLVg_counters vg; SFLVlan_counters vlan; SFLWifi_counters wifi; SFLProcessor_counters processor; SFLRadio_counters radio; SFLHostId hostId; SFLAdaptorList* adaptors; SFLHost_parent host_par; SFLHost_cpu_counters host_cpu; SFLHost_mem_counters host_mem; SFLHost_dsk_counters host_dsk; SFLHost_nio_counters host_nio; SFLHost_vrt_node_counters host_vrt_node; SFLHost_vrt_cpu_counters host_vrt_cpu; SFLHost_vrt_mem_counters host_vrt_mem; SFLHost_vrt_dsk_counters host_vrt_dsk; SFLHost_vrt_nio_counters host_vrt_nio; SFLHost_gpu_nvml host_gpu_nvml; SFLMemcache_counters memcache; SFLHTTP_counters http; SFLJVM_ID jvm; SFLJMX_counters jmx; SFLAPP_counters app; SFLAPP_resources appResources; SFLAPP_workers appWorkers; SFLVdi_counters vdi; SFLLACP_counters lacp; SFLPortName portName; } SFLCounters_type; typedef struct _SFLCounters_sample_element { struct _SFLCounters_sample_element* nxt; /* linked list */ uint32_t tag; /* SFLCounters_type_tag */ uint32_t length; SFLCounters_type counterBlock; } SFLCounters_sample_element; typedef struct _SFLCounters_sample { /* uint32_t tag; */ /* SFL_sample_tag -- enterprise = 0 : format = 2 */ /* uint32_t length; */ uint32_t sequence_number; /* Incremented with each counters sample generated by this source_id */ uint32_t source_id; /* fsSourceId */ uint32_t num_elements; SFLCounters_sample_element* elements; } SFLCounters_sample; /* same thing, but the expanded version, so ds_index can be a full 32 bits */ typedef struct _SFLCounters_sample_expanded { /* uint32_t tag; */ /* SFL_sample_tag -- enterprise = 0 : format = 2 */ /* uint32_t length; */ uint32_t sequence_number; /* Incremented with each counters sample generated by this source_id */ uint32_t ds_class; /* EXPANDED */ uint32_t ds_index; /* EXPANDED */ uint32_t num_elements; SFLCounters_sample_element* elements; } SFLCounters_sample_expanded; #define SFLADD_ELEMENT(_sm, _el) \ do { \ (_el)->nxt = (_sm)->elements; \ (_sm)->elements = (_el); \ } while (0) /* Format of a sample datagram */ enum SFLDatagram_version { SFLDATAGRAM_VERSION2 = 2, SFLDATAGRAM_VERSION4 = 4, SFLDATAGRAM_VERSION5 = 5 }; typedef struct _SFLSample_datagram_hdr { uint32_t datagram_version; /* (enum SFLDatagram_version) = VERSION5 = 5 */ SFLAddress agent_address; /* IP address of sampling agent */ uint32_t sub_agent_id; /* Used to distinguishing between datagram streams from separate agent sub entities within an device. */ uint32_t sequence_number; /* Incremented with each sample datagram generated */ uint32_t uptime; /* Current time (in milliseconds since device last booted). Should be set as close to datagram transmission time as possible.*/ uint32_t num_records; /* Number of tag-len-val flow/counter records to follow */ } SFLSample_datagram_hdr; #define SFL_MAX_DATAGRAM_SIZE 1500 #define SFL_MIN_DATAGRAM_SIZE 200 #define SFL_DEFAULT_DATAGRAM_SIZE 1400 #define SFL_DATA_PAD 400 #if defined(__cplusplus) } /* extern "C" */ #endif #endif /* SFLOW_H */ fastnetmon-1.1.4/src/sflow_plugin/sflow_collector.cpp000066400000000000000000000753501343111404700231060ustar00rootroot00000000000000#include #include #include #include "sflow_collector.h" // sflowtool-3.32 #include "sflow.h" // custom sFLOW data structures #include "sflow_data.h" #include "../fast_library.h" #include #include #include #include // UDP server #include #include #include #include #include #include // log4cpp logging facility #include "log4cpp/Category.hh" #include "log4cpp/Appender.hh" #include "log4cpp/FileAppender.hh" #include "log4cpp/OstreamAppender.hh" #include "log4cpp/Layout.hh" #include "log4cpp/BasicLayout.hh" #include "log4cpp/PatternLayout.hh" #include "log4cpp/Priority.hh" #ifdef ENABLE_LUA_HOOKS lua_State* sflow_lua_state = NULL; bool sflow_lua_hooks_enabled = false; std::string sflow_lua_hooks_path = "/usr/src/fastnetmon/src/sflow_hooks.lua"; #endif // Ethertype of outer tag in QinQ uint32_t sflow_qinq_ethertype = 0x8100; // Disable QinQ processing by default bool sflow_qinq_process = false; // sFLOW v4 specification: http://www.sflow.org/rfc3176.txt std::string plugin_name = "sflow"; std::string plugin_log_prefix = plugin_name + ": "; // Get logger from main program extern log4cpp::Category& logger; // Global configuration map extern std::map configuration_map; // Enable debug messages in log bool debug_sflow_parser = false; uint32_t getData32(SFSample* sample); bool skipTLVRecord(SFSample* sample, uint32_t tag, uint32_t len); bool readFlowSample(SFSample* sample, int expanded); void readFlowSample_header(SFSample* sample); void decode_ipv4_protocol(SFSample* sample); void decode_ipv6_protocol(SFSample* sample); void print_simple_packet(struct simple_packet& packet); process_packet_pointer sflow_process_func_ptr = NULL; // #include void start_sflow_collector(std::string interface_for_binding, unsigned int sflow_port); void start_sflow_collection(process_packet_pointer func_ptr) { std::string interface_for_binding = "0.0.0.0"; std::string sflow_ports = ""; logger << log4cpp::Priority::INFO << plugin_log_prefix << "plugin started"; // prctl(PR_SET_NAME,"fastnetmon_sflow", 0, 0, 0); sflow_process_func_ptr = func_ptr; if (configuration_map.count("sflow_port") != 0) { sflow_ports = configuration_map["sflow_port"]; } if (configuration_map.count("sflow_host") != 0) { interface_for_binding = configuration_map["sflow_host"]; } if (configuration_map.count("sflow_qinq_process") != 0) { if (configuration_map["sflow_qinq_process"] == "on") { sflow_qinq_process = true; logger << log4cpp::Priority::INFO << plugin_log_prefix << "qinq processing enabled"; if (configuration_map.count("sflow_qinq_ethertype") != 0) { if (convert_hex_as_string_to_uint(configuration_map["sflow_qinq_ethertype"], sflow_qinq_ethertype)) { logger << log4cpp::Priority::WARN << plugin_log_prefix << "can't parse value in sflow_qinq_ethertype variable"; logger << log4cpp::Priority::INFO << plugin_log_prefix << "disable qinq processing"; sflow_qinq_process = false; } } } } #ifdef ENABLE_LUA_HOOKS if (configuration_map.count("sflow_lua_hooks_path") != 0) { sflow_lua_hooks_path = configuration_map["sflow_lua_hooks_path"]; sflow_lua_hooks_enabled = true; } #endif #ifdef ENABLE_LUA_HOOKS if (sflow_lua_hooks_enabled) { sflow_lua_state = init_lua_jit(sflow_lua_hooks_path); if (sflow_lua_state == NULL) { sflow_lua_hooks_enabled = false; } } #endif boost::thread_group sflow_collector_threads; std::vector ports_for_listen; boost::split(ports_for_listen, sflow_ports, boost::is_any_of(","), boost::token_compress_on); logger << log4cpp::Priority::INFO << plugin_log_prefix << "We will listen on " << ports_for_listen.size() << " ports"; for (std::vector::iterator port = ports_for_listen.begin(); port != ports_for_listen.end(); ++port) { unsigned int sflow_port = convert_string_to_integer(*port); if (sflow_port == 0) { sflow_port = 6343; } sflow_collector_threads.add_thread( new boost::thread(start_sflow_collector, interface_for_binding, sflow_port )); } sflow_collector_threads.join_all(); } void start_sflow_collector(std::string interface_for_binding, unsigned int sflow_port) { logger << log4cpp::Priority::INFO << plugin_log_prefix << "plugin will listen on " << interface_for_binding << ":" << sflow_port << " udp port"; unsigned int udp_buffer_size = 65536; char udp_buffer[udp_buffer_size]; int sockfd = socket(AF_INET, SOCK_DGRAM, 0); struct sockaddr_in servaddr; memset(&servaddr, 0, sizeof(servaddr)); servaddr.sin_family = AF_INET; if (interface_for_binding == "0.0.0.0") { servaddr.sin_addr.s_addr = htonl(INADDR_ANY); } else { servaddr.sin_addr.s_addr = inet_addr(interface_for_binding.c_str()); } servaddr.sin_port = htons(sflow_port); int bind_result = bind(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr)); if (bind_result) { logger << log4cpp::Priority::ERROR << plugin_log_prefix << "can't listen port: " << sflow_port; return; } struct sockaddr_in6 peer; memset(&peer, 0, sizeof(peer)); /* We should specify timeout there for correct toolkit shutdown */ /* Because otherwise recvfrom will stay in blocked mode forever */ struct timeval tv; tv.tv_sec = 5; /* X Secs Timeout */ tv.tv_usec = 0; // Not init'ing this can cause strange errors setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(struct timeval)); while (true) { struct sockaddr_in cliaddr; socklen_t address_len = sizeof(cliaddr); int received_bytes = recvfrom(sockfd, udp_buffer, udp_buffer_size, 0, (struct sockaddr*)&cliaddr, &address_len); if (received_bytes > 0) { // printf("We receive %d\n", received_bytes); SFSample sample; memset(&sample, 0, sizeof(sample)); sample.rawSample = (uint8_t*)udp_buffer; sample.rawSampleLen = received_bytes; if (address_len == sizeof(struct sockaddr_in)) { struct sockaddr_in* peer4 = (struct sockaddr_in*)&cliaddr; sample.sourceIP.type = SFLADDRESSTYPE_IP_V4; memcpy(&sample.sourceIP.address.ip_v4, &peer4->sin_addr, 4); read_sflow_datagram(&sample); } else { // We do not support an IPv6 } } else { if (received_bytes == -1) { if (errno == EAGAIN) { // We got timeout, it's OK! } else { logger << log4cpp::Priority::ERROR << plugin_log_prefix << "data receive failed"; } } } // Add interruption point for correct application shutdown boost::this_thread::interruption_point(); } } uint32_t getData32_nobswap(SFSample* sample) { uint32_t ans = *(sample->datap)++; // make sure we didn't run off the end of the datagram. Thanks to // Sven Eschenberg for spotting a bug/overrun-vulnerabilty that was here before. if ((uint8_t*)sample->datap > sample->endp) { // SFABORT(sample, SF_ABORT_EOS); // Error!!! logger << log4cpp::Priority::ERROR << plugin_log_prefix << "we tried to read data in bad place! Fault!"; return 0; } return ans; } bool skipBytes(SFSample* sample, uint32_t skip) { int quads = (skip + 3) / 4; sample->datap += quads; if (skip > sample->rawSampleLen || (uint8_t*)sample->datap > sample->endp) { // SFABORT(sample, SF_ABORT_EOS); logger << log4cpp::Priority::ERROR << plugin_log_prefix << "very dangerous error from skipBytes function! We try to read from restricted memory region"; return false; } return true; } uint32_t getAddress(SFSample* sample, SFLAddress* address) { address->type = getData32(sample); if (address->type == SFLADDRESSTYPE_IP_V4) { address->address.ip_v4.addr = getData32_nobswap(sample); } else { memcpy(&address->address.ip_v6.addr, sample->datap, 16); skipBytes(sample, 16); } return address->type; } uint32_t getData32(SFSample* sample) { return ntohl(getData32_nobswap(sample)); } bool readFlowSample_v2v4(SFSample *sample) { sample->samplesGenerated = getData32(sample); uint32_t samplerId = getData32(sample); sample->ds_class = samplerId >> 24; sample->ds_index = samplerId & 0x00ffffff; sample->meanSkipCount = getData32(sample); sample->samplePool = getData32(sample); sample->dropEvents = getData32(sample); sample->inputPort = getData32(sample); sample->outputPort = getData32(sample); sample->packet_data_tag = getData32(sample); switch(sample->packet_data_tag) { case INMPACKETTYPE_HEADER: readFlowSample_header(sample); break; case INMPACKETTYPE_IPV4: logger << log4cpp::Priority::ERROR << plugin_log_prefix << "hit INMPACKETTYPE_IPV4, very strange"; return false; break; case INMPACKETTYPE_IPV6: logger << log4cpp::Priority::ERROR << plugin_log_prefix << "hit INMPACKETTYPE_IPV6, very strange"; return false; break; default: logger << log4cpp::Priority::ERROR << plugin_log_prefix << "unexpected packet_data_tag"; return false; break; } sample->extended_data_tag = 0; // We should read this data sample->num_extended = getData32(sample); if (sample->num_extended > 0) { logger << log4cpp::Priority::ERROR << plugin_log_prefix << "we have " << sample->num_extended << " extended fields"; logger << log4cpp::Priority::ERROR << plugin_log_prefix << "and sorry we haven't support for it :("; return false; } return true; } void read_sflow_datagram(SFSample* sample) { sample->datap = (uint32_t*)sample->rawSample; sample->endp = (uint8_t*)sample->rawSample + sample->rawSampleLen; sample->datagramVersion = getData32(sample); // printf("sFLOW version %d\n", sample->datagramVersion); if (sample->datagramVersion != 5 && sample->datagramVersion != 4) { logger << log4cpp::Priority::ERROR << plugin_log_prefix << "we do not support sFLOW v<< "<< sample->datagramVersion << " because it's too old. Please change version to sFLOW 4 or 5"; return; } /* get the agent address */ getAddress(sample, &sample->agent_addr); /* version 5 has an agent sub-id as well */ if (sample->datagramVersion >= 5) { sample->agentSubId = getData32(sample); // sf_log(sample,"agentSubId %u\n", sample->agentSubId); } else { sample->agentSubId = 0; } sample->sequenceNo = getData32(sample); /* this is the packet sequence number */ sample->sysUpTime = getData32(sample); uint32_t samplesInPacket = getData32(sample); // printf("We have %d samples in packet\n", samplesInPacket); uint32_t samp = 0; for (; samp < samplesInPacket; samp++) { if ((uint8_t*)sample->datap >= sample->endp) { logger << log4cpp::Priority::INFO << plugin_log_prefix << "we tried to read data outside packet! It's very dangerous, we stop all operations"; return; } // printf("Sample #%d\n", samp); /* just read the tag, then call the approriate decode fn */ sample->sampleType = getData32(sample); if (sample->datagramVersion >= 5) { switch (sample->sampleType) { case SFLFLOW_SAMPLE: // skipBytes(sample, getData32(sample)); if (!readFlowSample(sample, 0)) { logger << log4cpp::Priority::ERROR << plugin_log_prefix << "we failed in SFLFLOW_SAMPLE handler"; return; } break; case SFLCOUNTERS_SAMPLE: // We do not need counters for our task, skip it if (!skipBytes(sample, getData32(sample))) { logger << log4cpp::Priority::ERROR << plugin_log_prefix << "we failed in SFLCOUNTERS_SAMPLE handler"; return; } break; case SFLFLOW_SAMPLE_EXPANDED: // skipBytes(sample, getData32(sample)); if (!readFlowSample(sample, 1)) { logger << log4cpp::Priority::ERROR << plugin_log_prefix << "we failed in SFLFLOW_SAMPLE_EXPANDED handler"; return; } break; case SFLCOUNTERS_SAMPLE_EXPANDED: // We do not need counters for our task, skip it if (!skipBytes(sample, getData32(sample))) { logger << log4cpp::Priority::ERROR << plugin_log_prefix << "we failed in SFLCOUNTERS_SAMPLE_EXPANDED handler"; return; } break; default: if (!skipTLVRecord(sample, sample->sampleType, getData32(sample))) { logger << log4cpp::Priority::ERROR << plugin_log_prefix << "we failed in default handler in skipTLVRecord"; return; } break; } } else { // sFLOW v2 or v4 here switch(sample->sampleType) { case FLOWSAMPLE: if (!readFlowSample_v2v4(sample)) { // We have some troubles with old sFLOW parser return; } break; case COUNTERSSAMPLE: logger << log4cpp::Priority::ERROR << plugin_log_prefix << "we haven't support for COUNTERSSAMPLE for " << "sFLOW v4 and ignore it completely"; return; break; default: logger << log4cpp::Priority::ERROR << plugin_log_prefix << "unexpected sample type: " << sample->sampleType; return; break; } } } } bool skipTLVRecord(SFSample* sample, uint32_t tag, uint32_t len) { return skipBytes(sample, len); } bool length_check(SFSample *sample, const char *description, uint8_t *start, int len) { uint32_t actualLen = (uint8_t *)sample->datap - start; uint32_t adjustedLen = ((len + 3) >> 2) << 2; if (actualLen != adjustedLen) { logger << log4cpp::Priority::ERROR << plugin_log_prefix << description << " length error: expected " << len << " found " << actualLen; return false; } return true; } bool readFlowSample(SFSample* sample, int expanded) { uint32_t num_elements, sampleLength; uint8_t* sampleStart; sampleLength = getData32(sample); sampleStart = (uint8_t*)sample->datap; sample->samplesGenerated = getData32(sample); if (expanded) { sample->ds_class = getData32(sample); sample->ds_index = getData32(sample); } else { uint32_t samplerId = getData32(sample); sample->ds_class = samplerId >> 24; sample->ds_index = samplerId & 0x00ffffff; } sample->meanSkipCount = getData32(sample); // printf("Sample ratio: %d\n", sample->meanSkipCount); sample->samplePool = getData32(sample); sample->dropEvents = getData32(sample); if (expanded) { sample->inputPortFormat = getData32(sample); sample->inputPort = getData32(sample); sample->outputPortFormat = getData32(sample); sample->outputPort = getData32(sample); } else { uint32_t inp, outp; inp = getData32(sample); outp = getData32(sample); sample->inputPortFormat = inp >> 30; sample->outputPortFormat = outp >> 30; sample->inputPort = inp & 0x3fffffff; sample->outputPort = outp & 0x3fffffff; } num_elements = getData32(sample); uint32_t el; for (el = 0; el < num_elements; el++) { uint32_t tag, length; uint8_t* start; char buf[51]; tag = sample->elementType = getData32(sample); length = getData32(sample); start = (uint8_t*)sample->datap; // tag analyze if (tag == SFLFLOW_HEADER) { // process data readFlowSample_header(sample); } else { if (!skipTLVRecord(sample, tag, length)) { return false; } } if (!length_check(sample, "flow_sample_element", start, length)) { return false; } } if (!length_check(sample, "flow_sample", sampleStart, sampleLength)) { return false; } return true; } #define NFT_ETHHDR_SIZ 14 #define NFT_8022_SIZ 3 #define NFT_MAX_8023_LEN 1500 #define NFT_MIN_SIZ (NFT_ETHHDR_SIZ + sizeof(struct myiphdr)) void decode_link_layer(SFSample* sample) { uint8_t* start = (uint8_t*)sample->header; uint8_t* end = start + sample->headerLen; uint8_t* ptr = start; uint16_t type_len; /* assume not found */ sample->gotIPV4 = 0; sample->gotIPV6 = 0; if (sample->headerLen < NFT_ETHHDR_SIZ) { /* not enough for an Ethernet header */ return; } // sf_log(sample,"dstMAC %02x%02x%02x%02x%02x%02x\n", ptr[0], ptr[1], ptr[2], ptr[3], ptr[4], // ptr[5]); memcpy(sample->eth_dst, ptr, 6); ptr += 6; // sf_log(sample,"srcMAC %02x%02x%02x%02x%02x%02x\n", ptr[0], ptr[1], ptr[2], ptr[3], ptr[4], // ptr[5]); memcpy(sample->eth_src, ptr, 6); ptr += 6; type_len = (ptr[0] << 8) + ptr[1]; ptr += 2; if (sflow_qinq_process && type_len == sflow_qinq_ethertype && ((ptr[2] << 8) + ptr[3]) == 0x8100) { /* Outer VLAN tag - next two bytes */ uint32_t vlanData = (ptr[0] << 8) + ptr[1]; uint32_t vlan = vlanData & 0x0fff; uint32_t priority = vlanData >> 13; ptr += 2; sample->in_outer_vlan = vlan; /* now get the type_len again (next two bytes) */ type_len = (ptr[0] << 8) + ptr[1]; ptr += 2; } if (type_len == 0x8100) { /* Inner VLAN tag - next two bytes */ uint32_t vlanData = (ptr[0] << 8) + ptr[1]; uint32_t vlan = vlanData & 0x0fff; uint32_t priority = vlanData >> 13; ptr += 2; /* _____________________________________ */ /* | pri | c | vlan-id | */ /* ------------------------------------- */ /* [priority = 3bits] [Canonical Format Flag = 1bit] [vlan-id = 12 bits] */ // sf_log(sample,"decodedVLAN %u\n", vlan); // sf_log(sample,"decodedPriority %u\n", priority); sample->in_vlan = vlan; /* now get the type_len again (next two bytes) */ type_len = (ptr[0] << 8) + ptr[1]; ptr += 2; } /* assume type_len is an ethernet-type now */ sample->eth_type = type_len; if (type_len == 0x0800) { /* IPV4 */ if ((end - ptr) < sizeof(struct myiphdr)) { return; } /* look at first byte of header.... */ /* ___________________________ */ /* | version | hdrlen | */ /* --------------------------- */ if ((*ptr >> 4) != 4) return; /* not version 4 */ if ((*ptr & 15) < 5) return; /* not IP (hdr len must be 5 quads or more) */ /* survived all the tests - store the offset to the start of the ip header */ sample->gotIPV4 = 1; sample->offsetToIPV4 = (ptr - start); } if (type_len == 0x86DD) { /* IPV6 */ /* look at first byte of header.... */ if ((*ptr >> 4) != 6) return; /* not version 6 */ /* survived all the tests - store the offset to the start of the ip6 header */ sample->gotIPV6 = 1; sample->offsetToIPV6 = (ptr - start); printf("IPv6\n"); } // printf("vlan: %d\n",sample->in_vlan); } void readFlowSample_header(SFSample* sample) { sample->headerProtocol = getData32(sample); sample->sampledPacketSize = getData32(sample); if (sample->datagramVersion > 4) { /* stripped count introduced in sFlow version 5 */ sample->stripped = getData32(sample); } sample->headerLen = getData32(sample); sample->header = (uint8_t*)sample->datap; /* just point at the header */ skipBytes(sample, sample->headerLen); if (sample->headerProtocol == SFLHEADER_ETHERNET_ISO8023) { // Detect IPv4 or IPv6 here decode_link_layer(sample); // Process IP packets next if (sample->gotIPV4) { decode_ipv4_protocol(sample); } if (sample->gotIPV6) { decode_ipv6_protocol(sample); } } else { logger << log4cpp::Priority::ERROR << plugin_log_prefix << "not supported protocol: " << sample->headerProtocol; return; } } char* IP_to_a(uint32_t ipaddr, char* buf) { uint8_t* ip = (uint8_t*)&ipaddr; /* should really be: snprintf(buf, buflen,...) but snprintf() is not always available */ sprintf(buf, "%u.%u.%u.%u", ip[0], ip[1], ip[2], ip[3]); return buf; } char* printAddress(SFLAddress* address, char* buf) { switch (address->type) { case SFLADDRESSTYPE_IP_V4: IP_to_a(address->address.ip_v4.addr, buf); break; case SFLADDRESSTYPE_IP_V6: { uint8_t* b = address->address.ip_v6.addr; /* should really be: snprintf(buf, buflen,...) but snprintf() is not always available */ sprintf(buf, "%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x", b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7], b[8], b[9], b[10], b[11], b[12], b[13], b[14], b[15]); } break; default: sprintf(buf, "-"); } return buf; } void decodeIPLayer4(SFSample* sample, uint8_t* ptr) { uint8_t* end = sample->header + sample->headerLen; if (ptr > (end - 8)) { /* not enough header bytes left */ return; } simple_packet current_packet; if (sample->gotIPV6) { current_packet.ip_protocol_version = 6; memcpy(current_packet.src_ipv6.s6_addr, sample->ipsrc.address.ip_v6.addr, 16); memcpy(current_packet.dst_ipv6.s6_addr, sample->ipdst.address.ip_v6.addr, 16); } else { current_packet.ip_protocol_version = 4; current_packet.src_ip = sample->ipsrc.address.ip_v4.addr; current_packet.dst_ip = sample->ipdst.address.ip_v4.addr; } // Because sFLOW data is near real time we could get current time gettimeofday(¤t_packet.ts, NULL); current_packet.flags = 0; current_packet.number_of_packets = 1; current_packet.length = sample->sampledPacketSize; current_packet.sample_ratio = sample->meanSkipCount; switch (sample->dcd_ipProtocol) { case 1: { // ICMP current_packet.protocol = IPPROTO_ICMP; struct myicmphdr icmp; memcpy(&icmp, ptr, sizeof(icmp)); // printf("ICMPType %u\n", icmp.type); // printf("ICMPCode %u\n", icmp.code); sample->dcd_sport = icmp.type; sample->dcd_dport = icmp.code; sample->offsetToPayload = ptr + sizeof(icmp) - sample->header; } break; case 6: { // TCP current_packet.protocol = IPPROTO_TCP; struct mytcphdr tcp; int headerBytes; memcpy(&tcp, ptr, sizeof(tcp)); sample->dcd_sport = ntohs(tcp.th_sport); sample->dcd_dport = ntohs(tcp.th_dport); current_packet.source_port = sample->dcd_sport; current_packet.destination_port = sample->dcd_dport; // TODO: flags could be broken because our flags parser implemented with PF_RING style flags // PF_RING current_packet.flags = tcp.th_flags; sample->dcd_tcpFlags = tcp.th_flags; // printf("TCPSrcPort %u\n", sample->dcd_sport); // printf("TCPDstPort %u\n",sample->dcd_dport); // printf("TCPFlags %u\n", sample->dcd_tcpFlags); headerBytes = (tcp.th_off_and_unused >> 4) * 4; ptr += headerBytes; sample->offsetToPayload = ptr - sample->header; } break; case 17: { // UDP current_packet.protocol = IPPROTO_UDP; struct myudphdr udp; memcpy(&udp, ptr, sizeof(udp)); sample->dcd_sport = ntohs(udp.uh_sport); sample->dcd_dport = ntohs(udp.uh_dport); current_packet.source_port = sample->dcd_sport; current_packet.destination_port = sample->dcd_dport; sample->udp_pduLen = ntohs(udp.uh_ulen); // printf("UDPSrcPort %u\n", sample->dcd_sport); // printf("UDPDstPort %u\n", sample->dcd_dport); // printf("UDPBytes %u\n", sample->udp_pduLen); sample->offsetToPayload = ptr + sizeof(udp) - sample->header; } break; default: /* some other protcol */ sample->offsetToPayload = ptr - sample->header; break; } #ifdef ENABLE_LUA_HOOKS //sample->inputPort = fast_ntoh(sample->inputPort); //sample->outputPort = fast_ntoh(sample->outputPort); if (sflow_lua_hooks_enabled) { // This code could be used only for tests with pcap_reader if (sflow_lua_state == NULL) { sflow_lua_state = init_lua_jit(sflow_lua_hooks_path); } if (call_lua_function("process_sflow", sflow_lua_state, convert_ip_as_uint_to_string(sample->sourceIP.address.ip_v4.addr), (void*)sample)) { // We will process this packet } else { logger << log4cpp::Priority::DEBUG << "We will drop this packets because LUA script decided to do it"; return; } } #endif // Call external handler function sflow_process_func_ptr(current_packet); } void decode_ipv6_protocol(SFSample* sample) { uint8_t *ptr = sample->header + sample->offsetToIPV6; uint8_t *end = sample->header + sample->headerLen; int ipVersion = (*ptr >> 4); if (ipVersion != 6) { logger << log4cpp::Priority::ERROR << plugin_log_prefix << "sFLOW header decode error: unexpected IP version: " << ipVersion; return; } /* get the tos (priority) */ sample->dcd_ipTos = *ptr++ & 15; if (debug_sflow_parser) { logger << log4cpp::Priority::INFO << plugin_log_prefix << "IPTOS: " << sample->dcd_ipTos; } /* 24-bit label */ uint32_t label = *ptr++; label <<= 8; label += *ptr++; label <<= 8; label += *ptr++; if (debug_sflow_parser) { logger << log4cpp::Priority::INFO << plugin_log_prefix << "IP6_label: " << label; } /* payload */ uint16_t payloadLen = (ptr[0] << 8) + ptr[1]; ptr += 2; /* if payload is zero, that implies a jumbo payload */ if (debug_sflow_parser) { if (payloadLen == 0) { logger << log4cpp::Priority::INFO << plugin_log_prefix << "IPV6_payloadLen "; } else { logger << log4cpp::Priority::INFO << plugin_log_prefix << "IPV6_payloadLen " << payloadLen; } } /* next header */ uint32_t nextHeader = *ptr++; /* TTL */ sample->dcd_ipTTL = *ptr++; //sf_log(sample,"IPTTL %u\n", sample->dcd_ipTTL); /* src and dst address */ // char buf[101]; sample->ipsrc.type = SFLADDRESSTYPE_IP_V6; memcpy(&sample->ipsrc.address, ptr, 16); ptr +=16; if (debug_sflow_parser) { char buf[101]; logger << log4cpp::Priority::INFO << plugin_log_prefix << "srcIP6: " << printAddress(&sample->ipsrc, buf); } sample->ipdst.type = SFLADDRESSTYPE_IP_V6; memcpy(&sample->ipdst.address, ptr, 16); ptr +=16; if (debug_sflow_parser) { char buf[101]; logger << log4cpp::Priority::INFO << plugin_log_prefix << "dstIP6: " << printAddress(&sample->ipdst, buf); } /* skip over some common header extensions... http://searchnetworking.techtarget.com/originalContent/0,289142,sid7_gci870277,00.html */ while(nextHeader == 0 || /* hop */ nextHeader == 43 || /* routing */ nextHeader == 44 || /* fragment */ /* nextHeader == 50 => encryption - don't bother coz we'll not be able to read any further */ nextHeader == 51 || /* auth */ nextHeader == 60) { /* destination options */ uint32_t optionLen, skip; if (debug_sflow_parser) { logger << log4cpp::Priority::INFO << plugin_log_prefix << "IP6HeaderExtension: " << nextHeader; } nextHeader = ptr[0]; optionLen = 8 * (ptr[1] + 1); /* second byte gives option len in 8-byte chunks, not counting first 8 */ skip = optionLen - 2; ptr += skip; if (ptr > end) return; /* ran off the end of the header */ } /* now that we have eliminated the extension headers, nextHeader should have what we want to remember as the ip protocol... */ sample->dcd_ipProtocol = nextHeader; if (debug_sflow_parser) { logger << log4cpp::Priority::INFO << plugin_log_prefix << "IPProtocol: " << sample->dcd_ipProtocol; } decodeIPLayer4(sample, ptr); } void decode_ipv4_protocol(SFSample* sample) { char buf[51]; uint8_t* ptr = sample->header + sample->offsetToIPV4; /* Create a local copy of the IP header (cannot overlay structure in case it is not quad-aligned...some platforms would core-dump if we tried that). It's OK coz this probably performs just as well anyway. */ struct myiphdr ip; memcpy(&ip, ptr, sizeof(ip)); /* Value copy all ip elements into sample */ sample->ipsrc.type = SFLADDRESSTYPE_IP_V4; sample->ipsrc.address.ip_v4.addr = ip.saddr; sample->ipdst.type = SFLADDRESSTYPE_IP_V4; sample->ipdst.address.ip_v4.addr = ip.daddr; sample->dcd_ipProtocol = ip.protocol; sample->dcd_ipTos = ip.tos; sample->dcd_ipTTL = ip.ttl; // printf("ip.tot_len %d\n", ntohs(ip.tot_len)); /* Log out the decoded IP fields */ // printf("srcIP %s\n", printAddress(&sample->ipsrc, buf)); // printf("dstIP %s\n", printAddress(&sample->ipdst, buf)); // printf("IPProtocol %u\n", sample->dcd_ipProtocol); // printf("IPTOS %u\n", sample->dcd_ipTos); // printf("IPTTL %u\n", sample->dcd_ipTTL); /* check for fragments */ sample->ip_fragmentOffset = ntohs(ip.frag_off) & 0x1FFF; if (sample->ip_fragmentOffset > 0) { // printf("IPFragmentOffset %u\n", sample->ip_fragmentOffset); } else { /* advance the pointer to the next protocol layer */ /* ip headerLen is expressed as a number of quads */ ptr += (ip.version_and_headerLen & 0x0f) * 4; decodeIPLayer4(sample, ptr); } } fastnetmon-1.1.4/src/sflow_plugin/sflow_collector.h000066400000000000000000000003511343111404700225400ustar00rootroot00000000000000#ifndef SFLOW_PLUGIN_H #define SFLOW_PLUGIN_H #include "../fastnetmon_types.h" #include "sflow_data.h" void start_sflow_collection(process_packet_pointer func_ptr); // For tests void read_sflow_datagram(SFSample* sample); #endif fastnetmon-1.1.4/src/sflow_plugin/sflow_data.h000066400000000000000000000123201343111404700214620ustar00rootroot00000000000000#ifndef SFLOW_DATA_H #define SFLOW_DATA_H #include "sflow.h" #include // Packet headers for sFLOW v4 enum INMPacket_information_type { INMPACKETTYPE_HEADER = 1, /* Packet headers are sampled */ INMPACKETTYPE_IPV4 = 2, /* IP version 4 data */ INMPACKETTYPE_IPV6 = 3 /* IP version 4 data */ }; /* when I turn on optimisation with the Microsoft compiler it seems to change the values of these enumerated types and break the program - not sure why */ enum INMSample_types { FLOWSAMPLE = 1, COUNTERSSAMPLE = 2 }; /* same for tcp */ struct mytcphdr { uint16_t th_sport; /* source port */ uint16_t th_dport; /* destination port */ uint32_t th_seq; /* sequence number */ uint32_t th_ack; /* acknowledgement number */ uint8_t th_off_and_unused; uint8_t th_flags; uint16_t th_win; /* window */ uint16_t th_sum; /* checksum */ uint16_t th_urp; /* urgent pointer */ }; /* and UDP */ struct myudphdr { uint16_t uh_sport; /* source port */ uint16_t uh_dport; /* destination port */ uint16_t uh_ulen; /* udp length */ uint16_t uh_sum; /* udp checksum */ }; /* and ICMP */ struct myicmphdr { uint8_t type; /* message type */ uint8_t code; /* type sub-code */ /* ignore the rest */ }; /* define my own IP header struct - to ease portability */ struct myiphdr { uint8_t version_and_headerLen; uint8_t tos; uint16_t tot_len; uint16_t id; uint16_t frag_off; uint8_t ttl; uint8_t protocol; uint16_t check; uint32_t saddr; uint32_t daddr; }; #define SASAMPLE_EXTENDED_DATA_SWITCH 1 #define SASAMPLE_EXTENDED_DATA_ROUTER 4 #define SASAMPLE_EXTENDED_DATA_GATEWAY 8 #define SASAMPLE_EXTENDED_DATA_USER 16 #define SASAMPLE_EXTENDED_DATA_URL 32 #define SASAMPLE_EXTENDED_DATA_MPLS 64 #define SASAMPLE_EXTENDED_DATA_NAT 128 #define SASAMPLE_EXTENDED_DATA_MPLS_TUNNEL 256 #define SASAMPLE_EXTENDED_DATA_MPLS_VC 512 #define SASAMPLE_EXTENDED_DATA_MPLS_FTN 1024 #define SASAMPLE_EXTENDED_DATA_MPLS_LDP_FEC 2048 #define SASAMPLE_EXTENDED_DATA_VLAN_TUNNEL 4096 #define SASAMPLE_EXTENDED_DATA_NAT_PORT 8192 #define SA_MAX_EXTENDED_USER_LEN 200 #define SA_MAX_EXTENDED_URL_LEN 200 #define SA_MAX_EXTENDED_HOST_LEN 200 typedef struct _SFSample { SFLAddress sourceIP; SFLAddress agent_addr; uint32_t agentSubId; /* the raw pdu */ uint8_t* rawSample; uint32_t rawSampleLen; uint8_t* endp; time_t pcapTimestamp; /* decode cursor */ uint32_t* datap; uint32_t datagramVersion; uint32_t sampleType; uint32_t elementType; uint32_t ds_class; uint32_t ds_index; /* generic interface counter sample */ SFLIf_counters ifCounters; /* sample stream info */ uint32_t sysUpTime; uint32_t sequenceNo; uint32_t sampledPacketSize; uint32_t samplesGenerated; uint32_t meanSkipCount; uint32_t samplePool; uint32_t dropEvents; /* the sampled header */ uint32_t packet_data_tag; uint32_t headerProtocol; uint8_t* header; int headerLen; uint32_t stripped; /* header decode */ int gotIPV4; int gotIPV4Struct; int offsetToIPV4; int gotIPV6; int gotIPV6Struct; int offsetToIPV6; int offsetToPayload; SFLAddress ipsrc; SFLAddress ipdst; uint32_t dcd_ipProtocol; uint32_t dcd_ipTos; uint32_t dcd_ipTTL; uint32_t dcd_sport; uint32_t dcd_dport; uint32_t dcd_tcpFlags; uint32_t ip_fragmentOffset; uint32_t udp_pduLen; /* ports */ uint32_t inputPortFormat; uint32_t outputPortFormat; uint32_t inputPort; uint32_t outputPort; /* ethernet */ uint32_t eth_type; uint32_t eth_len; uint8_t eth_src[8]; uint8_t eth_dst[8]; /* vlan */ uint32_t in_outer_vlan; uint32_t in_vlan; uint32_t in_priority; uint32_t internalPriority; uint32_t out_vlan; uint32_t out_priority; int vlanFilterReject; /* extended data fields */ uint32_t num_extended; uint32_t extended_data_tag; /* IP forwarding info */ SFLAddress nextHop; uint32_t srcMask; uint32_t dstMask; /* BGP info */ SFLAddress bgp_nextHop; uint32_t my_as; uint32_t src_as; uint32_t src_peer_as; uint32_t dst_as_path_len; uint32_t* dst_as_path; /* note: version 4 dst as path segments just get printed, not stored here, however * the dst_peer and dst_as are filled in, since those are used for netflow encoding */ uint32_t dst_peer_as; uint32_t dst_as; uint32_t communities_len; uint32_t* communities; uint32_t localpref; /* user id */ uint32_t src_user_charset; uint32_t src_user_len; char src_user[SA_MAX_EXTENDED_USER_LEN + 1]; uint32_t dst_user_charset; uint32_t dst_user_len; char dst_user[SA_MAX_EXTENDED_USER_LEN + 1]; /* url */ uint32_t url_direction; uint32_t url_len; char url[SA_MAX_EXTENDED_URL_LEN + 1]; uint32_t host_len; char host[SA_MAX_EXTENDED_HOST_LEN + 1]; /* mpls */ SFLAddress mpls_nextHop; /* nat */ SFLAddress nat_src; SFLAddress nat_dst; /* counter blocks */ uint32_t statsSamplingInterval; uint32_t counterBlockVersion; /* exception handler context */ //jmp_buf env; } SFSample; #endif // SFLOW_DATA_H fastnetmon-1.1.4/src/snabbswitch_plugin/000077500000000000000000000000001343111404700203455ustar00rootroot00000000000000fastnetmon-1.1.4/src/snabbswitch_plugin/snabbswitch_collector.cpp000066400000000000000000000110131343111404700254220ustar00rootroot00000000000000// log4cpp logging facility #include "log4cpp/Category.hh" #include "log4cpp/Appender.hh" #include "log4cpp/FileAppender.hh" #include "log4cpp/OstreamAppender.hh" #include "log4cpp/Layout.hh" #include "log4cpp/BasicLayout.hh" #include "log4cpp/PatternLayout.hh" #include "log4cpp/Priority.hh" #include #include #include "../fast_library.h" // For support uint32_t, uint16_t #include // For config map operations #include #include #include #include #include #include "../fastnetmon_packet_parser.h" // For support: IPPROTO_TCP, IPPROTO_ICMP, IPPROTO_UDP #include #include #include #include "snabbswitch_collector.h" #include "unified_parser.hpp" #ifdef __cplusplus extern "C" { #endif // This code defined in SnabbSwitch int start_snabb_switch(int snabb_argc, const char **snabb_argv); #ifdef __cplusplus } #endif // Get log4cpp logger from main program extern log4cpp::Category& logger; // Pass unparsed packets number to main program extern uint64_t total_unparsed_packets; // Global configuration map extern std::map configuration_map; // This variable name should be uniq for every plugin! process_packet_pointer snabbswitch_process_func_ptr = NULL; inline void firehose_packet(const char *pciaddr, char *data, int length); /* Intel 82599 "Legacy" receive descriptor format. * See Intel 82599 data sheet section 7.1.5. * http://www.intel.com/content/dam/www/public/us/en/documents/datasheets/82599-10-gbe-controller-datasheet.pdf */ struct firehose_rdesc { uint64_t address; uint16_t length; uint16_t cksum; uint8_t status; uint8_t errors; uint16_t vlan; } __attribute__((packed)); void firehose_packet(const char *pciaddr, char *data, int length) { simple_packet packet; if (!parse_raw_packet_to_simple_packet((u_char*)data, length, packet, false)) { total_unparsed_packets++; return; } snabbswitch_process_func_ptr(packet); } #ifdef __cplusplus extern "C" { #endif int firehose_callback_v1(const char *pciaddr, char **packets, struct firehose_rdesc *rxring, int ring_size, int index); #ifdef __cplusplus } #endif int firehose_callback_v1(const char *pciaddr, char **packets, struct firehose_rdesc *rxring, int ring_size, int index) { while (rxring[index].status & 1) { int next_index = (index + 1) & (ring_size-1); __builtin_prefetch(packets[next_index]); firehose_packet(pciaddr, packets[index], rxring[index].length); rxring[index].status = 0; /* reset descriptor for reuse */ index = next_index; } return index; } void start_snabbswitch_collection(process_packet_pointer func_ptr) { logger << log4cpp::Priority::INFO << "SnabbSwitch plugin started"; snabbswitch_process_func_ptr = func_ptr; std::string interfaces_list = ""; if (configuration_map.count("interfaces_snabbswitch") != 0) { interfaces_list = configuration_map["interfaces_snabbswitch"]; } std::vector interfaces_for_capture; boost::split(interfaces_for_capture, interfaces_list, boost::is_any_of(","), boost::token_compress_on); if (interfaces_for_capture.size() == 0) { logger << log4cpp::Priority::ERROR << "Please specify list of PCI-e addresses for SnabbSwitch capture"; } logger << log4cpp::Priority::INFO << "SnabbSwitch will listen on " << interfaces_for_capture.size() << " interfaces"; boost::thread_group snabbswitch_main_threads; for (std::vector::iterator interface = interfaces_for_capture.begin(); interface != interfaces_for_capture.end(); ++interface) { // We could specify multiple NIC's for single thread with multiple --input const char* cli_arguments[5]; cli_arguments[0] = "snabb"; // emulate call of standard application cli_arguments[1] = "firehose"; cli_arguments[2] = "--input"; cli_arguments[3] = interface->c_str(); cli_arguments[4] ="weird_data"; int cli_number_of_arguments = sizeof(cli_arguments) / sizeof(char*); logger << log4cpp::Priority::INFO << "We are starting SnabbSwitch instance for PCIe interface " << *interface; snabbswitch_main_threads.add_thread( new boost::thread(start_snabb_switch, cli_number_of_arguments, cli_arguments) ); // We should sleep here because init code of SnabbSwitch is not thread safe sleep(10); } snabbswitch_main_threads.join_all(); } fastnetmon-1.1.4/src/snabbswitch_plugin/snabbswitch_collector.h000066400000000000000000000002511343111404700250710ustar00rootroot00000000000000#ifndef SNABBSWITCH_PLUGIN_H #define SNABBSWITCH_PLUGIN_H #include "../fastnetmon_types.h" void start_snabbswitch_collection(process_packet_pointer func_ptr); #endif fastnetmon-1.1.4/src/tests/000077500000000000000000000000001343111404700156225ustar00rootroot00000000000000fastnetmon-1.1.4/src/tests/af_packet.cpp000066400000000000000000000153661343111404700202560ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include /* the L2 protocols */ #include "../fastnetmon_packet_parser.h" /* Build it: g++ ../fastnetmon_packet_parser.c -ofastnetmon_packet_parser.o -c g++ af_packet.cpp fastnetmon_packet_parser.o -lboost_thread -lboost_system -lpthread */ // Copy and paste from netmap code void consume_pkt(u_char* buffer, int len) { /* struct pfring_pkthdr packet_header; memset(&packet_header, 0, sizeof(packet_header)); packet_header.len = len; packet_header.caplen = len; // We do not calculate timestamps because timestamping is very CPU intensive operation: // https://github.com/ntop/PF_RING/issues/9 u_int8_t timestamp = 0; u_int8_t add_hash = 0; fastnetmon_parse_pkt((u_char*)buffer, &packet_header, 4, timestamp, add_hash); */ //char print_buffer[512]; //fastnetmon_print_parsed_pkt(print_buffer, 512, (u_char*)buffer, &packet_header); //printf("%s\n", print_buffer); // logger.info("%s", print_buffer); } // Get interface number by name int get_interface_number_by_device_name(int socket_fd, std::string interface_name) { struct ifreq ifr; memset(&ifr, 0, sizeof(ifr)); if (interface_name.size() > IFNAMSIZ) { return -1; } strncpy(ifr.ifr_name, interface_name.c_str(), sizeof(ifr.ifr_name)); if (ioctl(socket_fd, SIOCGIFINDEX, &ifr) == -1) { return -1; } return ifr.ifr_ifindex; } unsigned int af_packet_threads = 1; uint64_t received_packets = 0; void speed_printer() { while (true) { uint64_t packets_before = received_packets; boost::this_thread::sleep(boost::posix_time::seconds(1)); uint64_t packets_after = received_packets; uint64_t pps = packets_after - packets_before; printf("We process: %llu pps\n", pps); } } int setup_socket(std::string interface_name, int fanout_group_id) { // More details here: http://man7.org/linux/man-pages/man7/packet.7.html // We could use SOCK_RAW or SOCK_DGRAM for second argument // SOCK_RAW - raw packets pass from the kernel // SOCK_DGRAM - some amount of processing // Third argument manage ether type of captured packets int packet_socket = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); if (packet_socket == -1) { printf("Can't create AF_PACKET socket\n"); return -1; } int interface_number = get_interface_number_by_device_name(packet_socket, interface_name); if (interface_number == -1) { printf("Can't get interface number by interface name\n"); return -1; } // Switch to PROMISC mode struct packet_mreq sock_params; memset(&sock_params, 0, sizeof(sock_params)); sock_params.mr_type = PACKET_MR_PROMISC; sock_params.mr_ifindex = interface_number; int set_promisc = setsockopt(packet_socket, SOL_PACKET, PACKET_ADD_MEMBERSHIP, (void *)&sock_params, sizeof(sock_params)); if (set_promisc == -1) { printf("Can't enable promisc mode\n"); return -1; } struct sockaddr_ll bind_address; memset(&bind_address, 0, sizeof(bind_address)); bind_address.sll_family = AF_PACKET; bind_address.sll_protocol = htons(ETH_P_ALL); bind_address.sll_ifindex = interface_number; // We will follow http://yusufonlinux.blogspot.ru/2010/11/data-link-access-and-zero-copy.html // And this: https://www.kernel.org/doc/Documentation/networking/packet_mmap.txt /* struct tpacket_req req; memset(&req, 0, sizeof(req); setsockopt(packet_socket, SOL_PACKET , PACKET_RX_RING , (void*)&req , sizeof(req)); setsockopt(packet_socket, SOL_PACKET , PACKET_TX_RING , (void*)&req , sizeof(req)); */ int bind_result = bind(packet_socket, (struct sockaddr *)&bind_address, sizeof(bind_address)); if (bind_result == -1) { printf("Can't bind to AF_PACKET socket\n"); return -1; } if (fanout_group_id) { // PACKET_FANOUT_LB - round robin // PACKET_FANOUT_CPU - send packets to CPU where packet arrived int fanout_type = PACKET_FANOUT_CPU; int fanout_arg = (fanout_group_id | (fanout_type << 16)); int setsockopt_fanout = setsockopt(packet_socket, SOL_PACKET, PACKET_FANOUT, &fanout_arg, sizeof(fanout_arg)); if (setsockopt_fanout < 0) { printf("Can't configure fanout\n"); return -1; } } // Most challenging option: PACKET_TX_RING return packet_socket; } void start_af_packet_capture(std::string interface_name, int fanout_group_id) { int packet_socket = setup_socket(interface_name, fanout_group_id); if (packet_socket == -1) { printf("Can't create socket\n"); return; } unsigned int capture_length = 1500; char buffer[capture_length]; while (true) { received_packets++; int readed_bytes = read(packet_socket, buffer, capture_length); // printf("Got %d bytes from interface\n", readed_bytes); consume_pkt((u_char*)buffer, readed_bytes); if (readed_bytes < 0) { break; } } } void get_af_packet_stats() { // getsockopt PACKET_STATISTICS } bool use_multiple_fanout_processes = true; // Could get some speed up on NUMA servers bool execute_strict_cpu_affinity = false; int main() { boost::thread speed_printer_thread( speed_printer ); int fanout_group_id = getpid() & 0xffff; if (use_multiple_fanout_processes) { boost::thread_group packet_receiver_thread_group; unsigned int num_cpus = 8; for (int cpu = 0; cpu < num_cpus; cpu++) { boost::thread::attributes thread_attrs; if (execute_strict_cpu_affinity) { cpu_set_t current_cpu_set; int cpu_to_bind = cpu % num_cpus; CPU_ZERO(¤t_cpu_set); // We count cpus from zero CPU_SET(cpu_to_bind, ¤t_cpu_set); int set_affinity_result = pthread_attr_setaffinity_np(thread_attrs.native_handle(), sizeof(cpu_set_t), ¤t_cpu_set); if (set_affinity_result != 0) { printf("Can't set CPU affinity for thread\n"); } } packet_receiver_thread_group.add_thread( new boost::thread(thread_attrs, boost::bind(start_af_packet_capture, "eth6", fanout_group_id)) ); } // Wait all processes for finish packet_receiver_thread_group.join_all(); } else { start_af_packet_capture("eth6", 0); } speed_printer_thread.join(); } fastnetmon-1.1.4/src/tests/af_packet_ring.cpp000066400000000000000000000217261343111404700212720ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include #include #include /* the L2 protocols */ #include "../fastnetmon_packet_parser.h" // 4194304 bytes unsigned int blocksiz = 1 << 22; // 2048 bytes unsigned int framesiz = 1 << 11; unsigned int blocknum = 64; struct block_desc { uint32_t version; uint32_t offset_to_priv; struct tpacket_hdr_v1 h1; }; /* Build it: g++ ../fastnetmon_packet_parser.c -ofastnetmon_packet_parser.o -c g++ af_packet.cpp fastnetmon_packet_parser.o -lboost_thread -lboost_system -lpthread */ // Get interface number by name int get_interface_number_by_device_name(int socket_fd, std::string interface_name) { struct ifreq ifr; memset(&ifr, 0, sizeof(ifr)); if (interface_name.size() > IFNAMSIZ) { return -1; } strncpy(ifr.ifr_name, interface_name.c_str(), sizeof(ifr.ifr_name)); if (ioctl(socket_fd, SIOCGIFINDEX, &ifr) == -1) { return -1; } return ifr.ifr_ifindex; } unsigned int af_packet_threads = 1; uint64_t received_packets = 0; uint64_t received_bytes = 0; void speed_printer() { while (true) { uint64_t packets_before = received_packets; boost::this_thread::sleep(boost::posix_time::seconds(1)); uint64_t packets_after = received_packets; uint64_t pps = packets_after - packets_before; printf("We process: %llu pps\n", pps); } } void flush_block(struct block_desc *pbd) { pbd->h1.block_status = TP_STATUS_KERNEL; } void walk_block(struct block_desc *pbd, const int block_num) { int num_pkts = pbd->h1.num_pkts, i; unsigned long bytes = 0; struct tpacket3_hdr *ppd; ppd = (struct tpacket3_hdr *) ((uint8_t *) pbd + pbd->h1.offset_to_first_pkt); for (i = 0; i < num_pkts; ++i) { bytes += ppd->tp_snaplen; // struct ethhdr *eth = (struct ethhdr *) ((uint8_t *) ppd + ppd->tp_mac); // Print packets // #define PRINT_PACKETS #ifdef PRINT_PACKETS struct pfring_pkthdr packet_header; memset(&packet_header, 0, sizeof(packet_header)); packet_header.len = ppd->tp_snaplen; packet_header.caplen = ppd->tp_snaplen; u_int8_t timestamp = 0; u_int8_t add_hash = 0; u_char* data_pointer = (u_char*)((uint8_t *) ppd + ppd->tp_mac); fastnetmon_parse_pkt(data_pointer, &packet_header, 4, timestamp, add_hash); char print_buffer[512]; fastnetmon_print_parsed_pkt(print_buffer, 512, data_pointer, &packet_header); printf("%s\n", print_buffer); #endif ppd = (struct tpacket3_hdr *) ((uint8_t *) ppd + ppd->tp_next_offset); } received_packets += num_pkts; received_bytes += bytes; } int setup_socket(std::string interface_name, int fanout_group_id) { // More details here: http://man7.org/linux/man-pages/man7/packet.7.html // We could use SOCK_RAW or SOCK_DGRAM for second argument // SOCK_RAW - raw packets pass from the kernel // SOCK_DGRAM - some amount of processing // Third argument manage ether type of captured packets int packet_socket = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); if (packet_socket == -1) { printf("Can't create AF_PACKET socket\n"); return -1; } // We whould use V3 bcause it could read/pool in per block basis instead per packet int version = TPACKET_V3; int setsockopt_packet_version = setsockopt(packet_socket, SOL_PACKET, PACKET_VERSION, &version, sizeof(version)); if (setsockopt_packet_version < 0) { printf("Can't set packet v3 version\n"); return -1; } int interface_number = get_interface_number_by_device_name(packet_socket, interface_name); if (interface_number == -1) { printf("Can't get interface number by interface name\n"); return -1; } // Switch to PROMISC mode struct packet_mreq sock_params; memset(&sock_params, 0, sizeof(sock_params)); sock_params.mr_type = PACKET_MR_PROMISC; sock_params.mr_ifindex = interface_number; int set_promisc = setsockopt(packet_socket, SOL_PACKET, PACKET_ADD_MEMBERSHIP, (void *)&sock_params, sizeof(sock_params)); if (set_promisc == -1) { printf("Can't enable promisc mode\n"); return -1; } struct sockaddr_ll bind_address; memset(&bind_address, 0, sizeof(bind_address)); bind_address.sll_family = AF_PACKET; bind_address.sll_protocol = htons(ETH_P_ALL); bind_address.sll_ifindex = interface_number; // We will follow http://yusufonlinux.blogspot.ru/2010/11/data-link-access-and-zero-copy.html // And this: https://www.kernel.org/doc/Documentation/networking/packet_mmap.txt struct tpacket_req3 req; memset(&req, 0, sizeof(req)); req.tp_block_size = blocksiz; req.tp_frame_size = framesiz; req.tp_block_nr = blocknum; req.tp_frame_nr = (blocksiz * blocknum) / framesiz; req.tp_retire_blk_tov = 60; // Timeout in msec req.tp_feature_req_word = TP_FT_REQ_FILL_RXHASH; int setsockopt_rx_ring = setsockopt(packet_socket, SOL_PACKET , PACKET_RX_RING , (void*)&req , sizeof(req)); if (setsockopt_rx_ring == -1) { printf("Can't enable RX_RING for AF_PACKET socket\n"); return -1; } // We use per thread structures uint8_t* mapped_buffer = NULL; struct iovec* rd = NULL; mapped_buffer = (uint8_t*)mmap(NULL, req.tp_block_size * req.tp_block_nr, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_LOCKED, packet_socket, 0); if (mapped_buffer == MAP_FAILED) { printf("mmap failed!\n"); return -1; } // Allocate iov structure for each block rd = (struct iovec*)malloc(req.tp_block_nr * sizeof(struct iovec)); // Initilize iov structures for (int i = 0; i < req.tp_block_nr; ++i) { rd[i].iov_base = mapped_buffer + (i * req.tp_block_size); rd[i].iov_len = req.tp_block_size; } int bind_result = bind(packet_socket, (struct sockaddr *)&bind_address, sizeof(bind_address)); if (bind_result == -1) { printf("Can't bind to AF_PACKET socket\n"); return -1; } if (fanout_group_id) { // PACKET_FANOUT_LB - round robin // PACKET_FANOUT_CPU - send packets to CPU where packet arrived int fanout_type = PACKET_FANOUT_CPU; int fanout_arg = (fanout_group_id | (fanout_type << 16)); int setsockopt_fanout = setsockopt(packet_socket, SOL_PACKET, PACKET_FANOUT, &fanout_arg, sizeof(fanout_arg)); if (setsockopt_fanout < 0) { printf("Can't configure fanout\n"); return -1; } } unsigned int current_block_num = 0; struct pollfd pfd; memset(&pfd, 0, sizeof(pfd)); pfd.fd = packet_socket; pfd.events = POLLIN | POLLERR; pfd.revents = 0; while (true) { struct block_desc *pbd = (struct block_desc *) rd[current_block_num].iov_base; if ((pbd->h1.block_status & TP_STATUS_USER) == 0) { poll(&pfd, 1, -1); continue; } walk_block(pbd, current_block_num); flush_block(pbd); current_block_num = (current_block_num + 1) % blocknum; } return packet_socket; } void start_af_packet_capture(std::string interface_name, int fanout_group_id) { setup_socket(interface_name, fanout_group_id); } void get_af_packet_stats() { // getsockopt PACKET_STATISTICS } // Could get some speed up on NUMA servers bool execute_strict_cpu_affinity = false; bool use_multiple_fanout_processes = true; int main() { int fanout_group_id = getpid() & 0xffff; boost::thread speed_printer_thread( speed_printer ); if (use_multiple_fanout_processes) { boost::thread_group packet_receiver_thread_group; unsigned int num_cpus = 8; for (int cpu = 0; cpu < num_cpus; cpu++) { boost::thread::attributes thread_attrs; if (execute_strict_cpu_affinity) { cpu_set_t current_cpu_set; int cpu_to_bind = cpu % num_cpus; CPU_ZERO(¤t_cpu_set); // We count cpus from zero CPU_SET(cpu_to_bind, ¤t_cpu_set); int set_affinity_result = pthread_attr_setaffinity_np(thread_attrs.native_handle(), sizeof(cpu_set_t), ¤t_cpu_set); if (set_affinity_result != 0) { printf("Can't set CPU affinity for thread\n"); } } packet_receiver_thread_group.add_thread( new boost::thread(thread_attrs, boost::bind(start_af_packet_capture, "eth6", fanout_group_id)) ); } // Wait all processes for finish packet_receiver_thread_group.join_all(); } else { start_af_packet_capture("eth6", 0); } speed_printer_thread.join(); } fastnetmon-1.1.4/src/tests/build_lpm_test.bash000077500000000000000000000006741343111404700215010ustar00rootroot00000000000000#!/usr/bin/env bash COMPILER=clang CPP_COMPILER=clang++ gcc -g -pg -O2 ../libpatricia/patricia.c -c -o patricia.o g++ -g -pg -O2 lpm_performance_tests.cpp patricia.o -olpm_performance_tests -lrt #$COMPILER -O4 ../libpatricia/patricia.c -c -o patricia.o #ar q patricia.a patricia.o #$CPP_COMPILER lpm_performance_tests.cpp -olpm_performance_tests.o -c #$CPP_COMPILER -v -O4 lpm_performance_tests.o patricia.a -olpm_performance_tests -lrt fastnetmon-1.1.4/src/tests/build_netmap.bash000077500000000000000000000003711343111404700211300ustar00rootroot00000000000000#!/usr/bin/env bash clang++ fastnetmon_packet_parser.cpp -c -ofastnetmon_packet_parser.o clang++ netmap.cpp -I/usr/local/include -L/usr/local/lib -I/usr/src/fastnetmon/tests/netmap_includes -lboost_thread -lboost_system fastnetmon_packet_parser.o fastnetmon-1.1.4/src/tests/conntrack_prototype.cpp000066400000000000000000000120661343111404700224420ustar00rootroot00000000000000#include #include #include #include #include #include #include "../fastnetmon_types.h" // It's very raw API implementation for connection tracking code. Due to HUGE amount of collisions it's very slow: ~1Mpps // For performance it's very close to std::map but much times more buggy :) // https://code.google.com/p/smhasher/source/browse/trunk/MurmurHash2.cpp // 64-bit hash for 64-bit platforms #define BIG_CONSTANT(x) (x##LLU) uint64_t MurmurHash64A(const void* key, int len, uint64_t seed) { const uint64_t m = BIG_CONSTANT(0xc6a4a7935bd1e995); const int r = 47; uint64_t h = seed ^ (len * m); const uint64_t* data = (const uint64_t*)key; const uint64_t* end = data + (len / 8); while (data != end) { uint64_t k = *data++; k *= m; k ^= k >> r; k *= m; h ^= k; h *= m; } const unsigned char* data2 = (const unsigned char*)data; switch (len & 7) { case 7: h ^= uint64_t(data2[6]) << 48; case 6: h ^= uint64_t(data2[5]) << 40; case 5: h ^= uint64_t(data2[4]) << 32; case 4: h ^= uint64_t(data2[3]) << 24; case 3: h ^= uint64_t(data2[2]) << 16; case 2: h ^= uint64_t(data2[1]) << 8; case 1: h ^= uint64_t(data2[0]); h *= m; }; h ^= h >> r; h *= m; h ^= h >> r; return h; } class conntrack_hash_struct_for_simple_packet_t { public: uint32_t src_ip; uint32_t dst_ip; uint16_t source_port; uint16_t destination_port; unsigned int protocol; bool operator==(const conntrack_hash_struct_for_simple_packet_t& rhs) { // TODO: not so smart, we should fix this! return memcmp(this, &rhs, sizeof(conntrack_hash_struct_for_simple_packet_t)) == 0; } }; // Extract only important for us fields from main simple_packet structure bool convert_simple_packet_toconntrack_hash_struct(simple_packet& packet, conntrack_hash_struct_for_simple_packet_t& conntrack_struct) { conntrack_struct.src_ip = packet.src_ip; conntrack_struct.dst_ip = packet.dst_ip; conntrack_struct.protocol = packet.protocol; conntrack_struct.source_port = packet.source_port; conntrack_struct.destination_port = packet.destination_port; } // Class prototype for connection tracking typedef std::vector< conntrack_hash_struct_for_simple_packet_t > vector_of_connetrack_structs_t; class connection_tracking_fast_storage_t { public: connection_tracking_fast_storage_t(unsigned int structure_size) { murmur_seed = 13; max_vector_size = 0; number_of_buckets = structure_size; buckets_storage.reserve(structure_size); } uint64_t get_bucket_number(conntrack_hash_struct_for_simple_packet_t& element) { uint64_t conntrack_hash = MurmurHash64A(&element, sizeof(conntrack_hash_struct_for_simple_packet_t), murmur_seed); return conntrack_hash % number_of_buckets; } bool lookup(conntrack_hash_struct_for_simple_packet_t* element) { uint64_t bucket_number = get_bucket_number(*element); vector_of_connetrack_structs_t* vector_pointer = &buckets_storage[bucket_number]; unsigned int vector_size = vector_pointer->size(); if (vector_size > max_vector_size) { max_vector_size = vector_size; if (max_vector_size > 100) { printf("We got %u collisions for key %llu\n", max_vector_size, bucket_number); } } if (vector_size == 0) { return false; } vector_of_connetrack_structs_t::iterator itr = std::find(vector_pointer->begin(), vector_pointer->end(), *element); if (itr == vector_pointer->end()) { return false; } return true; } bool insert(conntrack_hash_struct_for_simple_packet_t element) { uint64_t bucket_number = get_bucket_number(element); buckets_storage[bucket_number].push_back(element); } public: unsigned int number_of_buckets; std::vector buckets_storage; unsigned int murmur_seed; // conntrack_hash_struct_for_simple_packet_t conntrack_structure; unsigned int max_vector_size; }; connection_tracking_fast_storage_t my_conntrack_storage(32000); int main() { // fake data char data[1500]; simple_packet current_packet; // parse_raw_packet_to_simple_packet((u_char*)data, length, current_packet); conntrack_hash_struct_for_simple_packet_t conntrack_structure; convert_simple_packet_toconntrack_hash_struct(current_packet, conntrack_structure); if (my_conntrack_storage.lookup(&conntrack_structure)) { //printf("Already exists\n"); // found it } else { //printf("New\n"); my_conntrack_storage.insert(conntrack_structure); } } fastnetmon-1.1.4/src/tests/exabgp_pipe.c000066400000000000000000000024061343111404700202530ustar00rootroot00000000000000#include #include #include #include #include #include int ban_ip() { int exabgp_pipe = open("/var/run/exabgp.cmd", O_WRONLY); if (exabgp_pipe <= 0) { printf("Can't open exabgp PIPE"); exit(1); } char bgp_message[256]; char* ip_cidr_form = "10.10.10.123/32"; char* next_hop = "10.0.3.114"; char* exabgp_community = "65001:666"; // withdraw char* action = "announce"; sprintf(bgp_message, "%s route %s next-hop %s community %s\n", action, ip_cidr_form, next_hop, exabgp_community); int wrote_bytes = write(exabgp_pipe, bgp_message, strlen(bgp_message)); printf("We wrote %d bytes\n", wrote_bytes); close(exabgp_pipe); } int unban_ip() { char bgp_message[256]; char* ip_cidr_form = "10.10.10.123/32"; int exabgp_pipe = open("/var/run/exabgp.cmd", O_WRONLY); if (exabgp_pipe <= 0) { printf("Can't open exabgp PIPE"); exit(1); } char* action = "withdraw"; sprintf(bgp_message, "%s route %s\n", action, ip_cidr_form); int wrote_bytes = write(exabgp_pipe, bgp_message, strlen(bgp_message)); printf("We wrote %d bytes\n", wrote_bytes); close(exabgp_pipe); } int main() { unban_ip(); } fastnetmon-1.1.4/src/tests/ip_lookup.cpp000066400000000000000000000212511343111404700203300ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace std; vector exec(string cmd) { vector output_list; FILE* pipe = popen(cmd.c_str(), "r"); if (!pipe) return output_list; char buffer[256]; std::string result = ""; while (!feof(pipe)) { if (fgets(buffer, 256, pipe) != NULL) { size_t newbuflen = strlen(buffer); // remove newline at the end if (buffer[newbuflen - 1] == '\n') { buffer[newbuflen - 1] = '\0'; } output_list.push_back(buffer); } } pclose(pipe); return output_list; } typedef pair subnet; bool belongs_to_networks(vector& networks_list, uint32_t ip) { for (vector::iterator ii = networks_list.begin(); ii != networks_list.end(); ++ii) { if ((ip & (*ii).second) == ((*ii).first & (*ii).second)) { return true; } } return false; } uint32_t convert_ip_as_string_to_uint(string ip) { struct in_addr ip_addr; inet_aton(ip.c_str(), &ip_addr); // in network byte order return ip_addr.s_addr; } uint32_t convert_cidr_to_binary_netmask(int cidr) { uint32_t binary_netmask = 0xFFFFFFFF; binary_netmask = binary_netmask << (32 - cidr); // htonl from host byte order to network // ntohl from network byte order to host // I suppose we need network byte order here return htonl(binary_netmask); } int get_bit(uint32_t number, uint32_t ip) { return 1; } typedef struct leaf { bool bit; struct leaf* right, *left; } tree_leaf; #include void insert_prefix_bitwise_tree(tree_leaf* root, string subnet, int cidr_mask) { uint32_t netmask_as_int = convert_ip_as_string_to_uint(subnet); // std::cout<(netmask_as_int)<= 32 - cidr_mask; i--) { uint32_t result_bit = netmask_as_int & (1 << i); bool bit = result_bit == 0 ? false : true; // cout<<"Insert: "<right != NULL) { // Elemelnt already there, just switch pointer temp_root = temp_root->right; } else { // No element here, we should create it tree_leaf* new_leaf = new tree_leaf; new_leaf->right = new_leaf->left = NULL; new_leaf->bit = bit; temp_root->right = new_leaf; temp_root = new_leaf; } } else { // check left subtree if (temp_root->left != NULL) { // Elemelnt already there, just switch pointer temp_root = temp_root->left; } else { // No element here, we should create it tree_leaf* new_leaf = new tree_leaf; new_leaf->right = new_leaf->left = NULL; new_leaf->bit = bit; temp_root->left = new_leaf; temp_root = new_leaf; } } } // #include // std::cout<(netmask_as_int)<left == NULL && temp_root->right == NULL)) { return false; } // convert to host byte order ip = ntohl(ip); int bits_matched = 0; for (int i = 31; i >= 0; i--) { // cout<<"bit"<left == NULL && temp_root->right == NULL)) { if (bits_matched > 0) { // if we havent child elemets (leaf is terinal) and we have match for single bit at lease // thus, we found mask! // std::cout<<"Bits matched: "<right != NULL) { temp_root = temp_root->right; bits_matched++; } else { if (temp_root->left != NULL) { return false; } else { // already checked above } } } else { if (temp_root->left != NULL) { temp_root = temp_root->left; bits_matched++; } else { if (temp_root->right != NULL) { return false; } else { // already checked above } } } } // We will repeat same checks as in begin of function. But we need it because // we could pass cycle and do not hit any terminals - both childs become zeroes if ((temp_root->left == NULL && temp_root->right == NULL)) { if (bits_matched > 0) { // if we havent child elemets (leaf is terinal) and we have match for single bit at lease // thus, we found mask! // std::cout<<"Bits matched: "<left = root->right = NULL; // uint32_t ip_127 = convert_ip_as_string_to_uint("127.0.0.3"); // uint32_t ip_159 = convert_ip_as_string_to_uint("159.253.17.1"); // uint32_t ip_8 = convert_ip_as_string_to_uint("255.8.8.8"); // insert_prefix_bitwise_tree(root, "159.253.17.0", 24); // insert_prefix_bitwise_tree(root, "159.253.16.0", 24); // insert_prefix_bitwise_tree(root, "127.0.0.1", 24); // insert_prefix_bitwise_tree(root, "255.8.8.8", 32); // std::cout< networks_list_as_string; vector our_networks; vector network_list_from_config = exec("cat /etc/networks_list"); networks_list_as_string.insert(networks_list_as_string.end(), network_list_from_config.begin(), network_list_from_config.end()); for (vector::iterator ii = networks_list_as_string.begin(); ii != networks_list_as_string.end(); ++ii) { vector subnet_as_string; split(subnet_as_string, *ii, boost::is_any_of("/"), boost::token_compress_on); int cidr = atoi(subnet_as_string[1].c_str()); uint32_t subnet_as_int = convert_ip_as_string_to_uint(subnet_as_string[0]); uint32_t netmask_as_int = convert_cidr_to_binary_netmask(cidr); insert_prefix_bitwise_tree(root, subnet_as_string[0], cidr); subnet current_subnet = std::make_pair(subnet_as_int, netmask_as_int); our_networks.push_back(current_subnet); } uint32_t my_ip = convert_ip_as_string_to_uint("192.0.0.192"); // my_ip = ntohl(my_ip); // std::bitset<32> x(my_ip); // std::cout< #include int main() { for (int i = 0; i < 10000000; i++) { json_object * jobj = json_object_new_object(); /*Creating a json array*/ json_object *jarray = json_object_new_array(); /*Creating json strings*/ json_object *jstring1 = json_object_new_string("c"); json_object *jstring2 = json_object_new_string("c++"); json_object *jstring3 = json_object_new_string("php"); /*Adding the above created json strings to the array*/ json_object_array_add(jarray,jstring1); json_object_array_add(jarray,jstring2); json_object_array_add(jarray,jstring3); json_object_object_add(jobj, "languages", jarray); // After _array_add and _object_add operations all ownership moves to obj and will be freed up with jobj /*Now printing the json object*/ //printf ("The json object created: %sn", json_object_to_json_string(jobj)); // Free up memory json_object_put(jobj); } } fastnetmon-1.1.4/src/tests/lpm_performance_tests.cpp000066400000000000000000000150021343111404700227170ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include #include #include "../libpatricia/patricia.h" using namespace std; // main data structure for storing traffic and speed data for all our IPs class map_element { public: map_element() : in_bytes(0), out_bytes(0), in_packets(0), out_packets(0), tcp_in_packets(0), tcp_out_packets(0), tcp_in_bytes(0), tcp_out_bytes(0), udp_in_packets(0), udp_out_packets(0), udp_in_bytes(0), udp_out_bytes(0), in_flows(0), out_flows(0), icmp_in_packets(0), icmp_out_packets(0), icmp_in_bytes(0), icmp_out_bytes(0) { } unsigned int in_bytes; unsigned int out_bytes; unsigned int in_packets; unsigned int out_packets; // Additional data for correct attack protocol detection unsigned int tcp_in_packets; unsigned int tcp_out_packets; unsigned int tcp_in_bytes; unsigned int tcp_out_bytes; unsigned int udp_in_packets; unsigned int udp_out_packets; unsigned int udp_in_bytes; unsigned int udp_out_bytes; unsigned int icmp_in_packets; unsigned int icmp_out_packets; unsigned int icmp_in_bytes; unsigned int icmp_out_bytes; unsigned int in_flows; unsigned int out_flows; }; typedef vector vector_of_counters; typedef std::map map_of_vector_counters; typedef std::pair pair_of_subnets_with_key; typedef vector vector_of_vector_counters; map_of_vector_counters SubnetVectorMap; vector_of_vector_counters SubnetVectorVector; #include void subnet_vectors_allocator(prefix_t* prefix, void* data) { uint32_t subnet_as_integer = prefix->add.sin.s_addr; u_short bitlen = prefix->bitlen; int network_size_in_ips = pow(2, 32 - bitlen); network_size_in_ips = 1; SubnetVectorMap[subnet_as_integer] = new vector_of_counters(network_size_in_ips); pair_of_subnets_with_key my_pair; my_pair.first = subnet_as_integer; my_pair.second = new vector_of_counters(network_size_in_ips); SubnetVectorVector.push_back(my_pair); } void suxx_func(unsigned long suxx) { } uint32_t convert_ip_as_string_to_uint(string ip) { struct in_addr ip_addr; inet_aton(ip.c_str(), &ip_addr); // in network byte order return ip_addr.s_addr; } bool mysortfunction(pair_of_subnets_with_key i, pair_of_subnets_with_key j) { return (i.first < j.first); } int main() { patricia_tree_t* lookup_tree; lookup_tree = New_Patricia(32); make_and_lookup(lookup_tree, "46.36.216.0/21"); make_and_lookup(lookup_tree, "159.253.16.0/21"); make_and_lookup(lookup_tree, "5.45.112.0/21"); make_and_lookup(lookup_tree, "5.45.120.0/21"); make_and_lookup(lookup_tree, "5.101.112.0/21"); make_and_lookup(lookup_tree, "5.101.120.0/21"); make_and_lookup(lookup_tree, "185.4.72.0/22"); make_and_lookup(lookup_tree, "181.114.240.0/20"); make_and_lookup(lookup_tree, "193.42.142.0/24"); // patricia_process (lookup_tree, (void_fn_t)subnet_vectors_allocator); // std::sort(SubnetVectorVector.begin(), SubnetVectorVector.end(), mysortfunction); prefix_t prefix_for_check_adreess; prefix_for_check_adreess.family = AF_INET; prefix_for_check_adreess.bitlen = 32; patricia_node_t* found_patrica_node = NULL; // prefix_for_check_adreess.add.sin.s_addr = 123123123; // std::map lpm_cache; // Without cache: 16.7 million of operations int i_iter = 100; // Million operations int j_iter = 1000000; // printf("Preallocate table\n"); // Iterate over all our IP addresses // for (int j = 0; j < j_iter; j++) { // for (int i = 0; i < i_iter; i++) { // lpm_cache[i*j] = true; // } //} printf("Start tests\n"); timespec start_time; clock_gettime(CLOCK_REALTIME, &start_time); prefix_for_check_adreess.add.sin.s_addr = convert_ip_as_string_to_uint("159.253.17.1"); for (int j = 0; j < j_iter; j++) { for (int i = 0; i < i_iter; i++) { // Random Pseudo IP // prefix_for_check_adreess.add.sin.s_addr = i*j; patricia_node_t* found_patrica_node = patricia_search_best2(lookup_tree, &prefix_for_check_adreess, 1); unsigned long destination_subnet = 0; suxx_func(found_patrica_node != NULL); if (found_patrica_node != NULL) { destination_subnet = found_patrica_node->prefix->add.sin.s_addr; suxx_func(destination_subnet); // std::cout<<"*"; /* for (vector_of_vector_counters::iterator it = SubnetVectorVector.begin() ; it != SubnetVectorVector.end(); ++it) { std::cout<first<<","; if (it->first == destination_subnet) { suxx_func(destination_subnet); } } std::cout<<"\n"; */ /* map_of_vector_counters::iterator itr; itr = SubnetVectorMap.find(destination_subnet); if (itr == SubnetVectorMap.end()) { } else { suxx_func(destination_subnet); } */ } // prefix_for_check_adreess.add.sin.s_addr = i*j + 1; // patricia_node_t* found_second_patrica_node = patricia_search_best(lookup_tree, // &prefix_for_check_adreess); // std::map ::iterator itr = lpm_cache.find(i*j); // if (itr != lpm_cache.end()) { // found it! //} else { // cache miss // bool result = patricia_search_best(lookup_tree, &prefix_for_check_adreess) != NULL; // lpm_cache[i*j] = result; // not found! //} } } timespec finish_time; clock_gettime(CLOCK_REALTIME, &finish_time); unsigned long used_seconds = finish_time.tv_sec - start_time.tv_sec; unsigned long total_ops = i_iter * j_iter; float megaops_per_second = (float)total_ops / (float)used_seconds / 1000000; printf("Total time is %d seconds total ops: %d\nMillion of ops per second: %.1f\n", used_seconds, total_ops, megaops_per_second); Destroy_Patricia(lookup_tree, (void_fn_t)0); } fastnetmon-1.1.4/src/tests/lua_integration.cpp000066400000000000000000000026561343111404700215230ustar00rootroot00000000000000#include // Heh, we have luajit only for Debian Jessie and should think about custom compilation // https://packages.debian.org/search?keywords=luajit // This code will NOT work with lua 5.2 because 5.1 and 5.2 really incompatible: // http://lists.opensuse.org/opensuse-factory/2012-01/msg00265.html // Ubuntu 14.04 also has it: http://packages.ubuntu.com/trusty/luajit // apt-get install -y lua5.1 lua-json liblua5.1-dev // g++ lua_integration.cpp -lluajit-5.1 // Unfortunately, we haven't support for FFI in standard lua and should switch to luajit: // Info about bundled modules to luajit: http://luajit.org/extensions.html // apt-get install -y libluajit-5.1-dev int main() { typedef struct netflow_struct { int packets; int bytes; } netflow_t; netflow_t flow; flow.packets = 55; flow.bytes = 77; lua_State* L = luaL_newstate(); // load libraries luaL_openlibs(L); luaL_dofile(L, "json_parser.lua"); //luaL_dostring(L, "a = 10 + 5"); //lua_getglobal(L, "a"); //int i = lua_tointeger(L, -1); //printf("%d\n", i); lua_getfield(L, LUA_GLOBALSINDEX, "process_netflow"); //lua_pushstring(L, "first_arg"); lua_pushlightuserdata(L, (void*)&flow); // Call with 1 argumnents and 1 result lua_call(L, 1, 1); printf( "Lua gettop: %d\n", lua_gettop(L) ); printf( "Boolean result: %d\n", lua_toboolean(L, -1) ); lua_close(L); return 0; } fastnetmon-1.1.4/src/tests/mongodb_client.cpp000066400000000000000000000016461343111404700213200ustar00rootroot00000000000000#include #include #include // g++ mongodb_client.cpp $(PKG_CONFIG_PATH=/opt/mongo_c_driver/lib/pkgconfig pkg-config --cflags --libs libmongoc-1.0) int main (int argc, char *argv[]) { mongoc_client_t *client; mongoc_collection_t *collection; mongoc_cursor_t *cursor; bson_error_t error; bson_oid_t oid; bson_t *doc; mongoc_init (); client = mongoc_client_new ("mongodb://localhost:27017/"); collection = mongoc_client_get_collection (client, "test", "test"); doc = bson_new (); bson_oid_init (&oid, NULL); BSON_APPEND_OID (doc, "_id", &oid); BSON_APPEND_UTF8 (doc, "hello", "world"); if (!mongoc_collection_insert (collection, MONGOC_INSERT_NONE, doc, NULL, &error)) { printf ("Error: %s\n", error.message); } bson_destroy (doc); mongoc_collection_destroy (collection); mongoc_client_destroy (client); return 0; } fastnetmon-1.1.4/src/tests/netflow_exclude.json000066400000000000000000000004011343111404700216770ustar00rootroot00000000000000{"22.11.22.33" : { 33 : "BB", 4 : "BB", 3 : "BB" },"88.99.11.22" : { 559 : "BB", 572 : "BB", 613 : "BB", 542 : "BB", 565 : "BB", 558 : "BB", 561 : "BB", 543 : "BB", 555 : "BB", 545 : "BB", 568 : "BB", 551 : "BB", 574 : "BB" }, "10.0.1.2" : { 916 : "BB" } } fastnetmon-1.1.4/src/tests/netmap.cpp000066400000000000000000000124501343111404700176140ustar00rootroot00000000000000#include #include #include #define NETMAP_WITH_LIBS #include #include // For pooling operations #include #include "fastnetmon_packet_parser.h" int number_of_packets = 0; /* prototypes */ void netmap_thread(struct nm_desc* netmap_descriptor, int netmap_thread); void consume_pkt(u_char* buffer, int len); int receive_packets(struct netmap_ring* ring) { u_int cur, rx, n; cur = ring->cur; n = nm_ring_space(ring); for (rx = 0; rx < n; rx++) { struct netmap_slot* slot = &ring->slot[cur]; char* p = NETMAP_BUF(ring, slot->buf_idx); // process data consume_pkt((u_char*)p, slot->len); cur = nm_ring_next(ring, cur); } ring->head = ring->cur = cur; return (rx); } void consume_pkt(u_char* buffer, int len) { // static char packet_data[2000]; // printf("Got packet with length: %d\n", len); // memcpy(packet_data, buffer, len); struct pfring_pkthdr l2tp_header; memset(&l2tp_header, 0, sizeof(l2tp_header)); l2tp_header.len = len; l2tp_header.caplen = len; fastnetmon_parse_pkt((u_char*)buffer, &l2tp_header, 4, 1, 0); // char print_buffer[512]; // fastnetmon_print_parsed_pkt(print_buffer, 512, (u_char*)buffer, &l2tp_header); // printf("%s\n", print_buffer); __sync_fetch_and_add(&number_of_packets, 1); } void receiver(void) { struct nm_desc* netmap_descriptor; u_int num_cpus = sysconf(_SC_NPROCESSORS_ONLN); printf("We have %d cpus\n", num_cpus); struct nmreq base_nmd; bzero(&base_nmd, sizeof(base_nmd)); // Magic from pkt-gen.c base_nmd.nr_tx_rings = base_nmd.nr_rx_rings = 0; base_nmd.nr_tx_slots = base_nmd.nr_rx_slots = 0; std::string interface = "netmap:eth4"; netmap_descriptor = nm_open(interface.c_str(), &base_nmd, 0, NULL); if (netmap_descriptor == NULL) { printf("Can't open netmap device %s\n", interface.c_str()); exit(1); return; } printf("Mapped %dKB memory at %p\n", netmap_descriptor->req.nr_memsize >> 10, netmap_descriptor->mem); printf("We have %d tx and %d rx rings\n", netmap_descriptor->req.nr_tx_rings, netmap_descriptor->req.nr_rx_rings); /* protocol stack and may cause a reset of the card, which in turn may take some time for the PHY to reconfigure. We do the open here to have time to reset. */ int wait_link = 2; printf("Wait %d seconds for NIC reset\n", wait_link); sleep(wait_link); boost::thread* boost_threads_array[num_cpus]; for (int i = 0; i < num_cpus; i++) { struct nm_desc nmd = *netmap_descriptor; // This operation is VERY important! nmd.self = &nmd; uint64_t nmd_flags = 0; if (nmd.req.nr_flags != NR_REG_ALL_NIC) { printf("SHIT SHIT SHIT HAPPINED\n"); } nmd.req.nr_flags = NR_REG_ONE_NIC; nmd.req.nr_ringid = i; /* Only touch one of the rings (rx is already ok) */ nmd_flags |= NETMAP_NO_TX_POLL; struct nm_desc* new_nmd = nm_open(interface.c_str(), NULL, nmd_flags | NM_OPEN_IFNAME | NM_OPEN_NO_MMAP, &nmd); if (new_nmd == NULL) { printf("Can't open netmap descripto for netmap\n"); exit(1); } printf("My first ring is %d and last ring id is %d I'm thread %d\n", new_nmd->first_rx_ring, new_nmd->last_rx_ring, i); printf("Start new thread %d\n", i); // Start thread and pass netmap descriptor to it boost_threads_array[i] = new boost::thread(netmap_thread, new_nmd, i); } printf("Wait for thread finish\n"); // Wait all threads for completion for (int i = 0; i < num_cpus; i++) { boost_threads_array[i]->join(); } } void netmap_thread(struct nm_desc* netmap_descriptor, int thread_number) { struct nm_pkthdr h; u_char* buf; struct pollfd fds; fds.fd = netmap_descriptor->fd; // NETMAP_FD(netmap_descriptor); fds.events = POLLIN; struct netmap_ring* rxring = NULL; struct netmap_if* nifp = netmap_descriptor->nifp; printf("Reading from fd %d thread id: %d\n", netmap_descriptor->fd, thread_number); for (;;) { // We will wait 1000 microseconds for retry, for infinite timeout please use -1 int poll_result = poll(&fds, 1, 1000); if (poll_result == 0) { // printf("poll return 0 return code\n"); continue; } if (poll_result == -1) { printf("poll failed with return code -1\n"); } for (int i = netmap_descriptor->first_rx_ring; i <= netmap_descriptor->last_rx_ring; i++) { // printf("Check ring %d from thread %d\n", i, thread_number); rxring = NETMAP_RXRING(nifp, i); if (nm_ring_empty(rxring)) { continue; } int m = receive_packets(rxring); } // while ( (buf = nm_nextpkt(netmap_descriptor, &h)) ) { // consume_pkt(buf, h.len); //} } // nm_close(netmap_descriptor); } int main() { // receiver(); boost::thread netmap_thread(receiver); for (;;) { sleep(1); printf("We received %d packets in 1 second\n", number_of_packets); number_of_packets = 0; } netmap_thread.join(); } fastnetmon-1.1.4/src/tests/parser_performance_tests.cpp000066400000000000000000000077061343111404700234370ustar00rootroot00000000000000#include #include #include #include #include #include "../fastnetmon_packet_parser.h" #include #include using namespace Tins; /* gcc ../fastnetmon_packet_parser.c -o fastnetmon_packet_parser.o -c g++ parser_performance_tests.cpp fastnetmon_packet_parser.o -lpthread -ltins -std=c++11 */ /* Tins: C++ 98 We process: 3 557 647 pps We process: 3 554 012 pps Tins: C++11 We process: 3 529 692 pps We process: 3 529 249 pps PF_RING packet parser without hashing and timestamps: We process: 18 145 597 pps We process: 20 395 563 pps We process: 18 145 597 pps We process: 20 395 563 pps */ void call_fastnetmon_parser(void* ptr, int length); void call_tins_parser(void* ptr, int length); uint64_t received_packets = 0; void* speed_printer(void* ptr) { while (1) { uint64_t packets_before = received_packets; sleep(1); uint64_t packets_after = received_packets; uint64_t pps = packets_after - packets_before; printf("We process: %llu pps\n", pps); } } // We could print any payload with this function and use it for tests void print_packet_payload_in_c_form(unsigned char* data, int length) { int i = 0; printf("unsigned char payload[] = { "); for (i = 0; i < length; i++) { printf("0x%02X", (unsigned char)data[i]); if (i != length -1) { printf(","); } } printf(" }\n"); } int main() { pthread_t thread; pthread_create(&thread, NULL, speed_printer, NULL); pthread_detach(thread); unsigned char payload1[] = { 0x90,0xE2,0xBA,0x83,0x3F,0x25,0x90,0xE2,0xBA,0x2C,0xCB,0x02,0x08,0x00,0x45,0x00,0x00,0x2E,0x00,0x00,0x00,0x00,0x40,0x06,0x69,0xDC,0x0A,0x84,0xF1,0x83,0x0A,0x0A,0x0A,0xDD,0x04,0x01,0x00,0x50,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x50,0x02,0x00,0x0A,0x9A,0x92,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }; unsigned char payload2[] = { 0x90,0xE2,0xBA,0x83,0x3F,0x25,0x90,0xE2,0xBA,0x2C,0xCB,0x02,0x08,0x00,0x45,0x00,0x00,0x2E,0x00,0x00,0x00,0x00,0x40,0x06,0x69,0xDB,0x0A,0x84,0xF1,0x84,0x0A,0x0A,0x0A,0xDD,0x04,0x01,0x00,0x50,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x50,0x02,0x00,0x0A,0x9A,0x91,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }; unsigned char byte_value = 0; //int counter = 512; //while (counter > 0) { while(1) { // We use overflow here! byte_value++; // payload1[26] = byte_value; // first octet payload1[29] = byte_value; // last octet call_fastnetmon_parser((void*)payload1, sizeof(payload1)); //call_tins_parser((void*)payload1, sizeof(payload1)); } } void call_tins_parser(void* ptr, int length) { __sync_fetch_and_add(&received_packets, 1); EthernetII pdu((const uint8_t*)ptr, length); const IP &ip = pdu.rfind_pdu(); // Find the IP layer if (ip.protocol() == Tins::Constants::IP::PROTO_TCP) { const TCP &tcp = pdu.rfind_pdu(); // Find the TCP layer //std::cout << ip.src_addr() << ':' << tcp.sport() << " -> " // << ip.dst_addr() << ':' << tcp.dport() << std::endl; } else if (ip.protocol() == Tins::Constants::IP::PROTO_UDP) { const UDP &udp = pdu.rfind_pdu(); // Find the UDP layer } else if (ip.protocol() == Tins::Constants::IP::PROTO_ICMP) { const ICMP &icmp = pdu.rfind_pdu(); // Find the ICMP layer } } void call_fastnetmon_parser(void* ptr, int length) { __sync_fetch_and_add(&received_packets, 1); struct pfring_pkthdr packet_header; memset(&packet_header, 0, sizeof(struct pfring_pkthdr)); packet_header.len = length; packet_header.caplen = length; u_int8_t timestamp = 0; u_int8_t add_hash = 0; fastnetmon_parse_pkt((u_char*)ptr, &packet_header, 4, 0, 0); /* char print_buffer[512]; fastnetmon_print_parsed_pkt(print_buffer, 512, (u_char*)ptr, &packet_header); printf("packet: %s\n", print_buffer); */ } fastnetmon-1.1.4/src/tests/patch_for_custom_libc.patch000066400000000000000000000171211343111404700231750ustar00rootroot00000000000000diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a65734b..8a52b0b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -5,6 +5,13 @@ cmake_minimum_required (VERSION 2.8) # Debian 7 - 2.8.9 # CentOS 6 - 2.8.12 +# We should set compiler berfor project() call +if (ENABLE_BUILD_IN_CPP_11_CUSTOM_ENVIRONMENT) + # We use custom compiler too + set(CMAKE_C_COMPILER "/opt/gcc520/bin/gcc") + set(CMAKE_CXX_COMPILER "/opt/gcc520/bin/g++") +endif() + project(FastNetMon) # Unfortunately, Debian Squeeze haven't support for this feature @@ -20,11 +27,42 @@ set (FASTNETMON_VERSION_MINOR 1) # cmake -DENABLE_BUILD_IN_CPP_11_CUSTOM_ENVIRONMENT=ON .. if (ENABLE_BUILD_IN_CPP_11_CUSTOM_ENVIRONMENT) - # We use custom compiler too - set(CMAKE_C_COMPILER "/opt/gcc520/bin/gcc") - set(CMAKE_CXX_COMPILER "/opt/gcc520/bin/g++") + # Set blank sysroot + #set(CMAKE_SYSROOT "/opt/glibc_2.22") + + set(MY_LINK_DIRECTORIES "/opt/glibc_2.22/lib;/opt/gcc520/lib64;/opt/boost_1_58_0/stage/lib;/opt/libhiredis_0_13/lib;/opt/log4cpp1.1.1/lib;/opt/luajit_2.0.4/lib;/opt/ndpi/lib;/opt/pf_ring/lib;/opt/json-c-0.12/lib") + set(MY_INCLUDE_DIRECTORIES "/opt/glibc_2.22/include") + + # TODO: onlt temp code + include_directories("/usr/include/x86_64-linux-gnu") + include_directories("/usr/include") + #include_directories("/usr/src/linux-headers-3.16.0-4-common/include/uapi") + + # Remove all standard path's for C and C++ compilers + set(CMAKE_CXX_IMPLICIT_LINK_DIRECTORIES "") + set(CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES "") + set(CMAKE_CXX_IMPLICIT_LINK_LIBRARIES "") + + set(CMAKE_C_IMPLICIT_LINK_DIRECTORIES "") + set(CMAKE_C_IMPLICIT_INCLUDE_DIRECTORIES "") + set(CMAKE_C_IMPLICIT_LINK_LIBRARIES "") + + # Remove all default paths for platform + set(CMAKE_PLATFORM_IMPLICIT_LINK_DIRECTORIES "") + set(CMAKE_SYSTEM_INCLUDE_PATH "${MY_INCLUDE_DIRECTORIES}") + + set(CMAKE_C_IMPLICIT_LINK_DIRECTORIES "${MY_LINK_DIRECTORIES}") + set(CMAKE_C_IMPLICIT_INCLUDE_DIRECTORIES "${MY_LINK_DIRECTORIES}") + set(CMAKE_C_IMPLICIT_LINK_LIBRARIES "c") + + # Specify path's to custom compiled gcc and glibc + set(CMAKE_CXX_IMPLICIT_LINK_LIBRARIES "stdc++;gcc;gcc_s;m;c") + set(CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES "${MY_LINK_DIRECTORIES}") + set(CMAKE_CXX_IMPLICIT_LINK_DIRECTORIES "${MY_LINK_DIRECTORIES}") set(BOOST_INCLUDEDIR "/opt/boost_1_58_0") + include_directories("${BOOST_INCLUDEDIR}") + set(BOOST_LIBRARYDIR "/opt/boost_1_58_0/stage/lib/") # It's really nice part of this custom build process :) @@ -32,6 +70,9 @@ if (ENABLE_BUILD_IN_CPP_11_CUSTOM_ENVIRONMENT) # Disable warning from Boost when compiling with gcc 5.2 set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -Wno-deprecated-declarations") + + # Specify custom ld-linux dynamic linker path + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -Wl,--dynamic-linker=/opt/glibc_2.22/lib/ld-linux-x86-64.so.2") # Specify full RPATH for build tree SET(CMAKE_SKIP_BUILD_RPATH FALSE) @@ -39,7 +80,7 @@ if (ENABLE_BUILD_IN_CPP_11_CUSTOM_ENVIRONMENT) # Create builds in current folder with install RPATH SET(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) - SET(CMAKE_INSTALL_RPATH "/opt/gcc520/lib64;/opt/boost_1_58_0/stage/lib;/opt/libhiredis_0_13/lib;/opt/log4cpp1.1.1/lib;/opt/luajit_2.0.4/lib;/opt/ndpi/lib;/opt/pf_ring/lib;/opt/json-c-0.12/lib") + SET(CMAKE_INSTALL_RPATH "${MY_LINK_DIRECTORIES}") endif() # It's pretty safe and provide big speedup for our packet processor and patricia code @@ -169,7 +210,15 @@ endif() add_library(pcap_plugin STATIC pcap_plugin/pcap_collector.cpp) target_link_libraries(pcap_plugin pcap) -find_package(Threads) +#find_package(Threads) + +#if (Threads_FOUND) +# message(STATUS "We found threads library") +#else() +# message(FATAL_ERROR "We can't find threads library") +#endif() +# TODO: fix this hack +set(CMAKE_THREAD_LIBS_INIT "-lpthread") if (ENABLE_PFRING_SUPPORT) add_library(pfring_plugin STATIC pfring_plugin/pfring_collector.cpp) @@ -247,7 +296,7 @@ if (LOG4CPP_INCLUDES_FOLDER AND LOG4CPP_LIBRARY_PATH) include_directories(${LOG4CPP_INCLUDES_FOLDER}) message(STATUS "We have found log4cpp and will build project") else() - message(STATUS "We can't find log4cpp. We can't build project") + message(FATAL_ERROR "We can't find log4cpp. We can't build project") endif() ### Look for jsonc @@ -259,7 +308,7 @@ if (JSONC_INCLUDES_FOLDER AND JSONC_LIBRARY_PATH) include_directories(${JSONC_INCLUDES_FOLDER}) message(STATUS "We have found json-c library correctly: ${JSONC_LIBRARY_PATH}") else() - message(STATUS "We can't find json-c library! Can't build project") + message(FATAL_ERROR "We can't find json-c library! Can't build project") endif() target_link_libraries(fast_library ${JSONC_LIBRARY_PATH}) diff --git a/src/tests/patch_for_custom_libc.patch b/src/tests/patch_for_custom_libc.patch index 6426400..c7ea4ff 100644 --- a/src/tests/patch_for_custom_libc.patch +++ b/src/tests/patch_for_custom_libc.patch @@ -1,46 +0,0 @@ -diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt -index a65734b..406a5b9 100644 ---- a/src/CMakeLists.txt -+++ b/src/CMakeLists.txt -@@ -27,11 +27,32 @@ if (ENABLE_BUILD_IN_CPP_11_CUSTOM_ENVIRONMENT) - set(BOOST_INCLUDEDIR "/opt/boost_1_58_0") - set(BOOST_LIBRARYDIR "/opt/boost_1_58_0/stage/lib/") - -+ # Remove all system directories with default libraries -+ message(STATUS "CMAKE_CXX_IMPLICIT_LINK_DIRECTORIES=${CMAKE_CXX_IMPLICIT_LINK_DIRECTORIES}") -+ set(CMAKE_CXX_IMPLICIT_LINK_DIRECTORIES "") -+ -+ message(STATUS "CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES = ${CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES}") -+ set(CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES "/opt/glibc_2.22/include") -+ -+ set(CMAKE_CXX_IMPLICIT_LINK_DIRECTORIES "/opt/glibc_2.22/lib;/opt/gcc520/lib64;/opt/glibc_2.22/lib;/opt/gcc520/lib/gcc/x86_64-unknown-linux-gnu/5.2.0") -+ include_directories("/opt/glibc_2.22/include") -+ -+ message(STATUS "CMAKE_CXX_IMPLICIT_LINK_LIBRARIES=${CMAKE_CXX_IMPLICIT_LINK_LIBRARIES}") -+ set(CMAKE_CXX_IMPLICIT_LINK_LIBRARIES "") -+ set(CMAKE_CXX_IMPLICIT_LINK_LIBRARIES "stdc++;gcc;gcc_s;m;c") -+ -+ message(STATUS "CMAKE_CXX_IMPLICIT_LINK_LIBRARIES=${CMAKE_CXX_IMPLICIT_LINK_LIBRARIES}") -+ - # It's really nice part of this custom build process :) - set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -std=c++11") - - # Disable warning from Boost when compiling with gcc 5.2 - set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -Wno-deprecated-declarations") -+ -+ # Pass custom ld-linux for our own binary -+ set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -Wl,--dynamic-linker=/opt/glibc_2.22/lib/ld-linux-x86-64.so.2") -+ -+ set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -nodefaultlibs -nostdinc -nostdinc++") - - # Specify full RPATH for build tree - SET(CMAKE_SKIP_BUILD_RPATH FALSE) -@@ -39,7 +60,7 @@ if (ENABLE_BUILD_IN_CPP_11_CUSTOM_ENVIRONMENT) - # Create builds in current folder with install RPATH - SET(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) - -- SET(CMAKE_INSTALL_RPATH "/opt/gcc520/lib64;/opt/boost_1_58_0/stage/lib;/opt/libhiredis_0_13/lib;/opt/log4cpp1.1.1/lib;/opt/luajit_2.0.4/lib;/opt/ndpi/lib;/opt/pf_ring/lib;/opt/json-c-0.12/lib") -+ SET(CMAKE_INSTALL_RPATH "/opt/glibc_2.22/lib;/opt/gcc520/lib64;/opt/boost_1_58_0/stage/lib;/opt/libhiredis_0_13/lib;/opt/log4cpp1.1.1/lib;/opt/luajit_2.0.4/lib;/opt/ndpi/lib;/opt/pf_ring/lib;/opt/json-c-0.12/lib") - endif() - - # It's pretty safe and provide big speedup for our packet processor and patricia code fastnetmon-1.1.4/src/tests/patch_for_libcuckoo_as_flow_tracking_structure.patch000066400000000000000000000054221343111404700303610ustar00rootroot00000000000000diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index dfbe8c1..a84d696 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -10,6 +10,10 @@ project(FastNetMon) # Enable it and fix all warnigns! # add_definitions ("-Wall") +include_directories("/opt/libcuckoo/include/") +# We need C++11 support for libcuckoo +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") + set (Tutorial_VERSION_MAJOR 1) set (Tutorial_VERSION_MINOR 1) @@ -189,5 +193,7 @@ target_link_libraries(fastnetmon pcap_plugin) target_link_libraries(fastnetmon example_plugin) target_link_libraries(fastnetmon netmap_plugin) +target_link_libraries(fastnetmon /opt/libcuckoo/lib/libcityhash.so) + install(TARGETS fastnetmon DESTINATION bin) install(TARGETS fastnetmon_client DESTINATION bin) diff --git a/src/fastnetmon.cpp b/src/fastnetmon.cpp index 5308244..cd9e9dc 100644 --- a/src/fastnetmon.cpp +++ b/src/fastnetmon.cpp @@ -268,6 +268,8 @@ void process_packet(simple_packet& current_packet); void traffic_draw_program(); void interruption_signal_handler(int signal_number); +cuckoohash_map > flow_tracking_table_new_generation; + /* Class for custom comparison fields by different fields */ class TrafficComparatorClass { private: @@ -1029,6 +1031,24 @@ void process_packet(simple_packet& current_packet) { } } + + if (packet_direction == OUTGOING or packet_direction == INCOMING) { + std::string connection_tracking_hash_string = convert_ip_as_uint_to_string(current_packet.dst_ip) + "_" + convert_ip_as_uint_to_string(current_packet.src_ip) + "_" + + convert_ip_as_uint_to_string(current_packet.source_port) + "_" + convert_ip_as_uint_to_string(current_packet.destination_port) + "_" + convert_ip_as_uint_to_string(current_packet.protocol) + "_"; + get_direction_name(packet_direction); + + map_element temp_element; + + if (flow_tracking_table_new_generation.find(connection_tracking_hash_string, temp_element)) { + // found! + + } else { + // not found, create it + flow_tracking_table_new_generation.insert(connection_tracking_hash_string, temp_element); + } + } + + /* Because we support mirroring, sflow and netflow we should support different cases: - One packet passed for processing (mirror) - Multiple packets ("flows") passed for processing (netflow) diff --git a/src/fastnetmon_types.h b/src/fastnetmon_types.h index 8263645..6e098ca 100644 --- a/src/fastnetmon_types.h +++ b/src/fastnetmon_types.h @@ -5,6 +5,9 @@ #include // uint32_t #include // struct timeval +#include +#include + #include #include fastnetmon-1.1.4/src/tests/patch_for_switching_from_std_map_to_sorted_vector.patch000066400000000000000000000152431343111404700310720ustar00rootroot00000000000000diff --git a/src/fastnetmon.cpp b/src/fastnetmon.cpp index 5308244..2a67506 100644 --- a/src/fastnetmon.cpp +++ b/src/fastnetmon.cpp @@ -191,6 +191,12 @@ map_of_vector_counters SubnetVectorMap; // Flow tracking structures map_of_vector_counters_for_flow SubnetVectorMapFlow; +typedef std::vector lookup_vector_indexes_t; +typedef std::vector lookup_vector_data_t; + +lookup_vector_indexes_t lookup_vector_indexes; +lookup_vector_data_t lookup_vector_data; + /* End of our data structs */ boost::mutex data_counters_mutex; @@ -969,12 +975,22 @@ bool load_our_networks_list() { /* Preallocate data structures */ - patricia_process (lookup_tree, (void_fn_t)subnet_vectors_allocator); - logger<first); + lookup_vector_data.push_back(ii->second); + } + logger<= itr->second.size()) { + if (shift_in_vector < 0 or shift_in_vector >= itr->size()) { logger<< log4cpp::Priority::ERROR<<"We tried to access to element with index "<second.size(); + <<" which located outside allocated vector with size "<size(); logger<< log4cpp::Priority::ERROR<<"We expect issues with this packet in OUTGOING direction: "<second[shift_in_vector]; + map_element* current_element = &(*itr)[shift_in_vector]; // Main packet/bytes counter __sync_fetch_and_add(¤t_element->out_packets, sampled_number_of_packets); @@ -1135,16 +1154,16 @@ void process_packet(simple_packet& current_packet) { } else if (packet_direction == INCOMING) { int64_t shift_in_vector = (int64_t)ntohl(current_packet.dst_ip) - (int64_t)subnet_in_host_byte_order; - if (shift_in_vector < 0 or shift_in_vector >= itr->second.size()) { + if (shift_in_vector < 0 or shift_in_vector >= itr->size()) { logger<< log4cpp::Priority::ERROR<<"We tried to access to element with index "<second.size(); + <<" which located outside allocated vector with size "<size(); logger<< log4cpp::Priority::INFO<<"We expect issues with this packet in INCOMING direction: "<second[shift_in_vector]; + map_element* current_element = &(*itr)[shift_in_vector]; // Main packet/bytes counter __sync_fetch_and_add(¤t_element->in_packets, sampled_number_of_packets); @@ -1307,15 +1326,17 @@ void recalculate_speed() { uint64_t incoming_total_flows = 0; uint64_t outgoing_total_flows = 0; - for (map_of_vector_counters::iterator itr = SubnetVectorMap.begin(); itr != SubnetVectorMap.end(); ++itr) { - for (vector_of_counters::iterator vector_itr = itr->second.begin(); vector_itr != itr->second.end(); ++vector_itr) { - int current_index = vector_itr - itr->second.begin(); + for (lookup_vector_indexes_t::iterator itr = lookup_vector_indexes.begin(); itr != lookup_vector_indexes.end(); ++itr) { + vector_of_counters* vector_pointer = &lookup_vector_data[ itr - lookup_vector_indexes.begin() ]; + + for (vector_of_counters::iterator vector_itr = vector_pointer->begin(); vector_itr != vector_pointer->end(); ++vector_itr) { + int current_index = vector_itr - vector_pointer->begin(); // New element map_element new_speed_element; // convert to host order for math operations - uint32_t subnet_ip = ntohl(itr->first); + uint32_t subnet_ip = ntohl(*itr); uint32_t client_ip_in_host_bytes_order = subnet_ip + current_index; // covnert to our standard network byte order @@ -1364,7 +1385,7 @@ void recalculate_speed() { new_speed_element.icmp_in_bytes = uint64_t((double)vector_itr->icmp_in_bytes / speed_calc_period); new_speed_element.icmp_out_bytes = uint64_t((double)vector_itr->icmp_out_bytes / speed_calc_period); - conntrack_main_struct* flow_counter_ptr = &SubnetVectorMapFlow[itr->first][current_index]; + conntrack_main_struct* flow_counter_ptr = &SubnetVectorMapFlow[*itr][current_index]; // todo: optimize this operations! uint64_t total_out_flows = fastnetmon-1.1.4/src/tests/patricia_performance_tests.c000066400000000000000000000051111343111404700233630ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include "../libpatricia/patricia.h" /* How to compile: gcc ../libpatricia/patricia.c -c -opatricia.o gcc patricia_performance_tests.c patricia.o -o patricia_performance_test */ int main() { patricia_tree_t* lookup_tree; lookup_tree = New_Patricia(32); make_and_lookup(lookup_tree, "46.36.216.0/21"); make_and_lookup(lookup_tree, "159.253.16.0/21"); make_and_lookup(lookup_tree, "5.45.112.0/21"); make_and_lookup(lookup_tree, "5.45.120.0/21"); make_and_lookup(lookup_tree, "5.101.112.0/21"); make_and_lookup(lookup_tree, "5.101.120.0/21"); make_and_lookup(lookup_tree, "185.4.72.0/22"); make_and_lookup(lookup_tree, "181.114.240.0/20"); make_and_lookup(lookup_tree, "193.42.142.0/24"); prefix_t prefix_for_check_adreess; prefix_for_check_adreess.family = AF_INET; prefix_for_check_adreess.bitlen = 32; patricia_node_t* found_patrica_node = NULL; // prefix_for_check_adreess.add.sin.s_addr = 123123123; // std::map lpm_cache; // Without cache: 16.7 million of operations int i_iter = 100; // Million operations int j_iter = 1000000; // printf("Preallocate table\n"); // Iterate over all our IP addresses // for (int j = 0; j < j_iter; j++) { // for (int i = 0; i < i_iter; i++) { // lpm_cache[i*j] = true; // } //} printf("Start tests\n"); struct timespec start_time; clock_gettime(CLOCK_REALTIME, &start_time); int i, j; for (j = 0; j < j_iter; j++) { for (i = 0; i < i_iter; i++) { // Random Pseudo IP prefix_for_check_adreess.add.sin.s_addr = i*j; patricia_node_t* found_patrica_node = patricia_search_best2(lookup_tree, &prefix_for_check_adreess, 1); unsigned long destination_subnet = 0; if (found_patrica_node != NULL) { // printf("Found\n"); } } } struct timespec finish_time; clock_gettime(CLOCK_REALTIME, &finish_time); unsigned long used_seconds = finish_time.tv_sec - start_time.tv_sec; unsigned long total_ops = i_iter * j_iter; float megaops_per_second = (float)total_ops / (float)used_seconds / 1000000; printf("Total time is %d seconds total ops: %d\nMillion of ops per second: %.1f\n", used_seconds, total_ops, megaops_per_second); Destroy_Patricia(lookup_tree, (void_fn_t)0); } fastnetmon-1.1.4/src/tests/pcap_writer.cpp000066400000000000000000000030331343111404700206440ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include "../fastnetmon_pcap_format.h" #include "../packet_storage.h" int main() { packet_storage_t packet_storage; // We specify in in packets if (!packet_storage.allocate_buffer(500)) { printf("Can't allocate buffer"); return -1; } unsigned char payload1[] = { 0x90,0xE2,0xBA,0x83,0x3F,0x25,0x90,0xE2,0xBA,0x2C,0xCB,0x02,0x08,0x00,0x45,0x00,0x00,0x2E,0x00,0x00,0x00,0x00,0x40,0x06,0x69,0xDC,0x0A,0x84,0xF1,0x83,0x0A,0x0A,0x0A,0xDD,0x04,0x01,0x00,0x50,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x50,0x02,0x00,0x0A,0x9A,0x92,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }; if (!packet_storage.write_packet(payload1, sizeof(payload1))) { printf("Can't write packet to the storage\n"); return -1; } // Dump buffer to memory std::string pcap_file_path = "/tmp/fastnetmon_example.pcap"; int filedesc = open(pcap_file_path.c_str(), O_WRONLY|O_CREAT); if (filedesc <= 0) { printf("Can't open dump file for writing"); return -1; } std::cout << "Used size: " << packet_storage.get_used_memory() << std::endl; ssize_t wrote_bytes = write(filedesc, (void*)packet_storage.get_buffer_pointer(), packet_storage.get_used_memory()); if (wrote_bytes != packet_storage.get_used_memory()) { printf("Can't write data to the file\n"); return -1; } close(filedesc); return(0); } fastnetmon-1.1.4/src/tests/pfring_parser_zc_issue.c000066400000000000000000000047411343111404700225410ustar00rootroot00000000000000#include "pfring.h" #include /* How to compile me: g++ pfring_parser_zc_issue.c -I/opt/pf_ring/include -L/opt/pf_ring/lib/ -lpfring -lnuma */ void parse_packet_pf_ring(const struct pfring_pkthdr* h, const u_char* p, const u_char* user_bytes) { memset((void*)&h->extended_hdr.parsed_pkt, 0, sizeof(h->extended_hdr.parsed_pkt)); pfring_parse_pkt((u_char*)p, (struct pfring_pkthdr*)h, 4, 1, 0); char buffer[512]; pfring_print_parsed_pkt(buffer, 512, p, h); std::cout << buffer; } int main() { char* dev = "zc:eth3"; // We could pool device in multiple threads unsigned int num_threads = 1; bool promisc = true; /* This flag manages packet parser for extended_hdr */ bool use_extended_pkt_header = true; bool enable_hw_timestamp = false; bool dont_strip_timestamps = false; u_int32_t flags = 0; if (num_threads > 1) flags |= PF_RING_REENTRANT; if (use_extended_pkt_header) flags |= PF_RING_LONG_HEADER; if (promisc) flags |= PF_RING_PROMISC; if (enable_hw_timestamp) flags |= PF_RING_HW_TIMESTAMP; if (!dont_strip_timestamps) flags |= PF_RING_STRIP_HW_TIMESTAMP; // if (!we_use_pf_ring_in_kernel_parser) { // flags != PF_RING_DO_NOT_PARSE; //} // flags |= PF_RING_DNA_SYMMETRIC_RSS; /* Note that symmetric RSS is ignored by non-DNA drivers // */ // use default value from pfcount.c unsigned int snaplen = 128; pfring* pf_ring_descr = pfring_open(dev, snaplen, flags); if (pf_ring_descr == NULL) { std::cout << "pfring_open error: " << strerror(errno) << " (pf_ring not loaded or perhaps you use quick mode and have already a socket bound to: " << dev << ")"; return false; } u_int32_t version; // Set spplication name in /proc int pfring_set_application_name_result = pfring_set_application_name(pf_ring_descr, (char*)"fastnetmon"); if (pfring_set_application_name_result != 0) { std::cout << "Can't set program name for PF_RING: pfring_set_application_name"; } pfring_version(pf_ring_descr, &version); int pfring_set_socket_mode_result = pfring_set_socket_mode(pf_ring_descr, recv_only_mode); // enable ring if (pfring_enable_ring(pf_ring_descr) != 0) { std::cout << "Unable to enable ring :-("; pfring_close(pf_ring_descr); return false; } u_int8_t wait_for_packet = 1; pfring_loop(pf_ring_descr, parse_packet_pf_ring, (u_char*)NULL, wait_for_packet); } fastnetmon-1.1.4/src/tests/promisc.cpp000066400000000000000000000033451343111404700200070ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include // This code compiles on FreeBSD and Linux but did not work on FreeBSD /* Promisc management on FreeBSD is real nighmare, really. Issues with: ifr_flagshigh and IFF_PPROMISC vs IFF_PROMISC */ /* Good code examples here: https://github.com/fichtner/netmap/blob/master/extra/libpcap-netmap.diff */ int main() { // We need really any socket for ioctl int fd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); if (!fd) { printf("Can't create socket\n"); exit(1); } struct ifreq ethreq; memset(ðreq, 0, sizeof(ethreq)); strncpy(ethreq.ifr_name, "eth6", IFNAMSIZ); int ioctl_res = ioctl(fd, SIOCGIFFLAGS, ðreq); if (ioctl_res == -1) { printf("Can't get interface flags"); exit(1); } if (ethreq.ifr_flags & IFF_PROMISC) { printf("Interface in promisc mode already\n"); printf("Switch it off\n"); ethreq.ifr_flags &= ~IFF_PROMISC; int ioctl_res_set = ioctl(fd, SIOCSIFFLAGS, ðreq); if (ioctl_res_set == -1) { printf("Can't set interface flags"); exit(1); } printf("promisc mode disabled correctly\n"); } else { printf("Interface in non promisc mode now, switch it on\n"); ethreq.ifr_flags |= IFF_PROMISC; int ioctl_res_set = ioctl(fd, SIOCSIFFLAGS, ðreq); if (ioctl_res_set == -1) { printf("Can't set interface flags"); exit(1); } printf("promisc mode enabled\n"); } close(fd); } fastnetmon-1.1.4/src/tests/py_counters_performance_test.py000077500000000000000000000010071343111404700241670ustar00rootroot00000000000000#!/usr/bin/python import time my_dict = {} # Intel(R) Core(TM) i7-3635QM CPU @ 2.40GHz: 4.8 MOPS # Intel(R) Core(TM) i7-3820 CPU @ 3.60GHz: python 2.7 7.0 MOPS # Intel(R) Core(TM) i7-3820 CPU @ 3.60GHz: python 2.7/pypy: 8.6 MOPS iterations = 10**6*14 start = time.time() # Emulate 14.6 mpps for index in range(0, iterations): my_dict[index] = { "udp": 0 } stop = time.time() interval = stop - start print iterations / interval / 10**6, "millions of iterations per second" fastnetmon-1.1.4/src/tests/snabb/000077500000000000000000000000001343111404700167075ustar00rootroot00000000000000fastnetmon-1.1.4/src/tests/snabb/README.md000066400000000000000000000052751343111404700201770ustar00rootroot00000000000000### Here we store all code related with Snabb switch intergration :) We like it because it's awesome! First of all, please compile Snabb Switch from next branch: ```bash cd /usr/src/ git clone https://github.com/SnabbCo/snabbswitch.git -b next cd snabbswitch make ``` Then compile our .so library: ``` g++ -O3 ../../fastnetmon_packet_parser.c -c -o fastnetmon_packet_parser.o -fPIC g++ -O3 -shared -o capturecallback.so -fPIC capturecallback.cpp fastnetmon_packet_parser.o ``` Polly enabled clang compilation (offer significant speedup): ```bash alias pollycc="/usr/src/polly/llvm_build/bin/clang -Xclang -load -Xclang /usr/src/polly/llvm_build/lib/LLVMPolly.so" pollycc -O3 ../../fastnetmon_packet_parser.c -c -o fastnetmon_packet_parser.o -fPIC pollycc -O3 -shared -o capturecallback.so -fPIC capturecallback.c fastnetmon_packet_parser.o ``` Get NIC's PCI address: ```bash lspci -m|grep 82599 00:05.0 "Ethernet controller" "Intel Corporation" "82599ES 10-Gigabit SFI/SFP+ Network Connection" -r01 "Intel Corporation" "Ethernet Server Adapter X520-2" 00:06.0 "Ethernet controller" "Intel Corporation" "82599ES 10-Gigabit SFI/SFP+ Network Connection" -r01 "Intel Corporation" "Ethernet Server Adapter X520-2" ``` For example, we will use 00:06.0, we need convert this value to Snabb's format with adding 4 leading zeroes: 0000:00:06.0 Run capture: ```bash /usr/src/snabbswitch/src/snabb firehose --input 0000:03:00.0 --input 0000:03:00.1 /usr/src/fastnetmon/src/tests/snabb/capturecallback.so ``` I have got really amazing results: ```bash Loading shared object: ./capturecallback.so Initializing NIC: 0000:00:06.0 Run speed printer from C code Processing traffic... We process: 14566196 pps We process: 14820487 pps We process: 14856881 pps We process: 14863727 pps ``` We achieved this with only single logical core of i7 3820 CPU: ```bash 1 [ 0.0%] 5 [ 0.0%] 2 [ 0.0%] 6 [ 0.0%] 3 [ 0.0%] 7 [|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||100.0%] 4 [ 0.0%] 8 [ 0.0%] ``` Really awesome! Huge thanks to Luke Gorrie for great help with it! Once the testing is done we can rebind the NIC to the kernel `ixgbe` driver. This can be done either be reloading `ixgbe` or, less disruptively, like this: ```bash echo 0000:00:06.0 | sudo tee /sys/bus/pci/drivers/ixgbe/bind ``` fastnetmon-1.1.4/src/tests/snabb/build_ndpi.sh000077500000000000000000000011341343111404700213560ustar00rootroot00000000000000#!/bin/bash # apt-get install -y libhiredis-dev redis-server g++ -O3 ../../fastnetmon_packet_parser.c -c -o fastnetmon_packet_parser.o -fPIC g++ -O3 ../../fastnetmon_pcap_format.cpp -c -o fastnetmon_pcap_format.o -fPIC g++ -O3 ../../fast_dpi.cpp -c -o fast_dpi.o `PKG_CONFIG_PATH=/opt/ndpi/lib/pkgconfig pkg-config pkg-config --cflags --libs libndpi` -fPIC g++ -O3 -shared -o ndpicallback.so -fPIC ndpicallback.cpp fastnetmon_pcap_format.o fast_dpi.o fastnetmon_packet_parser.o `PKG_CONFIG_PATH=/opt/ndpi/lib/pkgconfig pkg-config pkg-config --cflags --libs libndpi` -std=c++11 -fPIC -lhiredis fastnetmon-1.1.4/src/tests/snabb/capturecallback.cpp000066400000000000000000000132751343111404700225430ustar00rootroot00000000000000// compile with: gcc -shared -o capturecallback.so -fPIC capturecallback.c #include #include #include #include #include #include #include #include #include "../../fastnetmon_packet_parser.h" double system_tsc_resolution_hz = 0; #ifdef __cplusplus extern "C" { #endif inline uint64_t rte_rdtsc(void) { union { uint64_t tsc_64; struct { uint32_t lo_32; uint32_t hi_32; }; } tsc; asm volatile("rdtsc" : "=a" (tsc.lo_32), "=d" (tsc.hi_32)); return tsc.tsc_64; } void set_tsc_freq_fallback() { uint64_t start = rte_rdtsc(); sleep(1); system_tsc_resolution_hz = (double)rte_rdtsc() - start; } #ifdef __cplusplus } #endif // C++ rewrite of Python code: https://gist.github.com/pavel-odintsov/652904287ca0ca6816f6 class token_bucket_t { public: token_bucket_t() { this->tokens = 0; this->rate = 0; this->burst = 0; this->last_timestamp = 0; } int64_t get_rate() { return this->rate; } int64_t get_burst() { return this->burst; } int64_t get_tokens() { return this->tokens; } bool set_rate(int64_t rate, int64_t burst) { this->rate = rate; this->tokens = burst; this->burst = burst; // Start counter! this->last_timestamp = (double)rte_rdtsc() / system_tsc_resolution_hz; } int64_t consume(int64_t consumed_tokens) { double current_time = (double)rte_rdtsc() / system_tsc_resolution_hz; double interval = (current_time - this->last_timestamp); if (interval < 0) { printf("Your TSC is buggy, we have last %llu and current time: %llu\n", this->last_timestamp, current_time); } this->last_timestamp = current_time; this->tokens = std::max( (double)0, std::min( double(this->tokens + this->rate * interval), double(this->burst) ) ) - 1; return this->tokens; } private: int64_t rate; int64_t tokens; int64_t burst; double last_timestamp; }; token_bucket_t global_token_bucket_counter; #ifdef __cplusplus extern "C" { #endif /* Called once before processing packets. */ void firehose_start(); /* optional */ /* Called once after processing packets. */ void firehose_stop(); /* optional */ void firehose_stop() { } /* * Process a packet received from a NIC. * * pciaddr: name of PCI device packet is received from * data: packet payload (ethernet frame) * length: payload length in bytes */ inline void firehose_packet(const char *pciaddr, char *data, int length); /* Intel 82599 "Legacy" receive descriptor format. * See Intel 82599 data sheet section 7.1.5. * http://www.intel.com/content/dam/www/public/us/en/documents/datasheets/82599-10-gbe-controller-datasheet.pdf */ struct firehose_rdesc { uint64_t address; uint16_t length; uint16_t cksum; uint8_t status; uint8_t errors; uint16_t vlan; } __attribute__((packed)); /* Traverse the hardware receive descriptor ring. * Process each packet that is ready. * Return the updated ring index. */ int firehose_callback_v1(const char *pciaddr, char **packets, struct firehose_rdesc *rxring, int ring_size, int index) { while (rxring[index].status & 1) { int next_index = (index + 1) & (ring_size-1); __builtin_prefetch(packets[next_index]); firehose_packet(pciaddr, packets[index], rxring[index].length); rxring[index].status = 0; /* reset descriptor for reuse */ index = next_index; } return index; } uint64_t received_packets = 0; void* speed_printer(void* ptr) { while (1) { uint64_t packets_before = received_packets; sleep(1); uint64_t packets_after = received_packets; uint64_t pps = packets_after - packets_before; printf("We process: %llu pps tokens %lld rate %lld burst %lld \n", (long long)pps, global_token_bucket_counter.get_tokens(), global_token_bucket_counter.get_rate(), global_token_bucket_counter.get_burst() ); } } void sigproc(int sig) { firehose_stop(); printf("We caught SINGINT and will finish application\n"); exit(0); } // We will start speed printer void firehose_start() { signal(SIGINT, sigproc); set_tsc_freq_fallback(); global_token_bucket_counter.set_rate(10000000, 15000000); //printf("tsq hz is: %f\n", system_tsc_resolution_hz); pthread_t thread; pthread_create(&thread, NULL, speed_printer, NULL); pthread_detach(thread); } void firehose_packet(const char *pciaddr, char *data, int length) { // Put packet to the cache /* struct pfring_pkthdr packet_header; memset(&packet_header, 0, sizeof(packet_header)); packet_header.len = length; packet_header.caplen = length; fastnetmon_parse_pkt((u_char*)data, &packet_header, 3, 0, 0); */ /* char print_buffer[512]; fastnetmon_print_parsed_pkt(print_buffer, 512, (u_char*)data, &packet_header); printf("packet: %s\n", print_buffer); */ int64_t consume_result = global_token_bucket_counter.consume(1); if (consume_result < 0) { printf("Overflow!\n"); } __sync_fetch_and_add(&received_packets, 1); //printf("Got packet with %d bytes.\n", length); } #ifdef __cplusplus } #endif fastnetmon-1.1.4/src/tests/snabb/capturetodisk.cpp000066400000000000000000000161511343111404700223000ustar00rootroot00000000000000// Author: Pavel.Odintsov@gmail.com // License GPLv2 #include #include #include #include #include #include #include #include #include #include #include #include #include "../../fastnetmon_pcap_format.h" /* Compile it: g++ -O3 -fPIC -std=c++11 ../../fastnetmon_pcap_format.cpp -c -o fastnetmon_pcap_format.o g++ -O3 -shared -o capturetodisk.so -fPIC capturetodisk.cpp fastnetmon_pcap_format.o -std=c++11 -lboost_system Run it: /usr/src/snabbswitch/src/snabb firehose --input 0000:02:00.0 --input 0000:02:00.1 /usr/src/fastnetmon/src/tests/snabb/capturetodisk.so Please use ext4 with writeback feature: mount -odata=writeback /dev/sdb /mnt */ class packet_buffer_t { public: unsigned char buffer[1600]; unsigned int length; }; typedef std::shared_ptr packet_buffer_shared_pointer_t; constexpr auto size_spsc_queue = 1048576; // We use persistent preallocation for ext4 and allocate 20 GB for storing data before any operations uint64_t preallocate_packet_dump_file_size = 1073741824ul * 20ul; typedef boost::lockfree::spsc_queue< packet_buffer_shared_pointer_t, boost::lockfree::capacity > my_spsc_queue_t; int pcap_file = 0; my_spsc_queue_t my_spsc_queue; #ifdef __cplusplus extern "C" { #endif /* Called once before processing packets. */ void firehose_start(); /* optional */ #ifdef __cplusplus } #endif /* Called once after processing packets. */ void firehose_stop(); /* optional */ void firehose_stop() { // Close file and flush data close(pcap_file); } /* * Process a packet received from a NIC. * * pciaddr: name of PCI device packet is received from * data: packet payload (ethernet frame) * length: payload length in bytes */ inline void firehose_packet(const char *pciaddr, char *data, int length); /* Intel 82599 "Legacy" receive descriptor format. * See Intel 82599 data sheet section 7.1.5. * http://www.intel.com/content/dam/www/public/us/en/documents/datasheets/82599-10-gbe-controller-datasheet.pdf */ struct firehose_rdesc { uint64_t address; uint16_t length; uint16_t cksum; uint8_t status; uint8_t errors; uint16_t vlan; } __attribute__((packed)); /* Traverse the hardware receive descriptor ring. * Process each packet that is ready. * Return the updated ring index. */ #ifdef __cplusplus extern "C" { #endif int firehose_callback_v1(const char *pciaddr, char **packets, struct firehose_rdesc *rxring, int ring_size, int index) { while (rxring[index].status & 1) { int next_index = (index + 1) & (ring_size-1); __builtin_prefetch(packets[next_index]); firehose_packet(pciaddr, packets[index], rxring[index].length); rxring[index].status = 0; /* reset descriptor for reuse */ index = next_index; } return index; } #ifdef __cplusplus } #endif uint64_t received_packets = 0; uint64_t received_bytes = 0; void* speed_printer(void* ptr) { while (1) { uint64_t packets_before = received_packets; uint64_t bytes_before = received_bytes; sleep(1); uint64_t packets_after = received_packets; uint64_t bytes_after = received_bytes; uint64_t pps = packets_after - packets_before; uint64_t bps = bytes_after - bytes_before; float gbps_speed = (float)bps/1024/1024/1024 * 8; float gb_total = (float)received_bytes/1024/1024/1024; printf("We process: %llu pps %.2f Gbps. We will store %.2f megabytes per second. We have stored %.2f GB of data\n", (long long)pps, gbps_speed, gbps_speed / 8 * 1024, gb_total); } } void write_packet_to_file(packet_buffer_shared_pointer_t packet) { struct timeval current_time; current_time.tv_sec = 0; current_time.tv_usec = 0; // It's performance killer! bool we_do_timestamps = false; if (we_do_timestamps) { gettimeofday(¤t_time, NULL); } struct fastnetmon_pcap_pkthdr pcap_packet_header; pcap_packet_header.ts_sec = current_time.tv_sec; pcap_packet_header.ts_usec = current_time.tv_usec; pcap_packet_header.incl_len = packet->length; pcap_packet_header.orig_len = packet->length; unsigned int packet_header_written_bytes = write(pcap_file, &pcap_packet_header, sizeof(pcap_packet_header)); if (packet_header_written_bytes != sizeof(pcap_packet_header)) { printf("Can't write pcap pcaket header\n"); } unsigned int packet_written_bytes = write(pcap_file, packet->buffer, packet->length); if (packet_written_bytes != packet->length) { printf("Can't write data to file\n"); } } void* packets_consumer(void* ptr) { printf("Start consumer thread\n"); packet_buffer_shared_pointer_t packet; while (true) { while (my_spsc_queue.pop(packet)) { write_packet_to_file(packet); __sync_fetch_and_add(&received_packets, 1); __sync_fetch_and_add(&received_bytes, packet->length); } } } void sigproc(int sig) { firehose_stop(); printf("We caught SINGINT and will finish application\n"); exit(0); } #ifdef __cplusplus extern "C" { #endif // We will start speed printer void firehose_start() { signal(SIGINT, sigproc); pcap_file = open("/mnt/traffic_capture.pcap", O_TRUNC|O_WRONLY|O_CREAT); if (pcap_file < 0) { printf("Can't open file for capture\n"); exit(-1); } printf("Preaallocate %llu bytes on file system for storing traffic\n", preallocate_packet_dump_file_size); int fallocate_result = posix_fallocate(pcap_file, 0, preallocate_packet_dump_file_size); if (fallocate_result != 0) { printf("fallocate failed! Please check disk space and Linux Kernel Code\n"); } /* Caching is useless for our case because we have average linear traffic in most cases // We enable full buffering: _IOFBF int setvbuf_result = setvbuf(pcap_file, NULL, _IONBF, 1024 * 1024 * 4); if (setvbuf_result != 0) { printf("Can't set buffer for file operation\n"); } */ struct fastnetmon_pcap_file_header pcap_header; fill_pcap_header(&pcap_header, 1600); unsigned int written_bytes = write(pcap_file, &pcap_header, sizeof(pcap_header)); if (written_bytes != sizeof(pcap_header)) { printf("Can't write pcap header\n"); } pthread_t thread; pthread_create(&thread, NULL, speed_printer, NULL); pthread_t consumer_thread; pthread_create(&consumer_thread, NULL, packets_consumer, NULL); pthread_detach(thread); pthread_detach(consumer_thread); } #ifdef __cplusplus } #endif void firehose_packet(const char *pciaddr, char *data, int length) { std::shared_ptr packet_pointer( new packet_buffer_t ); packet_pointer->length = length; if (length < 1600) { memcpy(packet_pointer->buffer, data, length); } else { printf("So big packet: %d\n", length); } // Put pointer to the tube! while (!my_spsc_queue.push(packet_pointer)); } fastnetmon-1.1.4/src/tests/snabb/ndpicallback.cpp000066400000000000000000000457021343111404700220320ustar00rootroot00000000000000// compile with: gcc -shared -o capturecallback.so -fPIC capturecallback.c #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../../fastnetmon_pcap_format.h" #include "../../fastnetmon_types.h" #include "../../fastnetmon_packet_parser.h" #include "../../fast_dpi.h" unsigned int redis_port = 6379; std::string redis_host = "127.0.0.1"; u_int32_t size_flow_struct = 0; u_int32_t size_id_struct = 0; double last_timestamp = 0; double system_tsc_resolution_hz = 0; redisContext* redis_context = NULL; #ifdef __cplusplus extern "C" { redisContext* redis_init_connection(); void store_data_in_redis(std::string key_name, std::string value) { redisReply* reply = NULL; //redisContext* redis_context = redis_init_connection(); if (!redis_context) { printf("Could not initiate connection to Redis\n"); return; } reply = (redisReply*)redisCommand(redis_context, "SET %s %s", key_name.c_str(), value.c_str()); // If we store data correctly ... if (!reply) { std::cout << "Can't increment traffic in redis error_code: " << redis_context->err << " error_string: " << redis_context->errstr; // Handle redis server restart corectly if (redis_context->err == 1 or redis_context->err == 3) { // Connection refused printf("Unfortunately we can't store data in Redis because server reject connection\n"); } } else { freeReplyObject(reply); } //redisFree(redis_context); } redisContext* redis_init_connection() { struct timeval timeout = { 1, 500000 }; // 1.5 seconds redisContext* redis_context = redisConnectWithTimeout(redis_host.c_str(), redis_port, timeout); if (redis_context->err) { std::cout << "Connection error:" << redis_context->errstr; return NULL; } // We should check connection with ping because redis do not check connection redisReply* reply = (redisReply*)redisCommand(redis_context, "PING"); if (reply) { freeReplyObject(reply); } else { return NULL; } return redis_context; } #endif inline uint64_t rte_rdtsc(void) { union { uint64_t tsc_64; struct { uint32_t lo_32; uint32_t hi_32; }; } tsc; asm volatile("rdtsc" : "=a" (tsc.lo_32), "=d" (tsc.hi_32)); return tsc.tsc_64; } void set_tsc_freq_fallback() { uint64_t start = rte_rdtsc(); sleep(1); system_tsc_resolution_hz = (double)rte_rdtsc() - start; } #ifdef __cplusplus } #endif class conntrack_hash_struct_for_simple_packet_t { public: uint32_t upper_ip; uint32_t lower_ip; uint16_t upper_port; uint16_t lower_port; unsigned int protocol; bool operator==(const conntrack_hash_struct_for_simple_packet_t& rhs) const { return memcmp(this, &rhs, sizeof(conntrack_hash_struct_for_simple_packet_t)) == 0; } }; namespace std { template<> struct hash { size_t operator()(const conntrack_hash_struct_for_simple_packet_t& x) const { std::size_t seed = 0; boost::hash_combine(seed, x.upper_ip); boost::hash_combine(seed, x.lower_ip); boost::hash_combine(seed, x.upper_port); boost::hash_combine(seed, x.lower_port); boost::hash_combine(seed, x.protocol); return seed; } }; } class ndpi_tracking_flow_t { public: ndpi_tracking_flow_t() { src = (struct ndpi_id_struct*)malloc(size_id_struct); memset(src, 0, size_id_struct); dst = (struct ndpi_id_struct*)malloc(size_id_struct); memset(dst, 0, size_id_struct); flow = (struct ndpi_flow_struct *)malloc(size_flow_struct); memset(flow, 0, size_flow_struct); update_timestamp(); } void update_timestamp() { this->last_timestamp = (double)rte_rdtsc() / system_tsc_resolution_hz; } ~ndpi_tracking_flow_t() { // We need use custom function because standard free could not free all memory here ndpi_free_flow(flow); free(dst); free(src); flow = NULL; dst = NULL; src = NULL; } ndpi_protocol detected_protocol; struct ndpi_id_struct *src = NULL; struct ndpi_id_struct *dst = NULL; struct ndpi_flow_struct *flow = NULL; bool protocol_detected = false; double last_timestamp; }; typedef std::unordered_map my_connection_tracking_storage_t; my_connection_tracking_storage_t my_connection_tracking_storage; typedef std::unordered_map known_http_hosts_t; known_http_hosts_t known_http_hosts; // For correct compilation with g++ #ifdef __cplusplus extern "C" { #endif void pcap_parse_packet(char* buffer, uint32_t len); void debug_printf(u_int32_t protocol, void *id_struct, ndpi_log_level_t log_level, const char *format, ...) { va_list va_ap; struct tm result; char buf[8192], out_buf[8192]; char theDate[32]; const char *extra_msg = ""; time_t theTime = time(NULL); va_start (va_ap, format); /* if(log_level == NDPI_LOG_ERROR) extra_msg = "ERROR: "; else if(log_level == NDPI_LOG_TRACE) extra_msg = "TRACE: "; else extra_msg = "DEBUG: "; */ memset(buf, 0, sizeof(buf)); strftime(theDate, 32, "%d/%b/%Y %H:%M:%S", localtime_r(&theTime, &result) ); vsnprintf(buf, sizeof(buf)-1, format, va_ap); snprintf(out_buf, sizeof(out_buf), "%s %s%s", theDate, extra_msg, buf); printf("%s", out_buf); fflush(stdout); va_end(va_ap); } struct ndpi_detection_module_struct* my_ndpi_struct = NULL; /* Called once before processing packets. */ void firehose_start(); /* optional */ /* Called once after processing packets. */ void firehose_stop(); /* optional */ /* * Process a packet received from a NIC. * * pciaddr: name of PCI device packet is received from * data: packet payload (ethernet frame) * length: payload length in bytes */ inline void firehose_packet(const char *pciaddr, char *data, int length); /* Intel 82599 "Legacy" receive descriptor format. * See Intel 82599 data sheet section 7.1.5. * http://www.intel.com/content/dam/www/public/us/en/documents/datasheets/82599-10-gbe-controller-datasheet.pdf */ struct firehose_rdesc { uint64_t address; uint16_t length; uint16_t cksum; uint8_t status; uint8_t errors; uint16_t vlan; } __attribute__((packed)); /* Traverse the hardware receive descriptor ring. * Process each packet that is ready. * Return the updated ring index. */ int firehose_callback_v1(const char *pciaddr, char **packets, struct firehose_rdesc *rxring, int ring_size, int index) { while (rxring[index].status & 1) { int next_index = (index + 1) & (ring_size-1); __builtin_prefetch(packets[next_index]); firehose_packet(pciaddr, packets[index], rxring[index].length); rxring[index].status = 0; /* reset descriptor for reuse */ index = next_index; } return index; } uint64_t received_packets = 0; uint64_t received_bytes = 0; void* speed_printer(void* ptr) { while (1) { uint64_t packets_before = received_packets; uint64_t bytes_before = received_bytes; sleep(1); uint64_t packets_after = received_packets; uint64_t bytes_after = received_bytes; uint64_t pps = packets_after - packets_before; uint64_t bps = bytes_after - bytes_before; printf("We process: %llu pps %.2f Gbps\n", (long long)pps, (float)bps/1024/1024/1024 * 8); // std::cout << "Hash size: " << my_connection_tracking_storage.size() << std::endl; std::cout << "Uniq hosts: " << known_http_hosts.size() << std::endl; } } // We will start speed printer void firehose_start() { my_ndpi_struct = init_ndpi(); // Connect to the Redis redis_context = redis_init_connection(); if (!redis_context) { printf("Can't connect to the Redis\n"); } // Tune timer set_tsc_freq_fallback(); // Set call time last_timestamp = (double)rte_rdtsc() / system_tsc_resolution_hz; size_id_struct = ndpi_detection_get_sizeof_ndpi_id_struct(); size_flow_struct = ndpi_detection_get_sizeof_ndpi_flow_struct(); pthread_t thread; pthread_create(&thread, NULL, speed_printer, NULL); pthread_detach(thread); } // https://code.google.com/p/smhasher/source/browse/trunk/MurmurHash2.cpp // 64-bit hash for 64-bit platforms #define BIG_CONSTANT(x) (x##LLU) uint64_t MurmurHash64A(const void* key, int len, uint64_t seed) { const uint64_t m = BIG_CONSTANT(0xc6a4a7935bd1e995); const int r = 47; uint64_t h = seed ^ (len * m); const uint64_t* data = (const uint64_t*)key; const uint64_t* end = data + (len / 8); while (data != end) { uint64_t k = *data++; k *= m; k ^= k >> r; k *= m; h ^= k; h *= m; } const unsigned char* data2 = (const unsigned char*)data; switch (len & 7) { case 7: h ^= uint64_t(data2[6]) << 48; case 6: h ^= uint64_t(data2[5]) << 40; case 5: h ^= uint64_t(data2[4]) << 32; case 4: h ^= uint64_t(data2[3]) << 24; case 3: h ^= uint64_t(data2[2]) << 16; case 2: h ^= uint64_t(data2[1]) << 8; case 1: h ^= uint64_t(data2[0]); h *= m; }; h ^= h >> r; h *= m; h ^= h >> r; return h; } // Copy and paste from netmap module inline bool parse_raw_packet_to_simple_packet(u_char* buffer, int len, simple_packet& packet) { struct pfring_pkthdr packet_header; memset(&packet_header, 0, sizeof(packet_header)); packet_header.len = len; packet_header.caplen = len; // We do not calculate timestamps because timestamping is very CPU intensive operation: // https://github.com/ntop/PF_RING/issues/9 u_int8_t timestamp = 0; u_int8_t add_hash = 0; fastnetmon_parse_pkt((u_char*)buffer, &packet_header, 4, timestamp, add_hash); // char print_buffer[512]; // fastnetmon_print_parsed_pkt(print_buffer, 512, (u_char*)buffer, &packet_header); // logger.info("%s", print_buffer); if (packet_header.extended_hdr.parsed_pkt.ip_version != 4 && packet_header.extended_hdr.parsed_pkt.ip_version != 6) { return false; } // We need this for deep packet inspection packet.packet_payload_length = len; packet.packet_payload_pointer = (void*)buffer; packet.ip_protocol_version = packet_header.extended_hdr.parsed_pkt.ip_version; if (packet.ip_protocol_version == 4) { // IPv4 /* PF_RING stores data in host byte order but we use network byte order */ packet.src_ip = htonl(packet_header.extended_hdr.parsed_pkt.ip_src.v4); packet.dst_ip = htonl(packet_header.extended_hdr.parsed_pkt.ip_dst.v4); } else { // IPv6 memcpy(packet.src_ipv6.s6_addr, packet_header.extended_hdr.parsed_pkt.ip_src.v6.s6_addr, 16); memcpy(packet.dst_ipv6.s6_addr, packet_header.extended_hdr.parsed_pkt.ip_dst.v6.s6_addr, 16); } packet.source_port = packet_header.extended_hdr.parsed_pkt.l4_src_port; packet.destination_port = packet_header.extended_hdr.parsed_pkt.l4_dst_port; packet.length = packet_header.len; packet.protocol = packet_header.extended_hdr.parsed_pkt.l3_proto; packet.ts = packet_header.ts; packet.ip_fragmented = packet_header.extended_hdr.parsed_pkt.ip_fragmented; packet.ttl = packet_header.extended_hdr.parsed_pkt.ip_ttl; // Copy flags from PF_RING header to our pseudo header if (packet.protocol == IPPROTO_TCP) { packet.flags = packet_header.extended_hdr.parsed_pkt.tcp.flags; } else { packet.flags = 0; } return true; } bool convert_simple_packet_toconntrack_hash_struct(simple_packet& packet, conntrack_hash_struct_for_simple_packet_t& conntrack_struct) { conntrack_struct.protocol = packet.protocol; // Build hash for lookup this connection uint32_t ip_src = packet.src_ip; uint32_t ip_dst = packet.dst_ip; uint16_t src_port = packet.source_port; uint16_t dst_port = packet.destination_port; // Build universal lookup structure which describes single connection if (ip_src < ip_dst) { conntrack_struct.lower_ip = ip_src; conntrack_struct.upper_ip = ip_dst; conntrack_struct.lower_port = src_port; conntrack_struct.upper_port = dst_port; } else { conntrack_struct.lower_ip = ip_dst; conntrack_struct.upper_ip = ip_src; conntrack_struct.lower_port = dst_port; conntrack_struct.upper_port = src_port; } return true; } unsigned int gc_call_timeout = 20; unsigned int gc_clean_how_old_records = 20; void firehose_packet(const char *pciaddr, char *data, int length) { // Garbadge collection code double current_timestamp = (double)rte_rdtsc() / system_tsc_resolution_hz; if (current_timestamp - last_timestamp > gc_call_timeout) { std::vector keys_to_remove; for (auto& itr : my_connection_tracking_storage) { // Remove all records who older than X seconds if (current_timestamp - itr.second.last_timestamp > gc_clean_how_old_records) { keys_to_remove.push_back(itr.first); } } //if (!keys_to_remove.empty()) { // std::cout << "We will remove " << keys_to_remove.size() << " keys" << std::endl; //} for (auto key_to_remove : keys_to_remove) { my_connection_tracking_storage.erase(key_to_remove); } last_timestamp = current_timestamp; } // GC code ends __sync_fetch_and_add(&received_packets, 1); __sync_fetch_and_add(&received_bytes, length); struct pfring_pkthdr packet_header; memset(&packet_header, 0, sizeof(packet_header)); packet_header.len = length; packet_header.caplen = length; // We do not calculate timestamps because timestamping is very CPU intensive operation: // https://github.com/ntop/PF_RING/issues/9 u_int8_t timestamp = 0; u_int8_t add_hash = 0; fastnetmon_parse_pkt((u_char*)data, &packet_header, 4, timestamp, add_hash); simple_packet current_packet; parse_raw_packet_to_simple_packet((u_char*)data, length, current_packet); conntrack_hash_struct_for_simple_packet_t conntrack_structure; convert_simple_packet_toconntrack_hash_struct(current_packet, conntrack_structure); ndpi_tracking_flow_t& dpi_tracking_structure = my_connection_tracking_storage[ conntrack_structure ]; // Protocol already detected /* if (dpi_tracking_structure.protocol_detected && dpi_tracking_structure.detected_protocol.protocol == NDPI_PROTOCOL_IRC) { char print_buffer[512]; fastnetmon_print_parsed_pkt(print_buffer, 512, (u_char*)data, &packet_header); printf("packet: %s\n", print_buffer); for (unsigned int index = packet_header.extended_hdr.parsed_pkt.offset.payload_offset; index < packet_header.len; index++) { printf("%c", data[index]); } printf("\n"); return; } */ dpi_tracking_structure.update_timestamp(); uint32_t current_tickt = 0 ; uint8_t* iph = (uint8_t*)(&data[packet_header.extended_hdr.parsed_pkt.offset.l3_offset]); // printf("vlan: %d\n", packet_header.extended_hdr.parsed_pkt.vlan_id); struct ndpi_iphdr* ndpi_ip_header = (struct ndpi_iphdr*)iph; unsigned int ipsize = packet_header.len; ndpi_protocol detected_protocol = ndpi_detection_process_packet(my_ndpi_struct, dpi_tracking_structure.flow, iph, ipsize, current_tickt, dpi_tracking_structure.src, dpi_tracking_structure.dst); if (detected_protocol.protocol == NDPI_PROTOCOL_UNKNOWN && detected_protocol.master_protocol == NDPI_PROTOCOL_UNKNOWN) { // printf("Can't detect protocol\n"); } else { dpi_tracking_structure.detected_protocol = detected_protocol; dpi_tracking_structure.protocol_detected = true; //printf("Master protocol: %d protocol: %d\n", detected_protocol.master_protocol, detected_protocol.protocol); char* protocol_name = ndpi_get_proto_name(my_ndpi_struct, detected_protocol.protocol); char* master_protocol_name = ndpi_get_proto_name(my_ndpi_struct, detected_protocol.master_protocol); if (detected_protocol.protocol == NDPI_PROTOCOL_HTTP) { std::string host_name = std::string((const char*)dpi_tracking_structure.flow->host_server_name); //printf("server name: %s\n", dpi_tracking_structure.flow->host_server_name); if (redis_context != NULL) { known_http_hosts_t::iterator itr = known_http_hosts.find(host_name); if (itr == known_http_hosts.end()) { // Not defined in internal cache // Add in local cache: known_http_hosts[ host_name ] = 1; // Add to Redis store_data_in_redis(host_name, "1"); } else { // Already stored } } } //printf("Protocol: %s master protocol: %s\n", protocol_name, master_protocol_name); bool its_bad_protocol = false; //if(ndpi_is_proto(detected_protocol, NDPI_PROTOCOL_TOR)) { // its_bad_protocol = true; //} if (detected_protocol.protocol == NDPI_PROTOCOL_IRC or detected_protocol.master_protocol == NDPI_PROTOCOL_IRC) { its_bad_protocol = true; } if (its_bad_protocol) { printf("Bad protocol %s master protocol %s found\n", protocol_name, master_protocol_name); char print_buffer[512]; fastnetmon_print_parsed_pkt(print_buffer, 512, (u_char*)data, &packet_header); printf("packet: %s\n", print_buffer); for (unsigned int index = packet_header.extended_hdr.parsed_pkt.offset.payload_offset; index < packet_header.len; index++) { printf("%c", data[index]); } printf("\n"); } } } #ifdef __cplusplus } #endif int main(int argc, char** argv) { my_ndpi_struct = init_ndpi(); size_id_struct = ndpi_detection_get_sizeof_ndpi_id_struct(); size_flow_struct = ndpi_detection_get_sizeof_ndpi_flow_struct(); if (argc != 2) { printf("Please specify path to dump file\n"); exit(-1); } const char* path = argv[1]; //pcap_reader(path, pcap_parse_packet); } fastnetmon-1.1.4/src/tests/sort_struct.cpp000066400000000000000000000061471343111404700207310ustar00rootroot00000000000000#include #include #include #include #include #include bool compare_min(unsigned int a, unsigned int b) { return a > b; } bool compare_max(unsigned int a, unsigned int b) { return a < b; } template class fast_priority_queue { public: fast_priority_queue(unsigned int queue_size) { this->queue_size = queue_size; internal_list.reserve(queue_size); } void insert(order_by_template_type main_value, int data) { // Because it's ehap we can remove // Append new element to the end of list internal_list.push_back(main_value); // Convert list to the complete heap // Up to logarithmic in the distance between first and last: Compares elements and // potentially swaps (or moves) them until rearranged as a longer heap. std::push_heap(internal_list.begin(), internal_list.end(), compare_min); if (this->internal_list.size() >= queue_size) { // And now we should remove minimal element from the internal_list // Prepare heap to remove min element std::pop_heap(internal_list.begin(), internal_list.end(), compare_min); // Remove element from the head internal_list.pop_back(); } } order_by_template_type get_min_element() { // We will return head of list because it's consists minimum element return internal_list.front(); } void print_internal_list() { for (unsigned int i = 0; i < internal_list.size(); i++) { std::cout << internal_list[i] << std::endl; } } void print() { // Create new list for sort because we can't do it in place std::vector sorted_list; // Allocate enough space sorted_list.reserve(internal_list.size()); // Copy to new vector with copy constructor sorted_list = internal_list; // Execute heap sort because array paritally sorted already std::sort_heap(sorted_list.begin(), sorted_list.end(), compare_min); for (unsigned int i = 0; i < sorted_list.size(); i++) { std::cout << sorted_list[i] << std::endl; } } private: order_by_template_type max_number; order_by_template_type min_number; unsigned int queue_size; // We can't use list here! std::vector internal_list; // std::priority_queue, std::less > class_priority_queue; }; int main() { fast_priority_queue my_priority_queue(10); for (int i = 0; i < 100; i++) { int current_value = rand() % 100; // std::cout< my_priority_queue.get_min_element()) { // Check for existence of this element in struct already my_priority_queue.insert(current_value, 0); } } std::cout << "Print internal list" << std::endl; my_priority_queue.print_internal_list(); std::cout << "Print sorted list" << std::endl; my_priority_queue.print(); } fastnetmon-1.1.4/src/tests/spsc_prototype.cpp000066400000000000000000000073651343111404700214360ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include "../libpatricia/patricia.h" #include "../fastnetmon_types.h" #include "../fast_library.h" #include "../netflow_plugin/netflow_collector.h" #include "../sflow_plugin/sflow_collector.h" #include "../pcap_plugin/pcap_collector.h" #ifdef PF_RING #include "../pfring_plugin/pfring_collector.h" #endif #include "../netmap_plugin/netmap_collector.h" #include #include #include // log4cpp logging facility #include "log4cpp/Category.hh" #include "log4cpp/Appender.hh" #include "log4cpp/FileAppender.hh" #include "log4cpp/OstreamAppender.hh" #include "log4cpp/Layout.hh" #include "log4cpp/BasicLayout.hh" #include "log4cpp/PatternLayout.hh" #include "log4cpp/Priority.hh" #include #include #include #include #include using namespace std; typedef simple_packet* simple_packet_shared_ptr_t; typedef boost::lockfree::spsc_queue< simple_packet_shared_ptr_t, boost::lockfree::capacity<1048576> > my_spsc_queue_t; uint64_t total_unparsed_packets = 0; my_spsc_queue_t my_spsc_queue[8]; std::string log_file_path = "/tmp/fastnetmon_plugin_tester.log"; log4cpp::Category& logger = log4cpp::Category::getRoot(); #include extern boost::pool_allocator alloc[8]; // #define DO_SUBNET_LOOKUP #ifdef DO_SUBNET_LOOKUP patricia_tree_t* lookup_tree; #endif // Global map with parsed config file std::map configuration_map; void init_logging() { log4cpp::PatternLayout* layout = new log4cpp::PatternLayout(); layout->setConversionPattern("%d [%p] %m%n"); log4cpp::Appender* appender = new log4cpp::FileAppender("default", log_file_path); appender->setLayout(layout); logger.setPriority(log4cpp::Priority::INFO); logger.addAppender(appender); logger.info("Logger initialized!"); } uint64_t received_packets = 0; void process_packet(simple_packet& current_packet) { //__sync_fetch_and_add(&received_packets, 1); //std::cout << print_simple_packet(current_packet); } std::unordered_map map_counter; void traffic_processor() { simple_packet_shared_ptr_t packet; //map_counter.reserve(16000000); while (1) { for (int i = 0; i < 8; i ++) { // while (!my_spsc_queue[thread_number].push(packet)); while (my_spsc_queue[i].pop(packet)) { //std::cout << print_simple_packet(packet); //map_counter[packet.src_ip]++; __sync_fetch_and_add(&received_packets, 1); delete packet; //alloc[i].deallocate(packet, 1); } } } } void speed_printer() { while (true) { uint64_t packets_before = received_packets; boost::this_thread::sleep(boost::posix_time::seconds(1)); uint64_t packets_after = received_packets; uint64_t pps = packets_after - packets_before; printf("We process: %llu pps\n", pps); } } int main(int argc, char* argv[]) { boost::thread speed_printer_thread( speed_printer ); boost::thread traffic_processor_thread(traffic_processor); init_logging(); // Required by Netmap and PF_RING plugins // We use fake interface name here because netmap could make server unreachable :) configuration_map["interfaces"] = "eth5"; start_netmap_collection(process_packet); traffic_processor_thread.join(); speed_printer_thread.join(); } fastnetmon-1.1.4/src/tests/store_data_to_graphite.cpp000066400000000000000000000021371343111404700230430ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include int main() { int sockfd = 0; if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { printf("\n Error : Could not create socket \n"); return 1; } struct sockaddr_in serv_addr; memset(&serv_addr, 0, sizeof(serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_port = htons(2003); if (inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr) <= 0) { printf("\n inet_pton error occured\n"); return 1; } if (connect(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) { printf("\n Error : Connect Failed \n"); return 1; } unsigned long long pps = 10000778; char buffer[256]; sprintf(buffer, "client.ip.in.udp %ld %ld\n", pps, time(NULL)); int write_result = write(sockfd, buffer, strlen(buffer)); printf("We store %d bytes\n", write_result); } fastnetmon-1.1.4/src/tests/test_cidr.cpp000066400000000000000000000027141343111404700203120ustar00rootroot00000000000000#include #include #include #include #include #include #include #include uint32_t convert_cidr_to_binary_netmask(unsigned int cidr) { uint32_t binary_netmask = 0xFFFFFFFF; binary_netmask = binary_netmask << (32 - cidr); // htonl from host byte order to network // ntohl from network byte order to host // We need network byte order at output return htonl(binary_netmask); } uint32_t convert_ip_as_string_to_uint(std::string ip) { struct in_addr ip_addr; inet_aton(ip.c_str(), &ip_addr); // in network byte order return ip_addr.s_addr; } int main() { uint32_t network_zero = convert_ip_as_string_to_uint("10.10.10.0"); uint32_t network_200 = convert_ip_as_string_to_uint("10.10.10.200"); uint32_t binary_netmask = convert_cidr_to_binary_netmask(24); uint32_t generated_subnet_address = network_200 & binary_netmask; std::cout << "network byte order" << std::endl; std::cout << "10.10.10.200/24\tnetwork byte order:" << network_200 << " host byte order:" << ntohl(network_200) << std::endl; std::cout << "10.10.10.0/24\tnetwork byte order:" << network_zero << " host byte order:" << ntohl(network_zero) << std::endl; std::cout << "generated \tnetwork byte order:" << generated_subnet_address << " host byte order:" << ntohl(generated_subnet_address) << std::endl; } fastnetmon-1.1.4/src/tests/tins_parser.cpp000066400000000000000000000007201343111404700206560ustar00rootroot00000000000000#include #include // g++ tins_parser.cpp -ltins using namespace Tins; bool callback(const PDU &pdu) { const IP &ip = pdu.rfind_pdu(); // Find the IP layer const TCP &tcp = pdu.rfind_pdu(); // Find the TCP layer std::cout << ip.src_addr() << ':' << tcp.sport() << " -> " << ip.dst_addr() << ':' << tcp.dport() << std::endl; return true; } int main() { Sniffer("eth0").sniff_loop(callback); } fastnetmon-1.1.4/src/tests/traffic_structures_performance_tests.cpp000066400000000000000000000237551343111404700260660ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../fastnetmon_types.h" // apt-get install -y libtbb-dev libsparsehash-dev // g++ -O3 traffic_structures_performance_tests.cpp -std=c++11 -lboost_system -lboost_thread -ltbb -I/opt/local/include -L/opt/local/lib // clang traffic_structures_performance_tests.cpp -std=c++11 -lboost_system -lboost_thread -ltbb -I/opt/local/include -L/opt/local/lib -lstdc++ -lm // Mac OS: // g++ traffic_structures_performance_tests.cpp -std=c++11 -lboost_system_mt -lboost_thread_mt -ltbb -I/opt/local/include -L/opt/local/lib -g -pg #ifndef __APPLE__ #include "tbb/concurrent_unordered_map.h" #endif #include // No speed benefits // std::map, boost::fast_pool_allocator> std::map DataCounter; boost::mutex data_counter_mutex; std::unordered_map DataCounterUnordered; std::unordered_map DataCounterUnorderedPreallocated; boost::unordered_map DataCounterBoostUnordered; boost::container::flat_map DataCounterBoostFlatMap; struct eqint { bool operator()(uint32_t a, uint32_t b) const { return a == b; } }; google::dense_hash_map, eqint> DataCounterGoogleDensehashMapPreallocated; google::dense_hash_map, eqint> DataCounterGoogleDensehashMap; #ifndef __APPLE__ tbb::concurrent_unordered_map DataCounterUnorderedConcurrent; #endif std::vector DataCounterVector; using namespace std; int number_of_ips = 10 * 1000 * 1000; int number_of_retries = 1; // #define enable_mutexex_in_test unsigned int number_of_threads = 1; // 83 seconds // without mutexes segmentation fault void packet_collector_thread_std_map() { for (int iteration = 0; iteration < number_of_retries; iteration++) { for (uint32_t i = 0; i < number_of_ips; i++) { #ifdef enable_mutexex_in_test data_counter_mutex.lock(); #endif DataCounter[i].udp_in_bytes++; #ifdef enable_mutexex_in_test data_counter_mutex.unlock(); #endif } } } void packet_collector_thread_boost_unordered_map() { for (int iteration = 0; iteration < number_of_retries; iteration++) { for (uint32_t i = 0; i < number_of_ips; i++) { #ifdef enable_mutexex_in_test data_counter_mutex.lock(); #endif DataCounterBoostUnordered[i].udp_in_bytes++; #ifdef enable_mutexex_in_test data_counter_mutex.unlock(); #endif } } } void packet_collector_thread_unordered_map() { for (int iteration = 0; iteration < number_of_retries; iteration++) { for (uint32_t i = 0; i < number_of_ips; i++) { #ifdef enable_mutexex_in_test data_counter_mutex.lock(); #endif DataCounterUnordered[i].udp_in_bytes++; #ifdef enable_mutexex_in_test data_counter_mutex.unlock(); #endif } } } void packet_collector_thread_google_dense_hash_map_preallocated() { for (int iteration = 0; iteration < number_of_retries; iteration++) { for (uint32_t i = 0; i < number_of_ips; i++) { #ifdef enable_mutexex_in_test data_counter_mutex.lock(); #endif DataCounterGoogleDensehashMapPreallocated[i].udp_in_bytes++; #ifdef enable_mutexex_in_test data_counter_mutex.unlock(); #endif } } } void packet_collector_thread_google_dense_hash_map() { for (int iteration = 0; iteration < number_of_retries; iteration++) { for (uint32_t i = 0; i < number_of_ips; i++) { #ifdef enable_mutexex_in_test data_counter_mutex.lock(); #endif DataCounterGoogleDensehashMap[i].udp_in_bytes++; #ifdef enable_mutexex_in_test data_counter_mutex.unlock(); #endif } } } void packet_collector_thread_unordered_map_preallocated() { for (int iteration = 0; iteration < number_of_retries; iteration++) { for (uint32_t i = 0; i < number_of_ips; i++) { #ifdef enable_mutexex_in_test data_counter_mutex.lock(); #endif DataCounterUnordered[i].udp_in_bytes++; #ifdef enable_mutexex_in_test data_counter_mutex.unlock(); #endif } } } void packet_collector_thread_flat_map_preallocated() { for (int iteration = 0; iteration < number_of_retries; iteration++) { for (uint32_t i = 0; i < number_of_ips; i++) { #ifdef enable_mutexex_in_test data_counter_mutex.lock(); #endif DataCounterBoostFlatMap[i].udp_in_bytes++; #ifdef enable_mutexex_in_test data_counter_mutex.unlock(); #endif } } } void packet_collector_thread_vector() { for (int iteration = 0; iteration < number_of_retries; iteration++) { for (uint32_t i = 0; i < number_of_ips; i++) { DataCounterVector[i].udp_in_bytes++; } } } #ifndef __APPLE__ void packet_collector_thread_unordered_concurrent_map() { for (int iteration = 0; iteration < number_of_retries; iteration++) { for (uint32_t i = 0; i < number_of_ips; i++) { DataCounterUnorderedConcurrent[i].udp_in_bytes++; } } } #endif // http://www.gnu.org/software/libc/manual/html_node/Elapsed-Time.html int timeval_subtract(struct timeval* result, struct timeval* x, struct timeval* y) { /* Perform the carry for the later subtraction by updating y. */ if (x->tv_usec < y->tv_usec) { int nsec = (y->tv_usec - x->tv_usec) / 1000000 + 1; y->tv_usec -= 1000000 * nsec; y->tv_sec += nsec; } if (x->tv_usec - y->tv_usec > 1000000) { int nsec = (x->tv_usec - y->tv_usec) / 1000000; y->tv_usec += 1000000 * nsec; y->tv_sec -= nsec; } /* Compute the time remaining to wait. tv_usec is certainly positive. */ result->tv_sec = x->tv_sec - y->tv_sec; result->tv_usec = x->tv_usec - y->tv_usec; /* Return 1 if result is negative. */ return x->tv_sec < y->tv_sec; } int run_tests(void (*tested_function)(void)) { timeval start_time; gettimeofday(&start_time, NULL); // std::cout << "Run "<< number_of_threads <<" threads" << endl; boost::thread* threads[number_of_threads]; for (int i = 0; i < number_of_threads; i++) { threads[i] = new boost::thread(tested_function); } // std::cout << "All threads started" << endl; // std::cout << "Wait for finishing" << endl; for (int i = 0; i < number_of_threads; i++) { threads[i]->join(); } //cout << "All threads finished" << endl; timeval finish_time; gettimeofday(&finish_time, NULL); double total_operations = number_of_ips * number_of_retries * number_of_threads; // We use ' for pretty print of long numbers // http://stackoverflow.com/questions/1499156/convert-astronomically-large-numbers-into-human-readable-form-in-c-c setlocale(LC_NUMERIC, "en_US.utf-8"); /* important */ timeval interval; timeval_subtract(&interval, &finish_time, &start_time); // Build time with float part double used_time = (double)interval.tv_sec + (double)interval.tv_usec / 1000000; // printf("We spent %f seconds\n", used_time); double ops_per_second = total_operations / used_time;; double mega_ops_per_second = ops_per_second / 1000 / 1000; printf("%'.1f mega ops per second\n", mega_ops_per_second); return 0; } bool enabled_slow_data_structures_test = true; int main() { std::cout << "Element size: " << sizeof(map_element) << std::endl; if (enabled_slow_data_structures_test) { std::cout << "std::map: "; run_tests(packet_collector_thread_std_map); DataCounter.clear(); #ifndef __APPLE__ std::cout << "tbb::concurrent_unordered_map: "; run_tests(packet_collector_thread_unordered_concurrent_map); DataCounterUnorderedConcurrent.clear(); #endif // Boost unordered map std::cout << "boost::unordered_map: "; run_tests(packet_collector_thread_boost_unordered_map); DataCounterBoostUnordered.clear(); // Boost flat_map DataCounterBoostFlatMap.reserve( number_of_ips ); std::cout << "boost::container::flat_map with preallocated elements: "; run_tests(packet_collector_thread_flat_map_preallocated); DataCounterBoostFlatMap.clear(); } std::cout << "std::unordered_map C++11: "; run_tests(packet_collector_thread_unordered_map); DataCounterUnordered.clear(); // Preallocate hash buckets DataCounterUnorderedPreallocated.reserve( number_of_ips ); std::cout << "std::unordered_map C++11 preallocated buckets: "; run_tests(packet_collector_thread_unordered_map_preallocated); DataCounterUnorderedPreallocated.clear(); std::cout << "google:dense_hashmap without preallocation: "; DataCounterGoogleDensehashMap.set_empty_key(UINT32_MAX); // We will got assert without it! run_tests(packet_collector_thread_google_dense_hash_map); DataCounterGoogleDensehashMap.clear(); std::cout << "google:dense_hashmap preallocated buckets: "; // We use UINT32_MAX as "empty" here, not a good idea but OK for tests DataCounterGoogleDensehashMapPreallocated.set_empty_key(UINT32_MAX); // We will got assert without it! DataCounterGoogleDensehashMapPreallocated.resize( number_of_ips ); run_tests(packet_collector_thread_google_dense_hash_map_preallocated); DataCounterGoogleDensehashMapPreallocated.clear(); // Preallocate vector DataCounterVector.reserve( number_of_ips ); std::cout << "std::vector preallocated: "; run_tests(packet_collector_thread_vector); DataCounterVector.clear(); } fastnetmon-1.1.4/src/tests/tsc_timers.cpp000066400000000000000000000034631343111404700205100ustar00rootroot00000000000000#include #include #include #include #include #include inline uint64_t read_tsc_cpu_register(void) { union { uint64_t tsc_64; struct { uint32_t lo_32; uint32_t hi_32; }; } tsc; asm volatile("rdtsc" : "=a" (tsc.lo_32), "=d" (tsc.hi_32)); return tsc.tsc_64; } uint64_t get_tsc_freq_with_sleep() { uint64_t start = read_tsc_cpu_register(); sleep(1); return read_tsc_cpu_register() - start; } uint64_t get_tsc_freq_from_clock(void) { //#ifdef CLOCK_MONOTONIC_RAW #define NS_PER_SEC 1E9 struct timespec sleeptime; sleeptime.tv_sec = 1; sleeptime.tv_nsec = 5E8; /* 1/2 second */ struct timespec t_start, t_end; if (clock_gettime(CLOCK_MONOTONIC_RAW, &t_start) == 0) { uint64_t ns, end, start; start = read_tsc_cpu_register(); nanosleep(&sleeptime,NULL); clock_gettime(CLOCK_MONOTONIC_RAW, &t_end); end = read_tsc_cpu_register(); ns = ((t_end.tv_sec - t_start.tv_sec) * NS_PER_SEC); ns += (t_end.tv_nsec - t_start.tv_nsec); double secs = (double)ns/NS_PER_SEC; return (uint64_t)((end - start)/secs); } //#endif } int main() { /* The frequency of the RDTSC timer resolution */ uint64_t fastnetmon_tsc_resolution_hz = 0; printf("Determine TSC freq with sleep\n"); fastnetmon_tsc_resolution_hz = get_tsc_freq_with_sleep(); printf("TSC freq is %llu\n", fastnetmon_tsc_resolution_hz); printf("Determing TSC freq with CLOCK_MONOTONIC_RAW\n"); fastnetmon_tsc_resolution_hz = get_tsc_freq_from_clock(); printf("TSC freq is %llu\n", fastnetmon_tsc_resolution_hz); printf("Current TSC value %llu\n", read_tsc_cpu_register()); } fastnetmon-1.1.4/src/unified_parser.cpp000066400000000000000000000047761343111404700202010ustar00rootroot00000000000000#include "unified_parser.hpp" #include "fastnetmon_packet_parser.h" bool parse_raw_packet_to_simple_packet(u_char* buffer, int len, simple_packet& packet, bool netmap_read_packet_length_from_ip_header) { struct pfring_pkthdr packet_header; memset(&packet_header, 0, sizeof(packet_header)); packet_header.len = len; packet_header.caplen = len; // We do not calculate timestamps because timestamping is very CPU intensive operation: // https://github.com/ntop/PF_RING/issues/9 u_int8_t timestamp = 0; u_int8_t add_hash = 0; fastnetmon_parse_pkt((u_char*)buffer, &packet_header, 4, timestamp, add_hash); // char print_buffer[512]; // fastnetmon_print_parsed_pkt(print_buffer, 512, (u_char*)buffer, &packet_header); // logger.info("%s", print_buffer); if (packet_header.extended_hdr.parsed_pkt.ip_version != 4 && packet_header.extended_hdr.parsed_pkt.ip_version != 6) { return false; } // We need this for deep packet inspection packet.packet_payload_length = len; packet.packet_payload_pointer = (void*)buffer; packet.ip_protocol_version = packet_header.extended_hdr.parsed_pkt.ip_version; if (packet.ip_protocol_version == 4) { // IPv4 /* PF_RING stores data in host byte order but we use network byte order */ packet.src_ip = htonl(packet_header.extended_hdr.parsed_pkt.ip_src.v4); packet.dst_ip = htonl(packet_header.extended_hdr.parsed_pkt.ip_dst.v4); } else { // IPv6 memcpy(packet.src_ipv6.s6_addr, packet_header.extended_hdr.parsed_pkt.ip_src.v6.s6_addr, 16); memcpy(packet.dst_ipv6.s6_addr, packet_header.extended_hdr.parsed_pkt.ip_dst.v6.s6_addr, 16); } packet.source_port = packet_header.extended_hdr.parsed_pkt.l4_src_port; packet.destination_port = packet_header.extended_hdr.parsed_pkt.l4_dst_port; if (netmap_read_packet_length_from_ip_header) { packet.length = packet_header.extended_hdr.parsed_pkt.ip_total_size; } else { packet.length = packet_header.len; } packet.protocol = packet_header.extended_hdr.parsed_pkt.l3_proto; packet.ts = packet_header.ts; packet.ip_fragmented = packet_header.extended_hdr.parsed_pkt.ip_fragmented; packet.ttl = packet_header.extended_hdr.parsed_pkt.ip_ttl; // Copy flags from PF_RING header to our pseudo header if (packet.protocol == IPPROTO_TCP) { packet.flags = packet_header.extended_hdr.parsed_pkt.tcp.flags; } else { packet.flags = 0; } return true; } fastnetmon-1.1.4/src/unified_parser.hpp000066400000000000000000000003371343111404700201730ustar00rootroot00000000000000#pragma once #include #include #include "fastnetmon_types.h" bool parse_raw_packet_to_simple_packet(u_char* buffer, int len, simple_packet& packet, bool netmap_read_packet_length_from_ip_header);