From 65acdb0c3e0c3c13a1fef94f9617413857afd5b7 Mon Sep 17 00:00:00 2001 From: Ulrich Weber Date: Mon, 22 Nov 2010 10:23:28 +0100 Subject: [PATCH] import mausezahn 0.40 Signed-off-by: Ulrich Weber --- AUTHORS | 3 + CMakeLists.txt | 20 + COPYING | 340 ++++++++++ ChangeLog | 308 +++++++++ INSTALL | 49 ++ NEWS | 55 ++ README | 115 ++++ doc/CMakeLists.txt | 7 + doc/README.example | 18 + doc/example_lldp.conf | 11 + doc/mops.html | 748 +++++++++++++++++++++ doc/mz.1 | 267 ++++++++ doc/mz.cfg.1 | 83 +++ doc/mzguide.html | 836 ++++++++++++++++++++++++ doc/view_rtp_avg.py | 67 ++ install_manifest.txt | 8 + src/CMakeLists.txt | 69 ++ src/automops.c | 1013 +++++++++++++++++++++++++++++ src/cdp.c | 769 ++++++++++++++++++++++ src/cli.c | 574 ++++++++++++++++ src/cli.h | 298 +++++++++ src/cli_arp.c | 232 +++++++ src/cli_bpdu.c | 750 +++++++++++++++++++++ src/cli_cmds.c | 1436 +++++++++++++++++++++++++++++++++++++++++ src/cli_dns.c | 53 ++ src/cli_eth.c | 269 ++++++++ src/cli_igmp.c | 322 +++++++++ src/cli_interface.c | 142 ++++ src/cli_ip.c | 888 +++++++++++++++++++++++++ src/cli_launch.c | 141 ++++ src/cli_legacy.c | 141 ++++ src/cli_lldp.c | 437 +++++++++++++ src/cli_packet.c | 1121 ++++++++++++++++++++++++++++++++ src/cli_rtp.c | 343 ++++++++++ src/cli_sequence.c | 263 ++++++++ src/cli_set.c | 350 ++++++++++ src/cli_tcp.c | 679 +++++++++++++++++++ src/cli_tools.c | 40 ++ src/cli_udp.c | 204 ++++++ src/directmops.c | 30 + src/dns.c | 817 +++++++++++++++++++++++ src/hextools.c | 322 +++++++++ src/init.c | 662 +++++++++++++++++++ src/interval | 376 +++++++++++ src/layer1.c | 383 +++++++++++ src/layer2.c | 902 ++++++++++++++++++++++++++ src/layer3.c | 417 ++++++++++++ src/layer4.c | 731 +++++++++++++++++++++ src/llist.c | 176 +++++ src/llist.h | 75 +++ src/lookupdev.c | 357 ++++++++++ src/modifications.c | 542 ++++++++++++++++ src/mops.c | 769 ++++++++++++++++++++++ src/mops.h | 1023 +++++++++++++++++++++++++++++ src/mops_checksums.c | 128 ++++ src/mops_dot1Q.c | 131 ++++ src/mops_ext.c | 466 +++++++++++++ src/mops_ext_arp.c | 239 +++++++ src/mops_ext_bpdu.c | 242 +++++++ src/mops_ext_igmp.c | 270 ++++++++ src/mops_ext_lldp.c | 430 ++++++++++++ src/mops_ext_rtp.c | 243 +++++++ src/mops_ip.c | 447 +++++++++++++ src/mops_mpls.c | 149 +++++ src/mops_sequence.c | 303 +++++++++ src/mops_tcp.c | 154 +++++ src/mops_threads.c | 639 ++++++++++++++++++ src/mops_tools.c | 259 ++++++++ src/mops_update.c | 422 ++++++++++++ src/mopsrx_arp.c | 301 +++++++++ src/mz.c | 341 ++++++++++ src/mz.h | 895 +++++++++++++++++++++++++ src/parse_xml.c | 568 ++++++++++++++++ src/rcv_rtp.c | 769 ++++++++++++++++++++++ src/rtp.c | 217 +++++++ src/send.c | 264 ++++++++ src/send_eth.c | 491 ++++++++++++++ src/syslog.c | 248 +++++++ src/time.c | 211 ++++++ src/tools.c | 1344 ++++++++++++++++++++++++++++++++++++++ src/tx_switch.c | 178 +++++ 81 files changed, 31400 insertions(+) create mode 100644 AUTHORS create mode 100644 CMakeLists.txt create mode 100644 COPYING create mode 100644 ChangeLog create mode 100644 INSTALL create mode 100644 NEWS create mode 100644 README create mode 100644 doc/CMakeLists.txt create mode 100644 doc/README.example create mode 100644 doc/example_lldp.conf create mode 100644 doc/mops.html create mode 100644 doc/mz.1 create mode 100644 doc/mz.cfg.1 create mode 100644 doc/mzguide.html create mode 100755 doc/view_rtp_avg.py create mode 100644 install_manifest.txt create mode 100644 src/CMakeLists.txt create mode 100644 src/automops.c create mode 100644 src/cdp.c create mode 100644 src/cli.c create mode 100644 src/cli.h create mode 100644 src/cli_arp.c create mode 100644 src/cli_bpdu.c create mode 100644 src/cli_cmds.c create mode 100644 src/cli_dns.c create mode 100644 src/cli_eth.c create mode 100644 src/cli_igmp.c create mode 100644 src/cli_interface.c create mode 100644 src/cli_ip.c create mode 100644 src/cli_launch.c create mode 100644 src/cli_legacy.c create mode 100644 src/cli_lldp.c create mode 100644 src/cli_packet.c create mode 100644 src/cli_rtp.c create mode 100644 src/cli_sequence.c create mode 100644 src/cli_set.c create mode 100644 src/cli_tcp.c create mode 100644 src/cli_tools.c create mode 100644 src/cli_udp.c create mode 100644 src/directmops.c create mode 100644 src/dns.c create mode 100644 src/hextools.c create mode 100644 src/init.c create mode 100644 src/interval create mode 100644 src/layer1.c create mode 100644 src/layer2.c create mode 100644 src/layer3.c create mode 100644 src/layer4.c create mode 100644 src/llist.c create mode 100644 src/llist.h create mode 100644 src/lookupdev.c create mode 100644 src/modifications.c create mode 100644 src/mops.c create mode 100644 src/mops.h create mode 100644 src/mops_checksums.c create mode 100644 src/mops_dot1Q.c create mode 100644 src/mops_ext.c create mode 100644 src/mops_ext_arp.c create mode 100644 src/mops_ext_bpdu.c create mode 100644 src/mops_ext_igmp.c create mode 100644 src/mops_ext_lldp.c create mode 100644 src/mops_ext_rtp.c create mode 100644 src/mops_ip.c create mode 100644 src/mops_mpls.c create mode 100644 src/mops_sequence.c create mode 100644 src/mops_tcp.c create mode 100644 src/mops_threads.c create mode 100644 src/mops_tools.c create mode 100644 src/mops_update.c create mode 100644 src/mopsrx_arp.c create mode 100644 src/mz.c create mode 100644 src/mz.h create mode 100644 src/parse_xml.c create mode 100644 src/rcv_rtp.c create mode 100644 src/rtp.c create mode 100644 src/send.c create mode 100644 src/send_eth.c create mode 100644 src/syslog.c create mode 100644 src/time.c create mode 100644 src/tools.c create mode 100644 src/tx_switch.c diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..f76c98e --- /dev/null +++ b/AUTHORS @@ -0,0 +1,3 @@ +Mausezahn (C) 2007-2010 by Herbert Haas +herbert AT perihel DOT at +http://www.perihel.at/sec/mz/ diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..cc2577d --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,20 @@ +project(mausezahn) + +cmake_minimum_required(VERSION 2.4.0 FATAL_ERROR) +if(COMMAND cmake_policy) + cmake_policy(SET CMP0003 NEW) +endif(COMMAND cmake_policy) + +SET(CMAKE_C_FLAGS "-Wall -pipe -fexceptions -fstack-protector --param=ssp-buffer-size=4 -fasynchronous-unwind-tables") + +ADD_CUSTOM_TARGET(uninstall + COMMAND ${CMAKE_COMMAND} -E echo Use 'xargs rm < install_manifest.txt' to uninstall this program + ) + +include_directories(./ ../ ${CMAKE_CURRENT_BINARY_DIR}) + +add_subdirectory (src) +add_subdirectory (doc) + + + diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..08ddefd --- /dev/null +++ b/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. + diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..ea94691 --- /dev/null +++ b/ChangeLog @@ -0,0 +1,308 @@ +version 0.40 +============ +2010-07-07 + +Important bugfixes and some interesting new features: + +- Configured packets can now be grouped to 'packet sequences', optionally with + additional delays between each packet chain. Use the 'sequence' command to + enter the sequence configuration mode. Only in interactive mode. +- IP auto-fragmentation is now supported in interactive mode. For every IP-based + packet you can configure a 'fragment-size' and optionally a 'fragment- + overlap'. +- Direct mode, jitter measurements: Now uses consistently the ssrc option to + distinguish different RTP streams. +- Corrected PVST+ spanning tree packets in both direct and interactive mode + (and exhaustively tested). +- Direct mode: Loose Source Route option works now correctly (tested it in + Cisco network). +- Interactice mode: 'start' command has been renamed to 'run'. Also improved + packet selection with this command, using IDs or names. Also sequences can + be started that way. +- Lots of bugfixes, more and less critical. + + + +version 0.39 +============ +2010-03-07 + +This is basically a stable version with a few more features. Everybody should +use only this version and not one of the previous! + +- Fixed 100% CPU-consumption bug, occurring when Mausezahn is used in + interactive mode or RTP-measurement mode and the system has a recent + libpcap-dev library. +- Added RTP support in MOPS. +- Improved RTP measurement mode (more efficient, some bugs fixed, internally + nanosecond resolution for future and hi-end machines). +- Fixed sporadic clean-up bugs (NULL-pointer verification). +- Updated man pages. + + +version 0.38 +============ +2009-12-24 + +Mausezahn now supports a multi-threaded mode with Cisco-style command line. +Try e. g. "mz -x 99" and telnet to port 99. This mode utilizes a new protocol +and packet management framework called MOPS (Mausezahn's Own Packet System). +From now on all new features are added to MOPS. + +This is a X-mas release and should be considered beta (although the legacy +part should be much more stable and cleaner than the version 0.34.9 and the +new MOPS part has been already tested somewhat...). PLEASE send be bug +reports! + +Known bugs: + +Due to a libpcap-problem (I assume) Mausezahn runs quite unstable +under Ubuntu 9.10 (100% CPU consumption in MOPS mode, segfaults upon +termination, unbounded numbers in jitter measurement mode...). Interestingly +no problems when running under Debian 5.0 or Ubuntu 8.04. + +Changelog: (only the most important) + +- Added basic support for IGMP (v1, v2), querier and client mode (currently + only TX, no RX). (MOPS) +- Added basic LLDP support (MOPS). Also supports 'bad' TLV creation. +- Multi-device support (MOPS). +- Support for nanosecond inter-packet delays (MOPS). The CLI now allows to + configure inter-packet delay in units hour, min, sec, msec, usec, and nsec. +- Support for greater transmission intervals. +- FIXED simulation mode; -V is now only the extended verbose mode and the new + switch -S enables the simulation mode (i. e. nothing is sent). +- Support for automatic L2-multicast addresses when L3 address is multicast + (MOPS). +- Added 'launch' commands for easier handling of usual tasks. (Currently only + synflood and BPDU). +- Added ARP observation service; this can be also used to detect malicious ARP + tricks on the LAN, e. g. ARP cache poisoning attacks. +- Improved IP range configuration in legacy code. +- Support for independent measurements of concurrent RTP streams (as part + of legacy code). Now each RTP stream can be assigned an arbitrary ID. + This solution is easier to handle (than IP-based) an NAT-independent. +- Added -r option for randomized interpacket delay. +- Prettier rtp measurement outputs via 'bar' (default) and 'txt' commands. +- More precise RTP jitter measurements now. +- Added 'bar' and 'txt' option for rtp jitter measurements. +- Migrated BPDU to mops. +- The -t option (protocol type) is now case insensitive for subsequent + protocol commands. +- Support for central mausezahn configuration file (/etc/mausezahn/mz.cfg) + containing username, password, enable, etc. In case the config file is + not present Mausezahn uses default credentials (user: mz, password: mz, + enable: mops). +- Better cleanup when aborting; fixed clean_up function to also clean-up + mops list +- Legacy code: Protocol command string is now case insensitive. +- Fixed str2hex function (potential stack smashing threat) + + + + +version 0.37 +============ +(not officially released) + +- Command 'clear all' now removes all packets except a plain init packet +- Added command 'clear packet' to remove a specific packet +- Added command 'reset packet' to re-initialize all data of a MOPS entry +- Changed mops id to u_int32_t +- Added 'load' command to load and run a config file +- Added 'launch' command to easily start predefined packet processes +- #defined MZ_DEFAULT_CONFIG_PATH "/etc/mausezahn/" which is now the default + location for the global config file (with passwords and other things) +- #defined MZ_DEFAULT_LOG_PATH "/var/log/mausezahn/" which is now the default + location for any dynamically created files such as logs and packet captures. +- Fixed limits check for strtoul and strtoull in str2int (etc) in tools.c. + + +version 0.36 +============ +(not officially released) + +- Added lots of new commands compared with version 0.35. (Too much to list here) +- Changed MOPS architecture from array to doubly linked list. (Code release + 'Cyanistes caeruleus' - all mops versions will be named after cute birds) +- Multi-threaded packet generation. +- Added full support for 802.1Q and MPLS (arbitrary number of tags) +- Migrated ARP, IP (no options), UDP, and TCP (stateless) to MOPS + + +version 0.35 +============ +(not officially released) + +- Added interactive (Cisco-style) commandline interface: Start Mausezahn with + the -x option (optionally specify port number, default is 25542) and then + Mausezahn accepts incoming Telnet connections. Currently this CLI should be + considered experimental. +- Allow arbitrary (user-defined) variable fields within frames (multi-precision + counters). +- Fixed automatic device detection bugs if vmware interfaces are present. Also + a bug that prevents choosing the loopback interface on some systems has been + fixed. +- Ascii payloads can now be specified in a file which is specified by the new + option-f . +- Hexadecimal payloads can no be specified in a file which is specified by the + new option -F . +- To improve usage consistency, added 'help' argument also for the 802.1Q + option, that is, also '-Q help' is now possible (as with many other options). + + + +version 0.34.7 +============== +2009-07-27 + +- RTP packet drop count and disorder count (the number of permutations + needed to regain the correct sequence) are not only printed on the + CLI but also in the 9th and 10th columns of the log file. +- Added RTP jitter estimation as specified in RFC3550 (smoothed mean deviation). + This value is printed on the CLI and in the 5th column of the rtp log-file. +- Fixed -V option with raw layer-2 frames (simulation mode). +- Fixed padding (-p) option (segfault when number too big) +- Fixed some potential stack overflow issues (thanks Vivek!) + + +version 0.34.6 +============== +2008-12-10 + +- Added the IP arguments df (don't fragment), mf (more fragments), and rf + (reserved flag). This makes it easier to build fragmented packets. + + +version 0.34.5 +============== +2008-10-30 + +- Added RTP packet drop count and disorder count (the number of permutations + needed to regain the correct sequence). These values are printed on the + CLI. +- Fixed usage of ranges even when no payload is specified; previously Mausezahn + had problems with that; error messages include: + a) mz/update_DPORT: Can't build TCP header: libnet_build_tcp(): payload + inconsistency + b) mz/update_IP_DA: IP address manipulation failed! + or similar. +- Updated the INSTALL file. + + + +version 0.34.1 +============== +2008-10-08 + +- Mausezahn now checks system clock precision and chooses monotonic nanosecond + clock if available or alternative clock otherwise. No nasty warning message + should occur anymore. + + + +version 0.34 +============ +2008-09-24 + +- Replaced gettimeofday() with the clock_gettime(CLOCK_MONOTONIC) which does + not jump when the system syncs via NTP (otherwise artefacts would occur in + long-term jitter-measurement data; additionally nanosecond precision is + (internally) supported, although it is not really required). +- Added IP options 'Loose Source Route' and 'Strict Source Route', as well as + the possibility to specify any IP option as hexadecimal string. +- IP packets can now be sent without any payload (previously at least a single + payload byte has been added) +- Added -V option (extended verbose) which prints only frame details but does + not put anything on the wire. +- Added icmp option echo request (ping), echo reply (default), and unreachable. + Also made some clean-ups here. + + +version 0.33.2 +============== +2008-09-11 + +- RTP packets are now (at least more) standard conform. Now Mausezahn per + default simulates a G.711 codec with 20 msec inter-packet delay and 160 bytes + payload. The usage of the timestamps should be correct now so that other + applications can also interprete Mausezahn's RTP header. +- Added 8 byte padding to BPDU packets (some switches rejected the packet; now + whole frame is always 60 bytes long (added 8 zero bytes or 4 for PVST+, + respectively). +- Fixed PVST+ packet format. Should be correct now; can be sent with (default) + or without 802.1Q tag. +- Better help text for BPDU mode. +- Added 'pvst' as additional keyword for specification of MAC addresses + + +version 0.33.1 +============== +2008-09-04 + +- Upon startup Mausezahn checks if it has root privileges (getuid, geteuid) +- 'Cleaned' the files README, INSTALL, and AUTHORS, fixed some missing information + and applied a line wrap at 80 chars to comply with packaging requirements. +- Also made a 80-char line-wrap in the source code's license statement. + + +version 0.33 +============ +2008-08-20 + +- Clearly defined GPLv2 as license. +- Support for the Syslog protocol. +- Inclusion of cmake for easier build process. +- Some minor bugfixes, e. g. now "mz -t help" works (before you had to enter + "mz eth0 -t help"). + +Thanks to Cristian Greco, Steve Grubb, Rauno Tuul, and Heinz Wiesinger for help +and feedback. + + + + +================================================================================ + +Since I was too lazy to maintain a changelog before version 0.32 let +me summarize at least all features and possible bugs at this point: + +*) Full support for the following protocols: + + - ARP + - BPDU (PVST+) + - CDP + - UDP + - TCP (only transmission, no state machine so far) + - DNS + +*) Limited support: + + - ICMP (started to implement ICMP redirects, did not had time for + tests so far; almost sure buggy) + + - RTP (only needed for jitter measurements, the timestamps used + are quite proprietary (sec and msec separately)) + +*) Other features + + - can send arbitrary byte sequences longer or equal 15 bytes + - randomized MAC and/or IP addresses + - ranges for IP addresses, port numbers, TCP sequence numbers + - 802.1Q VLAN tags (arbitrary depth) plus CoS values + - MPLS tags (arbitrary depth) plus CoS and TTL values + - automatic padding if desired + - inter-packet delays in usec (but can also be specified in msec + and sec) + - text or hex payloads + - RTP receiving mode to measure jitter with high precision (usec + is base, plan to measure true precision empirically, I guess + it is at least below 1 msec) + +*) Possible bugs + + - Limited checks for wrong arguments and options (however, I + tried to avoid buffer overflows, mostly) + + - ICMP packets (redirects) may be incorrect (I had no use for + those so far) diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000..4f647ee --- /dev/null +++ b/INSTALL @@ -0,0 +1,49 @@ + +REQUIREMENTS +============ + +You need the following packages on your system: + +* cmake +* libpcap0.8-dev (version >= 0.8 will definitely work) +* libnet1-dev (version >= 1.1 will definitely work) +* libcli-dev (version >= 1.9.1 will work) + +BUILD PROCESS +============= + +Then simply run + + cmake . + +Don't forget the period in this command. This which will create a Makefile for +your system. Cmake allows you to specify optional variables here, for example +for another install location use cmake . -DCMAKE_INSTALL_PREFIX=/path + + +Once this Makefile has been created, enter + + make + +and the executable binary 'mz' should be available in the +'src' directory. + + +INSTALLATION +============ + +Finally install the binary automatically on the right place: + + make install + +To specify another location see the comment above. + + +OTHER ISSUES +============ + +* Try out 'make help' to see all other targets. + +* To use the view_rtp_avg.py program (to visualize jitter data) you must + install the matplotlib package for Python. + diff --git a/NEWS b/NEWS new file mode 100644 index 0000000..dc5cc99 --- /dev/null +++ b/NEWS @@ -0,0 +1,55 @@ +Dear Mausezahn users, + +since version 0.38, Mausezahn supports a multi-threaded interactive mode with +a Cisco-style command line. + +Try e. g. "mz -x 99" and telnet to port 99. This mode utilizes a new protocol +and packet management framework called MOPS (Mausezahn's Own Packet System). +All new features will be added to the MOPS which represents the new core +framework of Mausezahn. + +If you execute Mausezahn traditionally, specifying all packet parameters as +arguments on the LINUX/UN*X shell, then MOPS is NOT used (libnet instead). +MOPS on the other hand does not need libnet anymore but creates all PDUs +completely by itself. This allows for a much more flexible header treatment, +such as an arbitrary number of MPLS headers or intentionally 'wrong' header +fields. + +The most interesting new features introduced by MOPS are: + +*) Nanosecond interpacket delay and overall transmission intervals (days, hours, ... ) +*) New additional protocols: LLDP, IGMPv1 and IGMPv2, extended spanning tree + options (for RSTP, MSTP, PVST+) +*) Valid or wrong checksums on multiple layers +*) RTP measurement compatibility with legacy and MOPS RTP mode +*) Access to *all* header fields, even to those "reserved" or "must be zero" +*) Very flexible and user friendly QoS options (ToS, DSCP, IPP, CoS, ...) in + different notations (hex, dec, binary, "EF", "AF32", ...) +*) New 'launch' command to start often-used packet processes quickly (will be + extended soon) +*) Each packet process can be tied to another physical interface +*) Each physical interface can be given an arbitrary fake MAC and/or IP address. +*) 802.1Q and MPLS tags with all parameters can be configured easily with the + 'tag' command. + + +You can store your packet configurations in text files and load them from the +Mausezahn command line interface. + + +*** PLEASE send be bug reports! *** + +Next planned features: (among many others) + +- IPv6 support for MOPS +- XML-based definitions of custom protocols +- Graphical user interface +- Logging service +- ICMP support for MOPS + +and other... + + +Best wishes + +herbert AT perihel.at diff --git a/README b/README new file mode 100644 index 0000000..54eefff --- /dev/null +++ b/README @@ -0,0 +1,115 @@ +What is Mausezahn? +================== + +Mausezahn is a free fast traffic generator written in C which allows you to +send nearly every possible and impossible packet. It is mainly used to test +VoIP or multicast networks but also for security audits to check whether your +systems are hardened enough for specific attacks. Mausezahn can be used for +example: + + * As traffic generator (e. g. to stress multicast networks) + * To precisely measure jitter (delay variations) between two hosts (e. g. + for VoIP-SLA verification) + * As didactical tool during a datacom lecture or for lab exercises + * For penetration testing of firewalls and IDS + * For DoS attacks on networks (for audit purposes of course) + * To find bugs in network software or appliances + * For reconnaissance attacks using ping sweeps and port scans + * To test network behaviour under strange circumstances (stress test, + malformed packets, ...) + +...and more. Mausezahn is basically a versatile packet creation tool on the +command line with a simple syntax and context help. It could also be used +within (bash-) scripts to perform combination of tests. + +Currently Mausezahn is only available for Linux platforms. There are no plans +for a Windows version and please do NOT PORT Mausezahn to Windows! + + +Quick Introduction +================== + +----------------------------------------------------------------------------- +NOTE: Since version 0.38 there is also an 'interactive mode' with more and +advanced features. Below, only the legacy 'direct mode' is explained. +Please consult mops.html in /usr/share/mz/ or http://www.perihel.at/sec/mz/ +for more information about this interactive mode. +----------------------------------------------------------------------------- + +Let me give you a quick example to demonstrate how simple it is to work with +Mausezahn. Please note that you must have root rights. + +▶ Send an arbitrary sequence of bytes through your network card 1000 times: + +# mz eth0 -c 1000 \ + "ff:ff:ff:ff:ff:ff ff:ff:ff:ff:ff:ff cc:dd 00:00:00:ca:fe:ba:be" + +Note that this 'frame' is (by intention) completely invalid with respect to the +Ethernet standard; the frame is too short (called a 'runt') and has a broadcast +source address. + +▶ But you can send more complex packets easily with the built-in packet +builders using the -t option. Let's send a forged DNS response to host +192.168.1.2 by impersonating the DNS server 10.7.7.42: + +# mz eth0 -A 10.7.7.42 -B 192.168.1.2 \ + -t dns "q=www.thehostyouseek.com, a=172.16.6.66" + +Of course you can manipulate much more in the DNS header, simply type mz -t dns +help for additional help. + +▶ Perform a TCP SYN-Flood attack against all hosts in subnet 10.5.5.0/24 which +are in VLAN 100. Try out all 1023 well-known ports. Provided that you are in +the native VLAN 50 you can reach the target via VLAN-hopping. Repeat the whole +attack endlessly by setting the count option to zero: + +# mz eth0 -c 0 -Q 50,100 -A rand -B 10.5.5.0/25 -t tcp "flags=syn, dp=1-1023" + +▶ Confuse the spanning tree: Behave like a root bridge and generate BPDUs with +lowest Bridge ID every two seconds: + +# mz eth0 -c 0 -d 2s -t bpdu + +As you see you don't even need to specify any other BPDU parameters because +Mausezahn assumes that your PC wants to be the root per default. Of course you +can modify every BPDU parameter. Event the Cisco-proprietary per-vlan spanning +tree PVST+ is supported: + +# mz eth0 -c 0 -d 2s -t bpdu vlan=314 + +▶ Voice over IP connections suffer from jitter (delay variations). Hence it is +important to know the jitter across a given path. Using Mausezahn you can +precisely measure the jitter continuously. Simply configure a Mausezahn sender +and a receiver: + +TX# mz eth0 -t rtp -B rx.somewhere.net +RX# mz eth0 -T rtp "log, path=/tmp" + +Using these settings, the sender (TX) sends RTP packets every 30 msec to the +specified receiver (RX). Station RX stores moving average data in +/tmp/rtp_avg_20080801-120233 (filename is current timestamp). The data is a +comma seperated list that can be easily analyzed and visualized with standard +tools, e. g. R, Matlab, Octave, or this python tool (which needs the +matplotlib). + + +Supported Packet Types +====================== + +For a list of all current supported packet types please enter + +# mz -t help + + +Disclaimer and License +====================== + +Mausezahn is basically a network and firewall testing tool. Don't use this +tool when you are not aware of its consequences or have only little knowledge +aqbout networks and data communication. If you abuse Mausezahn for +unauthorized attacks and get caught, or damage something of your own, then +this is completely your fault. + +Mausezahn (C)2008-2010 by Herbert Haas is licensed under the GNU Public +License (GPL) version 2. See the file COPYING for a license definition or +http://www.gnu.org/licenses/gpl-2.0.txt diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt new file mode 100644 index 0000000..4a05bd6 --- /dev/null +++ b/doc/CMakeLists.txt @@ -0,0 +1,7 @@ +install(FILES mz.1 DESTINATION share/man/man1) +install(FILES mz.cfg.1 DESTINATION share/man/man1) +install(FILES mzguide.html DESTINATION share/doc/mz) +install(FILES mops.html DESTINATION share/doc/mz) +install(FILES view_rtp_avg.py DESTINATION share/doc/mz) +install(FILES example_lldp.conf DESTINATION share/doc/mz) +install(FILES README.example DESTINATION share/doc/mz) diff --git a/doc/README.example b/doc/README.example new file mode 100644 index 0000000..1346530 --- /dev/null +++ b/doc/README.example @@ -0,0 +1,18 @@ +The file example_lldp.conf contains an example list of commands to create a +bogus LLDP packet. + +You can load this configuration from the Mausezahn command line, e. g. via: + +mz-0.39# load /home/hh/tmp/example_lldp.conf + +in case you copied the file in that path. Now when you enter 'show packet' you +will see a new packet entry in the packet list. Use the 'start slot ' +command to activate this packet. + +You can store your own packet creations in such file and easily load them when +you need them. Every command within such configuration files is executed on the +command line interface as if you had typed it in -- so be careful about the +order and don't forget to use 'configure terminal' as first command. + +You can even load other files from within a central config file. + diff --git a/doc/example_lldp.conf b/doc/example_lldp.conf new file mode 100644 index 0000000..36bdbcc --- /dev/null +++ b/doc/example_lldp.conf @@ -0,0 +1,11 @@ +configure terminal +packet +ethernet address source ff:ff:ff:ff:ff:ff +type lldp +chassis-id ff.ff.ff.ff.ff.ff +vlan 666 +ttl 65535 +bad-tlv ascii 13 5 CanYouDealWithThis +early-end +generic-tlv ascii 42 Mausezahn_rules +end diff --git a/doc/mops.html b/doc/mops.html new file mode 100644 index 0000000..185f848 --- /dev/null +++ b/doc/mops.html @@ -0,0 +1,748 @@ + + + + + + +Mausezahn User's Guide + + + + + + +
+

Mausezahn User's Guide

+

Part Two - Interactive Mode (MOPS)

+ +++ + + + + + + + + + + + +
Author:Herbert Haas
Address:
+herbert AT perihel DOT at
+http://www.perihel.at/sec/mz
+
+
Revision:0.38.1
Date:2010-02-10
Copyright:Copyright (c) 2007-2009 by Herbert Haas.
+ +
+

1   Note

+

This User's Guide explains Mausezahn's interactive mode relying on +Mausezahn's Own Packet System (MOPS). Most new and all more sophisticated +features are implemented inside this subsystem. MOPS provides an interactive +command line interface (similar as the famous Cisco CLI) and is +multi-threaded, allowing you to create an arbitrary number of transmission and +scanning process(es).

+

The legacy mode aka direct mode* (which allows you to create frames and +packets right from the Linux command line) is still supported and is described +in this document.

+
+
+

2   What is Mausezahn?

+

Mausezahn is a fast traffic generator written in C which allows you to +send nearly every possible and impossible packet. Mausezahn can be used for +example

+
+
    +
  • As traffic generator (e. g. to stress multicast networks)
  • +
  • For penetration testing of firewalls and IDS
  • +
  • For DoS attacks on networks (for audit purposes of course)
  • +
  • To find bugs in network software or appliances
  • +
  • For reconnaissance attacks using ping sweeps and port scans
  • +
  • To test network behaviour under strange circumstances (stress test, malformed packets, ...)
  • +
  • As didactical tool during lab exercises
  • +
+
+

...and more. Mausezahn is basically a versatile packet creation tool on the +command line with a simple syntax and online help. It could also be used +within (bash-) scripts to perform combination of tests.

+

Currently Mausezahn is only available for Linux (and other UNIX-like) +platforms. There will be no Windows version.

+
+
+

3   Disclaimer and License

+

Mausezahn is basically a traffic generator as well as a network and firewall +testing tool. Don't use this tool when you are not aware of its consequences +or have only little knowledge about networks and data communication. If you +abuse Mausezahn for unallowed attacks and get caught, or damage something of +your own, then this is completely your fault.

+

Since version 0.33 Mausezahn is licensed under GPLv2

+
+
+

4   First steps

+

Using the interactive mode requires to start Mausezahn as server:

+
+# mz -x
+
+

Now you can Telnet to that server using the default port number 25542, but +also an arbitrary port number can be specified:

+
+# mz -x 99
+Mausezahn accepts incoming Telnet connections on port 99.
+mz: Problems opening config file. Will use defaults
+
+

Either from another terminal or from another host try to Telnet to the +Mausezahn server:

+
+harpo$ telnet groucho 99
+Trying 192.168.0.4...
+Connected to groucho.
+Escape character is '^]'.
+
+------------------------------------------
+Mausezahn, version 0.38
+Copyright (C) 2007-2009 by Herbert Haas.
+------------------------------------------
+
+Mausezahn comes with ABSOLUTELY NO WARRANTY; for details
+type 'warranty'.  This is free software, and you are welcome
+to redistribute it under certain conditions; see COPYING
+(included in the Mausezahn source package) for details.
+
+For Mausezahn NEWS visit http://www.perihel.at/sec/mz/
+
+
+
+Username: mz
+Password: mz
+
+mz-0.38> enable
+Password: mops
+mz-0.38#
+
+

It is recommended to configure your own login credentials in +/etc/mausezahn/mz.cfg, such as:

+
+user = herbert
+password = TopSecret
+enable = MauseZa#n42
+
+

Since you reached the Mausezahn prompt, lets try some first commands. You can +use the '?' character at any time for a contect-sensitive help.

+

First try out the show command:

+
+
+
mz-0.38# show ?
+
packet Show defined packets +interfaces Show detailed interface information +mops Show MOPS details +set List general packet parameters +arp Show the advanced Mausezahn ARP table +license Show license and warranty details
+
+
+

Mausezahn maintains its own ARP table and observes anomalies. There is an +entry for every physical interface (however this host has only one):

+
+mz-0.38# sh arp
+Intf    Index     IP address     MAC address       last       Ch  UCast BCast Info
+----------------------------------------------------------------------------------
+eth0    [1] D     192.168.0.1  00:09:5b:9a:15:84  23:44:41     1     1     0  0000
+
+

The column Ch tells us that the announced MAC address has only changed one +time (= when it was learned). The columns Ucast and BCast tell us how +often this entry was announced via unicast or broadcast respectively.

+

Let's check our interfaces:

+
+mz-0.38# show interface
+Available network interfaces:
+
+               real             real                  used (fake)      used (fake)
+ device        IPv4 address     MAC address           IPv4 address     MAC address
+---------------------------------------------------------------------------------------
+> eth0         192.168.0.4      00:30:05:76:2e:8d     192.168.0.4      00:30:05:76:2e:8d
+  lo           127.0.0.1        00:00:00:00:00:00     127.0.0.1        00:00:00:00:00:00
+
+2 interfaces found.
+Default interface is eth0.
+
+
+
+

5   Defining packets

+

Let's check the current packet list:

+
+mz-0.38# sh packet
+Packet layer flags: E=Ethernet, S=SNAP, Q=802.1Q, M=MPLS, I/i=IP/delivery_off, U=UDP, T=TCP
+
+PktID  PktName           Layers  Proto    Size  State      Device      Delay       Count/CntX
+
+    1  sysARP_servic...  E-----  ARP        60  config     lo          100 msec        1/0 (100%)
+
+1 packets defined, 0 active.
+
+

We notice that there is already one system-defined packet process; it has been +created and used only once (during startup) by Mausezahn's ARP service. +Currently its state is config which means that the process is sleeping.

+
+

5.1   General packet options

+

Now let's create our own packet process and therefore switch into the global +configuration mode:

+
+mz-0.38# configure term
+
+mz-0.38(config)# packet
+Allocated new packet PKT0002 at slot 2
+
+mz-0.38(config-pkt-2)# ?
+...
+name                 Assign a unique name
+description          Assign a packet description text
+bind                 Select the network interface
+count                Configure the packet count value
+delay                Configure the inter-packet delay
+interval             Configure a greater interval
+type                 Specify packet type
+mac                  Configure packet's MAC addresses
+tag                  Configure tags
+payload              Configure a payload
+port                 Configure packet's port numbers
+end                  End packet configuration mode
+ethernet             Configure frame's Ethernet, 802.2, 802.3, or SNAP settings
+ip                   Configure packet's IP settings
+udp                  Configure packet's UDP header parameters
+tcp                  Configure packet's TCP header parameters
+
+

Here are a lot of options but normally you only need a few of them. When you +configure lots of different packets you might assign a reasonable name and +description for them:

+
+mz-0.38(config-pkt-2)# name Test
+mz-0.38(config-pkt-2)# desc This is just a test
+
+

You can e. g. change the default settings for the source and destination +MAC/IP addresses using the mac and ip commands:

+
+mz-0.38(config-pkt-2)# ip address dest 10.1.1.0 /24
+mz-0.38(config-pkt-2)# ip addr source random
+
+

In the example above we configured a range of addresses (all hosts in the +network 10.1.1.0 should be addressed). Additionally we spoof our source IP +address.

+

Of course We can add one or more VLAN and/or MPLS tag(s):

+
+mz-0.38(config-pkt-2)# tag ?
+  dot1q                Configure 802.1Q (and 802.1P) parameters
+  mpls                 Configure MPLS label stack
+
+mz-0.38(config-pkt-2)# tag dot ?
+Configure 802.1Q tags:
+
+  VLAN[:CoS] [VLAN[:CoS]] ...   The leftmost tag is the outer tag in the frame
+  remove <tag-nr> | all         Remove one or more tags (<tag-nr> starts with 1),
+                                by default the first (=leftmost,outer) tag is removed,
+                                keyword 'all' can be used instead of tag numbers.
+  cfi | nocfi [<tag-nr>]        Set or unset the CFI-bit in any tag (by default
+                                assuming the first tag).
+
+
+mz-0.38(config-pkt-2)# tag dot 1:7 200:5
+
+

Configure count and delay:

+
+mz-0.38(config-pkt-2)# count 1000
+
+mz-0.38(config-pkt-2)# delay ?
+delay <value> [hour | min | sec | msec | usec | nsec]
+
+Specify the inter-packet delay in hours, minutes, seconds, milliseconds,  microseconds,
+or nanoseconds. The default unit is milliseconds (i. e. when no unit is given).
+
+
+mz-0.38(config-pkt-2)# delay 1 msec
+Inter-packet delay set to 0 sec and 1000000 nsec
+
+
+mz-0.38(config-pkt-2)#
+
+
+
+

5.2   Configuring protocol types

+

Mausezahn's interactive mode supports a growing list of protocols and only +relies on the MOPS architecture (and not on libnet as it is the case with the +legacy direct mode):

+
+mz-0.38(config-pkt-2)# type
+Specify a packet type from the following list:
+
+ arp
+ bpdu
+ igmp
+ ip
+ lldp
+ tcp
+ udp
+
+mz-0.38(config-pkt-2)# type tcp
+
+mz-0.38(config-pkt-2-tcp)#
+....
+seqnr                Configure the TCP sequence number
+acknr                Configure the TCP acknowledgement number
+hlen                 Configure the TCP header length
+reserved             Configure the TCP reserved field
+flags                Configure a combination of TCP flags at once
+cwr                  Set or unset the TCP CWR flag
+ece                  Set or unset the TCP ECE flag
+urg                  Set or unset the TCP URG flag
+ack                  set or unset the TCP ACK flag
+psh                  set or unset the TCP PSH flag
+rst                  set or unset the TCP RST flag
+syn                  set or unset the TCP SYN flag
+fin                  set or unset the TCP FIN flag
+window               Configure the TCP window size
+checksum             Configure the TCP checksum
+urgent-pointer       Configure the TCP urgend pointer
+options              Configure TCP options
+end                  End TCP configuration mode
+
+
+mz-0.38(config-pkt-2-tcp)# flags syn fin rst
+Current setting is: --------------------RST-SYN-FIN
+
+
+mz-0.38(config-pkt-2-tcp)# end
+mz-0.38(config-pkt-2)# paylo ascii This is a dummy payload for my first packet
+mz-0.38(config-pkt-2)# end
+
+

Now configure another packet, for example let's assume we want an LLDP process:

+
+mz-0.38(config)# packet
+Allocated new packet PKT0003 at slot 3
+
+mz-0.38(config-pkt-3)# ty lldp
+mz-0.38(config-pkt-3-lldp)# exit
+mz-0.38(config)# exit
+
+

In the above example we only use the default LLDP settings and don't configure +further LLDP options or TLVs.

+

Back in the top level of the CLI let's verify what we had done:

+
+mz-0.38# sh pa
+Packet layer flags: E=Ethernet, S=SNAP, Q=802.1Q, M=MPLS, I/i=IP/delivery_off, U=UDP, T=TCP
+
+PktID  PktName            Layers  Proto    Size  State      Device   Delay      Count/CntX
+   1   sysARP_servic...   E-----  ARP        60  config     lo       100 msec       1/0 (100%)
+   2   Test               E-Q-IT            125  config     eth0    1000 usec    1000/1000 (0%)
+   3   PKT0003            E-----  LLDP       36  config     eth0      30 sec        0/0 (0%)
+
+3 packets defined, 0 active.
+
+

The column Layers indicates which major protocols have been combined. For example the +packet with packet-id 2 ("Test") utilizes Ethernet (E), IP (I), and TCP (T). Additionally an +802.1Q tag (Q) has been inserted.

+

Now start one of these packet processes:

+
+mz-0.38# start slot 3
+Activate [3]
+
+mz-0.38# sh pac
+Packet layer flags: E=Ethernet, S=SNAP, Q=802.1Q, M=MPLS, I/i=IP/delivery_off, U=UDP, T=TCP
+
+PktID  PktName            Layers  Proto    Size  State      Device   Delay      Count/CntX
+   1   sysARP_servic...   E-----  ARP        60  config     lo       100 msec       1/0 (100%)
+   2   Test               E-Q-IT            125  config     eth0    1000 usec    1000/1000 (0%)
+   3   PKT0003            E-----  LLDP       36  config     eth0      30 sec        0/1 (0%)
+
+3 packets defined, 1 active.
+
+

Let's have a more detailed look at a specific packet process:

+
+mz-0.38# sh pac 2
+Packet [2] Test
+Description: This is just a test
+State: config, Count=1000, delay=1000 usec (0 s 1000000 nsec), interval= (undefined)
+Headers:
+ Ethernet: 00-30-05-76-2e-8d => ff-ff-ff-ff-ff-ff  [0800 after 802.1Q tag]
+ Auto-delivery is ON (that is, the actual MAC is adapted upon transmission)
+ 802.1Q: 0 tag(s);  (VLAN:CoS)
+ IP:  SA=192.168.0.4 (not random) (no range)
+      DA=255.255.255.255 (no range)
+      ToS=0x00  proto=17  TTL=255  ID=0  offset=0  flags: -|-|-
+      len=49664(correct)  checksum=0x2e8d(correct)
+ TCP: 83 bytes segment size (including TCP header)
+      SP=0 (norange) (not random), DP=0 (norange) (not random)
+      SQNR=3405691582 (start 0, stop 4294967295, delta 0) -- ACKNR=0 (invalid)
+      Flags: ------------------------SYN----, reserved field is 00, urgent pointer= 0
+      Announced window size= 100
+      Offset= 0 (times 32 bit; value is valid), checksum= ffff (valid)
+      (No TCP options attached) - 0 bytes defined
+ Payload size: 43 bytes
+ Frame size: 125 bytes
+
+  1  ff:ff:ff:ff:ff:ff:00:30  05:76:2e:8d:81:00:e0:01  81:00:a0:c8:08:00:45:00  00:67:00:00:00:00:ff:06
+ 33  fa:e4:c0:a8:00:04:ff:ff  ff:ff:00:00:00:00:ca:fe  ba:be:00:00:00:00:a0:07  00:64:f7:ab:00:00:02:04
+ 65  05:ac:04:02:08:0a:19:35  90:c3:00:00:00:00:01:03  03:05:54:68:69:73:20:69  73:20:61:20:64:75:6d:6d
+ 97  79:20:70:61:79:6c:6f:61  64:20:66:6f:72:20:6d:79  20:66:69:72:73:74:20:70  61:63:6b:65:74
+
+
+ mz-0.38#
+
+

If you want to stop one or more packet processes, use the stop command. The "emergency +stop" is when you use stop all:

+
+mz-0.38# stop all
+Stopping
+[3] PKT0003
+
+
+Stopped 1 transmission processe(s)
+
+

The launch command provides a shortcut for commonly used packet processes. For example to +behave like a STP-capable bridge we want to start an BPDU process with typical parameters:

+
+mz-0.38# laun bpdu
+Allocated new packet sysBPDU at slot 5
+
+mz-0.38# sh pac
+Packet layer flags: E=Ethernet, S=SNAP, Q=802.1Q, M=MPLS, I/i=IP/delivery_off, U=UDP, T=TCP
+
+PktID  PktName           Layers  Proto    Size  State      Device      Delay       Count/CntX
+    1  sysARP_servic...  E-----  ARP        60  config     lo          100 msec        1/0 (100%)
+    2  Test              E-Q-IT            125  config     eth0       1000 usec     1000/1000 (0%)
+    3  PKT0003           E-----  LLDP       36  config     eth0          30 sec        0/12 (0%)
+    4  PKT0004           E---I-  IGMP       46  config     eth0        100 msec        0/0 (0%)
+    5  sysBPDU           ES----  BPDU       29  active     eth0           2 sec        0/1 (0%)
+
+5 packets defined, 1 active.
+
+

Now a Configuration BPDU is sent every 2 seconds, claiming to be the root bridge (and usually +confusing the LAN. Note that only packet 5 (i. e. the last row) is active and therefore sending +packets while all other packets are in state config (i. e. they have been configured but +they are not doing anything at the moment).

+
+
+

5.3   Configuring a greater interval

+

Sometimes you may want to send a burst of packets at a greater interval:

+
+mz-0.38(config)# pac 2
+Modify packet parameters for packet Test [2]
+
+mz-0.38(config-pkt-2)# interv
+Configure a greater packet interval in days, hours, minutes, or seconds
+
+Arguments: <value>  <days | hours | minutes | seconds>
+
+Use a zero value to disable an interval.
+
+
+mz-0.38(config-pkt-2)# interv 1 h
+
+mz-0.38(config-pkt-2)# count 10
+
+mz-0.38(config-pkt-2)# delay 15 usec
+Inter-packet delay set to 0 sec and 15000 nsec
+
+

Now this packet is sent ten times with an inter-packet delay of 15 microsecond and this is +repeated every hour. When you look at the packet list, an interval is indicated with the +additional flag 'i' when inactive or 'I' when active:

+
+mz-0.38# sh pa
+Packet layer flags: E=Ethernet, S=SNAP, Q=802.1Q, M=MPLS, I/i=IP/delivery_off, U=UDP, T=TCP
+
+PktID  PktName           Layers  Proto    Size  State      Device      Delay       Count/CntX
+    1  sysARP_servic...  E-----  ARP        60  config     lo          100 msec        1/0 (100%)
+    2  Test              E-Q-IT            125  config-i   eth0         15 usec       10/10 (0%)
+    3  PKT0003           E-----  LLDP       36  config     eth0          30 sec        0/12 (0%)
+    4  PKT0004           E---I-  IGMP       46  config     eth0        100 msec        0/0 (0%)
+    5  sysBPDU           ES----  BPDU       29  active     eth0           2 sec        0/251 (0%)
+
+5 packets defined, 1 active.
+
+
+mz-0.38# start sl 2
+Activate [2]
+
+
+mz-0.38# sh pa
+Packet layer flags: E=Ethernet, S=SNAP, Q=802.1Q, M=MPLS, I/i=IP/delivery_off, U=UDP, T=TCP
+
+PktID  PktName           Layers  Proto    Size  State      Device      Delay       Count/CntX
+    1  sysARP_servic...  E-----  ARP        60  config     lo          100 msec        1/0 (100%)
+    2  Test              E-Q-IT            125  config+I   eth0         15 usec       10/0 (100%)
+    3  PKT0003           E-----  LLDP       36  config     eth0          30 sec        0/12 (0%)
+    4  PKT0004           E---I-  IGMP       46  config     eth0        100 msec        0/0 (0%)
+    5  sysBPDU           ES----  BPDU       29  active     eth0           2 sec        0/256 (0%)
+
+5 packets defined, 1 active.
+
+

Note that the flag 'I' indicates that an interval has been specified for packet 2. The process +is not active at the moment (only packet 5 is active here) but it will become active in a +regular interval. You can verify the actual interval when viewing the packet details via the show +packet 2 command.

+
+
+
+

6   Load prepared configurations

+

You can prepare packet configurations using the same commands as you would type them in on the +CLI and then load them to the CLI.

+

For example assume we have prepared a file 'test.mops' containing:

+
+configure terminal
+packet
+name IGMP_TEST
+desc This is only a demonstration how to load a file to mops
+type igmp
+
+

Then we can add this packet configuration to our packet list using the load command:

+
+mz-0.38# load test.mops
+Read commands from test.mops...
+
+Allocated new packet PKT0002 at slot 2
+
+mz-0.38# sh pa
+Packet layer flags: E=Ethernet, S=SNAP, Q=802.1Q, M=MPLS, I/i=IP/delivery_off, U=UDP, T=TCP
+
+PktID  PktName           Layers  Proto    Size  State      Device      Delay       Count/CntX
+    1  sysARP_servic...  E-----  ARP        60  config     lo          100 msec        1/0 (100%)
+    2  IGMP_TEST         E---I-  IGMP       46  config     eth0        100 msec        0/0 (0%)
+
+2 packets defined, 0 active.
+
+
+
+

7   What's next?

+

The following features are planned, some of them are already experimental +and will be available soon:

+
+
    +
  • Other basic protocols, including RTP
  • +
  • Scanning processes
  • +
  • Logging processes
  • +
  • IPv6
  • +
  • Custom protocols via XML
  • +
  • Object oriented configuration
  • +
  • Benchmarking according RFC 2544 (device under tests)
  • +
+
+

and many others (the true list is much longer). Also a GUI is in preparation (which will surely not +replace the CLI).

+
+
+

8   Dear users

+

Mausezahn is still under heavy development and you may expect new features +very soon.

+

Please report to herbert AT perihel DOT at regarding:

+
+
    +
  • Bugs
  • +
  • Important features you miss
  • +
  • How you used Mausezahn (I am really interested in practical problems)
  • +
  • Interesting observations with Mausezahn at the network
  • +
+
+
+
+ + diff --git a/doc/mz.1 b/doc/mz.1 new file mode 100644 index 0000000..3f112d1 --- /dev/null +++ b/doc/mz.1 @@ -0,0 +1,267 @@ +.\" Hey, EMACS: -*- nroff -*- +.\" First parameter, NAME, should be all caps +.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection +.\" other parameters are allowed: see man(7), man(1) +.TH MZ 1 "March 7, 2010" +.\" Please adjust this date whenever revising the manpage. +.\" +.\" Some roff macros, for reference: +.\" .nh disable hyphenation +.\" .hy enable hyphenation +.\" .ad l left justify +.\" .ad b justify to both left and right margins +.\" .nf disable filling +.\" .fi enable filling +.\" .br insert line break +.\" .sp insert n+1 empty lines +.\" for manpage-specific macros, see man(7) +.SH NAME +mz \- a fast versatile packet generator +.SH SYNOPSIS +.B mz +.RI [ options ] " | " +.br +.SH DESCRIPTION +.PP +.\" TeX users may be more comfortable with the \fB\fP and +.\" \fI\fP escape sequences to invode bold face and italics, +.\" respectively. +\fBMausezahn\fP is a free fast traffic generator written in C which allows +you to send nearly every possible and impossible packet. +.br +Mausezahn can also be used for example as didactical tool in network labs or +for security audits including penetration and DoS testing. As traffic +generator Mausezahn is for example used test IP multicast or VoIP networks. +Speeds close to the Ethernet limit are reachable (depending on the hardware +platform, especially the quality of the network interface card). +.br +.SH USAGE + +Mausezahn supports two modes, \fBdirect mode\fP and a multi-threaded +\fBinteractive mode\fP. +.PP +The \fBdirect mode\fP allows you to create a packet directly on the Linux/UN*X +shell and every packet parameter is specified in the argument list when +calling Mausezahn. +.PP +The \fBinteractive mode\fP is an advanced multi-threaded configuration mode +with its own command line interface (CLI). This mode allows you to create an +arbitrary number of packet types and streams in parallel, each with different +parameters. The interactive mode utilizes a completely redesigned and more +flexible protocol framework called MOPS (Mausezahn's Own Packet System). +The look and feel of the CLI is very similar to the Cisco IOS(tm) command +line. You can start the interactive mode by executing Mausezahn with the +\fI-x\fP argument (an optional port number may follow, otherwise it is 25542). +Then use Telnet to connect to this Mausezahn instance (the default login +expects the user 'mz' with password 'mz', and enable password 'mops'; you can +change this in /etc/mausezahn/mz.cfg). More information about the interactive +mode and MOPS is provided on the Mausezahn website. +.PP +The \fBdirect mode\fP supports two specification schemes: The +\fBraw-layer-2\fP scheme, where every single byte +to be sent can be specified, and \fBhigher-layer\fP scheme, where packet +builder interfaces are used (using the \fI-t\fP option). +.br +To use the \fBraw-layer-2\fP scheme, simply specify the desired frame as +hexadecimal sequence (the \fIhex_string\fP), such as +.PP +mz eth0 "00:ab:cd:ef:00 00:00:00:00:00:01 08:00 ca:fe:ba:be" +.PP +In this example, the spaces within the byte string are optional and separate +the Ethernet fields (destination and source address, type field, and a short +payload). The only additional options supported are \fI-a\fP, \fI-b\fP, +\fI-c\fP, and +\fI-p\fP. The frame length MUST be greater or equal 15 bytes. +.br +The \fBhigher-layer\fP scheme is enabled using the \fI-t \fP +option. This option activates a packet builder and besides the +\fIpacket_type\fP an optional \fIarg_string\fP can be specified. The +\fIarg_string\fP contains packet-specific parameters, such as TCP flags, port +numbers, etc; see the EXAMPLES below. +.PP +Note that Mausezahn requires root privileges. Please see the Mausezahn User's +Guide for more details or use Mausezahn's command line help. +.SH OPTIONS +Mausezahn provides a built-in context-specific help. Simply append the keyword +\fBhelp\fP to the configuration options. +.br +The most important options are: +.TP +.B \-v +Verbose mode. Capital \-V is even more verbose. +.TP +.B \-S +Simulation mode, i. e. don't put anything on the wire. This is typically +combined with the verbose mode. +.TP +.B \-q +Quiet mode (only warnings and errors are displayed). +.TP +.B \-c +Send the packet count times (default: 1, infinite: 0). +.TP +.B \-d +Apply delay between transmissions. The delay value can be specified in usec +(default, no additional unit needed), or in msec (e. g. 100m or 100msec), or +in seconds (e. g. 100s or 100sec). Note: MOPS also supports nanosecond delay +granulation if you need it (see: interactive mode). +.TP +.B \-p +Pad the raw frame to specified length (using zero bytes). Note that for raw +layer 2 frames the specified length defines the whole frame length, while for +higher layer packets the number of additional padding bytes are specified. +.TP +.B \-a +Use specified source mac address (use hex notation such as 00:00:aa:bb:cc:dd). +By default the interface MAC address will be used. The keywords \fIrand\fP and +\fIown\fP refer to a random MAC address (only unicast addresses are created) +and the own address, respectively. You can also use the keywords mentioned +below (although broadcast-type source addresses are officially invalid). +.TP +.B \-b +Use specified destination mac address. By default a broadcast is sent in raw +layer 2 mode or the destination hosts/gateways interface MAC address in normal +(IP) mode. You can use the same keywords as mentioned above as well as +\fIbc\fP (or \fIbcast\fP), \fIcisco\fP, and \fIstp\fP. +Please note that for the destination MAC address the \fIrand\fP keyword is +supported but creates a random address only once, even when you send multiple +packets. +.TP +.B \-A +Use specified source IP address (default is own interface IP). Optionally the +keyword \fIrand\fP can again be used for a random source IP address or a range +can be specified, such as 192.168.1.1-192.168.1.100 or 10.1.0.0/16. Also a DNS +name can be specified for which Mausezahn tries to determine the corresponding +IP address automatically. +.TP +.B \-B +Use specified destination IP address (default is broadcast i. e. +255.255.255.255). As with the source address (see above) you can also specify +a range or a DNS name. +.TP +.B \-t +Create the specified packet type using the built-in packet builder. Currently +supported packet types are: \fIarp\fP, \fIbpdu\fP, \fIip\fP, \fIudp\fP, +\fItcp\fP, \fIrtp\fP, and \fIdns\fP. There is currently also a limited support +for ICMP. Enter \fI-t help\fP to verify which packet builders your actual +Mausezahn version supports. Also, for any particular packet type, for example +\fItcp\fP enter \fImz \-t tcp help\fP to receive a context specific help. +.TP +.B \-T +Make this Mausezahn instance the receiving station. Currently (version 0.30) +only \fIrtp\fP is an option here and provides precise jitter measurements. For +this purpose start another Mausezahn instance on the sending station and the +local receiving station will output jitter statistics. See \fImz \-T rtp +help\fP for a detailed help. +.TP +.B \-Q <[CoS:]vlan> [, <[CoS:]vlan>, ...] +Specify 802.1Q VLAN tag and optional Class of Service. An arbitrary number of +VLAN tags can be specified (that is you can simulate QinQ or even +QinQinQinQ...). Multiple tags must be separated via a comma or a period (e. g. +"5:10,20,2:30"). VLAN tags are not supported for ARP and BPDU packets (in +which case you could specify the whole frame in hex using the raw layer 2 +interface of Mausezahn). +.TP +.B \-M [, ] +Specify a MPLS label or even a MPLS label stack. Optionally for each label the +experimental bits (usually the Class of Service, CoS) and the Time To Live +(TTL) can be specified. And if you are really crazy you can set/unset the +Bottom of Stack (BoS) bit at each label using the \fIS\fP (set) and \fIs\fP +(unset) option. By default the BoS is set automatically and correctly. Any +other setting will lead to invalid frames. Enter \fI-M help\fP for detailed +instructions and examples. +.TP +.B \-P +Specify a cleartext payload. Alternatively each packet type supports a +hexadecimal specification of the payload (see for example \fI-t udp help\fP). +.br +.TP +.B \-f +Read the ASCII payload from the specified file. +.TP +.B \-F +Read the HEX payload from the specified file. Actually this file must be also +an ASCII file (text file) but must contain hexadecimal digits, e. g. +"aa:bb:cc:0f:e6...". You can use also spaces as separation characters. + +.SH COMBINATION OF RANGES +When multiple ranges are specified, e. g. destination port ranges AND +destination address ranges, then \fBall\fP possible combinations of ports and +addresses are used for packet generation. Furthermore, this can be mixed with +other ranges e. g. a TCP sequence number range. Note that combining ranges +can lead to a very huge number of frames to be sent. As a rule of thumb you +can assume that about 100,000 frames are sent in a fraction of one second, +depending on your network interface. +.br +.SH DISCLAIMER AND WARNING +Mausezahn has been designed as fast traffic generator so you can easily +overwhelm a LAN segment with myriads of packets. And because Mausezahn should +also support security audits it is also possible to create malicious or +\*(lqinvalid\*(rq packets, SYN floods, port and address sweeps, DNS and ARP poisoning, +etc. +.br +Therefore, don't use this tool when you are not aware of possible consequences +or have only little knowledge about networks and data communication. If you +abuse Mausezahn for 'unallowed' attacks and get caught, or damage something of +your own, then this is completely your fault. So the safest solution is to try +it out in a lab environment. +.br +.SH EXAMPLES +Send BPDU frames for VLAN 5 as used with Cisco's PVST+ type of STP. Per +default Mausezahn assumes that you want to become the root bridge: +.PP +# mz eth0 \-c 0 \-d 2s \-t bpdu vlan=5 +.PP +Perform a CAM table overflow attack: +.PP +# mz eth0 \-c 128000 \-a rand \-p 64 +.PP +Perform a SYN flood attack to another VLAN using VLAN hopping. This +only works if you are connected to the same VLAN which is configured as native +VLAN on the trunk. We assume that the victim VLAN is VLAN 100 and the native +VLAN is VLAN 5. Lets attack every host in VLAN 100 which use a IP prefix +of 10.100.100.0/24, also try out all ports between 1 and 1023 and use a random +source IP address: +.PP +# mz eth0 \-c 0 \-Q 5,100 \-t tcp "flags=syn,dp=1-1023" \-p 20 \-A rand \-B 10.100.100.0/24 +.PP +Send IP multicast packets to the multicast group 230.1.1.1 using a UDP header +with destination port 32000 and set the IP DSCP field to EF (46). Send one +frame every 10 msec: +.PP +# mz eth0 \-c 0 \-d 10msec \-B 230.1.1.1 \-t udp "dp=32000,dscp=46" \-P "Multicast test packet" +.PP +Send UDP packets to the destination host target.anynetwork.foo using all +possible destination ports and send every packet with all possible source +addresses of the range 172.30.0.0/16; additionally use a source port of 666 +and three MPLS labels, 100, 200, and 300, the outer (300) with QoS field +5. Send the frame with a VLAN tag 420 and CoS 6; eventually pad with 1000 bytes +and repeat the whole thing 10 times: +.PP +# mz eth0 \-Q 6:420 \-M 100,200,300:5 \-A 172.30.0.0/16 \-B target.anynetwork.foo \-t udp "sp=666,dp=1-65535" \-p 1000 \-c 10 +.PP +Send six forged Syslog messages with severity 3 to a Syslog server 10.1.1.9; use a +forged source IP address 192.168.33.42 and let Mausezahn decide which local +interface to use. Use an inter-packet delay of 10 seconds: +.PP +# mz \-t syslog sev=3 \-P "Main reactor reached critical temperature." \-A 192.168.33.42 \-B 10.1.1.9 \-c 6 \-d 10s +.PP +Send an invalid TCP packet with only a 5 byte payload as layer-2 broadcast and +also use the broadcast MAC address as source address. The target should be +10.1.1.6 but use a broadcast source address. The source and destination port +shall be 145 and the window size 0. Set the TCP flags SYN, URG, and RST +simultaneously and sweep through the whole TCP sequence number space with an +increment of 1500. Finally set the urgent pointer to 666, i. e. pointing to +nowhere: +.PP +# mz \-t tcp "flags=syn|urg|rst, sp=145, dp=145, win=0, s=0-4294967295, ds=1500, urg=666" \-a bcast \-b bcast \-A bcast \-B 10.1.1.6 \-p 5 +.br +.SH SEE ALSO + \fBmz.cfg(1)\fP +.SH AUTHOR +Herbert Haas +.PP +Visit www.perihel.at/sec/mz/ for Mausezahn news and additional information. +.PP +This manual page was written by Herbert Haas , +for the Debian project. diff --git a/doc/mz.cfg.1 b/doc/mz.cfg.1 new file mode 100644 index 0000000..cf0c15e --- /dev/null +++ b/doc/mz.cfg.1 @@ -0,0 +1,83 @@ +.\" Hey, EMACS: -*- nroff -*- +.\" First parameter, NAME, should be all caps +.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection +.\" other parameters are allowed: see man(7), man(1) +.TH mz.cfg 1 "March 7, 2010" +.\" Please adjust this date whenever revising the manpage. +.\" +.\" Some roff macros, for reference: +.\" .nh disable hyphenation +.\" .hy enable hyphenation +.\" .ad l left justify +.\" .ad b justify to both left and right margins +.\" .nf disable filling +.\" .fi enable filling +.\" .br insert line break +.\" .sp insert n+1 empty lines +.\" for manpage-specific macros, see man(7) +.SH NAME +mz \- a fast versatile packet generator +.SH SYNOPSIS +.B /etc/mausezahn/mz.cfg +.br +.SH DESCRIPTION +.PP +.\" TeX users may be more comfortable with the \fB\fP and +.\" \fI\fP escape sequences to invode bold face and italics, +.\" respectively. +\fBMausezahn\fP is a free fast traffic generator written in C which allows +you to send nearly every possible and impossible packet. Mausezahn's MOPS +subsystem (Mausezahn's Own Packet System) supports an interactive mode with +a Cisco-style command line interface (CLI). In order to activate this +interactive mode, execute Mausezahn using the \fI-x\fP argument, optionally +followed by an arbitrary TCP port number, such as +.PP +# mz \-x 99 +.PP +in which case you can connect to Mausezahn via +.PP +$ telnet 127.0.0.1 99 +.PP +If no port number is specified, Mausezahn uses the default port number 25542 +(which is the date of towel day followed by the answer to the universe and +everything; however, you don't need to understand this in order to continue). +.PP +Login credentials as well as other +MOPS-related parameters can be specified in the Mausezahn configuration file +\fImz.cfg\fP located in \f/etc/mausezahn\fP. Currently, user-specific +configuration files are not supported. +.PP +If no configuration file is present Mausezahn assumes the following default login +credentials: +.TP +.PP +username: mz +.br +password: mz +.br +enable password: mops +.PP + +Currently only login credentials can be configured within the configuration +file. Here is an example content of \fI/etc/mausezahn/mz.cfg\fP: +.TP +.PP +user = herbert +.br +password = moTTe +.br +enable = T0p5ecreT +.PP +Additional configuration options will be officially supported with the next +releases. +.SH FILES +\fI/etc/mausezahn/mz.cfg\fP +.SH SEE ALSO + \fBmz(1)\fP +.SH AUTHOR +Herbert Haas. +.PP +Visit www.perihel.at/sec/mz/ for Mausezahn news and additional information. +.PP +This manual page was written by Herbert Haas , +for the Debian project. diff --git a/doc/mzguide.html b/doc/mzguide.html new file mode 100644 index 0000000..215a6a4 --- /dev/null +++ b/doc/mzguide.html @@ -0,0 +1,836 @@ + + + + + + +Mausezahn User's Guide + + + + + + +
+

Mausezahn User's Guide

+

Part One - Direct Mode

+ +++ + + + + + + + + + + + +
Author:Herbert Haas
Address:
+herbert AT perihel DOT at
+http://www.perihel.at/sec/mz
+
+
Revision:0.38.1
Date:2010-02-10
Copyright:Copyright (c) 2007-2009 by Herbert Haas.
+ +
+

1   Note

+

This User's Guide explains Mausezahn's direct mode which allows you to +create frames and packets right from the Linux command line. This mode is the +legacy mode; it is NOT multi-threaded and lacks Mausezahn's advanced features +which are integrated in the newer interactive mode.

+
+
+

2   What is Mausezahn?

+

Mausezahn is a fast traffic generator written in C which allows you to +send nearly every possible and impossible packet. Mausezahn can be used for +example

+
+
    +
  • As traffic generator (e. g. to stress multicast networks)
  • +
  • For penetration testing of firewalls and IDS
  • +
  • For DoS attacks on networks (for audit purposes of course)
  • +
  • To find bugs in network software or appliances
  • +
  • For reconnaissance attacks using ping sweeps and port scans
  • +
  • To test network behaviour under strange circumstances (stress test, malformed packets, ...)
  • +
  • As didactical tool during lab exercises
  • +
+
+

...and more. Mausezahn is basically a versatile packet creation tool on the +command line with a simple syntax and online help. It could also be used +within (bash-) scripts to perform combination of tests.

+

Currently Mausezahn is only available for Linux (and other UNIX-like) platforms. +There will be no Windows version.

+
+
+

3   Disclaimer and License

+

Mausezahn is basically a traffic generator as well as a network and firewall +testing tool. Don't use this tool when you are not aware of its consequences +or have only little knowledge about networks and data communication. If you +abuse Mausezahn for unallowed attacks and get caught, or damage something of +your own, then this is completely your fault.

+

Since version 0.33 Mausezahn is licensed under GPLv2

+
+
+

4   Basics

+
+

4.1   How to specify hex digits

+

Many arguments allow direct byte input. Bytes are represented as two +hexadecimal digits. Multiple bytes must be separated either by spaces, colons, +or dashes -- whatever you prefer. The following byte strings are equivalent:

+
+"aa:bb cc-dd-ee ff 01 02 03-04 05"
+
+"aa bb cc dd ee ff:01:02:03:04 05"
+
+

As first example, you may want to send an arbitrary fancy (possibly invalid) +frame right through your network card:

+
+# mz ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:08:00:ca:fe:ba:be
+
+

or equivalently but more readable:

+
+# mz ff:ff:ff:ff:ff:ff-ff:ff:ff:ff:ff:ff-08:00-ca:fe:ba:be
+
+
+
+

4.2   Basic operations

+

All major command line options are listed when you execute Mausezahn without arguments.

+

For practical usage keep the following special (not so widely known) options in mind:

+
+-r                    Multiplies the specified delay with a random value.
+-p <length>           Pad the raw frame to specified length (using random
+                      bytes).
+
+-P <ASCII Payload>    Use the specified ASCII payload.
+-f <filename>         Read the ASCII payload from a file.
+-F <filename>         Read the hexadecimal payload from a file.
+
+-S                    Simulation mode: DOES NOT put anything on the wire.
+                      This is typically combined with one of the verbose
+                      modes (-v or V).
+
+

Many options require a keyword or a number but the -t option is an +exception since it requires both a packet type (such as ip, udp, +dns, etc) and an argument string which is specific for that packet +type.

+

Here are some simple examples:

+
+# mz -t help
+# mz -t tcp help
+# mz eth3 -t udp sp=69,dp=69,p=ca:fe:ba:be
+
+

Note: Don't forget that on the CLI the Linux shell (usually the Bash) +interpretes spaces as a delimiter character. That is, if you are specifying an +argument that consists of multiple words with spaces inbetween, you MUST group +this with quotes.

+

For example, instead of

+
+# mz eth0 -t udp sp=1, dp=80, p=00:11:22:33
+
+

you could either omit the spaces

+
+# mz eth0 -t udp sp=1,dp=80,p=00:11:22:33
+
+

or, even more safe, use quotes:

+
+# mz eth0 -t udp "sp=1, dp=80, p=00:11:22:33"
+
+

In order to monitor what's going on you can enable the verbose mode using +the -v option. The opposite is the quiet mode (-q) which will keep +Mausezahn absolutely quiet (except for error messages and warnings.)

+

Don't confuse the payload argument p=... with the padding option +-p. The latter is used outside the quotes!

+
+
+

4.3   The automatic packet builder

+

An important argument is "-t" which invokes a packet builder. Currently there +are packet builders for ARP, BPDU, CDP, IP, partly ICMP, UDP, TCP, RTP, DNS, +and SYSLOG. (Additionally you can insert a VLAN tag or a MPLS label stack but +this works independent of the packet builder.)

+

You get context specific help of every packet builder using the help +keyword, such as:

+
+# mz -t bpdu help
+# mz -t tcp help
+
+

For every packet you may specify an optional payload. This can be done either +via HEX notation using the payload (or short p) argument or directly +as ASCII text using the -P option:

+
+mz eth0 -t ip -P "Hello World"                           # ASCII payload
+mz eth0 -t ip p=68:65:6c:6c:6f:20:77:6f:72:6c:64         # hex payload
+mz eth0 -t ip "proto=89,                           \
+               p=68:65:6c:6c:6f:20:77:6f:72:6c:64, \     # same with other
+               ttl=1"                                    # IP arguments
+
+

Note: The raw link access mode only accepts hex payloads (because you specify +everything in hex here.)

+
+
+

4.4   Packet count and delay

+

Per default only one packet is sent. If you want to send more packets then use +the count option -c <count>. When count is zero then Mausezahn will send +forever.

+

Per default Mausezahn sends at maximum speed (and this is really fast ;-)). +If you don't want to overwhelm your network devices or have other reasons to +send at a slower rate then you might want to specify a delay using the +-d <delay> option.

+

If you only specify a numeric value it is interpreted in microsecond units. +Alternatively, for easier use, you might specify units such as seconds sec +or milliseconds msec. (You can also abbreviate this with s or m.)

+

Note: Don't use spaces between the value and the unit!

+

Here are typical examples:

+

Send infinite frames as fast as possible:

+
+# mz -c 0  "aa bb cc dd ...."
+
+

Send 100,000 frames with a 50 msec interval:

+
+# mz -c 100000  -d 50msec "aa bb cc dd ...."
+
+

Send infinite BPDU frames in a 2 second interval:

+
+# mz -c 0 -d 2s -t bpdu conf
+
+

Note: Mausezahn does not support fractional numbers. If you want to +specify for example 2.5 seconds then express this e. g. in milliseconds (2500 +msec).

+
+
+

4.5   Source and destination addresses

+

As mnemonic trick keep in mind that all packets run from "A" to "B".

+

You can always specify source and/or destination MAC addresses using the +-a and -b options, respectively. These options also allow keywords +such as rand, own, bpdu, cisco, and others.

+

Similarily you can specify source and destination IP addresses using the +-A and -B options, respectively. These options also support FQDNs +(i. e. domain names) and ranges such as 192.168.0.0/24 or +10.0.0.11-10.0.3.22. Additionally (only) the source address supports the +rand keyword (ideal for "attacks").

+

Note: When you use the packet builder for IP-based packets (e. g. UDP or +TCP) then Mausezahn automatically cares about correct MAC and IP addresses (i. +e. it performs ARP, DHCP, and DNS for you). But when you specify at least a +single link-layer address (or any other L2 option such as a VLAN tag or MPLS +header) then ARP is disabled and you must care for the Ethernet +destination address for yourself.

+
+
+
+

5   Layer-2

+ +
+

5.2   ARP

+

Mausezahn provides a simple interface to the ARP packet. You can specify the +ARP method (request|reply) and up to four arguments: sendermac, +targetmac, senderip, targetip, or short smac, tmac, +sip, tip.

+

By default an ARP reply is sent with your own interface addresses as source +MAC and IP address, and a broadcast destination MAC/IP address.

+

Send a gratitious ARP (as used for duplicate IP detection):

+
+mz eth0 -t arp
+
+

ARP cache poisoning:

+
+mz eth0 -t arp "reply, senderip=192.168.0.1, targetmac=00:00:0c:01:02:03, \
+                targetip=172.16.1.50"
+
+

where by default your interface MAC address will be used as sendermac, +senderip denotes the spoofed IP, targetmac and targetip identifies the +receiver.

+

By default the Ethernet source address is your interface MAC and the +destination address is broadcast. Of course you can change this using again +the flags -a and -b.

+
+
+

5.3   BPDU

+

Mausezahn provides a simple interface to the 802.1d BPDU frame format (used to +create the Spanning Tree in bridged networks).

+

By default standard IEEE 802.1d (CST) BPDUs are sent and it is assumed that +your computer wants to become the root bridge (rid=bid).

+

Optionally the 802.3 destination address can be a specified MAC address, +broadcast, own MAC, or Cisco's PVST+ MAC address. The destination MAC can be +specified using the -b command which (besides MAC addresses) accepts keywords +such as bcast, own, pvst, or stp (default).

+

Since version 0.16 PVST+ is supported. Simply specify the VLAN for which +you want to send a BPDU:

+
+mz eth0 -t bpdu "vlan=123, rid=2000"
+
+

See mz -t bpdu help for more details.

+
+
+

5.4   CDP

+

Mausezahn can send Cisco Discovery Protocol (CDP) messages since this protocol +has security relevance. Of course lots of dirty tricks are possible; for +example arbitrary TLVs can be created (using the hex-payload argument for +example p=00:0e:00:07:01:01:90) and if you want to stress the CDP database +of some device, Mausezahn can send each CDP message with another system-id +using the change keyword:

+
+# mz -t cdp change -c 0
+
+

Some routers and switches may run into deep problems ;-)

+

See mz -t cdp help for more details.

+
+
+

5.5   802.1Q VLAN Tags

+

Mausezahn allows simple VLAN tagging for IP (and other higher layer) packets. +Simply use the option -Q <[CoS:]VLAN>, such as -Q 10 or -Q 3:921. +By default CoS=0.

+

For example send a TCP packet in VLAN 500 using CoS=7:

+
+mz eth0 -t tcp -Q 7:500 "dp=80, flags=rst, p=aa:aa:aa"
+
+

You can create as many VLAN tags as you want! This is interesting to create +QinQ encapsulations or VLAN hopping:

+
+# Send a UDP packet with VLAN tags 100 (outer) and 651 (inner)
+mz eth0 -t udp "dp=8888, sp=13442" -P "Mausezahn is great" -Q 100,651
+
+# Don't know if this is useful anywhere but at least it is possible:
+mz eth0 -t udp "dp=8888, sp=13442" -P "Mausezahn is great"  \
+        -Q 6:5,7:732,5:331,5,6
+
+# Mix it with MPLS:
+mz eth0 -t udp "dp=8888, sp=13442" -P "Mausezahn is great" -Q 100,651 -M 314
+
+

Only in raw Layer 2 mode you must create the VLAN tag completely by +yourself. For example if you want to send a frame in VLAN 5 using CoS 0 simply +specify 81:00 as type field and for the next two bytes the CoS (, CFI) and +VLAN values:

+
+mz eth0 -b bc -a rand "81:00 00:05 08:00 aa-aa-aa-aa-aa-aa-aa-aa-aa"
+
+
+
+

5.6   MPLS labels

+

Mausezahn allows you to insert one or more MPLS headers. Simply use the option +-M <label:CoS:TTL:BoS> where only the label is mandatory. If you +specify a second number it is interpreted as the experimental bits (the +CoS usually). If you specify a third number it is interpreted as TTL. Per +default the TTL is set to 255.

+

The Bottom of Stack flag is set automatically (otherwise the frame would +be invalid) but if you want you can also set or unset it using the S (set) +and s (unset) argument. Note that the BoS must be the last argument in +each MPLS header definition.

+

Here are some examples:

+
+# Use MPLS label 214
+mz eth0 -M 214 -t tcp "dp=80" -P "HTTP..." -B myhost.com
+
+# Use three labels (the 214 is now the outer)
+mz eth0 -M 9999,51,214 -t tcp "dp=80" -P "HTTP..." -B myhost.com
+
+# Use two labels, one with CoS=5 and TTL=1, the other with CoS=7
+mz eth0 -M 100:5:1,500:7 -t tcp "dp=80" -P "HTTP..." -B myhost.com
+
+# Unset the BoS flag (which will result in an invalid frame)
+mz eth0 -M 214:s -t tcp "dp=80" -P "HTTP..." -B myhost.com
+
+
+
+
+

6   Layer 3-7

+

IP, UDP, and TCP packets can be padded using the -p option. Currently +0x42 is used as padding byte ('the answer'). You cannot pad DNS packets +(would be useless anyway).

+
+

6.1   IP

+

Mausezahn allows you to send any (malformed or correct) IP packet. Every field +in the IP header can be manipulated.

+

The IP addresses can be specified via the -A and -B options, denoting +the source and destination address, respectively. You can also specify an +address range or a host name (FQDN). Additionally, the source address can also +be random.

+

By default the source address is your interface IP address and the destination +address is a broadcast.

+

Here are some examples:

+
+# ascii payload:
+mz eth0 -t ip -A rand -B 192.168.1.0/24  -P "hello world"
+
+# hex payload:
+mz eth0 -t ip -A 10.1.0.1-10.1.255.254 -B 255.255.255.255 p=ca:fe:ba:be
+
+# will use correct source IP address:
+mz eth0 -t ip -B www.xyz.com
+
+

The Type of Service (ToS) byte can either be specified directly by two +hexadecimal digits (which means you can also easily set the Explicit +Congestion Notification (ECN) bits (LSB 1 and 2) or you may only want to +specify a common DSCP value (bits 3-8) using a decimal number (0..63):

+
+# Packet sent with DSCP = Expedited Forwarding (EF):
+mz eth0 -t ip dscp=46,ttl=1,proto=1,p=08:00:5a:a2:de:ad:be:af
+
+

If you leave the checksum zero (or unspecified) the correct checksum will +be automatically computed. Note that you can only use a wrong checksum when +you also specify at least one L2 field manually (because then the packet is +not sent through the kernel).

+
+
+

6.2   UDP

+

Mausezahn support easy UDP datagram generation. Simply specify the destination +address (-B option) and optionally an arbitrary source address (-A +option) and as arguments you may specify the port numbers using the dp +(destination port) and sp (source port) arguments and a payload.

+

You can also easily specify a whole port range which will result in sending +multiple packets. Here are some examples:

+

Send test packets to the RTP port range:

+
+mz eth0 -B 192.168.1.1 -t udp "dp=16384-32767, \
+   p=A1:00:CC:00:00:AB:CD:EE:EE:DD:DD:00"
+
+

Send a DNS request as local broadcast (often a local router replies):

+
+mz eth0 -t udp dp=53,p=c5-2f-01-00-00-01-00-00-00-00-00-00-03-77-77-\
+                       77-03-78-79-7a-03-63-6f-6d-00-00-01-00-01"
+
+

Additionally you may specify the lenght and checksum using the len and +sum arguments (will be set correctly by default).

+
+
Note
+
Several protocols have same arguments such as len (length) and +sum (checksum). If you specified a udp type packet (via -t udp) and +want to modify the IP length, then use the alternate keyword iplen and +ipsum. Also note that you must specify at least one L2 field which tells +Mausezahn to build everything without help of your kernel (the kernel would +not allow to modify the IP checksum and the IP length).
+
+
+
+

6.3   ICMP

+

Mausezahn currently only supports the following ICMP methods:

+
    +
  • PING (echo request)
  • +
  • Redirect (various types)
  • +
  • Unreachable (various types)
  • +
+

Additional ICMP types will be supported in future. Currently you would need to +taylor them by your own, e. g. using the IP packet builder (setting proto=1).

+

Use the mz -t icmp help for help on actually implemented options.

+
+
+

6.4   TCP

+

Mausezahn allows you to easily taylor any TCP packet. Similar as with UDP you +can specify source and destination port (ranges) using the sp and dp +arguments.

+

Then you can directly specify the desired flags using an "|" as delimiter +if you want to specify multiple flags. For example, a SYN-Flood attack against +host 1.1.1.1 using a random source IP address and periodically using all 1023 +well-known ports could be created via:

+
+mz eth0 -A rand -B 1.1.1.1 -c 0 -t tcp "dp=1-1023, flags=syn"  \
+        -P "Good morning! This is a SYN Flood Attack.          \
+            We apologize for any inconvenience."
+
+

Be careful with such SYN floods and only use them for firewall testing. Check +your legal position!

+

Remember that a host with an open TCP session only accepts packets with +correct socket information (addresses and ports) and a valid TCP sequence +number (SQNR). If you want to try a DoS attack by sending a RST-flood and you +do NOT know the target's initial SQNR (which is normally the case) then you +may want to sweep through a range of sequence numbers:

+
+mz eth0 -A legal.host.com -B target.host.com \
+        -t tcp "sp=80,dp=80,s=1-4294967295"
+
+

Fortunately the SQNR must match the target host's acknowledgement number plus +the announced window size. Since the typical window size is something between +40000 and 65535 you are MUCH quicker when using an increment using the ds +argument:

+
+mz eth0 -A legal.host.com -B target.host.com \
+        -t tcp "sp=80, dp=80, s=1-4294967295, ds=40000"
+
+

In the latter case Mausezahn will only send 107375 packets instead of +4294967295 (which results in a duration of approximately 1 second compared to +11 hours!).

+

Of course you can taylor any TCP packet you like. In future Mausezahn may +support an automatic 3-way handshake.

+

As with other L4 protocols Mausezahn builds a correct IP header but you can +additionally access every field in the IP packet (also in the Ethernet frame).

+
+
+

6.5   DNS

+

Mausezahn supports UDP-based DNS requests or responses. Typically you may want +to send a query or an answer. As usual you can modify every flag in +the header. Here is an example of a simple query:

+
+./mz eth0 -B mydns-server.com -t dns "q=www.ibm.com"
+
+

You can also create server-type messages:

+
+./mz eth0 -A spoofed.dns-server.com -B target.host.com \
+                   "q=www.topsecret.com, a=172.16.1.1"
+
+

The syntax according to the online help (-t dns help) is:

+
+query|q = <name>[:<type>]  ............. where type is per default "A"
+                                         (and class is always "IN")
+
+answer|a = [<type>:<ttl>:]<rdata> ...... ttl is per default 0.
+         = [<type>:<ttl>:]<rdata>/[<type>:<ttl>:]<rdata>/...
+
+

Note: If you only use the 'query' option then a query is sent. If you +additonally add an 'answer' then an answer is sent.

+
+

Examples:

+
+q = www.xyz.com
+q = www.xyz.com, a=192.168.1.10
+q = www.xyz.com, a=A:3600:192.168.1.10
+q = www.xyz.com, a=CNAME:3600:abc.com/A:3600:192.168.1.10
+
+
+

Please try out mz -t dns help to see the many other optional command line +options.

+
+
+

6.6   RTP and VoIP path measurements

+

Mausezahn can send arbitrary Real Time Protocol (RTP) packets. Per default a +classical G.711 codec (20 ms segment size, 160 bytes) is assumed.

+

You can measure jitter, packet loss and reordering along a path between two +hosts running Mausezahn. The jitter measurement is either done following the +variance low-pass filtered estimation specified in RFC 3550 or using an +alternative "real-time" method which is even more precise (the RFC-method is +used by default).

+

For example on Host1 you start a transmission process:

+
+# mz -t rtp -B 192.168.1.19
+
+

And on Host2 (192.168.1.19) a receiving process which performs the measurement:

+
+# mz -T rtp
+
+

Note that the option flag with the capital "T" means that it is a server RTP +process, waiting for incoming RTP packets from any Mausezahn source.

+

In case you want to restrict the measurement to a specific source or you want +to perform a bidirectional measurement, you must specify a stream +identifier.

+

Here is an example for bidirectional measurements which logs the running jitter average +in a file:

+
+Host1# mz -t rtp id=11:11:11:11 -B 192.168.2.2 &
+Host1# mz -T rtp id=22:22:22:22 "log, path=/tmp/mz/"
+
+Host2# mz -t rtp id=22:22:22:22 -B 192.168.1.1 &
+Host2# mz -T rtp id=11:11:11:11 "log, path=/tmp/mz/"
+
+

In any case the measurements are printed continuously onto the screen; by +default it looks like this:

+
+0.00                     0.19                      0.38                      0.57
+|-------------------------|-------------------------|-------------------------|
+#########                                                                      0.07 msec
+####################                                                           0.14 msec
+##                                                                             0.02 msec
+###                                                                            0.02 msec
+#########                                                                      0.07 msec
+####                                                                           0.03 msec
+#########                                                                      0.07 msec
+#############                                                                  0.10 msec
+##                                                                             0.02 msec
+###########################################                                    0.31 msec
+#########                                                                      0.07 msec
+##############################################                                 0.33 msec
+###############                                                                0.11 msec
+##########                                                                     0.07 msec
+###############                                                                0.11 msec
+##########################################################                     0.42 msec
+#####                                                                          0.04 msec
+
+

More information is shown using the txt keyword:

+
+# mz -T rtp txt
+Got 100 packets from host 192.168.0.3: 0 lost (0 absolute lost), 1 out of order
+  Jitter_RFC (low pass filtered) = 30 usec
+  Samples jitter (min/avg/max)   = 1/186/2527 usec
+  Delta-RX (min/avg/max)         = 2010/20167/24805 usec
+
+Got 100 packets from host 192.168.0.3: 0 lost (0 absolute lost), 1 out of order
+  Jitter_RFC (low pass filtered) = 17 usec
+  Samples jitter (min/avg/max)   = 1/53/192 usec
+  Delta-RX (min/avg/max)         = 20001/20376/20574 usec
+
+Got 100 packets from host 192.168.0.3: 0 lost (0 absolute lost), 1 out of order
+  Jitter_RFC (low pass filtered) = 120 usec
+  Samples jitter (min/avg/max)   = 0/91/1683 usec
+  Delta-RX (min/avg/max)         = 18673/20378/24822 usec
+
+

See mz -t rtp help and mz -T rtp help for more details.

+
+
+

6.7   Syslog

+

The traditional Syslog protocol is widely used even in professional networks +and is sometimes vulnerable. For example you might insert forged Syslog +messages by spoofing your source address (e. g. impersonate the address of a +legitime network device):

+
+mz -t syslog sev=3 -P "You have been mausezahned." -A 10.1.1.109 -B 192.168.7.7
+
+

See mz -t syslog help for more details.

+
+
+
+

7   Dear users

+

Mausezahn is still under heavy development and you may expect new features +very soon.

+

Please report to herbert AT perihel DOT at regarding:

+
+
    +
  • Bugs
  • +
  • Important features you miss
  • +
  • How you used Mausezahn (I am really interested in practical problems)
  • +
  • Interesting observations with Mausezahn at the network
  • +
+
+
+
+ + diff --git a/doc/view_rtp_avg.py b/doc/view_rtp_avg.py new file mode 100755 index 0000000..2250d76 --- /dev/null +++ b/doc/view_rtp_avg.py @@ -0,0 +1,67 @@ +#!/usr/bin/env python + +# +# view_rtp_avg.py - A datafile viewer for Mausezahn +# Copyright (C) 2008 Herbert Haas +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 as published +# by the Free Software Foundation. +# +# 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, see http://www.gnu.org/licenses/gpl-2.0.html +# + + +from matplotlib import rcParams +from pylab import * +import sys + +if size(sys.argv) < 2: ### no data file given + print "You must specify a data file as argument!" + exit() + +print "Read datafile. Please be patient..." + +datfile = open(sys.argv[1],'r'); + +javg_list = [] + +for line in datfile.readlines(): + if len(line)>1: + words = line.split(" ") + if words[0]!='#': + javg = line.split(", "); + javg_list.append(float(javg[4])/1000) # [x] means xth column (columns 0,1,..) + #### column 2 is jitter, column 4 is RFC 3550 jitter + +datfile.close() + +print "Data imported. Now calculating statistics..." + +# Erase statistical exceptions + +javg_list_ordered = sort(javg_list) + +s = size(javg_list_ordered); +s=int(s*0.95) ## assume that at maximum 5% of the data are exceptions + +print "Will remove exceptional data points. A total of %d packets will be processed." % (s, ) +## the histogram of the data +n, bins, patches = hist(javg_list_ordered[1:s], 100, normed=False) + +## add a 'best fit' line +#y = normpdf( bins, mu, sigma) +#l = plot(bins, y, 'r--', linewidth=2) +#xlim(40, 160) + +xlabel('Jitter (msec)') +ylabel('Number of Packets') +title("Average Jitter Probability") +#title(r'$\rm{IQ:}\/ \mu=100,\/ \sigma=15$') +####suptitle("Mausezahn Viewer (Avg RTP Jitter)") +show() diff --git a/install_manifest.txt b/install_manifest.txt new file mode 100644 index 0000000..f22255d --- /dev/null +++ b/install_manifest.txt @@ -0,0 +1,8 @@ +/usr/local/sbin/mz +/usr/local/share/man/man1/mz.1 +/usr/local/share/man/man1/mz.cfg.1 +/usr/local/share/doc/mz/mzguide.html +/usr/local/share/doc/mz/mops.html +/usr/local/share/doc/mz/view_rtp_avg.py +/usr/local/share/doc/mz/example_lldp.conf +/usr/local/share/doc/mz/README.example diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..c437116 --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,69 @@ +SET (SRCS + mz.c + layer1.c + layer2.c + layer3.c + layer4.c + init.c + hextools.c + tools.c + lookupdev.c + time.c + modifications.c + send_eth.c + send.c + cdp.c + rtp.c + dns.c + rcv_rtp.c + syslog.c + cli.c + cli_cmds.c + cli_launch.c + cli_legacy.c + cli_packet.c + cli_interface.c + cli_set.c + cli_dns.c + cli_arp.c + cli_bpdu.c + cli_eth.c + cli_ip.c + cli_udp.c + cli_tcp.c + cli_rtp.c + cli_tools.c + tx_switch.c + mops.c + mops_update.c + mops_tools.c + mops_checksums.c + mops_threads.c + mops_dot1Q.c + mops_mpls.c + mops_ip.c + mops_tcp.c + mops_ext.c + mops_ext_arp.c + mops_ext_bpdu.c + mops_ext_rtp.c + parse_xml.c + automops.c + mopsrx_arp.c + mops_ext_igmp.c + mops_ext_lldp.c + cli_igmp.c + cli_lldp.c + cli_sequence.c + mops_sequence.c + llist.c + directmops.c + mz.h + cli.h + mops.h + llist.h) + +add_executable (mz ${SRCS}) +target_link_libraries (mz net pcap rt cli pthread m) + +install(TARGETS mz DESTINATION sbin) diff --git a/src/automops.c b/src/automops.c new file mode 100644 index 0000000..b465346 --- /dev/null +++ b/src/automops.c @@ -0,0 +1,1013 @@ +/* + * Mausezahn - A fast versatile traffic generator + * Copyright (C) 2008-2010 Herbert Haas + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 as published by the + * Free Software Foundation. + * + * 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, see http://www.gnu.org/licenses/gpl-2.0.html + * +*/ + +#include "mz.h" +#include "mops.h" + + +// Creates first element, aka "head" element +// This element can also be used! See automops_alloc_protocol! +// +struct automops * automops_init() +{ + // Create initial automops element: + struct automops *new_automops = (struct automops*) malloc(sizeof(struct automops)); + new_automops->next = new_automops; + new_automops->prev = new_automops; + automops_set_defaults (new_automops); + new_automops->used = -1; // freshly created, no valid data in it + return new_automops; +} + + + +// (Re-)sets anything within the specified automops element +void automops_set_defaults(struct automops * cur) +{ + int i; + + mz_strncpy(cur->name, "user_proto", 16); + mz_strncpy(cur->desc, "undefined", 16); + cur->layers_on = 0; + cur->layers_off = 0; + + cur->etype = 0; + cur->proto = 0; + for (i=0; i<6; i++) { + cur->sa[i] = 0x00; + cur->da[i] = 0xff; // bcast (silly?) + } + cur->SA = cur->DA = 0; + cur->sp = cur->dp = 0; + + cur->payload_type = 0; // both ascii or hex + cur->payload = NULL; + cur->payload_s = 0; + cur->defined_externally = -1; // undefined + if (cur->field != NULL) automops_delete_fields (cur); + cur->field = NULL; +} + + +// Returns pointer to new automops element: +// 1) either insert a new automops element in list +// 2) or returns same pointer again if current automops element is empty +// Note that new element N is always PREPENDED to cur: +// ... = N-2 = N-1 = N = cur = 1 = 2 = ... +// Therefore, cur should be typically a pointer to the head element +// +struct automops * automops_alloc_protocol(struct automops *cur) +{ + struct automops *new_automops; + + if (cur->used == -1) // allows to use head element in list + { + new_automops = cur; // current automops was unused => no need to insert a new one! + } + else // create new automops element + { + new_automops = (struct automops *) malloc(sizeof(struct automops)); + if (new_automops==NULL) + { + fprintf(stderr, "MZ alert: cannot create new automops entry - memory full?\n"); + return NULL; // memory full? + } + new_automops->field=NULL; // don't remove this! See automops_set_defaults() to understand. + automops_set_defaults(new_automops); + } + + new_automops->used=0; + + // append to doubly linked list + new_automops->prev = cur->prev; + new_automops->next = cur; + cur->prev = new_automops; + new_automops->prev->next = new_automops; + + return new_automops; +} + + +// Delete particular protocol (remove it from list or mops). +// +// If amp_head is deleted, makes previous element amp_head. +// Note that the global amp_head must exist but within MOPS this +// is always the case. +// +// RETURN VALUE: +// +// - pointer to previous element in the list +// - NULL if current automops is used by some mops(es) +// (in this case, we may still need it, maybe the user wants +// to modify data or wants other information...?) +// +// - NULL if current element is a single element attached to a mops +// +struct automops * automops_delete_protocol(struct automops *cur) +{ + struct automops *last; + + // Maybe the following is not really practical? ///// + if (cur->used>0) { + return NULL; + } + ///////////////////////////////////////////////////// + + // delete fields list: + automops_delete_fields (cur); + + if (cur->payload_s) free (cur->payload); + + if ((cur!=amp_head) && (cur->prev==NULL) && (cur->next==NULL)) { + // this one is attached to a mops + if (cur!=NULL) free (cur); + return NULL; + } + + // part of linked list + last = cur->prev; + cur->next->prev = cur->prev; + cur->prev->next = cur->next; + if (cur==amp_head) { + amp_head = last; + } + if (cur!=NULL) free (cur); + + return last; +} + + + +// Search automops element for a given protocol name +// +// Returns pointer to that automops element +// or NULL if not found +// +struct automops * automops_search_protocol(struct automops *list, char *name) +{ + struct automops *head = list; + struct automops *cur = list; + + do { + if ( (strncasecmp(name, cur->name, + AUTOMOPS_MAX_NAME_LEN) == 0)) { + return cur; // FOUND! + } + cur = cur->next; + } while (head != cur); + + return NULL; // NOT FOUND! +} + + + +// Runs through all automops entries and dumps some basic info +// Returns the number of used protocols +// +int automops_dump_all(struct automops* list) +{ + struct automops *head = list; + struct automops *cur = list; + struct fields *f=NULL; + int anzmops=0, used=0; + char str[64], ft[32]; + uint32_t SA=0, DA=0; + uint8_t *x, *y; + char bits_on[18], bits_off[18]; + int i=0, j=0; + + do { + if (cur->used==-1) { + fprintf(stderr, "AUTOMOPS: Initial element\n"); + if ((cur->next==cur)&&(cur->prev==cur)) + fprintf(stderr, " No other elements found.\n"); + break; + } + if (cur->used>0) used++; + anzmops++; + SA=ntohl(cur->SA); x = (uint8_t*) &SA; + DA=ntohl(cur->DA); y = (uint8_t*) &DA; + char2bits(cur->layers_on, bits_on); + char2bits(cur->layers_off, bits_off); + fprintf(stderr, "Protocol %i: %s -- %s\n" + " Layercodes: X T U I M Q S E\n" + " requires %s (0x%02x)\n" + " conflicts %s (0x%02x)\n" + " L2: EtherType=%04x, sa=%02x:%02x:%02x:%02x:%02x:%02x, da=%02x:%02x:%02x:%02x:%02x:%02x\n" + , + anzmops, cur->name, cur->desc, + bits_on, cur->layers_on, bits_off, cur->layers_off, + cur->etype, cur->sa[0], cur->sa[1], cur->sa[2], cur->sa[3], cur->sa[4], cur->sa[5], + cur->da[0], cur->da[1], cur->da[2], cur->da[3], cur->da[4], cur->da[5]); + if (cur->layers_on&MOPS_IP) { + fprintf(stderr, " IP: proto=%i, SA=%u.%u.%u.%u, DA=%u.%u.%u.%u\n", + cur->proto, *x, *(x+1), *(x+2), *(x+3), *y, *(y+1), *(y+2), *(y+3)); + } else { + fprintf(stderr, " IP: ---\n"); + } + // Walk through field data: + f=cur->field; j=0; + while (f!=NULL) { + j++; // count number of fields + if (verbose) { + i=0; + if (f->longdesc!=NULL) { + mz_strncpy(str, f->longdesc, 60); + if (strnlen(str,60)>=59) i=1; + } + else { + mz_strncpy(str, "-- no long description specified --", 60); + } + amp_type2str(f->type, ft); + fprintf(stderr, " %02i Field [%i] '%s' -- %s\n" + " Description: %s%s\n" + " Type: %s %s %s (%lu/%lu) {%lu..%lu..%lu} shift: %i; %i chars\n" + ,f->i, f->index, f->name, f->shortdesc, + str, (i) ? "..." : "", + ft, (f->constant) ? "FIXED" : "", + (f->valname!=NULL) ? f->valname : "(no value name)" , + (long unsigned int) f->tlv_type, + (long unsigned int) f->tlv_len, + (long unsigned int) f->min, + (long unsigned int) f->val, + (long unsigned int) f->max, + f->leftshift, f->str_s); + } + f=f->next; + } + if (verbose==0) fprintf(stderr, " %i fields defined.\n", j); + //--------------------------------- + cur = cur->next; + } while (head != cur); + + return used; +} + + + +// Creates an independent automops element for mops +// (it will be not part of any linked list so, next=prev=NULL) +// +// RETURN VALUE: - Pointer to the cloned automops +// - NULL upon failure +// +struct automops * automops_clone_automops(struct automops * amp) +{ + struct automops *new_automops; + struct fields *f, *g, *h=NULL; + int i; + + // Allocate memory + new_automops = (struct automops *) malloc(sizeof(struct automops)); + if (new_automops==NULL) { + fprintf(stderr, "MZ alert: cannot create new automops element - memory full?\n"); + return NULL; // memory full? + } + + // Copy the automops items + new_automops->next = NULL; + new_automops->prev = NULL; + + strncpy(new_automops->name, amp->name, AUTOMOPS_MAX_NAME_LEN); + strncpy(new_automops->desc, amp->desc, AUTOMOPS_MAX_SHORTDESC_LEN); + new_automops->layers_on = amp->layers_on; + new_automops->layers_off = amp->layers_off; + new_automops->etype = amp->etype; + new_automops->proto = amp->proto; + for (i=0; i<6; i++) { + new_automops->da[i] = amp->da[i]; // dst mac + new_automops->sa[i] = amp->sa[i]; // src mac + } + new_automops->DA = amp->DA; // dst IP + new_automops->SA = amp->SA; // src IP + new_automops->dp = amp->dp; // dst port + new_automops->sp = amp->sp; // src port + new_automops->defined_externally = amp->defined_externally; + new_automops->payload_type = amp->payload_type; + if (amp->payload_s) { + new_automops->payload = (char*) malloc(amp->payload_s); + if (new_automops->payload==NULL) { + fprintf(stderr, "MZ alert: cannot create new automops payload element - memory full?\n"); + return NULL; // memory full? + } + memcpy((void*) new_automops->payload, amp->payload, amp->payload_s); + } + + new_automops->used = amp->used; + + //////////////////////////////////////////////////////////////////////////////////////////////// + // + // Copy the fields list + // + new_automops->field = NULL; + for (f=amp->field; f!=NULL; f=f->next) { + g = (struct fields *) malloc(sizeof(struct fields)); + if (g==NULL) { + fprintf(stderr, "MZ alert: cannot create new field element - memory full?\n"); + return NULL; // memory full? + } + if (new_automops->field==NULL) { // first element + new_automops->field = g; + h = g; + } else { // next elements + h->next = g; + h = g; + } + // copy all data. From here on 'h' is the new one, 'f' is the existing one + mz_strncpy(h->name, f->name, AUTOMOPS_MAX_NAME_LEN); + mz_strncpy(h->shortdesc, f->shortdesc, AUTOMOPS_MAX_SHORTDESC_LEN); + mz_strncpy(h->valname, f->valname, AUTOMOPS_MAX_NAME_LEN); + if (f->longdesc!=NULL) { + h->longdesc = (char*) + malloc(strnlen(f->longdesc, 1600)); // 80 chars x 20 lines should be enough + if (h->longdesc == NULL) { + fprintf(stderr, "MZ alert: cannot allocate memory!\n"); + return NULL; // memory full? + } + strncpy(h->longdesc, f->longdesc, 1600); + } + if (f->str_s) { + h->str_s = f->str_s; + h->str = (u_int8_t *) malloc(f->str_s); + if (h->str == NULL) { + fprintf(stderr, "MZ alert: cannot allocate memory!\n"); + return NULL; // memory full? + } + memcpy((void*) h->str, (void*) f->str, f->str_s); + } + h->constant = f->constant; + h->type = f->type; + h->tlv_type = f->tlv_type; + h->tlv_len = f->tlv_len; + h->val = f->val; + h->min = f->min; + h->max = f->max; + h->leftshift = f->leftshift; + h->index = f->index; + } + return new_automops; +} + + +// Add a new field object +struct fields * automops_add_field (struct automops *amp) +{ + struct fields *f, *f_prev=NULL, *g; + int i=0; + + // jump to the end of the fields list + f=amp->field; + while (f!=NULL) { + f_prev=f; + ++i; + f=f->next; + } + + g = (struct fields *) malloc(sizeof(struct fields)); + if (g==NULL) { + if (verbose) fprintf(stderr, "MZ alert: cannot create new field element - memory full?\n"); + return NULL; // memory full? + } + + if (amp->field==NULL) { // is is first element in amp + amp->field = g; + } else { // it is just another element in the fields list + f_prev->next = g; + } + g->next=NULL; // 'pointing to NULL' identifies the last element + g->i=i; // each field element has a unique internal number + g->index=0; // indicates 'empty' field + automops_field_set_defaults(g); + return g; +} + + +// Typically only used by automops_add_field() +// Only call this function after creating a new field element +void automops_field_set_defaults(struct fields *f) +{ + f->name[0]=0x00; + f->shortdesc[0]=0x00; + f->longdesc=NULL; + f->constant=0; + + //NOTE: f->i MUST NOT be reset! + f->index=0; + f->valname[0]=0x00; + f->tlv_type=0; + f->tlv_len=0; + f->val=0; + f->min=0; + f->max=0; + f->leftshift=0; + f->str=NULL; + f->str_s=0; + f->next=NULL; +} + + +// Returns integer equivalent for a string of basic protocols. +// For example returns MOPS_ETH | MOPS_IP for d="eth ip". +// See the definitions in mops.h. +// +// NOTE: Does (and must) NOT verify whether items are conflicting +// such as "udp tcp". This task MUST be done by callee, otherwise +// this function's purpose would be not generic enough. +// +// RETURN VALUE: +// The sum of basic protocols +// or -1 upon failure. +int mops_str2layers(char *d) +{ + int ret=0; + char *tok; + + // dissalow too long strings. + if (strlen(d)>50) return -1; // TODO: replace 100 to a more reasonable value + + tok=strtok(d, " "); + while (tok!=NULL) { + if (strncasecmp("eth", d, 10)==0) ret |= MOPS_ETH; + else + if (strncasecmp("snap", d, 10)==0) ret |= MOPS_SNAP; + else + if (strncasecmp("dot1q", d, 10)==0) ret |= MOPS_dot1Q; + else + if (strncasecmp("mpls", d, 10)==0) ret |= MOPS_MPLS; + else + if (strncasecmp("ip", d, 10)==0) ret |= MOPS_IP; + else + if (strncasecmp("udp", d, 10)==0) ret |= MOPS_UDP; + else + if (strncasecmp("tcp", d, 10)==0) ret |= MOPS_TCP; + else + return -1; // unknown + tok=strtok(NULL, " "); + } + return ret; +} + +// Returns one of 'enum fieldtypes' for a given ascii string +// or -1 if unknown field type given. +int amp_str2type(char *d) +{ + if (strncasecmp("byte8", d, 10)==0) return Byte8; + if (strncasecmp("byte16", d, 10)==0) return Byte16; + if (strncasecmp("byte32", d, 10)==0) return Byte32; + if (strncasecmp("flaginbyte", d, 16)==0) return Flag_in_Byte; + if (strncasecmp("multibytes", d, 16)==0) return MultiBytes; + if (strncasecmp("multibyteshex", d, 16)==0) return MultiBytesHex; + if (strncasecmp("tlv", d, 10)==0) return TLV; + return -1; +} + +// Converts integer field types into ascii string s[32]. +// Returns 0 upon success, 1 if unknown type +int amp_type2str(int t, char *s) +{ + switch (t) { + case Byte8: + mz_strncpy(s, "Byte8", 32); + break; + case Byte16: + mz_strncpy(s, "Byte16", 32); + break; + case Byte32: + mz_strncpy(s, "Byte32", 32); + break; + case Flag_in_Byte: + mz_strncpy(s, "FlagInByte", 32); + break; + case MultiBytes: + mz_strncpy(s, "MultiBytes", 32); + break; + case MultiBytesHex: + mz_strncpy(s, "MultiBytesHex", 32); + break; + case TLV: + mz_strncpy(s, "TLV", 32); + break; + default: + mz_strncpy(s, "[unknown/same]", 32); + return 1; + } + return 0; +} + + +// Searches the automops object with specified name 'd'. +// NOTE: The names are case insensitive! +// +// RETURN VALUE: pointer to that object +// or NULL if not found +// +struct automops * amp_getamp_byname(struct automops *head, char *d) +{ + struct automops *a; + a = head; + do { + if (strncasecmp(a->name, d, AUTOMOPS_MAX_NAME_LEN)==0) return a; + a=a->next; + } while (a!=head); + return NULL; // not found +} + + +// Add data 'd' identified by tag 'xntag' to the automops entry 'amp'. +// +// RETURN VALUE: 0 upon success, 1 upon failure +// +int amp_add_pentry (struct automops *amp, int xntag, char *d) +{ + int i=0; + char *tok; + u_int8_t x[MAX_MOPS_MSG_SIZE]; + struct automops *g; + + switch (xntag) { + case xml_name: + if (strpbrk(d," \t")!=NULL) return ampInvalidName; // name must not consist of multiple words! + g = amp_getamp_byname(amp_head, d); + if (g!=NULL) return ampDuplicateName; // name already exists! + mz_strncpy(amp->name, d, AUTOMOPS_MAX_NAME_LEN); + if (verbose==2) { + fprintf(stderr, "Adding protocol '%s'\n", amp->name); + } + break; + + case xml_desc: + mz_strncpy(amp->desc, d, AUTOMOPS_MAX_SHORTDESC_LEN); + break; + + case xml_requires: + i = mops_str2layers(d); + if (i==-1) return ampInvalidLayer; + if ((i&MOPS_UDP) && (i&MOPS_TCP)) return ampTCPandUDP; // cannot require both! + amp->layers_on |= i; // must be ORed because several same-tags allowed + break; + + case xml_conflicts: + i = mops_str2layers(d); + if (i==-1) return ampInvalidLayer; + amp->layers_off |= i; // must be ORed because several same-tags allowed + break; + + case xml_payloadtype: // 0=none, 1=ascii, 2=hex, 3=any + tok = strtok (d," "); + while (tok!=NULL) { + if (strncasecmp("allowed", d, 10)==0) { + // only change if payload_type is still zero + if (amp->payload_type==0) amp->payload_type=3; + } else + if (strncasecmp("ascii", d, 10)==0) amp->payload_type|=1; + else + if (strncasecmp("hex", d, 10)==0) amp->payload_type|=2; + else + if (strncasecmp("any", d, 10)==0) amp->payload_type=3; + else + if (strncasecmp("none", d, 10)==0) amp->payload_type=0; + else return ampPayloadType; // unknown + tok=strtok(NULL, " "); + } + break; + + case xml_payload: + i=strnlen(d,MAX_MOPS_MSG_SIZE); + if (i==MAX_MOPS_MSG_SIZE) return ampPayloadLen; + amp->payload = (char*) malloc (i+1); + mz_strncpy(amp->payload, d, i+1); + amp->payload_s = i; + break; + + case xml_payloadhex: + i=str2hex(d,x,MAX_MOPS_MSG_SIZE); + if (i==MAX_MOPS_MSG_SIZE) return ampPayloadLen; + if (i==-1) return 1; + amp->payload = (char*) malloc (i+1); + memcpy((void*)amp->payload, (void*) x, i); + amp->payload_s = i; + break; + + default: + return ampUnknownTag; + + } + return 0; +} + +// Checks if given index value would be valid for the specified amp. +// (Index values must increase monotonic, successive same-values are +// allowed, step size is 0 or 1 but not greater. First index value +// must be 1. Example: 1,2,2,2,3,4,5,5,5,5,5,6,7,7,7.) +// +// RETURN VALUE: 0 if ok, 1 if wrong +// +int amp_checkindex(struct automops *amp, int i) +{ + int last_i=0; + struct fields *g, *h=NULL; + + g=amp->field; + while (g!=NULL) { // jump to last field object P->F1->F2->NULL + if (g->index==0) break; // stop if empty field object found + h=g; + g=g->next; + } // now h is the penultimate object +// printf("CHECKINDEX: try for %i, amp='%s' -- field '%s', index %i, [%i]\n", +// i, amp->name, h->name, h->index, h->i); + if (h==NULL) return 0; // first element, so any i is ok + last_i=h->index; + if (i1) return 1; // index increase step larger 1! + return 0; +} + + + +// Searches the field object with specified name 'd'. +// NOTE: The names ar case insensitive! +// +// RETURN VALUE: pointer to that object +// or NULL if not found +// +struct fields * amp_getfield_byname(struct automops *amp, char *d) +{ + struct fields *f; + + f = amp->field; + + while (f!=NULL) { + if (strncasecmp(f->name, d, AUTOMOPS_MAX_NAME_LEN)==0) return f; + f=f->next; + } + return NULL; // not found +} + + + +// This strange function ensures that 'w' consists of a single word. +// If 'w' consists of multiple words, it removes all but the first +// word. Additionally surrounding spaces are removed. +// +// RETURN VALUE: number of words found +// +// EXAMPLE: "Hello world" => "Hello" +// (return value = 2) +// +int ensure_single_word(char *w) +{ + char *t, *t0; + int i=0; + + t=strtok(w," "); + t0=t; + while (t!=NULL) { + i++; + t=strtok(NULL, " "); + } + mz_strncpy(w, t0, AUTOMOPS_MAX_NAME_LEN); + return i; +} + + + + +// Add data 'd' identified by tag 'xntag' to the field entry 'f' +int amp_add_fentry (struct automops *amp, struct fields *f, int xntag, char *d) +{ + int i=0; + unsigned long long int ulli=0; + struct fields *g=NULL; + + switch(xntag) { + case xml_index: + i = (int) str2int(d); + if (amp_checkindex(amp, i)) return ampInvalidIndex; // invalid index + f->index = (int) i; + break; + + case xml_name: + if (ensure_single_word(d)>1) return ampInvalidName; // name must be single word + g = amp_getfield_byname(amp, d); + if (g!=NULL) return 1; // name already exists + mz_strncpy(f->name, d, AUTOMOPS_MAX_NAME_LEN); + break; + + case xml_desc: + mz_strncpy(f->shortdesc, d, AUTOMOPS_MAX_SHORTDESC_LEN); + break; + + case xml_longdesc: + i = strnlen(d, 400); + if (i==400) return ampDescTooLong; + f->longdesc = (char*) malloc(i+1); + mz_strncpy(f->longdesc, d, i+1); + break; + + case xml_type: + i = amp_str2type(d); + if (i==-1) return ampInvalidType; + f->type = i; + break; + + case xml_constant: + if (strncasecmp(d, "yes", 6)==0) f->constant=1; + else + if (strncasecmp(d, "no", 6)==0) f->constant=0; + else return ampUnknownKeyword; // unknown keyword + break; + + case xml_valname: + if (ensure_single_word(d)>1) return ampSingleWordRequired; // name must be single word + i = strnlen(d, AUTOMOPS_MAX_NAME_LEN); + if (i==AUTOMOPS_MAX_NAME_LEN) return 1; // too long + mz_strncpy(f->valname, d, AUTOMOPS_MAX_NAME_LEN); + break; + + case xml_value: + ulli = str2lint(d); + if (ulli>0xffffffff) return ampRangeError; + f->val = (u_int32_t) ulli; + break; + + case xml_min: + ulli = str2lint(d); + if (ulli>0xffffffff) return ampRangeError; + f->min = (u_int32_t) ulli; + break; + + case xml_max: + ulli = str2lint(d); + if (ulli>0xffffffff) return ampRangeError; + if (ullimin) return 1; // max must be greater or equal min + f->max = (u_int32_t) ulli; + break; + + case xml_tlvt: + ulli = str2lint(d); + if (ulli>0xffffffff) return ampRangeError; + f->tlv_type = (u_int32_t) ulli; + break; + + case xml_tlvl: + ulli = str2lint(d); + if (ulli>0xffffffff) return ampRangeError; + f->tlv_len = (u_int32_t) ulli; + break; + + case xml_lshift: + i = (int) str2int(d); + if (i>7) return ampRangeError; + f->leftshift=i; + break; + + default: + return ampUnknownTag; // unknown tag + } + return 0; +} + + +// Delete linked list of field elements for a given automops +// Returns the number of deleted elements +int automops_delete_fields (struct automops *amp) +{ + struct fields * cur = amp->field; + struct fields * tmp; + int i=0; + + if (cur==NULL) return 0; + + do { + tmp = cur; + cur = cur->next; + if (tmp->str_s) { + if (tmp->str!=NULL) { + free (tmp->str); + tmp->str=NULL; + } + } + if (tmp->longdesc!=NULL) { + free(tmp->longdesc); + tmp->longdesc=NULL; + } + if (tmp!=NULL) { + free(tmp); + tmp=NULL; + } + i++; + } while (cur!=NULL); + + return i; +} + + + +// Deletes all elements except the specified element which us usually +// the head element. Also 'used' elements will be removed! +// +void automops_delete_all (struct automops *list) +{ + struct automops *head = list; + struct automops *cur = list->next; + struct automops *tmp; + + // Delete all but head element: + while (head != cur) + { + tmp = cur->next; + if (verbose) { + fprintf(stderr, " Deleting '%s'\n",cur->name); + } + automops_delete_protocol(cur); + cur = tmp; + } + head->next = head; + head->prev = head; + + if (verbose) { + fprintf(stderr, " Deleting '%s'\n",head->name); + } + + if (head->payload_s) { + if (head->payload!=NULL) { + free (head->payload); + head->payload=NULL; + } + } + automops_set_defaults(head); +} + + +// Completely clean up. +// After that, there is no automops list anymore. +// You only need this function when stopping mausezahn. +// +void automops_cleanup (struct automops *list) +{ + // 1) delete all elements except head: + automops_delete_all(list); + + // 2) delete head: + automops_delete_fields (list); + if (list->payload_s) { + if (list->payload!=NULL) { + free (list->payload); + list->payload=NULL; + } + } + if (list!=NULL) { + free(list); + list=NULL; + } +} + +// Converts amperr error values in 'e' to string messages 's' +// which must be at least 64 bytes in size. +// +// RETURN VALUE: 0 if convertable, 1 else +// +int amperr2str (int e, char *s) +{ + switch (e) { + + case ampSuccess: + break; + case ampInvalidIndex: + mz_strncpy(s, "invalid index", 64); + break; + case ampInvalidName: + mz_strncpy(s, "invalid name", 64); + break; + + case ampDuplicateName: + mz_strncpy(s, "duplicate name", 64); + break; + + case ampDescTooLong: + mz_strncpy(s, "description too long", 64); + break; + + case ampInvalidLayer: + mz_strncpy(s, "invalid layer", 64); + break; + + case ampTCPandUDP: + mz_strncpy(s, "either TCP or UDP", 64); + break; + + + case ampInvalidType: + mz_strncpy(s, "invalid type", 64); + break; + + case ampUnknownKeyword: + mz_strncpy(s, "unknown keyword", 64); + break; + + case ampSingleWordRequired: + mz_strncpy(s, "single word required", 64); + break; + + case ampRangeError: + mz_strncpy(s, "invalid range", 64); + break; + + case ampPayloadType: + mz_strncpy(s, "invalid payload type", 64); + break; + + case ampPayloadLen: + mz_strncpy(s, "payload length exceeded", 64); + break; + + + case ampUnknownTag: + mz_strncpy(s, "unknown tag (check mausezahn version?)", 64); + break; + + default: + mz_strncpy(s, "completely unknown cause", 64); + return 1; + } + return 0; +} + + + + +// Open amp file (containing XML data describing one or more protocols for automops) +// and copy the data into a char array. +// +// NOTE that the char array must be free'd by the caller. +// +// RETURN VALUE: - pointer to char array with the XML data +// - NULL upon failure +// +char * mapfile (char *fn) +{ + int i, c; + long fn_s; + FILE *fd; + char *blob; + + fd = fopen (fn, "r"); + if (fd==NULL) return NULL; + + // Determine length of file + (void) fseek(fd, 0L, SEEK_END); + fn_s = ftell(fd); + if (fn_s > AUTOMOPS_MAX_FILE_SIZE) { + fprintf(stderr, " File '%s' exceeds max allowed length (%lu>%i)\n", + fn, fn_s, AUTOMOPS_MAX_FILE_SIZE); + fclose(fd); + return NULL; + } + if (verbose) fprintf(stderr, " Parsing %lu bytes from '%s'...\n", fn_s, fn); + rewind(fd); + + blob = (char*) malloc(fn_s+1); + if (blob==NULL) { + fclose(fd); + return NULL; + } + + i=0; + while ((c=fgetc(fd)) != EOF) { + blob[i]=(char) c; + i++; + if (i>fn_s) { + fprintf(stderr, " WARNING: parsing '%s' exceeded EOF\n", fn); + break; // should not reach here + } + } + fclose(fd); + blob[i]='\0'; + return blob; +} + + + +// Create automops PDU within *mp based on data in *amp +// +int automops_update (struct mops *mp, struct automops *amp) +{ + + return 0; +} + diff --git a/src/cdp.c b/src/cdp.c new file mode 100644 index 0000000..d198d96 --- /dev/null +++ b/src/cdp.c @@ -0,0 +1,769 @@ +/* + * Mausezahn - A fast versatile traffic generator + * Copyright (C) 2008 Herbert Haas + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 as published by the + * Free Software Foundation. + * + * 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, see http://www.gnu.org/licenses/gpl-2.0.html + * +*/ + +///////////////////////////////////////////////////////////////////// +// +// Send CDP packets +// +///////////////////////////////////////////////////////////////////// + + +#include "mz.h" +#include "cli.h" + + +#define MZ_CDP_HELP \ + "| CDP type: Send arbitrary CDP packets.\n" \ + "| Note:\n" \ + "| - The Ethernet dst and src MAC addresses can be specified but can be also 'rand'.\n" \ + "| - If dst and src are NOT specified then practical defaults are used (src=own MAC, dst=01:00:0C:CC:CC:CC).\n" \ + "|\n" \ + "| ARGUMENT SYNTAX: -t cdp [arguments]\n" \ + "|\n" \ + "| ARGUMENTS:\n" \ + "|\n" \ + "| version ...... 0-255, default: 2\n" \ + "| ttl ...... 0-255, default: 180 s\n" \ + "| sum ...... 0000-ffff, default: automatically computed\n" \ + "|\n" \ + "| TLVs: Description: Example:\n" \ + "|\n" \ + "| tlv_id ....... Device ID Mausezahn station\n" \ + "| tlv_address ....... Sending interface address 10.1.1.2\n" \ + "| tlv_portid ....... Port Identifier 2/23\n" \ + "| tlv_cap ....... Capabilities (hex<7f) 2a\n" \ + "| tlv_version ....... Software Version ver3.0\n" \ + "| tlv_platform ....... Hardware Platform WS-C6509-E\n" \ + "| tlv_vtpdomain ....... VTP Management Domain MyVTPdomain\n" \ + "| tlv_native ....... Native VLAN number (0-4095) 42\n" \ + "| tlv_duplex ....... Full or half duplex full\n" \ + "| tlv_mgmt ....... Management IP address 192.168.1.2\n" \ + "|\n" \ + "| tlv .......... Create ANY TLV using the format: tlv=/, such as tlv=42/mausezahn\n" \ + "| Note: Currently you must omit spaces within ! Use underscore instead.\n" \ + "| tlvhex .......... Create ANY TLV and specify the value in hexformat, such as tlv=42/ca:fe:ba:be\n" \ + "| payload|p .......... Optional additional TLVs or any other bytes specified in hex\n" \ + "|\n" \ + "| When the tlv* arguments are used, the TLV length parameter is automatically set.\n" \ + "|\n" \ + "| The capability flags from MSB to LSB are:\n" \ + "| 0 - Repeater - IGMP - Host - Switch - SrcRouteBrdg - TranspBrdg - Router\n" \ + "|\n" \ + "| Optionally the keyword 'change' will create a different System name TLV every time a CDP\n" \ + "| packet is sent. This can be used to fill up a CDP database with different test values.\n" \ + "| Additionally use the '-a rand' command to use different source MAC addresses.\n" \ + "|\n" \ + "| EXAMPLES:\n" \ + "|\n" \ + "| Announce Device ID 'Espresso3000', Capabilities: Router, native VLAN 301:\n" \ + "| mz eth0 -t cdp \"tlv_id=Espresso3000, tlv_cap=01, tlv_native=301\"\n" \ + "|\n" \ + "| Create another TLV using the payload interface (here voice VLAN 400):\n" \ + "| mz eth0 -t cdp p=00:0e:00:07:01:01:90\n" + + + + + +u_int16_t checksum16 (u_int16_t len, u_int8_t buff[]) +{ + + u_int16_t word16; + u_int32_t sum=0; + u_int16_t i; + + // make 16 bit words out of every two adjacent 8 bit words in the packet and add them up + for (i=0; i>16) + sum = (sum & 0xFFFF)+(sum >> 16); + + // one's complement the result + sum = ~sum; + + return ((u_int16_t) sum); +} + + +// Creates a TLV and returns the whole length of the TLV +unsigned int create_tlv (u_int16_t type, // The 16-bit TYPE number + u_int8_t *value, // The VALUE as prepared hex-array + unsigned int value_len, // needed because VALUE maybe not \0 terminated + u_int8_t *target) // the RESULT i. e. the complete TLV +{ + unsigned int tlvlen; + u_int8_t *x; + + x = (u_int8_t*) &type; // set TYPE + target[0] = *(x+1); + target[1] = *(x); + + tlvlen = value_len + 4; // set VALUE + x = (u_int8_t*) &tlvlen; + target[2] = *(x+1); + target[3] = *(x); + + target+=4; + memcpy((void*) target, (void*) value, (size_t) value_len); + + return tlvlen; +} + + + + +// NOTE: The Length field indicates the total length, in bytes, of the type, length, and value fields! +// +// Interesting TLVs: +// +// TYPE VALUE +// 0001 Device-ID +// 0002 IP Addresses +// 0003 Port ID such as 2/22 +// 0004 Capabilities (Len=8, consists of flags only: Router, TBrdg, SRBrdgm, Switch, Host, IGMP, Repeater) +// 0005 SW Version +// 0006 Platform +// 0009 VTP Domain +// 000a Native VLAN, e.g. 00:0a 00:06 01:2d identifies native VLAN number 301 (=01:2d) +// 000b Duplex +// 000e VoIP VLAN, e.g. 00:0e 00:07 01 01:90 identifies DATA (=01) and VLAN 400 (=01:90) +// 0012 Trust Bitmap +// 0013 Untrusted Port CoS +// 0014 System Name (!!!) +// 0015 System Object Identifier +// 0016 Management Address (!!!), e.g. 0016 0011(=len 17) 00-00-00-01(=one IP only) 01-01-cc-00-04-90-fe-f8-10(=144.254.248.16) +// 0017 Location +// 001a Unknown (???) +// +// The IP address format is a bit strange as 0016 for example demonstrates... + + + +int send_cdp () +{ + libnet_t *l; + libnet_ptag_t t; + char + errbuf[LIBNET_ERRBUF_SIZE], + argval[1024]; + + u_int8_t + packet[MAX_PAYLOAD_SIZE], // this one will finally contain the whole cdp packet (without LLC/SNAP!) + *x, + value[1024], // USE THIS FOR ANYTHING YOU LIKE !!! + value1[1024], // This one is reserved for some code - Don't use it again! + value2[1024], // This one is reserved for some code - Don't use it again! + tlv[1024], + default_id[15] = "Mausezahn rules", + llcsnap[8]= + { + 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x0c, 0x20, 0x00 + }; + + unsigned int + len=0, + len1=0, + len2=0, + type1, + type2; + + u_int16_t + dummy16=0, + tlv_len=0; + + u_int32_t + next_pbyte=0, // points to the next free byte in tx.cdp_payload + dummy32=0, + packet_s; + + char + pld[2048]; + + + unsigned int i=0, count, delay; + int + eth_src_rand=0, + change_value=0; + long int j=0; + + + if (tx.dot1Q) + { + fprintf(stderr," Note: CDP mode does not support 802.1Q builder.\n"); + exit(1); + } + + if (tx.mpls) + { + fprintf(stderr," Note: CDP mode does not support MPLS builder.\n"); + exit(1); + } + + + if (getarg(tx.arg_string,"help", NULL)==1) + { + if (mz_port) + { + cli_print(gcli, "%s", MZ_CDP_HELP); + return -1; + } + else + { + fprintf(stderr,"\n" + MAUSEZAHN_VERSION + "\n%s", MZ_CDP_HELP); + exit(0); + } + + } + + /////////////////////////////////////////////////////////////////////// + // initial defaults: + if (tx.cdp_ttl==0) tx.cdp_ttl=0xb4; // 180 seconds + + if (tx.cdp_version==0) tx.cdp_version = 0x02; + + // The ID is the only required TLV + // If another function already specified it then it must also set the lenght: + if (tx.cdp_tlv_id_len==0) // not set + { + memcpy((void*) tx.cdp_tlv_id, (void*) default_id, 15); + tx.cdp_tlv_id_len=15; + } + + + + + /////////////////////////////////////////////////////////////////////// + // + // Now check for user arguments: + + + if ( (getarg(tx.arg_string,"version", argval)==1) || (getarg(tx.arg_string,"ver", argval)==1) ) + { + if (str2int(argval)>255) + { + fprintf(stderr," mz/send_cdp: version range exceeded, adjusted to max value.\n"); + tx.cdp_version = 0xff; + } + else + { + tx.cdp_version = (u_int8_t) str2int(argval); + } + } + + + if (getarg(tx.arg_string,"ttl", argval)==1) + { + if (str2int(argval)>255) + { + fprintf(stderr," mz/send_cdp: TTL range exceeded, adjusted to max value.\n"); + tx.cdp_ttl = 0xff; + } + else + { + tx.cdp_ttl = (u_int8_t) str2int(argval); + } + } + + if (getarg(tx.arg_string,"sum", argval)==1) + { + + if (strtol(argval,NULL,16)>65535) + { + fprintf(stderr," mz/send_cdp: checksum range exceeded, adjusted to max value.\n"); + tx.cdp_sum = 0xffff; + } + else + { + tx.cdp_sum = (u_int16_t) strtol(argval,NULL,16); + } + } + + //////// + // + // Provide a basic interface for the most important TLVs: + // + + if (getarg(tx.arg_string,"tlv_id", argval)==1) + { + // simply overwrite current content in tx.cdp_tlv_id + tx.cdp_tlv_id[0] = '\0'; + strncpy((char*) tx.cdp_tlv_id, argval,2048); + tx.cdp_tlv_id_len = strlen ((char*)tx.cdp_tlv_id); + } + + + // + // This is something ugly ;-) + // + + if (getarg(tx.arg_string,"change", NULL)==1) + { + memcpy((void*) tx.cdp_tlv_id, (void*) "Mausezahn 00000000000", 21); + tx.cdp_tlv_id_len=21; + change_value = 1; + } + + + // + // NOW write the ID-TLV; this is the only REQUIRED TLV !!! + // and this TLV should be the FIRST one - that's why we + // write it immediately here now: + // + tlv_len = create_tlv (1, tx.cdp_tlv_id, tx.cdp_tlv_id_len, tlv); + memcpy((void*) tx.cdp_payload+next_pbyte, (void*) tlv, tlv_len); + next_pbyte += tlv_len; + + // + // Now the other TLVs may follow: + // + + // Format: Type=2, Len=17, NrOfAddr=00:00:00:01, Protocol=01:01:cc:00, AddrLen=4, IP_Address + // Example: tlv_address = 192.168.1.10 + // Note: currently only one address supported + if (getarg(tx.arg_string,"tlv_address", argval)==1) + { + dummy32 = str2ip32 (argval); + x = (u_int8_t*) &dummy32; + value[0] = 0x00; // NrOfAddr + value[1] = 0x00; + value[2] = 0x00; + value[3] = 0x01; + + value[4] = 0x01; // Protocol + value[5] = 0x01; + value[6] = 0xcc; + value[7] = 0x00; + + value[8] = 0x04; // AddrLen + + value[9] = *(x+3); + value[10] = *(x+2); + value[11] = *(x+1); + value[12] = *(x); + + tlv_len = create_tlv (2, value, 13, tlv); + memcpy((void*) tx.cdp_payload+next_pbyte, (void*) tlv, tlv_len); + next_pbyte += tlv_len; + } + + + + // Format: Type=3 + // Example: tlv_portid = 2/23 + // Note: + if (getarg(tx.arg_string,"tlv_portid", argval)==1) + { + tlv_len = create_tlv (3, (u_int8_t*) argval, strlen(argval), tlv); + memcpy((void*) tx.cdp_payload+next_pbyte, (void*) tlv, tlv_len); + next_pbyte += tlv_len; + } + + // Format: Type=4 + // Example: "tlv_cap = 2a" (= 0010 1010) + // Flags: MSB=0 - Repeater - IGMP - Host - Switch - SrcRouteBrdg - TranspBrdg - Router(LSB) + if (getarg(tx.arg_string,"tlv_cap", argval)==1) + { + if (strlen(argval)>2) + { + fprintf(stderr," mz/send_cdp: Capability value must be specified as a two-digit hexadecimal value!\n"); + exit(1); + } + else + { + str2hex(argval, value+3, 1020); + if (value[3]>0x7f) + { + fprintf(stderr," mz/send_cdp: Capability value must not exceed 7F(hex)\n"); + exit(1); + } + } + + value[0]=0x00; + value[1]=0x00; + value[2]=0x00; + tlv_len = create_tlv (4, value, 4, tlv); + memcpy((void*) tx.cdp_payload+next_pbyte, (void*) tlv, tlv_len); + next_pbyte += tlv_len; + } + + + // Format: Type=5 + // Example: tlv_version = Mausezahn_version_xyz + // Note: Avoid spaces, use underscore instead + if (getarg(tx.arg_string,"tlv_version", argval)==1) + { + tlv_len = create_tlv (5, (u_int8_t*) argval, strlen(argval), tlv); + memcpy((void*) tx.cdp_payload+next_pbyte, (void*) tlv, tlv_len); + next_pbyte += tlv_len; + } + + + // Format: Type=6 + // Example: tlv_platform = WS-C6509-E + // Note: + if (getarg(tx.arg_string,"tlv_platform", argval)==1) + { + tlv_len = create_tlv (6, (u_int8_t*) argval, strlen(argval), tlv); + memcpy((void*) tx.cdp_payload+next_pbyte, (void*) tlv, tlv_len); + next_pbyte += tlv_len; + } + + // Format: Type=9 + // Example: tlv_vtpdomain = MyVTPdomain + // Note: + if (getarg(tx.arg_string,"tlv_vtpdomain", argval)==1) + { + tlv_len = create_tlv (9, (u_int8_t*) argval, strlen(argval), tlv); + memcpy((void*) tx.cdp_payload+next_pbyte, (void*) tlv, tlv_len); + next_pbyte += tlv_len; + } + + + // Format: Type=10, Len=17 + // Example: tlv_native = 100 + // Note: + if (getarg(tx.arg_string,"tlv_native", argval)==1) + { + dummy16 = (u_int16_t) str2int(argval); + if (dummy16>4095) + { + fprintf(stderr," mz/WARNING: native VLAN value exceeds max value (4095) - hope you know what you do!\n"); + } + + x = (u_int8_t*) &dummy16; + value[0] = *(x+1); + value[1] = *(x); + tlv_len = create_tlv (10, value, 2, tlv); + memcpy((void*) tx.cdp_payload+next_pbyte, (void*) tlv, tlv_len); + next_pbyte += tlv_len; + } + + // Format: Type=11 + // Example: tlv_duplex = full | half + // Note: + if (getarg(tx.arg_string,"tlv_duplex", argval)==1) + { + if (strncmp(argval,"full",10)==0) + { + value[0]=0x01; + } + else if (strncmp(argval,"half",10)==0) + { + value[0]=0x00; + } + else + { + value[0]=(u_int8_t) str2int(argval); + if (!quiet) + { + fprintf(stderr," mz/Warning: Only keywords 'half' or 'full' supported." + " Will interprete input as integer.\n"); + } + + } + + tlv_len = create_tlv (11, value, 1, tlv); + memcpy((void*) tx.cdp_payload+next_pbyte, (void*) tlv, tlv_len); + next_pbyte += tlv_len; + } + + // Format: Type=22, Len=17, NrOfAddr=00:00:00:01, Protocol=01:01:cc:00, AddrLen=4, IP_Address + // Example: tlv_mgmt = 10.1.1.99 + // Note: Same format as tlv_address + if (getarg(tx.arg_string,"tlv_mgmt", argval)==1) + { + dummy32 = str2ip32 (argval); + x = (u_int8_t*) &dummy32; + value[0] = 0x00; // NrOfAddr + value[1] = 0x00; + value[2] = 0x00; + value[3] = 0x01; + + value[4] = 0x01; // Protocol + value[5] = 0x01; + value[6] = 0xcc; + value[7] = 0x00; + + value[8] = 0x04; // AddrLen + + value[9] = *(x+3); + value[10] = *(x+2); + value[11] = *(x+1); + value[12] = *(x); + + tlv_len = create_tlv (22, value, 13, tlv); + memcpy((void*) tx.cdp_payload+next_pbyte, (void*) tlv, tlv_len); + next_pbyte += tlv_len; + + } + + + + // + // Eventually there are two generic TLV interfaces: tlv and tlvhex + // + + if (getarg(tx.arg_string,"tlv", argval)==1) + { + // split in TYPE and VALUE + sscanf(argval, "%u/%s", &type1, value1); + len1 = strlen((const char*) value1); + + } + + if (getarg(tx.arg_string,"tlvhex", argval)==1) + { + // split in TYPE and VALUE + sscanf(argval, "%u/%s", &type2, pld); + len2 = str2hex(pld, value2, 1023); + } + + + // + // Finally the optional payload interface allows to specify subsequent TLVs or any other bytes: + // + if ( (getarg(tx.arg_string,"payload", argval)==1) || (getarg(tx.arg_string,"p", argval)==1)) + { + len = str2hex (argval, value, 1023); + memcpy((void*) tx.cdp_payload+next_pbyte, (void*) value, len); + next_pbyte += len; + } + + + + /////////////////////////////////////////////////////////////// + + + + // Write other TLVs: First the ASCII specified: + if (len1) + { + tlv_len = create_tlv (type1, value1, len1 , tlv); + memcpy((void*) tx.cdp_payload+next_pbyte, (void*) tlv, tlv_len); + next_pbyte += tlv_len; + } + + // Write other TLVs: Then the HEX specified: + if (len2) + { + tlv_len = create_tlv (type2, value2, len2 , tlv); + memcpy((void*) tx.cdp_payload+next_pbyte, (void*) tlv, tlv_len); + next_pbyte += tlv_len; + } + + + tx.cdp_payload_s = next_pbyte; + + // CHECK: + // bs2str(tx.cdp_payload, pld, tx.cdp_payload_s); + // printf("PAYLOAD= %s\n",pld); + + +//////////////////////////// +// + + // Open the link - for the intermediate CDP/LLC frame + l = libnet_init(LIBNET_LINK_ADV, tx.device, errbuf); + + if (l == NULL) + { + fprintf(stderr, "%s", errbuf); + exit(EXIT_FAILURE); + } + + if (check_eth_mac_txt(ETH_DST)) // if '1' then user did not set MAC address (or problem occurred) + { + str2hex("01:00:0C:CC:CC:CC", tx.eth_dst, 6); + } + + if (check_eth_mac_txt(ETH_SRC)) // if '1' then user did not set MAC address (or problem occurred) + { + // own mac per default (see init.c) + } + + count = tx.count; + eth_src_rand = tx.eth_src_rand; + delay = tx.delay; + + // --------------------------------------------------- + // If you want to change CDP fields during a LOOP then + // START the loop from HERE: + // + + //////////////////////////////////// + // Now create the whole CDP packet: + + packet[0] = tx.cdp_version; // VERSION + packet[1] = tx.cdp_ttl; // TTL + packet[2] = 0x00; // CHECKSUM + packet[3] = 0x00; + + // Now add the TLVs + memcpy ((void*) packet+4, (void*) tx.cdp_payload, tx.cdp_payload_s); + packet_s = tx.cdp_payload_s + 4; + + // Check whether packet is an even length (i.e. is a multiple of 16 bits = 2 bytes); + if (packet_s%2>0) + { + packet[packet_s++]=0x00; + packet[packet_s++]=0x17; + packet[packet_s++]=0x00; + packet[packet_s++]=0x05; + packet[packet_s++]=0x00; + } + + + // Now update the checksum: + if (tx.cdp_sum == 0) // Otherwise user specified the checksum (usually a wrong one ;-)) + { + tx.cdp_sum = checksum16(packet_s, packet); + } + x = (u_int8_t *) &tx.cdp_sum; + packet[2] = *(x+1); + packet[3] = *(x); + + // CHECK the CDP packet + //bs2str(packet, pld, packet_s); + //printf("CDP= %s\n",pld); + + +// printf("Len = %u Checksum = %04x \n", packet_s-8, tx.cdp_sum); + + + /////////////////////////////////////////////////////////////// + // Now create the whole tx.eth_payload = LLC/SNAP + CDP packet + // First the LLC/SNAP header: + memcpy ((void*) tx.eth_payload, (void*) llcsnap, 8); + memcpy ((void*) tx.eth_payload+8, (void*) packet, packet_s); + tx.eth_payload_s = packet_s +8; + + + // CHECK the whole 802.3 payload + // bs2str(tx.eth_payload, pld, tx.eth_payload_s); + // printf("PACKET = %s\n",pld); + + + t = libnet_build_802_3 (tx.eth_dst, + tx.eth_src, + tx.eth_payload_s, + tx.eth_payload, + tx.eth_payload_s, + l, + 0); + + + + // this is for the statistics: + mz_start = clock(); + total_d = tx.count; + + if (!count) goto AGAIN; + + for (i=0; i +#include "mops.h" + +#define CLI_DEBUG_PACKET 0x0001 + +#define MZ_MODE_BENCHMARK 1002 +#define MZ_MODE_SCAN 1003 + +#define MZ_MODE_PACKET 1100 + +#define MZ_MODE_PACKET_ARP 1101 +#define MZ_MODE_PACKET_BPDU 1102 +#define MZ_MODE_PACKET_CDP 1103 +#define MZ_MODE_PACKET_DNS 1104 +#define MZ_MODE_PACKET_IP 1105 +#define MZ_MODE_PACKET_ICMP 1106 +#define MZ_MODE_PACKET_LLDP 1107 +#define MZ_MODE_PACKET_RTP 1108 +#define MZ_MODE_PACKET_SYSLOG 1109 +#define MZ_MODE_PACKET_TCP 1110 +#define MZ_MODE_PACKET_UDP 1111 +#define MZ_MODE_PACKET_ETH 1112 +#define MZ_MODE_PACKET_IGMP 1113 + +#define MZ_MODE_INTERFACE 1200 +#define MZ_MODE_SEQUENCE 1300 + +#define MZ_BANNER_TEXT \ + "\n" \ + "------------------------------------------\n" \ + "Mausezahn, version " MAUSEZAHN_VERSION_SHORT " \n" \ + "Copyright (C) 2007-2009 by Herbert Haas.\n" \ + "------------------------------------------\n\n" \ + "Mausezahn comes with ABSOLUTELY NO WARRANTY; for details\n" \ + "type 'warranty'. This is free software, and you are welcome\n" \ + "to redistribute it under certain conditions; see COPYING\n" \ + "(included in the Mausezahn source package) for details.\n\n" \ + "For Mausezahn NEWS visit http://www.perihel.at/sec/mz/\n\n" + + +#define MZ_WARRANTY_TEXT \ + "\nMausezahn, version " MAUSEZAHN_VERSION_SHORT " - a fast versatile traffic generator.\n" \ + "Copyright (C) 2007-2009 by Herbert Haas ~ www.perihel.at\n" \ + "\n" \ + "This program is free software; you can redistribute it and/or modify it under\n" \ + "the terms of the GNU General Public License version 2 as published by the \n" \ + "Free Software Foundation.\n" \ + "\n" \ + "This program is distributed in the hope that it will be useful, but WITHOUT\n" \ + "ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n" \ + "FOR A PARTICULAR PURPOSE. See the GNU General Public License for more \n" \ + "details.\n" \ + "\n" \ + "You should have received a copy of the GNU General Public License along with\n" \ + "this program; if not, see http://www.gnu.org/licenses/gpl-2.0.html\n\n" + +#define MZ_PROMPT "mz-" MAUSEZAHN_VERSION_SHORT + +#define MZ_DEFAULT_USERNAME "mz" +#define MZ_DEFAULT_PASSWORD "mz" +#define MZ_DEFAULT_ENABLE_PASSWORD "mops" +#define MZ_DEFAULT_PORT 25542 // Towel day and 42 + +struct cli_def *gcli; + +char mz_username[32]; +char mz_password[32]; +char mz_enable[32]; +int mz_port; +struct mops *clipkt; // actual packet used by CLI thread + +int clidev; + +// ================================================================= +int cli_debug; + +// Flags from 0x0000 to 0xFFFF +// cli_debug & 8000 => Developer specific debugs +// cli_debug & 0001 => Packet transmission debugging +// ... + +// ================================================================= + + +/////////////////////////////////////////////////////////////////////////////// +// Prototypes + +void mz_cli_init(); +int cli_read_cfg(char *str); +int mz_def16 (char *def, u_int16_t val, char *str256); +int cli(); + +int debug_all (struct cli_def *cli, char *command, char *argv[], int argc); +int debug_packet (struct cli_def *cli, char *command, char *argv[], int argc); + +int cmd_end_to_config(struct cli_def *cli, char *command, char *argv[], int argc); +int tx_switch(struct cli_def *cli); +int cmd_test(struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_reset_interface (struct cli_def *cli, char *command, char *argv[], int argc); + +int show_system(struct cli_def *cli, char *command, char *argv[], int argc); +int show_packets(struct cli_def *cli, char *command, char *argv[], int argc); +int show_set(struct cli_def *cli, char *command, char *argv[], int argc); +int show_interfaces(struct cli_def *cli, char *command, char *argv[], int argc); +int show_mops(struct cli_def *cli, char *command, char *argv[], int argc); +int show_arp (struct cli_def *cli, char *command, char *argv[], int argc); + +int cmd_set(struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_run_id (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_run_name (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_run_sequence (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_run_all (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_stop (struct cli_def *cli, char *command, char *argv[], int argc); + +int launch_bpdu (struct cli_def *cli, char *command, char *argv[], int argc); +int launch_synflood (struct cli_def *cli, char *command, char *argv[], int argc); + +int stop_mausezahn(struct cli_def *cli, char *command, char *argv[], int argc); +int warranty(struct cli_def *cli, char *command, char *argv[], int argc); +int transmit (struct cli_def *cli, char *command, char *argv[], int argc); +int clear_all(struct cli_def *cli, char *command, char *argv[], int argc); +int clear_packet(struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_reset_packet(struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_load (struct cli_def *cli, char *command, char *argv[], int argc); + +int enter_interface (struct cli_def *cli, char *command, char *argv[], int argc); +int conf_ip_address (struct cli_def *cli, char *command, char *argv[], int argc); +int conf_mac_address (struct cli_def *cli, char *command, char *argv[], int argc); +int conf_tag_dot1q (struct cli_def *cli, char *command, char *argv[], int argc); +int conf_tag_mpls (struct cli_def *cli, char *command, char *argv[], int argc); + +int conf_frame_limit (struct cli_def *cli, char *command, char *argv[], int argc); + +int conf_sequence (struct cli_def *cli, char *command, char *argv[], int argc); +int sequence_add (struct cli_def *cli, char *command, char *argv[], int argc); +int sequence_delay (struct cli_def *cli, char *command, char *argv[], int argc); +int sequence_remove (struct cli_def *cli, char *command, char *argv[], int argc); +int sequence_show (struct cli_def *cli, char *command, char *argv[], int argc); + + +int enter_packet (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_packet_type(struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_packet_end(struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_packet_clone (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_packet_name (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_packet_description (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_packet_count (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_packet_delay (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_packet_interval (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_packet_bind (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_packet_mac_address_source (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_packet_mac_address_destination (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_eth_type (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_eth_length (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_eth_llc (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_eth_snap (struct cli_def *cli, char *command, char *argv[], int argc); + +int cmd_packet_dot1q (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_packet_mpls (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_packet_payload_hex (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_packet_payload_ascii (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_packet_payload_raw (struct cli_def *cli, char *command, char *argv[], int argc); + +int cmd_port_source (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_port_destination (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_udp_sum (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_udp_len (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_udp_end(struct cli_def *cli, char *command, char *argv[], int argc); + +int cmd_tcp_seqnr (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_tcp_acknr (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_tcp_offset (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_tcp_res (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_tcp_flags (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_tcp_cwr (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_tcp_ece (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_tcp_urg (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_tcp_ack (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_tcp_psh (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_tcp_rst (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_tcp_syn (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_tcp_fin (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_tcp_window (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_tcp_sum (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_tcp_urgptr(struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_tcp_options (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_tcp_end(struct cli_def *cli, char *command, char *argv[], int argc); + +int cmd_dns_query(struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_dns_answer(struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_dns_ttl(struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_dns_end(struct cli_def *cli, char *command, char *argv[], int argc); + +int cmd_arp_hwtype (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_arp_prtype (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_arp_hwaddrsize (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_arp_praddrsize (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_arp_opcode (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_arp_smac (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_arp_sip (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_arp_tmac (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_arp_tip (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_arp_trailer (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_arp_end(struct cli_def *cli, char *command, char *argv[], int argc); + +int cmd_bpdu_id (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_bpdu_version (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_bpdu_type (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_bpdu_flags (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_bpdu_rid (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_bpdu_pc (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_bpdu_bid (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_bpdu_pid (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_bpdu_age (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_bpdu_maxage (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_bpdu_hello (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_bpdu_fwd (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_bpdu_mode (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_bpdu_vlan(struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_bpdu_end(struct cli_def *cli, char *command, char *argv[], int argc); + +int cmd_igmpv2_genquery (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_igmpv2_specquery (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_igmpv2_report (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_igmpv2_leave (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_igmpv1_query (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_igmpv1_report (struct cli_def *cli, char *command, char *argv[], int argc); + +int cmd_lldp_conformance (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_lldp_chassis_id (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_lldp_port_id (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_lldp_ttl (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_lldp_vlan (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_lldp_opt_tlv (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_lldp_opt_tlv_bad (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_lldp_opt_org (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_lldp_endtlv (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_lldp_reset (struct cli_def *cli, char *command, char *argv[], int argc); + +int cmd_ip_address_source (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_ip_address_destination (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_ip_version (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_ip_ttl (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_ip_protocol (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_ip_hlen (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_ip_len (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_ip_id (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_ip_offset (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_ip_sum (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_ip_tos (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_ip_dscp (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_ip_rsv (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_ip_df (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_ip_mf (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_ip_fragsize (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_ip_fragoverlap (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_ip_option (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_ip_delivery (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_ip_end(struct cli_def *cli, char *command, char *argv[], int argc); + +int cmd_rtp_version (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_rtp_padding (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_rtp_xten (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_rtp_marker (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_rtp_cc (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_rtp_pt (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_rtp_ssrc (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_rtp_sqnr (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_rtp_time (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_rtp_extension (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_rtp_source (struct cli_def *cli, char *command, char *argv[], int argc); +int cmd_rtp_cclist (struct cli_def *cli, char *command, char *argv[], int argc); + +#endif + diff --git a/src/cli_arp.c b/src/cli_arp.c new file mode 100644 index 0000000..7181575 --- /dev/null +++ b/src/cli_arp.c @@ -0,0 +1,232 @@ +/* + * Mausezahn - A fast versatile traffic generator + * Copyright (C) 2008-2010 Herbert Haas + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 as published by the + * Free Software Foundation. + * + * 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, see http://www.gnu.org/licenses/gpl-2.0.html + * +*/ + + + +#include "mz.h" +#include "cli.h" +#include "mops.h" + + +// ISSUES: +// +// - Currently only IP/MAC resolution supported (i.e. hw_size=6, pr_size=4) +// - Add macro support: commands like request/response should set all params correctly + + + + +int cmd_arp_hwtype (struct cli_def *cli, char *command, char *argv[], int argc) +{ + struct mops_ext_arp * pd = clipkt->p_desc; + + if ( (strncmp(argv[argc-1],"?",1)==0) || (argc!=1) ) + { + cli_print(cli,"Specify the hardware type (0-ffff, default 1=Eth)\n"); + } + else if (mops_pdesc_2byte(&pd->hw_type, argv[0], 1, 0, 0xffff)) + { + cli_print(cli, "Hardware type must be between 0 and ffff\n"); + } + + return CLI_OK; +} + + + +int cmd_arp_prtype (struct cli_def *cli, char *command, char *argv[], int argc) +{ + struct mops_ext_arp * pd = clipkt->p_desc; + + if ( (strncmp(argv[argc-1],"?",1)==0) || (argc!=1) ) + { + cli_print(cli,"Specify the protocol type (0-ffff, default=800=IP)\n"); + } + else if (mops_pdesc_2byte(&pd->pr_type, argv[0], 1, 0, 0xffff)) + { + cli_print(cli, "Protocol type must be between 0 and ffff\n"); + } + + return CLI_OK; +} + + + +int cmd_arp_hwaddrsize (struct cli_def *cli, char *command, char *argv[], int argc) +{ + struct mops_ext_arp * pd = clipkt->p_desc; + + if ( (strncmp(argv[argc-1],"?",1)==0) || (argc!=1) ) + { + cli_print(cli,"Specify the hardware address size (0-255, default=6)\n"); + } + else if (mops_pdesc_1byte(&pd->hw_size, argv[0], 0, 0, 255)) + { + cli_print(cli, "Hardware size must be between 0 and 255\n"); + } + + return CLI_OK; +} + + +int cmd_arp_praddrsize (struct cli_def *cli, char *command, char *argv[], int argc) +{ + struct mops_ext_arp * pd = clipkt->p_desc; + + if ( (strncmp(argv[argc-1],"?",1)==0) || (argc!=1) ) + { + cli_print(cli,"Specify the protocol address size (0-255, default=4)\n"); + } + else if (mops_pdesc_1byte(&pd->pr_size, argv[0], 0, 0, 255)) + { + cli_print(cli, "Protocol size must be between 0 and 255\n"); + } + + return CLI_OK; +} + + +int cmd_arp_opcode (struct cli_def *cli, char *command, char *argv[], int argc) +{ + struct mops_ext_arp * pd = clipkt->p_desc; + + if ( (strncmp(argv[argc-1],"?",1)==0) || (argc!=1) ) + { + cli_print(cli,"Specify the ARP operation code (0-ffff)\n"); + cli_print(cli,"Optional keywords: 'request' (default) or 'response'\n"); + } + else if (mz_strcmp(argv[0],"request", 3)==0) + { + cli_print(cli, "Set ARP mode to request\n"); + pd->opcode = 1; + } + else if (mz_strcmp(argv[0],"response", 3)==0) + { + cli_print(cli, "Set ARP mode to response\n"); + pd->opcode = 2; + } + else + { + cli_print(cli, "Invalid ARP mode\n"); + } + + return CLI_OK; +} + + + +int cmd_arp_smac (struct cli_def *cli, char *command, char *argv[], int argc) +{ + struct mops_ext_arp * pd = clipkt->p_desc; + + if ( (strncmp(argv[argc-1],"?",1)==0) || (argc!=1) ) + { + cli_print(cli,"Specify a source MAC address (format: XX:XX:XX:XX:XX:XX)\n"); + } + else + { + if (mops_pdesc_mac(pd->sender_mac, argv[0])) + { + cli_print(cli,"Invalid MAC address (use format: XX:XX:XX:XX:XX:XX)\n"); + } + } + return CLI_OK; +} + + + +int cmd_arp_sip (struct cli_def *cli, char *command, char *argv[], int argc) +{ + struct mops_ext_arp * pd = clipkt->p_desc; + + if ( (strncmp(argv[argc-1],"?",1)==0) || (argc!=1) ) + { + cli_print(cli,"Specify a source IP address (format: A.B.C.D)\n"); + } + else if (mops_pdesc_ip (pd->sender_ip, argv[0])) + { + cli_print(cli,"Invalid IP address (use format: A.B.C.D)\n"); + } + + return CLI_OK; +} + + + +int cmd_arp_tmac (struct cli_def *cli, char *command, char *argv[], int argc) +{ + struct mops_ext_arp * pd = clipkt->p_desc; + + if ( (strncmp(argv[argc-1],"?",1)==0) || (argc!=1) ) + { + cli_print(cli,"Specify a target MAC address (format: XX:XX:XX:XX:XX:XX)\n"); + } + else if (mops_pdesc_mac(pd->target_mac, argv[0])) + { + cli_print(cli,"Invalid MAC address (use format: XX:XX:XX:XX:XX:XX)\n"); + } + + return CLI_OK; +} + + + +int cmd_arp_tip (struct cli_def *cli, char *command, char *argv[], int argc) +{ + struct mops_ext_arp * pd = clipkt->p_desc; + + if ( (strncmp(argv[argc-1],"?",1)==0) || (argc!=1) ) + { + cli_print(cli,"Specify a target IP address (format: A.B.C.D)\n"); + } + else if (mops_pdesc_ip (pd->target_ip, argv[0])) + { + cli_print(cli,"Invalid IP address (use format: A.B.C.D)\n"); + } + + return CLI_OK; +} + + + +int cmd_arp_trailer (struct cli_def *cli, char *command, char *argv[], int argc) +{ + struct mops_ext_arp * pd = (MOPS_EXT_ARP) clipkt->p_desc; + + if ( (strncmp(argv[argc-1],"?",1)==0) || (argc!=1) ) + { + cli_print(cli,"Specify the trailer length (0-2000, default=18)\n"); + } + else if (mops_pdesc_2byte(&pd->trailer, argv[0], 0, 0, 2000)) + { + cli_print(cli, "Trailer must be between 0 and 2000\n"); + } + + return CLI_OK; +} + + + +int cmd_arp_end(struct cli_def *cli, char *command, char *argv[], int argc) +{ + char prompt[16]; + sprintf(prompt, "pkt-%i",clipkt->id); + cli_set_configmode(cli, MZ_MODE_PACKET, prompt); + return CLI_OK; +} + diff --git a/src/cli_bpdu.c b/src/cli_bpdu.c new file mode 100644 index 0000000..bd422ff --- /dev/null +++ b/src/cli_bpdu.c @@ -0,0 +1,750 @@ +/* + * Mausezahn - A fast versatile traffic generator + * Copyright (C) 2008-2010 Herbert Haas + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 as published by the + * Free Software Foundation. + * + * 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, see http://www.gnu.org/licenses/gpl-2.0.html + * +*/ + + + +#include "mz.h" +#include "cli.h" +#include "mops.h" + + + +int cmd_bpdu_id (struct cli_def *cli, char *command, char *argv[], int argc) +{ + struct mops_ext_bpdu * pd = clipkt->p_desc; + char str[256]; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc!=1) ) + { + mz_def16("0x0000", pd->id, str); + cli_print(cli, "Specify the BPDU identifier (0..65535)\r"); + cli_print(cli, "%s\n", str); + cli_print(cli, "\n"); + return CLI_OK; + } + + if (mops_pdesc_2byte(&pd->id, argv[0], 0, 0, 65535)) + { + cli_print(cli, "Specify a value between 0 and 65535\n"); + } + + return CLI_OK; +} + + +int cmd_bpdu_version (struct cli_def *cli, char *command, char *argv[], int argc) +{ + struct mops_ext_bpdu * pd = clipkt->p_desc; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc!=1) ) + { + cli_print(cli, "Specify the BPDU version (0..255)\r"); + cli_print(cli, "\n"); + return CLI_OK; + } + + if (mops_pdesc_1byte(&pd->version, argv[0], 0, 0, 255)) + { + cli_print(cli, "Specify a value between 0 and 255\n"); + } + + + return CLI_OK; +} + + + +int cmd_bpdu_type (struct cli_def *cli, char *command, char *argv[], int argc) +{ + struct mops_ext_bpdu * pd = clipkt->p_desc; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc!=1) ) { + cli_print(cli, "Specify the BPDU type, either via keyword or number (0..255)\n"); + cli_print(cli, "Keywords:\n"); + cli_print(cli, " conf .... Configuration BPDU\r"); + cli_print(cli, " tcn ..... Topology Change BPDU\r"); + cli_print(cli, "\n"); + return CLI_OK; + } + + if (mz_strcmp(argv[0], "configuration", 1)==0) { + pd->bpdu_type = 0x00; + } else if (mz_strcmp(argv[0], "tcn", 1)==0) { + pd->bpdu_type = 0x80; + } else if (mops_pdesc_1byte(&pd->bpdu_type, argv[0], 0, 0, 255)) { + cli_print(cli, "Specify a value between 0 and 255\n"); + } + + return CLI_OK; +} + + +int cmd_bpdu_flags (struct cli_def *cli, char *command, char *argv[], int argc) +{ + struct mops_ext_bpdu * pd = clipkt->p_desc; + int i; + char str[16]; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc>8) ) + { + cli_print(cli, "Specify the BPDU flags by keyword.\r"); + cli_print(cli, "Note that not-mentioned flags will be set to zero!\n"); + cli_print(cli, "General keywords:\n"); + cli_print(cli, " ack .... Topology Change Acknowledgement\r"); + cli_print(cli, " tcn .... Topology Change Notification\r"); + cli_print(cli, "\r"); + cli_print(cli, "RSTP-specific keywords:\n"); + cli_print(cli, " agree .... Agreement\r"); + cli_print(cli, " prop .... Proposal\r"); + cli_print(cli, " fwd .... Forward State\r"); + cli_print(cli, " learn .... Learn State\r"); + cli_print(cli, "\r"); + cli_print(cli, " Port roles:\n"); + cli_print(cli, " unknown .... Unknown\r"); + cli_print(cli, " alt .... Alternate or Backup\r"); + cli_print(cli, " root .... Root\r"); + cli_print(cli, " desg .... Designated\r"); + cli_print(cli, "\n"); + return CLI_OK; + } + + // 7 6 5 4 3 2 1 0 + // tcnack agree fwd learn X X proposal TCN + // where XX is 00 unknown + // 01 alternate or backup + // 10 root + // 11 designated + + if (argc) + { + pd->flags = 0x00; // always reset to zero (= normal Configuration BPDU) + + for (i=0; iflags |= 0x80; + else + if (mz_strcmp(argv[i], "tcn", 2)==0) pd->flags |= 0x01; + else + if (mz_strcmp(argv[i], "agree", 2)==0) pd->flags |= 0x40; + else + if (mz_strcmp(argv[i], "fwd", 2)==0) pd->flags |= 0x20; + else + if (mz_strcmp(argv[i], "learn", 2)==0) pd->flags |= 0x10; + else + if (mz_strcmp(argv[i], "proposal", 2)==0) pd->flags |= 0x02; + else + if (mz_strcmp(argv[i], "unknown", 2)==0) pd->flags &= 0xf3; + else + if (mz_strcmp(argv[i], "alt", 2)==0) { pd->flags &= 0xf7; pd->flags |= 0x04; } + else + if (mz_strcmp(argv[i], "root", 2)==0) { pd->flags &= 0xfb; pd->flags |= 0x08; } + else + if (mz_strcmp(argv[i], "desg", 2)==0) pd->flags |= 0x0c; + } + // Feedback + char2bits(pd->flags, str); + cli_print(cli, "Flags: %s\n", str); + } + else + { + cli_print(cli, "No flags configured (use '?')\n"); + } + + return CLI_OK; +} + + + + + + + + + +int cmd_bpdu_rid (struct cli_def *cli, char *command, char *argv[], int argc) +{ + + struct mops_ext_bpdu * pd = clipkt->p_desc; + char p[64], e[64]; + int pri, esi, r; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc>2) ) + { + cli_print(cli, "Specify the BPDU root identifier, using the following format:\n"); + cli_print(cli, " [:ext-sys-id] [interface | MAC-Address]\n"); + cli_print(cli, " ....... priority (0-15)\r"); + cli_print(cli, " ....... extended system-id (0-4095)\n"); + cli_print(cli, "Optionally the MAC address of the root bridge can be given, either directly as arbitrary\r"); + cli_print(cli, "address (format: XX:XX:XX:XX:XX:XX) or by referring to an existing interface.\n"); + cli_print(cli, "Per default the MAC address of the default interface is used and a priority of zero.\n"); + cli_print(cli, "\n"); + return CLI_OK; + } + + if (argc==0) { + cli_print(cli, "Please specify at least the priority (use ?)\n"); + return CLI_OK; + } + + mz_tok(argv[0], ":", 2, p, e); + + pri = (int) str2int(p); + if (e!=NULL) + esi = (int) str2int(e); + else + esi = 0; + + if (argc==1) // no MAC given + { + r = mops_create_bpdu_bid (clipkt, pri, esi, NULL, 1); // 1 means RID (0 means BID) + } + else + r = mops_create_bpdu_bid (clipkt, pri, esi, argv[1], 1); // 1 means RID (0 means BID) + + + // Check return value + switch (r) + { + case 1: + cli_print(cli, "Priority must be within 0..15\n"); + return CLI_OK; + break; + case 2: + cli_print(cli, "Extended System-ID must be within 0..4095\n"); + return CLI_OK; + break; + case 3: + cli_print(cli, "Invalid MAC address or interface\n"); + return CLI_OK; + break; + case 4: + cli_print(cli, "Invalid format - use ?\n"); + return CLI_OK; + break; + } + + + + //--------- + // Verify: + bs2str(pd->root_id, p, 8); + cli_print(cli, "RID is now %s\n", p); + // ------- + // + return CLI_OK; +} + + + + + + +int cmd_bpdu_pc (struct cli_def *cli, char *command, char *argv[], int argc) +{ + struct mops_ext_bpdu * pd = clipkt->p_desc; + unsigned long long int i; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc>1) ) + { + cli_print(cli, "Specify the BPDU root path cost (0..4294967295)\r"); + cli_print(cli, "\n"); + return CLI_OK; + } + + if (argc==0) + { + cli_print(cli, "Missing argument (use ?)\n"); + return CLI_OK; + } + + i = str2lint (argv[0]); + if (i>0xffffffff) + { + cli_print(cli, "Range exceeded (0..4294967295)\n"); + } + else + pd->root_pc = (u_int32_t) i; + + return CLI_OK; +} + + + + +int cmd_bpdu_bid (struct cli_def *cli, char *command, char *argv[], int argc) +{ + + struct mops_ext_bpdu * pd = clipkt->p_desc; + char p[64], e[64]; + int pri, esi, r; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc>2) ) + { + cli_print(cli, "Specify the BPDU bridge identifier, using the following format:\n"); + cli_print(cli, " [:ext-sys-id] [interface | MAC-Address]\n"); + cli_print(cli, " ....... priority (0-15)\r"); + cli_print(cli, " ....... extended system-id (0-4095)\n"); + cli_print(cli, "Optionally the MAC address of the root bridge can be given, either directly as arbitrary\r"); + cli_print(cli, "address (format: XX:XX:XX:XX:XX:XX) or by referring to an existing interface.\n"); + cli_print(cli, "Per default the MAC address of the default interface is used and a priority of zero.\n"); + cli_print(cli, "\n"); + return CLI_OK; + } + + + if (argc==0) + { + cli_print(cli, "Please specify at least the priority (use ?)\n"); + return CLI_OK; + } + + mz_tok(argv[0], ":", 2, p, e); + + pri = (int) str2int(p); + if (e!=NULL) + esi = (int) str2int(e); + else + esi = 0; + + if (argc==1) // no MAC given + { + r = mops_create_bpdu_bid (clipkt, pri, esi, NULL, 0); // 0 means BID (1 means RID) + } + else + r = mops_create_bpdu_bid (clipkt, pri, esi, argv[1], 0); // 0 means BID (1 means RID) + + + // Check return value + switch (r) + { + case 1: + cli_print(cli, "Priority must be within 0..15\n"); + return CLI_OK; + break; + case 2: + cli_print(cli, "Extended System-ID must be within 0..4095\n"); + return CLI_OK; + break; + case 3: + cli_print(cli, "Invalid MAC address or interface\n"); + return CLI_OK; + break; + case 4: + cli_print(cli, "Invalid format - use ?\n"); + return CLI_OK; + break; + } + + + + //--------- + // Verify: + bs2str(pd->bridge_id, p, 8); + cli_print(cli, "BID is now %s\n", p); + // ------- + // + return CLI_OK; +} + + + + + +int cmd_bpdu_pid (struct cli_def *cli, char *command, char *argv[], int argc) +{ + struct mops_ext_bpdu * pd = clipkt->p_desc; + u_int32_t i; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc>1) ) + { + cli_print(cli, "Specify the BPDU port identifier (0..65535)\r"); + cli_print(cli, "\n"); + return CLI_OK; + } + + if (argc==0) + { + cli_print(cli, "Missing argument (use ?)\n"); + return CLI_OK; + } + + i = (u_int32_t) str2int (argv[0]); + + if (i>0xffff) + { + cli_print(cli, "The port identifier must be within 0..65535\n"); + return CLI_OK; + } + + pd->port_id = (u_int16_t) i; + + return CLI_OK; +} + +// +// +// NOTE: +// +// All timers are multiples of 1/256 sec. Thus times range from 0 to 255 seconds. +// +// + +int cmd_bpdu_age (struct cli_def *cli, char *command, char *argv[], int argc) +{ + struct mops_ext_bpdu * pd = clipkt->p_desc; + u_int32_t i; + char str[256]; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc>2) ) + { + mz_def16("0", pd->message_age, str); + + cli_print(cli, "Specify the message age:\n"); + cli_print(cli, " - either in seconds (0..256) e. g. '14 sec'\r"); + cli_print(cli, " - or as multiples of 1/256 seconds (0..65535)\n"); + cli_print(cli, "%s\n", str); + cli_print(cli, "\n"); + return CLI_OK; + } + + if (argc==0) + { + cli_print(cli, "Missing argument (use ?)\n"); + return CLI_OK; + } + + i = (u_int32_t) str2int (argv[0]); + + if (argc==1) // absolute + { + if (i>0xffff) + cli_print(cli, "The age must be within 0..65535\n"); + else + pd->message_age = (u_int16_t) i; + } + else if (mz_strcmp(argv[1], "seconds", 1)==0) // in seconds + { + if (i>256) + cli_print(cli, "The age must be within 0..256 seconds\n"); + else + { + if (i==256) + i = 0xffff; // since 256*256=65536 which exceeds 0xffff but 65535/256 = 255.996 + else + i = i * 256; + + pd->message_age = (u_int16_t) i; + } + + } + else + cli_print(cli, "Invalid argument\n"); + + return CLI_OK; + +} + + + + + + + +int cmd_bpdu_maxage (struct cli_def *cli, char *command, char *argv[], int argc) +{ + struct mops_ext_bpdu * pd = clipkt->p_desc; + u_int32_t i; + char str[256]; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc>2) ) + { + mz_def16("20 seconds", pd->max_age, str); + + cli_print(cli, "Specify the maximum message age:\n"); + cli_print(cli, " - either in seconds (0..256) e. g. '20 sec'\r"); + cli_print(cli, " - or as multiples of 1/256 seconds (0..65535)\n"); + cli_print(cli, "%s\n", str); + cli_print(cli, "\n"); + return CLI_OK; + } + + if (argc==0) + { + cli_print(cli, "Missing argument (use ?)\n"); + return CLI_OK; + } + + i = (u_int32_t) str2int (argv[0]); + + if (argc==1) // absolute + { + if (i>0xffff) + cli_print(cli, "The max age must be within 0..65535\n"); + else + pd->max_age = (u_int16_t) i; + } + else if (mz_strcmp(argv[1], "seconds", 1)==0) // in seconds + { + if (i>256) + cli_print(cli, "The max age must be within 0..256 seconds\n"); + else + { + if (i==256) + i = 0xffff; // since 256*256=65536 which exceeds 0xffff but 65535/256 = 255.996 + else + i = i * 256; + + pd->max_age = (u_int16_t) i; + } + + } + else + cli_print(cli, "Invalid argument\n"); + + return CLI_OK; + +} + + + + + +int cmd_bpdu_hello (struct cli_def *cli, char *command, char *argv[], int argc) +{ + struct mops_ext_bpdu * pd = clipkt->p_desc; + u_int32_t i; + char str[256]; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc>2) ) + { + mz_def16("2 seconds", pd->hello_time, str); + + cli_print(cli, "Specify the hello interval:\n"); + cli_print(cli, " - either in seconds (0..256) e. g. '2 sec'\r"); + cli_print(cli, " - or as multiples of 1/256 seconds (0..65535)\n"); + cli_print(cli, "%s\n", str); cli_print(cli, "\n"); + return CLI_OK; + } + + if (argc==0) + { + cli_print(cli, "Missing argument (use ?)\n"); + return CLI_OK; + } + + + i = (u_int32_t) str2int (argv[0]); + + if (argc==1) // absolute + { + if (i>0xffff) + cli_print(cli, "The hello interval must be within 0..65535\n"); + else + pd->hello_time = (u_int16_t) i; + } + else if (mz_strcmp(argv[1], "seconds", 1)==0) // in seconds + { + if (i>256) + cli_print(cli, "The hello interval must be within 0..256 seconds\n"); + else + { + if (i==256) + i = 0xffff; // since 256*256=65536 which exceeds 0xffff but 65535/256 = 255.996 + else + i = i * 256; + + pd->hello_time = (u_int16_t) i; + } + + } + else + cli_print(cli, "Invalid argument\n"); + + return CLI_OK; + +} + +int cmd_bpdu_fwd (struct cli_def *cli, char *command, char *argv[], int argc) +{ + struct mops_ext_bpdu * pd = clipkt->p_desc; + u_int32_t i; + char str[256]; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc>2) ) + { + mz_def16("15 seconds", pd->f_delay, str); + + cli_print(cli, "Specify the forward delay:\n"); + cli_print(cli, " - either in seconds (0..256) e. g. '15 sec'\r"); + cli_print(cli, " - or as multiples of 1/256 seconds (0..65535)\n"); + cli_print(cli, "%s\n", str); + cli_print(cli, "\n"); + return CLI_OK; + } + + if (argc==0) + { + cli_print(cli, "Missing argument (use ?)\n"); + return CLI_OK; + } + + + i = (u_int32_t) str2int (argv[0]); + + if (argc==1) // absolute + { + if (i>0xffff) + cli_print(cli, "The forward delay must be within 0..65535\n"); + else + pd->f_delay = (u_int16_t) i; + } + else if (mz_strcmp(argv[1], "seconds", 1)==0) // in seconds + { + if (i>256) + cli_print(cli, "The forward delay must be within 0..256 seconds\n"); + else + { + if (i==256) + i = 0xffff; // since 256*256=65536 which exceeds 0xffff but 65535/256 = 255.996 + else + i = i * 256; + + pd->f_delay = (u_int16_t) i; + } + + } + else + cli_print(cli, "Invalid argument\n"); + + return CLI_OK; + +} + + + +int cmd_bpdu_mode (struct cli_def *cli, char *command, char *argv[], int argc) +{ + struct mops_ext_bpdu * pd = clipkt->p_desc; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc>1) ) + { + cli_print(cli, "Specify the BPDU mode using the keywords:\n"); + cli_print(cli, " stp ...... IEEE 802.1d (traditional CST)\r"); + cli_print(cli, " rstp ...... IEEE 802.1w (Rapid STP)\r"); + cli_print(cli, " mstp ...... IEEE 802.1s (Multiple STP)\r"); + cli_print(cli, " pvst ...... Cisco Per-VLAN STP\r"); + cli_print(cli, " rpvst ...... Cisco Per-VLAN RSTP\r"); + cli_print(cli, "\n"); + return CLI_OK; + } + + if (argc==0) + { + cli_print(cli, "Missing argument (use ?)\n"); + return CLI_OK; + } + + + if (mz_strcmp(argv[0], "stp", 1)==0) + { + pd->version=0; + pd->rstp=0; + pd->pvst=0; + pd->mstp=0; + } + else if (mz_strcmp(argv[0], "rstp", 2)==0) + { + pd->version=2; + pd->rstp=1; + pd->mstp=0; + } + else if (mz_strcmp(argv[0], "mstp", 1)==0) + { + pd->version=3; + pd->mstp=1; + } + else if (mz_strcmp(argv[0], "pvst", 1)==0) + { + pd->version=0; + pd->pvst=1; + pd->rstp=0; + pd->mstp=0; + } + else if (mz_strcmp(argv[0], "rpvst", 2)==0) + { + pd->version=2; + pd->rstp=1; + pd->pvst=1; + pd->mstp=0; + } + + + // TODO: also change version to 2 if RSTP, 0 if legacy + + + return CLI_OK; +} + + + + +int cmd_bpdu_vlan(struct cli_def *cli, char *command, char *argv[], int argc) +{ + u_int32_t i; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc>1) ) + { + cli_print(cli, "Specify the VLAN number for PVST+ messages (0..4095)\n"); + cli_print(cli, "\n"); + return CLI_OK; + } + + if (argc==0) + { + cli_print(cli, "Missing argument (use ?)\n"); + return CLI_OK; + } + + i = (u_int32_t) str2int(argv[0]); + + if (i>65535) + { + cli_print(cli, "VLAN number is definitely too large! (0..65535 at maximum)\n"); + return CLI_OK; + } + + if (i>4095) + { + cli_print(cli, "Warning: Invalid VLAN number (0..4095) - but let's try it...\n"); + } + + mops_create_bpdu_trailer(clipkt, (u_int16_t) i); + + return CLI_OK; +} + + + +int cmd_bpdu_end(struct cli_def *cli, char *command, char *argv[], int argc) +{ + char prompt[16]; + sprintf(prompt, "pkt-%i",clipkt->id); + cli_set_configmode(cli, MZ_MODE_PACKET, prompt); + return CLI_OK; +} + diff --git a/src/cli_cmds.c b/src/cli_cmds.c new file mode 100644 index 0000000..cc9edb1 --- /dev/null +++ b/src/cli_cmds.c @@ -0,0 +1,1436 @@ +/* + * Mausezahn - A fast versatile traffic generator + * Copyright (C) 2008-2010 Herbert Haas + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 as published by the + * Free Software Foundation. + * + * 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, see http://www.gnu.org/licenses/gpl-2.0.html + * +*/ + + + +#include "mz.h" +#include "cli.h" +#include "mops.h" +#include "llist.h" + +// Callback functions for the commands. +// __FUNCTION__ contains the name of the current callback function (for troubleshootig) + + +//////////////////////////////////////////////////////////////////////////////// +int cmd_test(struct cli_def *cli, char *command, char *argv[], int argc) +{ + cli_print(cli, "called %s with %s\r\n", __FUNCTION__, command); + return CLI_OK; +} + + + +//////////////////////////////////////////////////////////////////////////////// +int debug_all (struct cli_def *cli, char *command, char *argv[], int argc) +{ + if ( strncmp(argv[argc-1], "?", 1) == 0) + { + cli_print(cli, "Will debug everything. (Be careful!)\n"); + return CLI_OK; + } + + + cli_debug = 0x7fff; + cli_print(cli, "Debug all enabled - stop with undebug all\r"); + cli_print(cli, "Note: _Already_ active packets will not be omitted!\n"); + + if (mz_strcmp(argv[argc-1], "dev", 3)==0) + { + cli_print(cli, "*** Developer mode debugging enabled ***\n"); + cli_debug = 0xffff; + } + + return CLI_OK; +} + + + + +//////////////////////////////////////////////////////////////////////////////// +// Clear all _legacy_ Mausezahn settings (reinitialize anything) +int clear_all(struct cli_def *cli, char *command, char *argv[], int argc) +{ + if (argc) { + cli_print(cli, "No argument required! Try again.\n"); + return CLI_OK; + } + + reset(); + cli_print(cli, "All legacy Mausezahn parts have been reinitialized.\r"); + mops_delete_all(mp_head); + mops_reset_packet (mp_head); + cli_print(cli, "MOPS has been reinitialized.\n"); + return CLI_OK; +} + + +int clear_packet(struct cli_def *cli, char *command, char *argv[], int argc) +{ + + struct mops *cur; + u_int32_t i; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc!=1) ) + { + cli_print(cli, "Delete a single packet (i. e. MOPS entry).\r"); + cli_print(cli, "Expects a single argument which is either a packet's ID or name.\r"); + cli_print(cli, "NOTE: If the name matches an ID then the name has higher preference.\n"); + return CLI_OK; + } + + + if (argc!=1) { + cli_print(cli, "Please specify only the packets ID or name\n"); + return CLI_OK; + } + + cur = mops_search_name (mp_head, argv[0]); + if (cur==NULL) { + i = (u_int32_t) str2int (argv[0]); + cur = mops_search_id (mp_head, i); + if (cur==NULL) { + cli_print(cli, "No packet found with that ID or name!\n"); + return CLI_OK; + } + } + clipkt = mops_delete_packet(cur); + cli_print(cli, "Packet deleted.\n"); + return CLI_OK; +} + + +int cmd_reset_packet(struct cli_def *cli, char *command, char *argv[], int argc) +{ + struct mops *cur; + u_int32_t i; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc!=1) ) + { + cli_print(cli, "Resets a single packet (i. e. MOPS entry).\r"); + cli_print(cli, "Expects a single argument which is either a packet's ID or name.\r"); + cli_print(cli, "NOTE: If the name matches an ID then the name has higher preference.\n"); + return CLI_OK; + } + + + if (argc!=1) { + cli_print(cli, "Please specify only the packets ID or name\n"); + return CLI_OK; + } + + cur = mops_search_name (mp_head, argv[0]); + if (cur==NULL) { + i = (u_int32_t) str2int (argv[0]); + cur = mops_search_id (mp_head, i); + if (cur==NULL) { + cli_print(cli, "No packet found with that ID or name!\n"); + return CLI_OK; + } + } + + mops_reset_packet(cur); + cli_print(cli, "New packet name: %s\n", cur->packet_name); + return CLI_OK; +} + + + + +//////////////////////////////////////////////////////////////////////////////// +int warranty(struct cli_def *cli, char *command, char *argv[], int argc) +{ + cli_print(cli, MZ_WARRANTY_TEXT); + return CLI_OK; +} + + +int show_system(struct cli_def *cli, char *command, char *argv[], int argc) +{ + cli_print(cli, "Not supported in this version\n"); + return CLI_OK; +} + + + + +//////////////////////////////////////////////////////////////////////////////// +// Run through packet list and print some details about existing packets. +// SYNTAX: +// +// show packet +// show packet MyPacket +// +int show_packets(struct cli_def *cli, char *command, char *argv[], int argc) +{ + int a=0, i, j=0, k, v, active_only=0; + u_int32_t t; + char c,T; + char name[32], ds[16], pr[16], ps[16]; + char myframe[MAX_MOPS_FRAME_SIZE*3]; + char mystate[32]; + char line[150], line2[150], line3[150]; + char delay_str[64]; + unsigned char *x0, *x1, *x2, *x3; + + struct mops *head = mp_head; + struct mops *mp = mp_head; + + + if (strncmp(argv[argc-1], "?", 2)==0) { + cli_print(cli, " Show list of all defined packets\r"); + cli_print(cli, "active Only show active packets\r"); + cli_print(cli, " Show detailed info about given packet\r"); +//TODO cli_print(cli, "type Only list packets r"); + cli_print(cli, "\n"); + return CLI_OK; + } + + if (argc==1) { + if (mz_strcmp(argv[0], "active", 1)==0) { + active_only=1; + } + } + + if ((argc==0) || (active_only)) // show packet summary + { + cli_print(cli, "Packet layer flags: E=Ethernet, S=SNAP, Q=802.1Q, M=MPLS, I/i=IP/delivery_off, U=UDP, T=TCP\n"); + cli_print(cli, "PktID PktName Layers Proto Size State Device Delay Count/CntX\n"); + + do + { + if (active_only) { + if (mp->state < MOPS_STATE_ACTIVE) { + mp = mp->next; + j++; + continue; + } + } + + ds[0]='\0'; + ps[0]='\0'; + pr[0]='\0'; + + if (mp->use_ETHER) strcat(ds,"E"); else strcat(ds,"-"); + if (mp->use_SNAP) strcat(ds,"S"); else strcat(ds,"-"); + if (mp->use_dot1Q) strcat(ds,"Q"); else strcat(ds,"-"); + if (mp->use_MPLS) strcat(ds,"M"); else strcat(ds,"-"); + if (mp->use_IP) { + if (mp->auto_delivery_off) + strcat(ds,"i"); + else + strcat(ds,"I"); + } else strcat(ds,"-"); + + if (mp->use_UDP) + strcat(ds,"U"); + else if + (mp->use_TCP) strcat(ds,"T"); + else strcat(ds,"-"); + + + + switch (mp->p_desc_type) + { + case MOPS_ARP: + strncpy(pr, "ARP", 8); + break; + case MOPS_BPDU: + strncpy(pr, "BPDU", 8); + break; + case MOPS_CDP: + strncpy(pr, "CDP", 8); + break; + case MOPS_DNS: + strncpy(pr, "DNS", 8); + break; + case MOPS_ICMP: + strncpy(pr, "ICMP", 8); + break; + case MOPS_IGMP: + strncpy(pr, "IGMP", 8); + break; + case MOPS_LLDP: + strncpy(pr, "LLDP", 8); + break; + case MOPS_RTP: + strncpy(pr, "RTP", 8); + break; + case MOPS_SYSLOG: + strncpy(pr, "SYSLOG", 8); + break; + default: + break; + } + + + switch (mops_state(mp)) + { + case MOPS_STATE_NULL: + strcat(ps, "NULL"); // should never happen! + break; + case MOPS_STATE_INIT: + strcat(ps, "init"); + break; + case MOPS_STATE_CONFIG: + strcat(ps, "config"); + break; + case MOPS_STATE_ACTIVE: + strcat(ps, "active"); + a++; + break; + case MOPS_STATE_SEQACT: + strcat(ps, "actseq"); + a++; + break; + default: + strcat(ps, "unknown"); + break; + } + + switch (mp->interval_used) { + case 1: // interval only configured, not started + strncat(ps, "-i", 2); + break; + + case 2: + strncat(ps, "+I", 2); + break; + default: + break; + } + + + strncpy (name, mp->packet_name, 13); // only show first 13 chars + + if (strnlen(mp->packet_name, MAX_MOPS_PACKET_NAME_LEN)>13) + { + name[13]=0x00; + strcat(name, "..."); + } + + // To determine the actual packet length *** + // we must reassemble everything: *** + mops_ext_update (mp); + mops_update (mp); + + timespec2str(&mp->ndelay, delay_str); + + // ID name lrs prot size state dev del count/cntx/% + sprintf(line, "%5i %-16s %s %-8s %4i %-9s %-6s %10s%9lu/%lu (%i%%)\r", + mp->id, // ID + name, // packet_name + ds, // layers + pr, // protocol + mp->frame_s, // size + ps, // state + mp->device, // device + delay_str, // delay + mp->count, // Configured count value + mp->cntx, // Current count + (mp->count) ? (int) (100 * (mp->count - mp->cntx)/mp->count) : 0 ); + cli_print(cli, "%s\r", line); + mp = mp->next; + j++; + } + while (head != mp); + + cli_print(cli, "\r"); + cli_print(cli, "%i packets defined, %i active.\n", j, a); + } + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + else if (argc == 1) // show details about a specific packet ********************************************** + { + if ( (mp = mops_search_name (mp_head, argv[0])) == NULL)// not found + { + if ( (mp = mops_search_id (mp_head, (int) str2int(argv[0]))) == NULL)// not found + { + cli_print (cli, "Packet not in list.\n"); + return CLI_OK; + } + } + + // To determine the actual packet length *** + // we must reassemble everything: *** + mops_ext_update (mp); + mops_update (mp); + + cli_print(cli, "Packet [%i] %s\r", mp->id, mp->packet_name); + cli_print(cli, " Description: %s \r", + (strnlen(mp->description, MAX_MOPS_DESCRIPTION_LEN)) ? mp->description : "(no description)"); + + switch(mp->state) + { + case MOPS_STATE_NULL: + sprintf(mystate, "NULL"); + break; + case MOPS_STATE_INIT: + sprintf(mystate, "init"); + break; + case MOPS_STATE_CONFIG: + sprintf(mystate, "config"); + break; + case MOPS_STATE_ACTIVE: + sprintf(mystate, "active(tx)"); + break; + default: + sprintf(mystate, "unknown"); + } + + timespec2str(&mp->ndelay, delay_str); + if (mp->interval_used) + timespec2str(&mp->interval, line2); + else + sprintf(line2, "(undefined)"); + + sprintf(line, "State: %s, Count=%lu, delay=%s (%lu s %lu nsec), interval= %s\r", + mystate, + mp->count, + delay_str, + mp->ndelay.tv_sec, + mp->ndelay.tv_nsec, + line2); + cli_print(cli, " %s\r", line); + + cli_print(cli, " Headers:\r"); + i=0; + if (mp->use_ETHER) + { + if (mp->eth_src_israndom) + { + cli_print(cli, " Ethernet: *** RANDOMIZED SMAC *** => %02x-%02x-%02x-%02x-%02x-%02x [%04x%s]\r", + mp->eth_dst[0],mp->eth_dst[1],mp->eth_dst[2],mp->eth_dst[3],mp->eth_dst[4],mp->eth_dst[5], + mp->eth_type, (mp->use_dot1Q) ? " after 802.1Q tag" : ""); + } + else + { + cli_print(cli, " Ethernet: %02x-%02x-%02x-%02x-%02x-%02x => %02x-%02x-%02x-%02x-%02x-%02x [%04x%s]\r", + mp->eth_src[0],mp->eth_src[1],mp->eth_src[2],mp->eth_src[3],mp->eth_src[4],mp->eth_src[5], + mp->eth_dst[0],mp->eth_dst[1],mp->eth_dst[2],mp->eth_dst[3],mp->eth_dst[4],mp->eth_dst[5], + mp->eth_type, (mp->use_dot1Q) ? " after 802.1Q tag" : ""); + } + + if (mp->use_IP) { + if (mp->auto_delivery_off) + cli_print(cli, " NOTE: Auto-delivery is OFF (that is, the destination MAC is fixed)\r"); + else + cli_print(cli, " Auto-delivery is ON (that is, the actual MAC is determined upon transmission)\r"); + } + i++; + } + if (mp->use_SNAP) + { + bs2str(clipkt->eth_snap, line, clipkt->eth_snap_s); + cli_print(cli, " LLC/SNAP: %s\r", line); + i++; + } + if (mp->use_dot1Q) + { + k = clipkt->dot1Q_s/4; // number of tags + sprintf(line, "%i tag(s); ", k); + for (j=0; jdot1Q[(j*4)+2]; + x1 = (unsigned char*) &clipkt->dot1Q[(j*4)+3]; + v = (*x0 & 0x0f)*256 + *x1; // VLAN +// c = *x0 & 0xe0; // CoS e0=11100000 + c = *x0 >> 5; + sprintf(ds, "%i:%i%s", + v, + (unsigned char) c, + (*x0 & 0x10) ? "[CFI]" : ""); // CFI + strncat(line, ds, 14); + if (j<(k-1)) strcat(line, ", "); + } + + cli_print(cli, " 802.1Q: %s (VLAN:CoS)\r", line); + i++; + } + if (mp->use_MPLS) + { + k = clipkt->mpls_s/4; // number of tags + sprintf(line, "%i tag(s); ", k); + for (j=0; jmpls[(j*4)+0]; + x1 = (unsigned char*) &clipkt->mpls[(j*4)+1]; + x2 = (unsigned char*) &clipkt->mpls[(j*4)+2]; + x3 = (unsigned char*) &clipkt->mpls[(j*4)+3]; + t = *x0; + t <<= 12; + t += *x1 * 16; + t += (*x2 & 0xf0) >> 4; + c = (*x2 & 0x0e) >> 1; + T = *x3; + sprintf(ds, "%i:%i:%i%s", + t, + (unsigned char) c, + (unsigned char) T, + (*x2 & 0x01) ? "[BoS]" : ""); // Bottom of Stack? + strncat(line, ds, 20); + if (j<(k-1)) strcat(line, ", "); + } + + cli_print(cli, " MPLS: %s (Label:CoS:TTL)\r", line); + + i++; + } + if (mp->use_IP) + { + // Source IP settings: + x0 = (unsigned char*) & clipkt->ip_src; + line2[0]=0x00; + if (clipkt->ip_src_isrange) + { + x1 = (unsigned char*) & clipkt->ip_src_start; + x2 = (unsigned char*) & clipkt->ip_src_stop; + sprintf(line2, "%u.%u.%u.%u-%u.%u.%u.%u", + (unsigned char) *(x1+3), (unsigned char) *(x1+2), (unsigned char) *(x1+1) , (unsigned char) *x1, + (unsigned char) *(x2+3), (unsigned char) *(x2+2), (unsigned char) *(x2+1) , (unsigned char) *x2); + } + sprintf(line, "SA=%u.%u.%u.%u %s %s %s", + (unsigned char) *(x0+3), (unsigned char) *(x0+2), (unsigned char) *(x0+1) , (unsigned char) *x0, + (clipkt->ip_src_israndom) ? "RANDOM" : "(not random)", + (clipkt->ip_src_isrange) ? "RANGE:" : "(no range)", + line2); + + cli_print(cli, " IP: %s\r", line); + //Destination IP settings: + x0 = (unsigned char*) & clipkt->ip_dst; + line2[0]=0x00; + if (clipkt->ip_dst_isrange) + { + x1 = (unsigned char*) & clipkt->ip_dst_start; + x2 = (unsigned char*) & clipkt->ip_dst_stop; + sprintf(line2, "%u.%u.%u.%u-%u.%u.%u.%u", + (unsigned char) *(x1+3), (unsigned char) *(x1+2), (unsigned char) *(x1+1) , (unsigned char) *x1, + (unsigned char) *(x2+3), (unsigned char) *(x2+2), (unsigned char) *(x2+1) , (unsigned char) *x2); + } + + sprintf(line, "DA=%u.%u.%u.%u %s %s", + (unsigned char) *(x0+3), (unsigned char) *(x0+2), (unsigned char) *(x0+1) , (unsigned char) *x0, + (clipkt->ip_dst_isrange) ? "RANGE:" : "(no range)", + line2); + cli_print(cli, " %s\r", line); + + sprintf(line, "ToS=0x%02x proto=%u TTL=%u ID=%u offset=%u flags: %s|%s|%s", + clipkt->ip_tos, clipkt->ip_proto, clipkt->ip_ttl, clipkt->ip_id, clipkt->ip_frag_offset, + (clipkt->ip_flags_RS) ? "RS" : "-", + (clipkt->ip_flags_DF) ? "DF" : "-", + (clipkt->ip_flags_MF) ? "MF" : "-"); + + cli_print(cli, " %s\r", line); + + if (clipkt->ip_fragsize) { + sprintf(line, "NOTE: Auto-fragmentation is ON! Fragment size %u bytes, overlap %u", + clipkt->ip_fragsize, + clipkt->ip_frag_overlap); + cli_print(cli, " %s\r", line); + } + + sprintf(line, "len=%u(%s) checksum=0x%02x%02x(%s)", + clipkt->frame[clipkt->begin_IP+2]*256+clipkt->frame[clipkt->begin_IP+3], + (clipkt->ip_len_false) ? "false" : "correct", + clipkt->frame[clipkt->begin_IP+10], + clipkt->frame[clipkt->begin_IP+11], + (clipkt->ip_sum_false) ? "false" : "correct"); + + cli_print(cli, " %s\r", line); + + i++; + } + if (mp->use_UDP) + { + if (clipkt->sp_isrange) + sprintf(line2, "RANGE: %u-%u", clipkt->sp_start, clipkt->sp_stop); + else + sprintf(line2, "(norange)"); + if (clipkt->dp_isrange) + sprintf(line3, "RANGE: %u-%u", clipkt->dp_start, clipkt->dp_stop); + else + sprintf(line3, "(norange)"); + sprintf(line, "SP=%i %s %s, DP=%i %s %s\r", + clipkt->sp, + line2, + (clipkt->sp_isrand) ? "RANDOM" : "(not random)", + clipkt->dp, + line3, + (clipkt->dp_isrand) ? "RANDOM" : "(not random)"); + cli_print(cli, " UDP: %s\r", line); + sprintf(line, "checksum= %04x (%s), length= %u (%s)", + clipkt->udp_sum, (clipkt->udp_sum_false) ? "false" : "correct", + clipkt->udp_len, (clipkt->udp_len_false) ? "false" : "correct"); + cli_print(cli, " %s\r", line); + i++; + } + if (mp->use_TCP) + { + sprintf(line, "%u bytes segment size (including TCP header)", mp->tcp_len); + cli_print(cli, " TCP: %s\r", line); + if (clipkt->sp_isrange) + sprintf(line2, "RANGE: %u-%u", clipkt->sp_start, clipkt->sp_stop); + else + sprintf(line2, "(norange)"); + if (clipkt->dp_isrange) + sprintf(line3, "RANGE: %u-%u", clipkt->dp_start, clipkt->dp_stop); + else + sprintf(line3, "(norange)"); + sprintf(line, "SP=%i %s %s, DP=%i %s %s\r", + clipkt->sp, + line2, + (clipkt->sp_isrand) ? "RANDOM" : "(not random)", + clipkt->dp, + line3, + (clipkt->dp_isrand) ? "RANDOM" : "(not random)"); + cli_print(cli, " %s\r", line); + sprintf(line, "SQNR=%u (start %u, stop %u, delta %u) -- ACKNR=%u %s", + clipkt->tcp_seq, + clipkt->tcp_seq_start, + clipkt->tcp_seq_stop, + clipkt->tcp_seq_delta, + clipkt->tcp_ack, + (clipkt->tcp_ctrl_ACK) ? "(valid)" : "(invalid)"); + cli_print(cli, " %s\r", line); + mops_tcp_flags2str(clipkt,line2); + sprintf(line, "Flags: %s, reserved field is %02x, urgent pointer= %u", + line2, + clipkt->tcp_res, + clipkt->tcp_urg); + cli_print(cli, " %s\r", line); + sprintf(line, "Announced window size= %u", clipkt->tcp_win); + cli_print(cli, " %s\r", line); + sprintf(line, "Offset= %u (times 32 bit; value is %s), checksum= %04x (%s)", + clipkt->tcp_offset, + (clipkt->tcp_offset_false) ? "FALSE" : "valid", + clipkt->tcp_sum, + (clipkt->tcp_sum_false) ? "FALSE" : "valid"); + cli_print(cli, " %s\r", line); + sprintf(line, "%s - %u bytes defined", + (clipkt->tcp_option_used) ? "TCP options attached" : "(No TCP options attached)", + clipkt->tcp_option_s); + cli_print(cli, " %s\r", line); + i++; + } + + if (!i) cli_print(cli, " No headers defined.\r"); + + if (mp->msg_s) { + cli_print(cli, " Payload size: %i bytes\r", mp->msg_s); + } + + cli_print(cli, " Frame size: %i bytes\n", mp->frame_s); + + mops_print_frame(mp, myframe); + cli_print(cli, "%s\n", myframe); + } + + return CLI_OK; +} + + +//////////////////////////////////////////////////////////////////////////////// +int show_interfaces (struct cli_def *cli, char *command, char *argv[], int argc) +{ + int i, j=0; + char line[100]; + char ip[20]; + + + if (strncmp(argv[argc-1], "?", 2)==0) { + cli_print(cli, " Show summary list of all interfaces found\r"); + cli_print(cli, "detailed Additionally show network, mask, default gatway, and MTU\r"); + cli_print(cli, "\n"); + return CLI_OK; + } + + // Some safety checks + if (argc>1) return CLI_OK; + if (argc==1) { + if (mz_strcmp(argv[0], "detailed", 1)!=0) { + cli_print(cli, "invalid keyword (use ?)\n"); + return CLI_OK; + } + } + + /* Refresh interface data */ + + lookupdev(); + + for (i=0; i %s\r", + (device_list[i].cli) ? "C" : " ", + (device_list[i].mgmt_only) ? "!" : "", + line); + j=i; + } + else + cli_print(cli, "%s%s %s\r", + (device_list[i].cli) ? "C" : " ", + (device_list[i].mgmt_only) ? "M" : "", + line); + } + } + ///////////////////////// + else + + /* keyword detailed used */ + if (mz_strcmp(argv[0], "detailed", 1)==0) { + cli_print(cli, "Detailed interface list:\n"); + for (i=0; i>>%s<<<\r", tx.ascii_payload); + cli_print(cli, "-------------------------------- \n"); + } + + if (tx.hex_payload_s) + { cli_print(cli, "\r"); + cli_print(cli, "---- Hexadecimal payload is set: ----- \r"); + bs2str(tx.hex_payload, hexload, tx.hex_payload_s); + cli_print(cli, "%s\r", hexload); + cli_print(cli, "-------------------------------------- \n"); + } + + if (tx.padding) + { + cli_print(cli, "Configured padding: %u\r", tx.padding); + } + + cli_print(cli, "\r"); + cli_print(cli, "Packet count value %u\r", tx.count); + cli_print(cli, "Interpacket delay (usec) %u\r", tx.delay); + cli_print(cli, "\r"); + cli_print(cli, "Used network device(s): %s\r", tx.device); + cli_print(cli, "\n"); + return CLI_OK; +} + + + + + + + +//////////////////////////////////////////////////////////////////////////////// +int stop_mausezahn (struct cli_def *cli, char *command, char *argv[], int argc) +{ + if (strncmp(argv[argc-1], "?", 2)==0) { + cli_print(cli, "now Terminate the mausezahn server! BEWARE!\n"); + return CLI_OK; + } + + if (argc!=1) { + cli_print(cli, "The only allowed argument is 'now' -- anything else is ignored\n"); + return CLI_OK; + } + + if (mz_strcmp(argv[0], "now", 3)==0) { + cli_print(cli, "Good bye...\n"); + cli_done(cli); + clean_up(0); + return CLI_OK; + } else { + cli_print(cli, "Invalid argument. If you want to stop the Mausezahn server then\r"); + cli_print(cli, "enter 'terminate now'. You cannot abbreviate the argument 'now'. \n"); + } + + return CLI_OK; +} + + + + +int cmd_run_id (struct cli_def *cli, char *command, char *argv[], int argc) +{ + int i, slot; + struct mops *mp; + + if (argc == 0) { + cli_print(cli, "Specify one or more packet identifiers to run.\n"); + return CLI_OK; + } + + if ( strncmp(argv[argc-1], "?", 1) == 0) { + cli_print(cli, "Run packet transmission processes for given list of packet identifiers\n"); + return CLI_OK; + } + + // User provided packet id numbers + if (argc > 0) { + for (i=0; iid); + return CLI_OK; + break; + default: + cli_print (cli, "Activate [%i] ", slot ); + break; + } + } + } + cli_print (cli, "\n"); + } + return CLI_OK; +} + + +int cmd_run_name (struct cli_def *cli, char *command, char *argv[], int argc) +{ + int i; + struct mops *mp; + + if (argc == 0) { + cli_print(cli, "Specify one or more packet name(s) to run.\n"); + return CLI_OK; + } + + if ( strncmp(argv[argc-1], "?", 1) == 0) { + cli_print(cli, "Run packet transmission processes for specified packet name(s).\n"); + return CLI_OK; + } + + if (argc > 0) { + for (i=0; iid); + return CLI_OK; + break; + default: + cli_print (cli, "Activate [%i] ", mp->id ); + break; + } + } + } + cli_print (cli, "\n"); + } + return CLI_OK; +} + + +int cmd_run_sequence (struct cli_def *cli, char *command, char *argv[], int argc) +{ + struct mz_ll *cur; + int ret=0; + if (argc != 1) { + cli_print(cli, "Specify one (and only one) packet sequence name to run.\n"); + return CLI_OK; + } + + if ( strncmp(argv[argc-1], "?", 1) == 0) { + cli_print(cli, "Run sequence transmission processes for specified sequence name.\n"); + return CLI_OK; + } + + cur = mz_ll_search_name (packet_sequences, argv[0]); + if (cur==NULL) { // NOT FOUND !!! + cli_print(cli, "Sequence %s does not exist.", argv[0]); + return CLI_OK; + } + ret = mops_tx_sequence(cur); + switch (ret) { + case 0: cli_print(cli, "Sequence %s is runnning\n", cur->name); + break; + case 1: cli_print(cli, "Cannot run sequence: All packets must be in config state!\n"); + break; + case 2: cli_print(cli, "Cannot run sequence: All packets must have a finite count!\n"); + break; + case 3: cli_print(cli, "Cannot run sequence: Unable to start sequence transmission process.\n"); + break; + } + return CLI_OK; +} + + + +int cmd_run_all (struct cli_def *cli, char *command, char *argv[], int argc) +{ + int i; + struct mops *mp; + struct mops *head; + + if ( strncmp(argv[argc-1], "?", 1) == 0) { + cli_print(cli, "Run all user-specified packets.\n"); + return CLI_OK; + } + + if (argc>0) { + cli_print(cli, "No arguments expected!\n"); + return CLI_OK; + } + + // Send all valid packets + i=0; + head = mp_head; + mp = mp_head; + do { + if ((mp->mz_system==0) && (mops_state(mp) == MOPS_STATE_CONFIG)) { + switch (mops_tx_simple (mp)) { + case 1: + cli_print(cli, "Cannot create sending process.\r"); + return CLI_OK; + break; + case 3: + cli_print(cli, "Packet [%i] has already an active sending process\r", mp->id); + return CLI_OK; + break; + default: + break; + } + i++; + cli_print (cli, "Activate [%i] %s\r", mp->id, mp->packet_name ); + } + mp = mp->next; + } + while (head != mp); + if (i==0) { + cli_print (cli, "No valid packets found\n"); + } else { + cli_print (cli, "\r"); + cli_print (cli, "Activated %i packets \n", i); + } + return CLI_OK; +} + + + +int cmd_stop (struct cli_def *cli, char *command, char *argv[], int argc) +{ + struct mops *mp; + int i, ret=0, slot=0; + + struct mops *head = mp_head; + struct mops *cur = mp_head; + + if ((strncmp(argv[argc-1], "?", 2)==0) || (argc==0)) { + cli_print(cli, "Stop transmission process(es) or an active sequence.\r"); + cli_print(cli, "SYNTAX: 1) Either specify one or more packet-ids or packet names of active packets\r"); + cli_print(cli, " 2) Or enter 'sequence ' to stop an active sequence and its associated packets.\n"); + return CLI_OK; + } + + // Did the user specify a sequence? (ONE SEQUENCE ONLY) + if ((mz_strcmp(argv[0], "sequence", 3)==0) && (argc==2)) { + ret = stop_sequence (argv[1]); + switch (ret) { + case 0: + cli_print(cli, "Sequence '%s' stopped.\n", argv[1]); + break; + + case 1: + cli_print(cli, "Sequence '%s' does not exist!\n", argv[1]); + break; + case 2: + cli_print(cli, "Sequence '%s' is not active. Nothing to stop.\n", argv[1]); + break; + } + return CLI_OK; + } + + + if (((mz_strcmp(argv[0], "all", 3)==0) || (mz_strcmp(argv[0], "*", 1)==0)) && (argc==1)) { + i=0; + cli_print(cli, "Stopping "); + do { + if (mops_destroy_thread (cur)==0) { + i++; + cli_print(cli, "[%i] %s", cur->id, cur->packet_name); + } + cur = cur->next; + } + while (head != cur); + cli_print(cli, "\n"); + if (i) { + cli_print(cli, "Stopped %i transmission processe(s)\r", i); + } + else { + cli_print(cli, "No active transmission processes found.\r"); + } + + i = stop_all_sequences (); + if (i) { + cli_print(cli, "Stopped %i sequence(s)\n", i); + } + else { + cli_print(cli, "No active sequences found.\n"); + } + + return CLI_OK; + } + + // Stop all specified packets: + // + for (i=0; iid, mp->packet_name); + } else + cli_print (cli, "Stopped transission process for packet [%i] '%s'.\r", mp->id, mp->packet_name); + } + } + + cli_print(cli, "\r"); + return CLI_OK; +} + + + +int show_mops(struct cli_def *cli, char *command, char *argv[], int argc) +{ + char tmp[120]; + + if (strncmp(argv[argc-1], "?", 2)==0) { + cli_print(cli, " Check MOPS version and details\n"); + return CLI_OK; + } + + cli_print(cli, "-----------------------------------------------------\r"); + cli_print(cli, "Mops version %s [%s]\n", MOPS_VERSION, MOPS_CODENAME); + cli_print(cli, "Maximum packet sequence length is %i packets\r", MAX_PACKET_SEQUENCE_LEN); + cli_print(cli, "Maximum frame size is %i bytes\r", MAX_MOPS_FRAME_SIZE); + cli_print(cli, "Minimum frame size is %i bytes\r", MIN_MOPS_FRAME_SIZE); + cli_print(cli, "PCAP readout delay is %i msec\r", PCAP_READ_TIMEOUT_MSEC); + cli_print(cli, "Maximum payload size is %i bytes\r", MAX_MOPS_MSG_SIZE); + cli_print(cli, "Maximum chunk size is %i bytes\r", MAX_MOPS_MSG_CHUNK_SIZE); + cli_print(cli, "Maximum counters per packet is %i\r", MAX_MOPS_COUNTERS_PER_PACKET); + cli_print(cli, "Maximum number of 802.1Q tags is %i\r", MAX_MOPS_DOT1Q_TAGS); + cli_print(cli, "Maximum number of MPLS tags is %i\r", MAX_MOPS_MPLS_TAGS); + cli_print(cli, "Maximum length of packet names is %i characters\r", MAX_MOPS_PACKET_NAME_LEN); + cli_print(cli, "Maximum length of packet descriptions is %i characters\r", MAX_MOPS_DESCRIPTION_LEN); + cli_print(cli, "Bytes per line for formatted frame output %i\r", MAX_CLI_LINE_BYTES); + cli_print(cli, "Maximum LLDP optional section length is %i bytes\r", MAX_LLDP_OPT_TLVS); + if (AUTOMOPS_ENABLED) { + cli_print(cli, "Auto-MOPS subsystem is enabled\r"); + cli_print(cli, " Maximum nesting depth is %i\r", XN_MAX_STACK); + cli_print(cli, " Maximum file size for protocol definitions is %i\r", AUTOMOPS_MAX_FILE_SIZE); + cli_print(cli, " Maximum names length is %i\r", AUTOMOPS_MAX_NAME_LEN); + cli_print(cli, " Maximum short description length is %i\r", AUTOMOPS_MAX_SHORTDESC_LEN); + cli_print(cli, " Maximum XML tag length is %i\r", XML_MAX_TAG_LEN); + } else cli_print(cli, "Auto-MOPS subsystem is disabled\r"); + + if (mops_dump_all(mp_head, tmp)) { + cli_print(cli, "No mopses found.\n"); // keine Möpse gefunden ;-) + } else { + cli_print(cli, "%s\n", tmp); + } + + return CLI_OK; +} + + + + +int cmd_reset_interface (struct cli_def *cli, char *command, char *argv[], int argc) +{ + int i; + + if (strncmp(argv[argc-1], "?", 2)==0) { + cli_print(cli, " Check MOPS version and details\n"); + return CLI_OK; + } + + if (argc>0) { + cli_print(cli, "Unknown parameter\n"); + return CLI_OK; + } + + lookupdev(); + + for (i=0; i [max-frame-size]\n"); + return CLI_OK; + } + + if (argc>2) + { + cli_print(cli, "Two arguments allowed: [max-frame-size]\n"); + return CLI_OK; + } + + tmp = (unsigned int) str2int (argv[0]); + if (tmp < MIN_MOPS_FRAME_SIZE) + { + cli_print(cli, "This Mausezahn requires that the minimum frame size is at least %i bytes\n", MIN_MOPS_FRAME_SIZE); + return CLI_OK; + } + + if (tmp>(max_frame_s-2)) + { + cli_print(cli, "The minimum frame size must be below %i bytes\n", max_frame_s-1); + return CLI_OK; + } + + min_frame_s = tmp; + + if (argc==2) + { + tmp = (unsigned int) str2int (argv[1]); + + if (tmp > MAX_MOPS_FRAME_SIZE-MOPS_SIZE_MARGIN) + { + cli_print(cli, "This Mausezahn requires that the maximum frame size is not greater than %i bytes\n", + MAX_MOPS_FRAME_SIZE-MOPS_SIZE_MARGIN); + return CLI_OK; + } + + if (tmp<(min_frame_s+2)) + { + cli_print(cli, "The maximum frame size must be greater than %i bytes\n", min_frame_s+1); + return CLI_OK; + } + + max_frame_s = tmp; + } + + return CLI_OK; +} + + + + +int cmd_load (struct cli_def *cli, char *command, char *argv[], int argc) +{ + int i; + FILE *fp; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc!=1) ) { + cli_print(cli, "Load commands from one or more specified file(s)\r"); + cli_print(cli, "\n"); + return CLI_OK; + } + + if (!argc){ + cli_print(cli, "Specify one or more configuration files\n"); + return CLI_OK; + } + + for (i=0; i shows the advanced Mausezahn ARP table\n"); + return CLI_OK; + } + + if (argc>0) { + cli_print(cli, "Unknown parameter\n"); + return CLI_OK; + } + + + cli_print(cli, "Intf Index IP address MAC address last Ch UCast BCast Info\r"); + cli_print(cli, "----------------------------------------------------------------------------------\r"); +// ------------------------------------------------------------------------------ +// wlan0 [1] DL 192.168.0.1 at 00:09:5b:9a:15:84 3'42'' 1 + + for (i=0; isip[0],cur->sip[1],cur->sip[2],cur->sip[3]); + if (cur->changed>99999) mz_strncpy(ch,"ALERT",6); else sprintf(ch,"%lu", cur->changed); + if (cur->uni_resp>99999) mz_strncpy(uc,"ALERT",6); else sprintf(uc,"%lu", cur->uni_resp); + if (cur->bc_resp>99999) mz_strncpy(bc,"ALERT",6); else sprintf(bc,"%lu", cur->bc_resp); + sprintf(s, "%-7s [%i] %s%s %15s %02x:%02x:%02x:%02x:%02x:%02x %8s %5s %5s %5s %04x", + device_list[i].dev, + cur->index, + (cur->dynamic) ? "D" : "U", + (cur->locked) ? "L" : "", + ip, + cur->smac[0], + cur->smac[1], + cur->smac[2], + cur->smac[3], + cur->smac[4], + cur->smac[5], + cur->when, + ch, + uc, + bc, + cur->flags); + cli_print(cli, "%s\r", s); + if (cur->changed>1) { + now.sec = cur->sec; + now.nsec = cur->nsec; + prev.sec = cur->sec_prev; + prev.nsec= cur->nsec_prev; + printf("sec=%u nsec=%u sec=%u nsec=%u\n", cur->sec, cur->nsec, cur->sec_prev, cur->nsec_prev); + timestamp_subtract(&now, &prev, &result); + sprintf(s," previous MAC was: %02x:%02x:%02x:%02x:%02x:%02x time delta: %u sec %u msec", + cur->smac_prev[0], + cur->smac_prev[1], + cur->smac_prev[2], + cur->smac_prev[3], + cur->smac_prev[4], + cur->smac_prev[5], + (unsigned int) result.sec, (unsigned int) result.nsec/1000000); + cli_print(cli, " %s\r", s); + } + cur=cur->next; + } + + } + return CLI_OK; +} + + +// general 'end' command to return to global config mode +int cmd_end_to_config(struct cli_def *cli, char *command, char *argv[], int argc) +{ + cli_set_configmode(cli, MODE_CONFIG, NULL); + return CLI_OK; +} diff --git a/src/cli_dns.c b/src/cli_dns.c new file mode 100644 index 0000000..204c9d4 --- /dev/null +++ b/src/cli_dns.c @@ -0,0 +1,53 @@ +/* + * Mausezahn - A fast versatile traffic generator + * Copyright (C) 2008-2010 Herbert Haas + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 as published by the + * Free Software Foundation. + * + * 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, see http://www.gnu.org/licenses/gpl-2.0.html + * +*/ + + + +#include "mz.h" +#include "cli.h" +#include "mops.h" + +int cmd_dns_query(struct cli_def *cli, char *command, char *argv[], int argc) +{ + + return CLI_OK; +} + + +int cmd_dns_answer(struct cli_def *cli, char *command, char *argv[], int argc) +{ + + return CLI_OK; +} + + +int cmd_dns_ttl(struct cli_def *cli, char *command, char *argv[], int argc) +{ + + return CLI_OK; +} + + +int cmd_dns_end(struct cli_def *cli, char *command, char *argv[], int argc) +{ + char prompt[16]; + sprintf(prompt, "pkt-%i",clipkt->id); + cli_set_configmode(cli, MZ_MODE_PACKET, prompt); + return CLI_OK; +} + diff --git a/src/cli_eth.c b/src/cli_eth.c new file mode 100644 index 0000000..c3b0c90 --- /dev/null +++ b/src/cli_eth.c @@ -0,0 +1,269 @@ +/* + * Mausezahn - A fast versatile traffic generator + * Copyright (C) 2008-2010 Herbert Haas + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 as published by the + * Free Software Foundation. + * + * 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, see http://www.gnu.org/licenses/gpl-2.0.html + * +*/ + + +#include "mz.h" +#include "cli.h" +#include "mops.h" + + + + + +int cmd_packet_mac_address_source (struct cli_def *cli, char *command, char *argv[], int argc) +{ + int i,j; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc>1) ) + { + cli_print(cli, "XX:XX:XX:XX:XX:XX Configure a source MAC address\n"); + cli_print(cli, "Optionally you may use randomly generated (unicast)\r"); + cli_print(cli, "MAC addresses, using the keyword 'random'\n"); + return CLI_OK; + } + + if (argc==1) + { + if (mz_strcmp(argv[0], "random", 3)==0) + { + clipkt->eth_src_israndom = 1; + return CLI_OK; + } + + if (mz_strcmp(argv[0], "default", 3)==0) + { + // find index of device_list with the device configured in clipkt: + i=0; + while (strncmp(device_list[i].dev, clipkt->device, 10) && (ieth_src[j] = device_list[i].mac_mops[j]; + clipkt->eth_src_israndom = 0; + return CLI_OK; + } + + if (mops_pdesc_mac(clipkt->eth_src, argv[0])) + { + cli_print(cli,"Invalid MAC address (use format: XX:XX:XX:XX:XX:XX)\n"); + } + else // MAC was OK + { + clipkt->eth_src_israndom = 0; + } + } + else + cli_print(cli, "Invalid MAC format!\n"); + + + return CLI_OK; +} + + + +int cmd_packet_mac_address_destination (struct cli_def *cli, char *command, char *argv[], int argc) +{ + + if ( (strcmp(argv[argc-1],"?")==0) || (argc>1) ) + { + cli_print(cli, "XX:XX:XX:XX:XX:XX Configure a destination MAC address\n"); + return CLI_OK; + } + if (argc==1) + { + if (mz_strcmp(argv[0], "bcast", 2)==0) + { + mops_pdesc_mac (clipkt->eth_dst, "ff:ff:ff:ff:ff:ff"); + return CLI_OK; + } + else if (mz_strcmp(argv[0], "pvst", 2)==0) + { + mops_pdesc_mac (clipkt->eth_dst, "01:00:0C:CC:CC:CD"); + return CLI_OK; + } + else if (mz_strcmp(argv[0], "cisco", 2)==0) + { + mops_pdesc_mac (clipkt->eth_dst, "01:00:0C:CC:CC:CC"); + return CLI_OK; + } + else if (mz_strcmp(argv[0], "stp", 2)==0) + { + mops_pdesc_mac (clipkt->eth_dst, "01:80:C2:00:00:00"); + return CLI_OK; + } + + if (mops_pdesc_mac(clipkt->eth_dst, argv[0])) + { + cli_print(cli,"Invalid MAC address (use format: XX:XX:XX:XX:XX:XX)\n"); + } + } + else + cli_print(cli, "Invalid MAC format!\n"); + + return CLI_OK; +} + + + + + + +int cmd_eth_type (struct cli_def *cli, char *command, char *argv[], int argc) +{ + unsigned long int t32; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc>1) ) + { + cli_print(cli, "Specify the Ethernet type field in hexadecimal format.\n"); + cli_print(cli, "For example:\n"); + cli_print(cli, " 800 ......... IP\r"); + cli_print(cli, " 806 ......... ARP\r"); + cli_print(cli, " 835 ......... RARP\r"); + cli_print(cli, " 8100 ......... 802.1Q\r"); + cli_print(cli, " 888E ......... 802.1X\r"); + cli_print(cli, "\n"); + return CLI_OK; + } + + if (argc==1) + { + t32 = xstr2int(argv[0]); + if (t32>0xffff) + { + cli_print(cli, "EtherType must not exceed ffff.\n"); + return CLI_OK; + } + if (t32<0x800) + { + cli_print(cli, "WARNING: 'Officially' the EtherType must be greater or equal 800.\n"); + } + + clipkt->eth_type = (u_int16_t) t32; + } + else + { + cli_print(cli, "Only one parameter accepted.\n"); + } + + return CLI_OK; +} + + + + +int cmd_eth_length (struct cli_def *cli, char *command, char *argv[], int argc) +{ + unsigned long int t32; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc>1) ) + { + cli_print(cli, "Specify the 802.3 length field in decimal notation.\r"); + cli_print(cli, "\n"); + return CLI_OK; + } + + if (argc==1) + { + t32 = str2int(argv[0]); + if (t32>0xffff) + { + cli_print(cli, "The length field must not exceed 65535.\n"); + return CLI_OK; + } + if (t32>0x7ff) + { + cli_print(cli, "WARNING: 'Officially' the 802.3 length field must not be greater than 1522.\n"); + } + + clipkt->eth_len = (u_int16_t) t32; + } + else + { + cli_print(cli, "Only one parameter accepted.\n"); + } + + + + return CLI_OK; +} + + + + + +int cmd_eth_llc (struct cli_def *cli, char *command, char *argv[], int argc) +{ + + if ( (strcmp(argv[argc-1],"?")==0) || (argc>1) ) + { + cli_print(cli, "Specify the IEEE 802.2 Logical Link Control (LLC) in hexadecimal format.\n"); + return CLI_OK; + } + + // DSAP-SSAP-Ctrl + // ***** TODO ***** + cli_print(cli, "Not supported in this version.\n"); + + return CLI_OK; +} + + + + +int cmd_eth_snap (struct cli_def *cli, char *command, char *argv[], int argc) +{ + + u_int8_t + oui[16], + etp[16], + t8[16] = {0xAA, 0xAA, 0x03}; + + + if ( (strcmp(argv[argc-1],"?")==0) || (argc>1) ) + { + cli_print(cli, "Specify the SNAP header (OUI+Type) in hexadecimal format\r"); + cli_print(cli, "Example: 00:00:0e 08:00\r"); + cli_print(cli, "\n"); + return CLI_OK; + } + + if (argc!=2) + { + cli_print(cli, "Two arguments required: 3-byte OUI and 2-byte EtherType\n"); + return CLI_OK; + } + + if (str2hex(argv[0], oui, 15)!=3) + { + cli_print(cli, "Three bytes required for the OUI\n"); + return CLI_OK; + } + + if (str2hex(argv[1], etp, 15)!=2) + { + cli_print(cli, "Two bytes required for the EtherType\n"); + return CLI_OK; + } + + + memcpy(&clipkt->eth_snap[0], &t8, 3); + memcpy(&clipkt->eth_snap[3], &oui, 3); + memcpy(&clipkt->eth_snap[6], &etp, 2); + clipkt->eth_snap_s = 8; + + + + return CLI_OK; +} diff --git a/src/cli_igmp.c b/src/cli_igmp.c new file mode 100644 index 0000000..4fa5867 --- /dev/null +++ b/src/cli_igmp.c @@ -0,0 +1,322 @@ +/* + * Mausezahn - A fast versatile traffic generator + * Copyright (C) 2008-2010 Herbert Haas + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 as published by the + * Free Software Foundation. + * + * 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, see http://www.gnu.org/licenses/gpl-2.0.html + * +*/ + + + +#include "mz.h" +#include "cli.h" +#include "mops.h" + + +int cmd_igmpv2_genquery (struct cli_def *cli, char *command, char *argv[], int argc) +{ + int mrt, sum; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc>2) ) { + cli_print(cli, "Configure a IGMPv2 general query.\n"); + cli_print(cli, "ARGUMENTS: [ []]\n"); + cli_print(cli, " ... maximum response time in 100 msec units (default: 10 s)\r"); + cli_print(cli, " ... user-defined checksum (usually wrong by intention) in \r"); + cli_print(cli, " hexadecimal notation (e. g. 'c7b3').\n"); + return CLI_OK; + } + + if (argc>=1) { + if (mz_strisnum(argv[0])==0) { + cli_print(cli, "Maximum response time must only contain numbers!\n"); + return CLI_OK; + } + mrt = (int) str2int(argv[0]); + } else mrt = 100; // default: 10 s + + if (argc==2) { + if (mz_strishex(argv[1])==0) { + cli_print(cli, "Checksum must only contain hexadecimal numbers!\n"); + return CLI_OK; + } + sum = (int) xstr2int(argv[1]); + if (sum>0xffff) { + cli_print(cli, "Checksum must be a 2-byte value!\n"); + return CLI_OK; + } + + } else sum = -1; + + clipkt->ip_dst = str2ip32("224.0.0.1"); + clipkt->ip_ttl = 1; + clipkt->ndelay.tv_sec = 125; + clipkt->ndelay.tv_nsec = 0; + if (mops_create_igmpv2 (clipkt, 0, IGMP_GENERAL_QUERY, mrt, sum, 0)) + cli_print(cli, "Invalid parameters!\n"); + + return CLI_OK; +} + + +int cmd_igmpv2_specquery (struct cli_def *cli, char *command, char *argv[], int argc) +{ + int mrt=100, sum=-1; + u_int8_t IP[4]; + u_int32_t mip=0; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc>3) ) { + cli_print(cli, "Configure a IGMPv2 group-specific query.\n"); + cli_print(cli, "ARGUMENTS: [ []]\n"); + cli_print(cli, " ... multicast group to be queried (can be ANY IP address!)\r"); + cli_print(cli, " ... maximum response time in 100 msec units (default: 10 s)\r"); + cli_print(cli, " ... user-defined checksum (usually wrong by intention) in \r"); + cli_print(cli, " hexadecimal notation (e. g. 'c7b3').\n"); + return CLI_OK; + } + + + if (argc==0) { + cli_print(cli, "You must at least specify the group address\n"); + return CLI_OK; + } + + if (argc>=1) { + if (mops_pdesc_ip (IP, argv[0])==0) // check if format is really an IP address + mip = str2ip32(argv[0]); + else { + cli_print(cli, "Invalid IP address\n"); + return CLI_OK; + } + } + + if (argc>=2) { + if (mz_strisnum(argv[1])==0) { + cli_print(cli, "Maximum response time must only contain numbers!\n"); + return CLI_OK; + } + mrt = (int) str2int(argv[1]); + } + + if (argc==3) { + if (mz_strishex(argv[2])==0) { + cli_print(cli, "Checksum must only contain hexadecimal numbers!\n"); + return CLI_OK; + } + sum = (int) xstr2int(argv[2]); + if (sum>0xffff) { + cli_print(cli, "Checksum must be a 2-byte value!\n"); + return CLI_OK; + } + } + + clipkt->ip_dst = mip; + clipkt->ip_ttl = 1; + clipkt->ndelay.tv_sec = 125; + clipkt->ndelay.tv_nsec = 0; + if (mops_create_igmpv2 (clipkt, 0, IGMP_GSPEC_QUERY, mrt, sum, mip)) + cli_print(cli, "Invalid parameters!\n"); + + return CLI_OK; +} + + + + + +int cmd_igmpv2_report (struct cli_def *cli, char *command, char *argv[], int argc) +{ + int sum; + u_int8_t IP[4]; + u_int32_t mip=0; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc>2) || (argc==0)) { + cli_print(cli, "Configure a IGMPv2 membership report.\n"); + cli_print(cli, "ARGUMENTS: []\n"); + cli_print(cli, " ... multicast group address to be reported (but ANY IP\r"); + cli_print(cli, " address allowed, Mausezahn is really generous...)\r"); + cli_print(cli, " ... user-defined checksum (usually wrong by intention) in \r"); + cli_print(cli, " hexadecimal notation (e. g. 'c7b3').\n"); + return CLI_OK; + } + + + if (argc>=1) { + if (mops_pdesc_ip (IP, argv[0])==0) // check if format is really an IP address + mip = str2ip32(argv[0]); + else { + cli_print(cli, "Invalid IP address\n"); + return CLI_OK; + } + } + + if (argc==2) { + if (mz_strishex(argv[1])==0) { + cli_print(cli, "Checksum must only contain hexadecimal numbers!\n"); + return CLI_OK; + } + sum = (int) xstr2int(argv[1]); + if (sum>0xffff) { + cli_print(cli, "Checksum must be a 2-byte value!\n"); + return CLI_OK; + } + } else sum = -1; + + clipkt->ip_dst = mip; + clipkt->ip_ttl = 1; + clipkt->ndelay.tv_sec = 1; + clipkt->ndelay.tv_nsec = 0; + + if (mops_create_igmpv2 (clipkt, 0, IGMP_V2_REPORT, 0, sum, mip)) + cli_print(cli, "Invalid parameters!\n"); + + return CLI_OK; +} + + +int cmd_igmpv2_leave (struct cli_def *cli, char *command, char *argv[], int argc) +{ + int sum; + u_int8_t IP[4]; + u_int32_t mip=0; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc>2) || (argc==0)) { + cli_print(cli, "Configure a IGMPv2 leave group message.\n"); + cli_print(cli, "ARGUMENTS: []\n"); + cli_print(cli, " ... multicast group address that should be left; use\r"); + cli_print(cli, " the special address 0.0.0.0 for a 'general leave'\r"); + cli_print(cli, " ... user-defined checksum (usually wrong by intention) in \r"); + cli_print(cli, " hexadecimal notation (e. g. 'c7b3').\n"); + return CLI_OK; + } + + + if (argc>=1) { + if (mops_pdesc_ip (IP, argv[0])==0) // check if format is really an IP address + mip = str2ip32(argv[0]); + else { + cli_print(cli, "Invalid IP address\n"); + return CLI_OK; + } + } + + if (argc==2) { + if (mz_strishex(argv[1])==0) { + cli_print(cli, "Checksum must only contain hexadecimal numbers!\n"); + return CLI_OK; + } + sum = (int) xstr2int(argv[1]); + if (sum>0xffff) { + cli_print(cli, "Checksum must be a 2-byte value!\n"); + return CLI_OK; + } + } else sum = -1; + + clipkt->ip_dst = str2ip32("224.0.0.2"); + clipkt->ip_ttl = 1; + clipkt->ndelay.tv_sec = 1; + clipkt->ndelay.tv_nsec = 0; + + if (mops_create_igmpv2 (clipkt, 0, IGMP_LEAVE, 0, sum, mip)) + cli_print(cli, "Invalid parameters!\n"); + + return CLI_OK; +} + + + + + +int cmd_igmpv1_query (struct cli_def *cli, char *command, char *argv[], int argc) +{ + int sum; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc>1) ) { + cli_print(cli, "Configure a IGMPv1 query.\n"); + cli_print(cli, "OPTIONAL ARGUMENT: []\n"); + cli_print(cli, " ... user-defined checksum (usually wrong by intention) in \r"); + cli_print(cli, " hexadecimal notation (e. g. 'c7b3').\n"); + return CLI_OK; + } + + if (argc==1) { + if (mz_strishex(argv[0])==0) { + cli_print(cli, "Checksum must only contain hexadecimal numbers!\n"); + return CLI_OK; + } + sum = (int) xstr2int(argv[0]); + if (sum>0xffff) { + cli_print(cli, "Checksum must be a 2-byte value!\n"); + return CLI_OK; + } + } else sum = -1; + + clipkt->ip_dst = str2ip32("224.0.0.1"); + clipkt->ip_ttl = 1; + clipkt->ndelay.tv_sec = 125; + clipkt->ndelay.tv_nsec = 0; + if (mops_create_igmpv2 (clipkt, 0, IGMP_GENERAL_QUERY, 0, sum, 0)) + cli_print(cli, "Invalid parameters!\n"); + + return CLI_OK; +} + + +int cmd_igmpv1_report (struct cli_def *cli, char *command, char *argv[], int argc) +{ + int sum; + u_int8_t IP[4]; + u_int32_t mip=0; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc>2) || (argc==0)) { + cli_print(cli, "Configure a IGMPv1 membership report.\n"); + cli_print(cli, "ARGUMENTS: []\n"); + cli_print(cli, " ... multicast group address to be reported (but ANY IP\r"); + cli_print(cli, " address allowed, Mausezahn is really generous...)\r"); + cli_print(cli, " ... user-defined checksum (usually wrong by intention) in \r"); + cli_print(cli, " hexadecimal notation (e. g. 'c7b3').\n"); + return CLI_OK; + } + + + if (argc>=1) { + if (mops_pdesc_ip (IP, argv[0])==0) // check if format is really an IP address + mip = str2ip32(argv[0]); + else { + cli_print(cli, "Invalid IP address\n"); + return CLI_OK; + } + } + + if (argc==2) { + if (mz_strishex(argv[1])==0) { + cli_print(cli, "Checksum must only contain hexadecimal numbers!\n"); + return CLI_OK; + } + sum = (int) xstr2int(argv[1]); + if (sum>0xffff) { + cli_print(cli, "Checksum must be a 2-byte value!\n"); + return CLI_OK; + } + } else sum = -1; + + clipkt->ip_dst = mip; + clipkt->ip_ttl = 1; + clipkt->ndelay.tv_sec = 1; + clipkt->ndelay.tv_nsec = 0; + + if (mops_create_igmpv2 (clipkt, 0, IGMP_V1_REPORT, 0, sum, mip)) + cli_print(cli, "Invalid parameters!\n"); + + return CLI_OK; +} + diff --git a/src/cli_interface.c b/src/cli_interface.c new file mode 100644 index 0000000..c15978c --- /dev/null +++ b/src/cli_interface.c @@ -0,0 +1,142 @@ +/* + * Mausezahn - A fast versatile traffic generator + * Copyright (C) 2008-2010 Herbert Haas + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 as published by the + * Free Software Foundation. + * + * 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, see http://www.gnu.org/licenses/gpl-2.0.html + * +*/ + + +#include "mz.h" +#include "cli.h" +#include "mops.h" + + + + +// Enter interface config mode: +// +int enter_interface (struct cli_def *cli, char *command, char *argv[], int argc) +{ + int i, j=0; + char prompt[10]; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc>1) ) + { + cli_print(cli, "Specify an interface to configure\n"); + return CLI_OK; + } + + if (argc) + { + for (i=0; i1) ) + { + cli_print(cli, "A.B.C.D Specify a default interface IP address\n"); + return CLI_OK; + } + + if (argc) + { + if (mops_pdesc_ip (device_list[clidev].ip_mops, argv[0])) + { + cli_print(cli,"Invalid IP address (use format: A.B.C.D)\n"); + } + } + else + cli_print(cli, "A.B.C.D Specify a default interface IP address\n"); + + return CLI_OK; +} + + + +int conf_mac_address (struct cli_def *cli, char *command, char *argv[], int argc) +{ + if ( (strcmp(argv[argc-1],"?")==0) || (argc>1) ) + { + cli_print(cli, "XX:XX:XX:XX:XX:XX Configure a default interface MAC address\n"); + return CLI_OK; + } + + if (argc) + { + if (mops_pdesc_mac (device_list[clidev].mac_mops, argv[0])) + { + cli_print(cli,"Invalid MAC address (use format: XX:XX:XX:XX:XX:XX)\n"); + } + } + else + cli_print(cli, "A.B.C.D Specify a default interface IP address\n"); + + return CLI_OK; +} + + + +int conf_tag_dot1q (struct cli_def *cli, char *command, char *argv[], int argc) +{ + if ( (strcmp(argv[argc-1],"?")==0) || (argc>1) ) + { + cli_print(cli, "Specify one or more 802.1Q (and optionally 802.1P) tags\n"); + return CLI_OK; + } + cli_print(cli, "Not supported in this version\n"); + return CLI_OK; +} + +int conf_tag_mpls (struct cli_def *cli, char *command, char *argv[], int argc) +{ + if ( (strcmp(argv[argc-1],"?")==0) || (argc>1) ) + { + cli_print(cli, "Specify one or more MPLS labels (and parameters)\n"); + return CLI_OK; + } + cli_print(cli, "Not supported in this version\n"); + return CLI_OK; +} + + + + diff --git a/src/cli_ip.c b/src/cli_ip.c new file mode 100644 index 0000000..13fdd1a --- /dev/null +++ b/src/cli_ip.c @@ -0,0 +1,888 @@ +/* + * Mausezahn - A fast versatile traffic generator + * Copyright (C) 2008-2010 Herbert Haas + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 as published by the + * Free Software Foundation. + * + * 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, see http://www.gnu.org/licenses/gpl-2.0.html + * +*/ + + + +#include "mz.h" +#include "cli.h" +#include "mops.h" + + +// ------- TOC --------- +// +// int cmd_ip_address_source (struct cli_def *cli, char *command, char *argv[], int argc) +// int cmd_ip_address_destination (struct cli_def *cli, char *command, char *argv[], int argc) +// int cmd_ip_version (struct cli_def *cli, char *command, char *argv[], int argc) +// int cmd_ip_ttl (struct cli_def *cli, char *command, char *argv[], int argc) +// int cmd_ip_protocol (struct cli_def *cli, char *command, char *argv[], int argc) +// int cmd_ip_hlen (struct cli_def *cli, char *command, char *argv[], int argc) +// int cmd_ip_len (struct cli_def *cli, char *command, char *argv[], int argc) +// int cmd_ip_id (struct cli_def *cli, char *command, char *argv[], int argc) +// int cmd_ip_offset (struct cli_def *cli, char *command, char *argv[], int argc) +// int cmd_ip_sum (struct cli_def *cli, char *command, char *argv[], int argc) +// int cmd_ip_tos (struct cli_def *cli, char *command, char *argv[], int argc) +// int cmd_ip_dscp (struct cli_def *cli, char *command, char *argv[], int argc) +// int cmd_ip_rsv (struct cli_def *cli, char *command, char *argv[], int argc) +// int cmd_ip_df (struct cli_def *cli, char *command, char *argv[], int argc) +// int cmd_ip_mf (struct cli_def *cli, char *command, char *argv[], int argc) +// int cmd_ip_option (struct cli_def *cli, char *command, char *argv[], int argc) + + + +// ip-address source default||rand|range +// +// default +// random +// A.B.C.D +// A.B.C.D /24 +// A.B.C.D E.F.G.H +int cmd_ip_address_source (struct cli_def *cli, char *command, char *argv[], int argc) +{ + u_int8_t IP1[4], IP2[4]; + u_int32_t ip1, ip2; + unsigned int prefix; + u_int32_t mask, invmask; + int i,r; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc>2) ) + { + cli_print(cli, "A.B.C.D configure a source IP address\n"); + cli_print(cli, "Optionally you may specify\r"); + cli_print(cli, "- a range of addresses, such as: 192.168.0.0 /16\r"); + cli_print(cli, " or: 192.168.0.1 192.168.255.255\r"); + cli_print(cli, "- 'random' for a randomly generated source address\r"); + cli_print(cli, "- 'default' for the interface default settings\n"); + return CLI_OK; + } + + switch (argc) + { + case 1: + if (mz_strcmp(argv[0], "default", 3)==0) + { + // find index of device_list with the device configured in clipkt: + i=0; + while (strncmp(device_list[i].dev, clipkt->device, 10) && (iip_src = device_list[i].ip_mops[3] + + device_list[i].ip_mops[2] * 256 + + device_list[i].ip_mops[1] * 256 * 256 + + device_list[i].ip_mops[0] * 256 * 256 * 256; + clipkt->ip_src_israndom = 0; + clipkt->ip_src_isrange = 0; + } + else if (mz_strcmp(argv[0], "random", 3)==0) + { + clipkt->ip_src_israndom = 1; + clipkt->ip_src_isrange = 0; + } + else if (mops_pdesc_ip (IP1, argv[0])==0) // check if format is really an IP address + { + clipkt->ip_src = str2ip32(argv[0]); + clipkt->ip_src_israndom = 0; + clipkt->ip_src_isrange = 0; + } + else // wrong input + { + cli_print(cli,"Invalid address/keyword\n"); + } + break; + case 2: // MUST be either like '10.1.1.0 /24' or '10.1.1.1 10.1.1.254' + if (mops_pdesc_ip (IP1, argv[0])==0) // check if format is really an IP address + { + clipkt->ip_src_start = str2ip32(argv[0]); + if (strlen(argv[1])<4) // probably prefix? + { + r=sscanf(argv[1],"/%u",&prefix); + if ((r==EOF) || (r==0) || (prefix<1) || (prefix>31)) + cli_print(cli, "Invalid prefix!\n"); + else + { + mask = 0xffffffff; + mask <<= (32-prefix); + invmask = 0xffffffff - mask; + ip1 = ((str2ip32 (argv[0])) & mask) +1; // the '+1' is to ensure that we do not start with the net-id + ip2 = ip1 | invmask; + clipkt->ip_src_start = ip1; + clipkt->ip_src_stop = ip2; + clipkt->ip_src_isrange = 1; + clipkt->ip_src_israndom = 0; + } + } + else if (mops_pdesc_ip (IP2, argv[1])==0) // probably 2nd IP address? + { + if (str2ip32(argv[1]) > clipkt->ip_src_start) + { + clipkt->ip_src_stop = str2ip32(argv[1]); + clipkt->ip_src_isrange = 1; + clipkt->ip_src_israndom = 0; + } + else + { + cli_print(cli, "Invalid range! The second IP address must be greater than the first!\n"); + } + } + else + { + cli_print(cli, "Second parameter must be either a valid IP address or a prefix length \n"); + } + } + else // first string is not a valid IP address + { + cli_print(cli, "First parameter must be a valid IP address\n"); + } + break; + default: + cli_print(cli, "Invalid format!\n"); + } + + return CLI_OK; +} + + + +// ip-address destination |range +int cmd_ip_address_destination (struct cli_def *cli, char *command, char *argv[], int argc) +{ + u_int8_t IP1[4], IP2[4]; + u_int32_t ip1, ip2; + unsigned int prefix; + u_int32_t mask, invmask; + int r; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc>2) ) + { + cli_print(cli, "A.B.C.D configure a destination IP address\n"); + cli_print(cli, "Optionally specify a range of addresses, such as: 192.168.0.0 /16\r"); + cli_print(cli, " or: 192.168.0.1 192.168.255.255\n"); + return CLI_OK; + } + + switch (argc) + { + case 1: + if (mops_pdesc_ip (IP1, argv[0])==0) // check if format is really an IP address + { + clipkt->ip_dst = str2ip32(argv[0]); + clipkt->ip_dst_isrange = 0; + } + else // wrong input + { + cli_print(cli,"Invalid address/range\n"); + } + break; + case 2: // MUST be either like '10.1.1.0 /24' or '10.1.1.1 10.1.1.254' + if (mops_pdesc_ip (IP1, argv[0])==0) // check if format is really an IP address + { + clipkt->ip_dst_start = str2ip32(argv[0]); + if (strlen(argv[1])<4) // probably prefix? + { + r=sscanf(argv[1],"/%u",&prefix); + if ((r==EOF) || (r==0) || (prefix<1) || (prefix>31)) + cli_print(cli, "Invalid prefix!\n"); + else + { + mask = 0xffffffff; + mask <<= (32-prefix); + invmask = 0xffffffff - mask; + ip1 = ((str2ip32 (argv[0])) & mask) +1; // the '+1' is to ensure that we do not start with the net-id + ip2 = ip1 | invmask; + clipkt->ip_dst_start = ip1; + clipkt->ip_dst_stop = ip2; + clipkt->ip_dst_isrange = 1; + } + } + else if (mops_pdesc_ip (IP2, argv[1])==0) // probably 2nd IP address? + { + if (str2ip32(argv[1]) > clipkt->ip_dst_start) + { + clipkt->ip_dst_stop = str2ip32(argv[1]); + clipkt->ip_dst_isrange = 1; + } + else + { + cli_print(cli, "Range requirement: The second IP address must be greater than the first!\n"); + } + } + else + { + cli_print(cli, "Second parameter must be either a valid IP address or a prefix length \n"); + } + } + else // first string is not a valid IP address + { + cli_print(cli, "First parameter must be a valid IP address\n"); + } + break; + default: + cli_print(cli, "Invalid IP or range specification!\n"); + } + + return CLI_OK; +} + + + + +int cmd_ip_version (struct cli_def *cli, char *command, char *argv[], int argc) +{ + int ver; + + if (strncmp(argv[argc-1], "?", 2)==0) + { + cli_print(cli, "Specify the IP version (default: 4).\n"); + return CLI_OK; + } + + ver = (int) str2int(argv[0]); + + if (ver>15) + { + cli_print(cli, "Version must be within range 0..15\n"); + return CLI_OK; + } + + clipkt->ip_version = ver; + + return CLI_OK; +} + + + +int cmd_ip_ttl (struct cli_def *cli, char *command, char *argv[], int argc) +{ + int ttl; + + if (strncmp(argv[argc-1], "?", 2)==0) + { + cli_print(cli, "Specify the TTL (default: 255).\n"); + + return CLI_OK; + } + + ttl = (int) str2int(argv[0]); + + if (ttl>255) + { + cli_print(cli, "TTL must be within range 0..255\n"); + return CLI_OK; + } + + clipkt->ip_ttl = ttl; + + + return CLI_OK; +} + + + +int cmd_ip_protocol (struct cli_def *cli, char *command, char *argv[], int argc) +{ + int proto; + + if (strncmp(argv[argc-1], "?", 2)==0) + { + cli_print(cli, "Specify the protocol number (default: 0).\n"); + + return CLI_OK; + } + + proto = (int) str2int(argv[0]); + + if (proto>255) + { + cli_print(cli, "The protocol number must be within range 0..255\n"); + return CLI_OK; + } + + clipkt->ip_proto = proto; + + return CLI_OK; +} + + + + + +int cmd_ip_hlen (struct cli_def *cli, char *command, char *argv[], int argc) +{ + int ihl; + + if (strncmp(argv[argc-1], "?", 2)==0) + { + cli_print(cli, "Specify the header length in multiple of 4 bytes.\n"); + + return CLI_OK; + } + + ihl = (int) str2int(argv[0]); + + if (ihl>15) + { + cli_print(cli, "The IHL must be within range 0..15\n"); + return CLI_OK; + } + + clipkt->ip_IHL = ihl; + + return CLI_OK; +} + + + + + +int cmd_ip_len (struct cli_def *cli, char *command, char *argv[], int argc) +{ + int len; + + if (strncmp(argv[argc-1], "?", 2)==0) + { + cli_print(cli, "Specify the total packet length (0..65535).\n"); + + return CLI_OK; + } + + len = (int) str2int(argv[0]); + + if (len>65535) + { + cli_print(cli, "The packet length must be within range 0..65535\n"); + return CLI_OK; + } + + clipkt->ip_len = len; + + return CLI_OK; +} + + + + + +int cmd_ip_id (struct cli_def *cli, char *command, char *argv[], int argc) +{ + + u_int32_t id; + + if (strncmp(argv[argc-1], "?", 2)==0) + { + cli_print(cli, "Specify the packet identification number (0..4294967295).\n"); + return CLI_OK; + } + + if (mz_strcmp(argv[0], "hex", 2)==0) + { + id = xstr2int (argv[1]); + } + else + { + id = str2int (argv[0]); + } + + clipkt->ip_id = id; + + return CLI_OK; +} + + + + + + +int cmd_ip_offset (struct cli_def *cli, char *command, char *argv[], int argc) +{ + + int offset; + + if (strncmp(argv[argc-1], "?", 2)==0) { + cli_print(cli, "Specify the fragment offset in multiples of 8 bytes.\n"); + return CLI_OK; + } + + offset = (int) str2int(argv[0]); + + if (offset>8191) { + cli_print(cli, "The fragment offset must be within range 0..8191\n"); + return CLI_OK; + } + + clipkt->ip_frag_offset = offset; + + return CLI_OK; +} + + + + + +int cmd_ip_sum (struct cli_def *cli, char *command, char *argv[], int argc) +{ + int sum; + + if (strncmp(argv[argc-1], "?", 2)==0) + { + cli_print(cli, "Specify the IP checksum in hexadecimal or use the keyword 'auto'.\r"); + cli_print(cli, "By default, the checksum is computed automatically.\n"); + return CLI_OK; + } + + if (mz_strcmp(argv[0], "auto", 2)==0) + { + clipkt->ip_sum_false=0; + return CLI_OK; + } + + sum = (int) xstr2int(argv[0]); + + if (sum>0xffff) + { + cli_print(cli, "The checksum must be within range 0..ffff\n"); + return CLI_OK; + } + + clipkt->ip_sum = (u_int16_t) sum; + clipkt->ip_sum_false=1; + + return CLI_OK; +} + + + + + + + +int cmd_ip_tos (struct cli_def *cli, char *command, char *argv[], int argc) +{ + char *tmp; + + if (strncmp(argv[argc-1], "?", 2)==0) + { + cli_print(cli, "Specify the Type of Service field: [] [MBZ]\n"); + cli_print(cli, " - IP precedence (IPP) 0..7\r"); + cli_print(cli, " - ToS: delay/throughput/reliability/cost 0..15\r"); + cli_print(cli, " - MBZ ('must be zero' - however, not with Mausezahn...)\r"); + cli_print(cli, "Or, alternatively, configure the whole byte in hex.\n"); + cli_print(cli, "EXAMPLES:\n"); + cli_print(cli, " 5 ... IPP = 5\r"); + cli_print(cli, " 5 9 ... IPP = 5 and ToS = 9\r"); + cli_print(cli, " 5 MBZ ... IPP = 5 and MBZ is set\r"); + cli_print(cli, " 5 9 MBZ ... All three fields configured\r"); + cli_print(cli, " hex a8 ... the whole byte is set to 10101000\r"); + cli_print(cli, " 10101000 ... the whole byte in binary\n"); + return CLI_OK; + } + + if ((argc==1) && (mz_strisbinary(argv[0])==8)) + { + clipkt->ip_tos = (u_int8_t) str2bin8 (argv[0]); + return CLI_OK; + } + + if ((argc==2) && (mz_strcmp(argv[0], "hex", 2)==0)) + { + tmp = argv[1]; + + if (strlen(tmp)!=2) + { + cli_print(cli, "You must specify a 2-digit hexadecimal value\n"); + return CLI_OK; + } + + if (!(isxdigit(tmp[0])) || (!(isxdigit(tmp[1])))) + { + cli_print(cli, "Non-hexadecimal value!\n"); + return CLI_OK; + } + + clipkt->ip_tos = (u_int8_t) xstr2int (tmp); + return CLI_OK; + } + + switch (argc) + { + case 1: + if (mz_strcmp(argv[0], "mbz", 1)==0) + { + mops_ip_tos(clipkt, -1, -1, 1); + } + else + { + if (mops_ip_tos(clipkt, (int)str2int(argv[0]), -1, 0)) + cli_print(cli, "Invalid IP Precedence value\n"); + } + break; + + case 2: + if (mz_strcmp(argv[1], "mbz", 1)==0) + { + if (mops_ip_tos(clipkt, (int)str2int(argv[0]), -1, 1)) + cli_print(cli, "Invalid IP Precedence value\n"); + } + else + { + if (mops_ip_tos(clipkt, (int)str2int(argv[0]), (int)str2int(argv[1]), 0)) + cli_print(cli, "Invalid values\n"); + } + break; + + case 3: + if (mz_strcmp(argv[2], "mbz", 1)!=0) + cli_print(cli, "In this case the 3rd argument must be 'mbz'\n"); + else + if (mops_ip_tos(clipkt, (int)str2int(argv[0]), (int)str2int(argv[1]), 1)) + cli_print(cli, "Invalid values\n"); + break; + } + + return CLI_OK; +} + + + + + + + +int cmd_ip_dscp (struct cli_def *cli, char *command, char *argv[], int argc) +{ + if ((argc!=1) || (strncmp(argv[argc-1], "?", 2)==0)) + { + cli_print(cli, "Specify the Type of Service field using the DSCP format.\r"); + cli_print(cli, "Multiple notations are supported.\n"); + cli_print(cli, "Examples:\r"); + cli_print(cli, " AF32 .... specify AF codepoint with class 3 and drop probability 2\r"); + cli_print(cli, " EF .... specify Expedited Forwarding\r"); + cli_print(cli, " CS7 .... specify Code Selector 7\r"); + cli_print(cli, " 101110 .... specify the DSCP in binary\r"); + cli_print(cli, " 56 .... specify the DSCP in decimal\r"); + cli_print(cli, "\r"); + return CLI_OK; + } + + switch (mops_ip_dscp(clipkt, argv[0])) + { + case -1: + cli_print(cli, "Invalid DSCP specification (use '?')\n"); + break; + case 1: + cli_print(cli, "Invalid AF code point (use '?')\n"); + break; + case 2: + cli_print(cli, "Invalid Code Selector (CS0..CS7)\n"); + break; + case 3: + cli_print(cli, "Invalid DSCP value (0..63)\n"); + break; + } + + return CLI_OK; +} + + + + + +int cmd_ip_rsv (struct cli_def *cli, char *command, char *argv[], int argc) +{ + + if (strncmp(argv[argc-1], "?", 2)==0) + { + cli_print(cli, "Set or unset the reserved flag.\n"); + return CLI_OK; + } + + if (argc!=1) + { + cli_print(cli, "Use the 'set' or 'unset' keywords.\n"); + return CLI_OK; + } + + + if (mz_strcmp(argv[0], "set", 1)==0) + { + clipkt->ip_flags_RS = 1; + return CLI_OK; + } + + if (mz_strcmp(argv[0], "unset", 1)==0) + { + clipkt->ip_flags_RS = 0; + return CLI_OK; + } + + cli_print(cli, "Unknown keyword. Use the 'set' or 'unset' keywords.\n"); + + return CLI_OK; + +} + + + + + +int cmd_ip_df (struct cli_def *cli, char *command, char *argv[], int argc) +{ + + if (strncmp(argv[argc-1], "?", 2)==0) + { + cli_print(cli, "Set or unset the don't fragment flag.\n"); + + return CLI_OK; + } + + if (argc!=1) + { + cli_print(cli, "Use the 'set' or 'unset' keywords.\n"); + return CLI_OK; + } + + + if (mz_strcmp(argv[0], "set", 1)==0) + { + clipkt->ip_flags_DF = 1; + return CLI_OK; + } + + if (mz_strcmp(argv[0], "unset", 1)==0) + { + clipkt->ip_flags_DF = 0; + return CLI_OK; + } + + cli_print(cli, "Unknown keyword. Use the 'set' or 'unset' keywords.\n"); + + + return CLI_OK; + +} + + + + + +int cmd_ip_mf (struct cli_def *cli, char *command, char *argv[], int argc) +{ + + if (strncmp(argv[argc-1], "?", 2)==0) + { + cli_print(cli, "Set or unset the more fragments flag.\n"); + + return CLI_OK; + } + + if (argc!=1) + { + cli_print(cli, "Use the 'set' or 'unset' keywords.\n"); + return CLI_OK; + } + + + if (mz_strcmp(argv[0], "set", 1)==0) + { + clipkt->ip_flags_MF = 1; + return CLI_OK; + } + + if (mz_strcmp(argv[0], "unset", 1)==0) + { + clipkt->ip_flags_MF = 0; + return CLI_OK; + } + + cli_print(cli, "Unknown keyword. Use the 'set' or 'unset' keywords.\n"); + + return CLI_OK; + +} + + +int cmd_ip_fragsize (struct cli_def *cli, char *command, char *argv[], int argc) +{ + u_int32_t fragsize=0; + + if (strncmp(argv[argc-1], "?", 2)==0) { + cli_print(cli, "Enable fragmentation by configuring a fragment size.\n"); + cli_print(cli, "Note that the fragment size specifies the number of bytes in the IP payload\r"); + cli_print(cli, "and NOT the assumed MTU on that link. The total packet size of each fragment\r"); + cli_print(cli, "will be 20 bytes larger (=size of IP header if no IP options are used).\n"); + cli_print(cli, "WARNING: The fragment size SHOULD be a multiple of 8 bytes if you expect\r"); + cli_print(cli, " a valid result.\n"); + cli_print(cli, "ARGUMENTS: \n"); + return CLI_OK; + } + + if (argc!=1) { + cli_print(cli, "Specify the fragment size in bytes.\n"); + return CLI_OK; + } + + + fragsize = (u_int32_t) str2int(argv[0]); + + if ((fragsize<0) || (fragsize>8000)) { + cli_print(cli, "The fragment size must be within range 0..8000\n"); + return CLI_OK; + } + + if (fragsize%8) { + cli_print(cli, "Warning: The fragment-size is not a multiple of 8.\n"); + } + + clipkt->ip_fragsize = fragsize; + + return CLI_OK; + +} + + + +int cmd_ip_fragoverlap (struct cli_def *cli, char *command, char *argv[], int argc) +{ + u_int32_t overlap=0; + + if (strncmp(argv[argc-1], "?", 2)==0) { + cli_print(cli, "Specify how many bytes should overlap when IP fragmentation is enabled.\n"); + cli_print(cli, "NOTE: The number of overlap bytes is either 0 (default, no overlap) or\r"); + cli_print(cli, " a multiple of 8 bytes but smaller than frag-size.\n"); + cli_print(cli, "ARGUMENTS: \n"); + return CLI_OK; + } + + if (argc!=1) { + cli_print(cli, "Specify how many bytes should overlap between successive IP fragments.\n"); + return CLI_OK; + } + + + overlap = (u_int32_t) str2int(argv[0]); + + if (clipkt->ip_fragsize == 0) { + cli_print(cli, "Please configure the fragment size first.\n"); + return CLI_OK; + } + + if ((overlap>clipkt->ip_fragsize) || (overlap%8)) { + cli_print(cli, "The overlap MUST be a multiple of 8 and MUST NOT exceed frag-size!\n"); + return CLI_OK; + } + + clipkt->ip_frag_overlap = overlap; + + return CLI_OK; +} + + + + + +int cmd_ip_option (struct cli_def *cli, char *command, char *argv[], int argc) +{ + int val=0; + + if ((strncmp(argv[argc-1], "?", 2)==0) || (argc==0)) { + cli_print(cli, "Add or delete IP options.\n"); + cli_print(cli, "You can only add one option after the other; if you want to configure multiple\r"); + cli_print(cli, "options then repeat this command. The options are added to the IP header in the\r"); + cli_print(cli, "same order as you configure them.\n"); + cli_print(cli, "Currently the following options are supported:\n"); + cli_print(cli, "router-alert [] ... signal transit routers to examine the content of this\r"); + cli_print(cli, " packet.\r"); + cli_print(cli, "\n"); + cli_print(cli, "clear ..................... remove all options from the packet\n"); + return CLI_OK; + } + + if (mz_strcmp(argv[0], "router-alert", 3)==0) { + switch (argc) { + case 1: + val=0; + break; + case 2: + val = (int) str2int(argv[1]); + break; + default: + cli_print(cli, "Too many arguments!\n"); + return CLI_OK; + } + if (mops_ip_option_ra (clipkt, val)) { + cli_print(cli, "Value must be within 0..65535\n"); + return CLI_OK; + } + + } else if (mz_strcmp(argv[0], "loose-source-route", 3)==0) { + cli_print(cli, "Currently not implemented\n"); + return CLI_OK; + } else if (mz_strcmp(argv[0], "record-route", 3)==0) { + cli_print(cli, "Currently not implemented\n"); + return CLI_OK; + } + + else if (mz_strcmp(argv[0], "clear", 2)==0) { + mops_ip_option_remove_all (clipkt); + } + + return CLI_OK; +} + + + +// By default we use ARP to determine the destination MAC and therefore support +// automatic (in)direct delivery of IP packets. Alternatively the user may turn +// this off and may configure an arbitrary destination MAC address +// +int cmd_ip_delivery (struct cli_def *cli, char *command, char *argv[], int argc) +{ + char str[16]; + if (strncmp(argv[argc-1], "?", 2)==0) { + cli_print(cli, "Enable or disable IP auto-delivery.\n"); + sprintf(str, "%s", (clipkt->auto_delivery_off) ? "DISABLED" : "ENABLED"); + cli_print(cli, "Currently, IP auto-delivery is %s\n", str); + return CLI_OK; + } + + if (argc!=1) { + cli_print(cli, "Argument missing. Enter either 'enable' or 'disable'\n"); + return CLI_OK; + } + + if (mz_strcmp(argv[0], "enable", 1)==0) + clipkt->auto_delivery_off=0; + else if (mz_strcmp(argv[0], "disable", 1)==0) + clipkt->auto_delivery_off=1; + else { + cli_print(cli, "Unknown keyword. Enter either 'enable' or 'disable'\n"); + return CLI_OK; + } + + sprintf(str, "%s", (clipkt->auto_delivery_off) ? "DISABLED" : "ENABLED"); + cli_print(cli, "IP auto-delivery is now %s\n", str); + + return CLI_OK; + +} + + + +int cmd_ip_end(struct cli_def *cli, char *command, char *argv[], int argc) +{ + char prompt[16]; + sprintf(prompt, "pkt-%i",clipkt->id); + cli_set_configmode(cli, MZ_MODE_PACKET, prompt); + return CLI_OK; +} diff --git a/src/cli_launch.c b/src/cli_launch.c new file mode 100644 index 0000000..f6671da --- /dev/null +++ b/src/cli_launch.c @@ -0,0 +1,141 @@ +/* + * Mausezahn - A fast versatile traffic generator + * Copyright (C) 2008-2010 Herbert Haas + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 as published by the + * Free Software Foundation. + * + * 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, see http://www.gnu.org/licenses/gpl-2.0.html + * +*/ + + + +#include "mz.h" +#include "cli.h" +#include "mops.h" + + + +int launch_bpdu (struct cli_def *cli, char *command, char *argv[], int argc) +{ + int conf=0; + struct mops_ext_bpdu * pd; + + if ( (strncmp(argv[argc-1],"?",2)==0) || (argc>1) ) { + cli_print(cli, "Launch a(nother) BPDU process:\n"); + cli_print(cli, " Per default a TCN-BPDU is sent.\r"); + cli_print(cli, "conf Use this keyword to emit configuration BPDUs\r"); + cli_print(cli, " (with this host as root bridge)\n"); + return CLI_OK; + } + + if (argc==1) { + if (mz_strcmp(argv[0], "conf", 1)==0) conf=1; + } + + if ((clipkt = mops_alloc_packet(mp_head)) == NULL) { // Problem, memory full? + cli_print(cli, "Cannot allocate additional memory!\n"); + return CLI_OK; + } + + strncpy (clipkt->packet_name, "sysBPDU", 7); + // OK, created a new packet + cli_print(cli, "Allocated new packet %s at slot %i",clipkt->packet_name, clipkt->id); + mops_set_defaults(clipkt); + if (mops_ext_add_pdesc (clipkt, MOPS_BPDU)) + cli_print(cli, "Cannot configure BPDU parameters!?\n"); + else { + clipkt->use_ETHER = 1; + clipkt->use_SNAP = 1; + clipkt->count = 0; + clipkt->ndelay.tv_sec = 2; + clipkt->ndelay.tv_nsec = 0; + pd = clipkt->p_desc; + if (conf) + pd->bpdu_type = 0x00; + else + pd->bpdu_type = 0x80; + mops_set_conf(clipkt); + if (mops_tx_simple (clipkt)) { + cli_print(cli, "Cannot create sending process.\r"); + } + } + + return CLI_OK; +} + + + +int launch_synflood (struct cli_def *cli, char *command, char *argv[], int argc) +{ + u_int8_t IP[4]; + int valid_ip=0, valid_port=0; + + if ( (strncmp(argv[argc-1],"?",2)==0) || (argc>2) || (argc==0)) { + cli_print(cli, "Launch a(nother) TCP SYN-Flood process:\n"); + cli_print(cli, " At least you must specify the destination IP address\r"); + cli_print(cli, " Optionally specify the destination port (default: range from 1-1023)\n"); + return CLI_OK; + } + + if (mops_pdesc_ip (IP, argv[0])==0) { // check if format is really an IP address + valid_ip=1; + } else { + cli_print(cli, "Invalid IP address\n"); + return CLI_OK; + } + + if (argc==2) { + if (mz_strisnum(argv[1])==0) { + cli_print(cli, "Invalid port number\n"); + return CLI_OK; + } + valid_port = (int) str2int(argv[1]); + if (valid_port>65535) { + cli_print(cli, "Invalid port number\n"); + return CLI_OK; + } + } + + + if ((clipkt = mops_alloc_packet(mp_head)) == NULL) { // Problem, memory full? + cli_print(cli, "Cannot allocate additional memory!\n"); + return CLI_OK; + } + + strncpy (clipkt->packet_name, "sysFlood_TCPSYN", 15); + // OK, created a new packet + cli_print(cli, "Allocated new packet %s at slot %i",clipkt->packet_name, clipkt->id); + mops_set_defaults(clipkt); + clipkt->use_ETHER = 1; + clipkt->use_IP = 1; + clipkt->use_TCP = 1; + clipkt->ip_proto = 6; + clipkt->count = 0; + clipkt->ip_dst = str2ip32(argv[0]); + clipkt->ip_src_israndom=1; + if (valid_port) { + clipkt->dp = valid_port; + } else { + clipkt->dp_isrange=1; + clipkt->dp_start=1; + clipkt->dp_stop=1023; + } + clipkt->ndelay.tv_sec = 0; + clipkt->ndelay.tv_nsec = 0; + mops_set_conf(clipkt); + mops_tcp_add_option (clipkt,64,0,0,0,0); + if (mops_tx_simple (clipkt)) { + cli_print(cli, "Cannot create sending process.\r"); + } + + return CLI_OK; +} diff --git a/src/cli_legacy.c b/src/cli_legacy.c new file mode 100644 index 0000000..3b56621 --- /dev/null +++ b/src/cli_legacy.c @@ -0,0 +1,141 @@ +/* + * Mausezahn - A fast versatile traffic generator + * Copyright (C) 2008 Herbert Haas + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 as published by the + * Free Software Foundation. + * + * 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, see http://www.gnu.org/licenses/gpl-2.0.html + * +*/ + + +#include "mz.h" +#include "cli.h" +#include "mops.h" + + +int transmit (struct cli_def *cli, char *command, char *argv[], int argc) +{ + int i; + char argstr[10000]; + + argstr[0]='\0'; + + if (argc>1) + { + for (i=1; i10000) + { + cli_print(cli, "Argument list too long!\n"); + return CLI_OK; + } + if (strncmp(argv[i], "?", 1)==0) + { + strcat(argstr, ",help"); + } + else + strncat(argstr, argv[i], 5000); // TODO: This is ugly! + } + // TEST: cli_print(cli, "argc=%i, got '%s'\n", argc, argstr); + } + + + if (argv[0] == NULL) // raw hex string given + { + mode = BYTE_STREAM; + } + else if (strcmp(argv[0],"arp")==0) + { + mode = ARP; + } + else if (strcmp(argv[0],"bpdu")==0) + { + mode = BPDU; + } + else if (strcmp(argv[0],"ip")==0) + { + mode = IP; + } + else if (strcmp(argv[0],"udp")==0) + { + mode = UDP; + } + else if (strcmp(argv[0],"icmp")==0) + { + mode = ICMP; + } + else if (strcmp(argv[0],"tcp")==0) + { + mode = TCP; + } + else if (strcmp(argv[0],"dns")==0) + { + mode = DNS; + } + else if (strcmp(argv[0],"cdp")==0) + { + mode = CDP; + } + else if (strcmp(argv[0],"syslog")==0) + { + mode = SYSLOG; + } + else if (strcmp(argv[0],"lldp")==0) + { + mode = LLDP; + tx.packet_mode=0; // create whole frame by ourself + } + else if (strcmp(argv[0],"rtp")==0) + { + mode = RTP; + } + else if (strcmp(argv[0],"raw")==0) + { + strncpy(tx.arg_string, argstr, MAX_PAYLOAD_SIZE); + send_eth(); + } + else if (strcmp(argv[0],"?")==0) + { + cli_print(cli, + "| The following packet types are currently implemented:\n" + "|\n" + "| arp ... sends ARP packets\n" + "| bpdu ... sends BPDU packets (STP)\n" + "| cdp ... sends CDP messages\n" + "| ip ... sends IPv4 packets\n" + "| udp ... sends UDP datagrams\n" + "| tcp ... sends TCP segments\n" + "| icmp ... sends ICMP messages\n" + "| dns ... sends DNS messages\n" + "| rtp ... sends RTP datagrams\n" + "| syslog ... sends Syslog messages\n" + "| lldp ... sends LLDP datagrams\n" + "|\n" + "| raw ... raw layer 2 mode (specify whole frame in hex)\n" + "\n" + ); + return CLI_OK; + } + else + { + cli_print(cli, "Unknown packet type '%s'\r", argv[0]); + } + + + if (mode) + { + strncpy(tx.arg_string, argstr, MAX_PAYLOAD_SIZE); + tx_switch(cli); + } + + return CLI_OK; +} diff --git a/src/cli_lldp.c b/src/cli_lldp.c new file mode 100644 index 0000000..7fd73f9 --- /dev/null +++ b/src/cli_lldp.c @@ -0,0 +1,437 @@ +/* + * Mausezahn - A fast versatile traffic generator + * Copyright (C) 2008-2010 Herbert Haas + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 as published by the + * Free Software Foundation. + * + * 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, see http://www.gnu.org/licenses/gpl-2.0.html + * +*/ + + + +#include "mz.h" +#include "cli.h" +#include "mops.h" + + +int cmd_lldp_conformance (struct cli_def *cli, char *command, char *argv[], int argc) +{ + struct mops_ext_lldp * pd = clipkt->p_desc; + + + if ( (strcmp(argv[argc-1],"?")==0) || (argc!=1)) { + cli_print(cli, "Enables or disables LLDP standard conformance mode.\n"); + cli_print(cli, "Keywords: enable | disable\n"); + cli_print(cli, "Per default, standard LLDP messages are created which require a fixed\r"); + cli_print(cli, "order of the mandatory TLVs. If the standard conformance mode is disabled\r"); + cli_print(cli, "then you can configure an arbitrary sequence of LLDP TLVs. \n"); + cli_print(cli, "Currently, the LLDP standard conformance mode is %s\n", (pd->non_conform) ? "DISABLED" : "ENABLED"); + return CLI_OK; + } + + if (mz_strcmp(argv[0], "enable", 1)==0) { + pd->non_conform = 0; + return CLI_OK; + } + + + if (mz_strcmp(argv[0], "disable", 1)==0) { + pd->non_conform = 1; + return CLI_OK; + } + + cli_print(cli, "Enter enable or disable\n"); + return CLI_OK; +} + + +int cmd_lldp_chassis_id (struct cli_def *cli, char *command, char *argv[], int argc) +{ + struct mops_ext_lldp * pd = clipkt->p_desc; + int subtype = 4; + char *cid; + int cl, cidl; + u_int8_t tmp[512]; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc>2)) { + cli_print(cli, "Configure a Chassis ID TLV.\n"); + cli_print(cli, "ARGUMENTS: [] \n"); + cli_print(cli, "By default the is of kind 'mac address (4)' and the \r"); + cli_print(cli, "must be a hexadecimal string (e. g. 00:01:ca:fe:de:ad) of max 255 bytes\n"); + return CLI_OK; + } + + if (argc==2) { + subtype = (int) str2int(argv[0]); + if ((subtype>255) || (mz_strisnum(argv[0])==0)) { + cli_print(cli, "Invalid subtype\n"); + return CLI_OK; + } + cid = argv[1]; + } else + cid = argv[0]; + + cl=strnlen(cid, 1024); + + if (cl>=1024) { + cli_print(cli, "Chassis-ID too long\n"); + return CLI_OK; + } else cidl=str2hex(cid, tmp, 511); + + if (pd->non_conform == 0) { + pd->chassis_id_subtype = subtype; + memcpy((void*) pd->chassis_id, (void*)tmp, cidl); + pd->chassis_id_len = cidl; + } else { + // non_conform + mops_lldp_opt_tlv_chassis (clipkt, subtype, cidl, tmp); + } + + return CLI_OK; +} + + + + +int cmd_lldp_port_id (struct cli_def *cli, char *command, char *argv[], int argc) +{ + struct mops_ext_lldp * pd = clipkt->p_desc; + int subtype = 4; + char *pid; + int pl; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc>2)) { + cli_print(cli, "Configure a Port ID TLV.\n"); + cli_print(cli, "ARGUMENTS: [] \n"); + cli_print(cli, "By default the is of kind 'Interface name (5)' and the \r"); + cli_print(cli, "must be a ascii string (usually the name of the interface e. g. eth3) of\r"); + cli_print(cli, "max 255 bytes.\n"); + return CLI_OK; + } + + if (argc==2) { + subtype = (int) str2int(argv[0]); + if ((subtype>255) || (mz_strisnum(argv[0])==0)) { + cli_print(cli, "Invalid subtype\n"); + return CLI_OK; + } + pid = argv[1]; + } else + pid = argv[0]; + + pl=strnlen(pid, 256); + + if (pl>255) { + cli_print(cli, "Port-ID too long\n"); + return CLI_OK; + } + + + if (pd->non_conform == 0) { + pd->port_id_subtype = subtype; + memcpy((void*) pd->port_id, (void*) pid, pl); + pd->port_id_len = pl; + } else { + // non_conform + mops_lldp_opt_tlv_port (clipkt, subtype, pl, (u_int8_t*) pid); + } + + return CLI_OK; +} + + + +int cmd_lldp_ttl (struct cli_def *cli, char *command, char *argv[], int argc) +{ + struct mops_ext_lldp * pd = clipkt->p_desc; + int ttl; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc!=1)) { + cli_print(cli, "Configure the LLDP TTL.\n"); + cli_print(cli, "ARGUMENTS: \n"); + cli_print(cli, "The TTL must be within 0..65535\n"); + return CLI_OK; + } + + ttl = (int) str2int(argv[0]); + + if (mz_strisnum(argv[0])==0) { + cli_print(cli, "Invalid argument\n"); + return CLI_OK; + } + + if (ttl>0xffff) { + cli_print(cli, "TTL must be within 0..65535\n"); + return CLI_OK; + } + + if (pd->non_conform == 0) { + pd->TTL = ttl; + } else { + // non_conform + mops_lldp_opt_tlv_TTL (clipkt, ttl); + } + + return CLI_OK; +} + + + +int cmd_lldp_vlan (struct cli_def *cli, char *command, char *argv[], int argc) +{ + int vlan; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc!=1)) { + cli_print(cli, "Configure the LLDP Port VLAN-ID.\n"); + cli_print(cli, "ARGUMENTS: \n"); + cli_print(cli, "The vlan-id must be within 0..65535\n"); + return CLI_OK; + } + + vlan = (int) str2int(argv[0]); + + if (mz_strisnum(argv[0])==0) { + cli_print(cli, "Invalid argument\n"); + return CLI_OK; + } + + if (vlan>0xffff) { + cli_print(cli, "The VLAN-ID must be within 0..65535\n"); + return CLI_OK; + } + + + mops_lldp_opt_tlv_vlan (clipkt, vlan); + + return CLI_OK; +} + + + +int cmd_lldp_opt_tlv (struct cli_def *cli, char *command, char *argv[], int argc) +{ + int type=0, len=0; + u_int8_t tmp[512]; + + + if ( (strcmp(argv[argc-1],"?")==0) || (argc!=3)) { + cli_print(cli, "Configure an arbitrary optional TLV.\n"); + cli_print(cli, "ARGUMENTS: ascii|hex \n"); + cli_print(cli, "The TLV type must be between 0..127, the value length is up to 511 bytes.\n"); + return CLI_OK; + } + + + if (mz_strcmp(argv[0], "ascii", 1)==0) { + if ((len=strnlen(argv[2],512))>511) { + cli_print(cli, " must be smaller or equal 511 characters\n"); + return CLI_OK; + } + mz_strncpy((char*) tmp, argv[2], 511); + } else if (mz_strcmp(argv[0], "hex", 1)==0) { + len=str2hex(argv[2], tmp, 512); + if (len>511) { + cli_print(cli, " must be smaller or equal 511 bytes\n"); + return CLI_OK; + } + } + + type = (int) str2int(argv[1]); + + if (mz_strisnum(argv[1])==0) { + cli_print(cli, "Invalid type\n"); + return CLI_OK; + } + + if (type>127) { + cli_print(cli, " must be within 0..127\n"); + return CLI_OK; + } + + + if (mops_lldp_opt_tlv (clipkt, type, len, tmp)==0) + cli_print(cli, "Invalid TLV values\n"); + + return CLI_OK; +} + + + + +int cmd_lldp_opt_tlv_bad (struct cli_def *cli, char *command, char *argv[], int argc) +{ + int type, len=0, wronglen; + u_int8_t tmp[512]; + + + if ( (strcmp(argv[argc-1],"?")==0) || (argc!=4)) { + cli_print(cli, "Configure an arbitrary optional *BAD* TLV.\n"); + cli_print(cli, "ARGUMENTS: ascii|hex \n"); + cli_print(cli, "Using this command you can add a custom TLV with a wrong length parameter.\r"); + cli_print(cli, "Such TLV can be used to verify whether LLDP receivers are robust enough\r\r"); + cli_print(cli, "since a too small could cause a buffer overflow. The TLV type\r"); + cli_print(cli, "must be between 0..127, the can be within 0..511 (and can be\r"); + cli_print(cli, "also the true length of course\n"); + return CLI_OK; + } + + + if (mz_strcmp(argv[0], "ascii", 1)==0) { + if ((len=strnlen(argv[3],512))>511) { + cli_print(cli, " must be smaller or equal 511 characters\n"); + return CLI_OK; + } + mz_strncpy((char*) tmp, argv[3], 511); + } else if (mz_strcmp(argv[0], "hex", 1)==0) { + len=str2hex(argv[3], tmp, 512); + if (len>511) { + cli_print(cli, " must be smaller or equal 511 bytes\n"); + return CLI_OK; + } + } + + type = (int) str2int(argv[1]); + + if (mz_strisnum(argv[1])==0) { + cli_print(cli, "Invalid type\n"); + return CLI_OK; + } + + if (type>127) { + cli_print(cli, " must be within 0..127\n"); + return CLI_OK; + } + + wronglen = (int) str2int(argv[2]); + + if (mz_strisnum(argv[2])==0) { + cli_print(cli, "Invalid length\n"); + return CLI_OK; + } + + if (wronglen>511) { + cli_print(cli, " must be within 0..511\n"); + return CLI_OK; + } + + if (mops_lldp_opt_tlv_bad (clipkt, type, wronglen, len, tmp)==0) + cli_print(cli, "Invalid TLV values\n"); + + return CLI_OK; +} + + + +int cmd_lldp_opt_org (struct cli_def *cli, char *command, char *argv[], int argc) +{ + int subtype, len=0, oui=0; + u_int8_t tmp[512]; + + + if ( (strcmp(argv[argc-1],"?")==0) || (argc!=4)) { + cli_print(cli, "Configure an organisational TLV.\n"); + cli_print(cli, "ARGUMENTS: ascii|hex \n"); + cli_print(cli, "Using this command you can add an arbitrary organisational TLV. The represents\r"); + cli_print(cli, "the 'Organisational Unique Identifier' and consists of exactly three bytes in hexadecimal\r"); + cli_print(cli, "format, such as '00005e' The is a value between <0..255>, and the length of the\r"); + cli_print(cli, "value is up to 507 bytes.\n"); + return CLI_OK; + } + + + if (mz_strcmp(argv[0], "ascii", 1)==0) { + if ((len=strnlen(argv[3],512))>511) { + cli_print(cli, " must be smaller or equal 511 characters\n"); + return CLI_OK; + } + mz_strncpy((char*) tmp, argv[3], 511); + } else if (mz_strcmp(argv[0], "hex", 1)==0) { + len=str2hex(argv[3], tmp, 512); + if (len>511) { + cli_print(cli, " must be smaller or equal 511 bytes\n"); + return CLI_OK; + } + } + + oui = xstr2int(argv[1]); + if (mz_strishex(argv[1])==0) { + cli_print(cli, "Invalid oui value\n"); + return CLI_OK; + } + + if (oui>0xffffff) { + cli_print(cli, " must be within 0..ffffff\n"); + return CLI_OK; + } + + subtype = (int) str2int(argv[2]); + + if (mz_strisnum(argv[2])==0) { + cli_print(cli, "Invalid subtype\n"); + return CLI_OK; + } + + if (subtype>255) { + cli_print(cli, " must be within 0..255\n"); + return CLI_OK; + } + + + if (mops_lldp_opt_tlv_org (clipkt, oui, subtype, len, tmp)==0) + cli_print(cli, "Invalid TLV values\n"); + + return CLI_OK; +} + + + + + +int cmd_lldp_endtlv (struct cli_def *cli, char *command, char *argv[], int argc) +{ + + if ( (strcmp(argv[argc-1],"?")==0) || (argc>0)) { + cli_print(cli, "Add an 'End of LLDP' TLV\n"); + cli_print(cli, "ARGUMENTS: none\n"); + cli_print(cli, "This command allows you to insert an 'End of LLDP' TLV at any\r"); + cli_print(cli, "point within the optional TLV list. You usually want this to\r"); + cli_print(cli, "create an invalid LLDPU to test the receiver.\n"); + return CLI_OK; + } + + + mops_lldp_opt_tlv_end (clipkt); + + return CLI_OK; +} + + +int cmd_lldp_reset (struct cli_def *cli, char *command, char *argv[], int argc) +{ + + if ( (strcmp(argv[argc-1],"?")==0) || (argc>0)) { + cli_print(cli, "Reset the LLPDU and clear all optional TLVs.\n"); + cli_print(cli, "ARGUMENTS: none\n"); + cli_print(cli, "All optional TLVs are added in the sequence as you configure them.\r"); + cli_print(cli, "Use this command to reset the LLDP and reconfigure all optional\r"); + cli_print(cli, "TLVs again. Additionally the parameters of the mandatory TLVs are\r"); + cli_print(cli, "reset to defaults.\n"); + return CLI_OK; + } + + mops_init_pdesc_lldp(clipkt); + + return CLI_OK; +} + + + diff --git a/src/cli_packet.c b/src/cli_packet.c new file mode 100644 index 0000000..fd8c938 --- /dev/null +++ b/src/cli_packet.c @@ -0,0 +1,1121 @@ +/* + * Mausezahn - A fast versatile traffic generator + * Copyright (C) 2008-2010 Herbert Haas + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 as published by the + * Free Software Foundation. + * + * 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, see http://www.gnu.org/licenses/gpl-2.0.html + * +*/ + + +#include "mz.h" +#include "cli.h" +#include "mops.h" + + +int debug_packet (struct cli_def *cli, char *command, char *argv[], int argc) +{ + cli_debug = CLI_DEBUG_PACKET; + cli_print (cli, "Packet debugging enabled\n"); + return CLI_OK; +} + + + + +// Enter packet config mode: +// +// 1) either with an optional packet slot number => modify existing slot +// 2) or without number to allocate a new slot entry +// +int enter_packet (struct cli_def *cli, char *command, char *argv[], int argc) +{ + char prompt[16]; + + if (argc==0) { // Allocate new packet + if ((clipkt = mops_alloc_packet(mp_head)) == NULL) { // Problem, memory full? + cli_print(cli, "Holy flying spaghetti monster! Cannot allocate additional memory!\n"); + return CLI_OK; + } + // OK, created a new packet + snprintf(prompt, 16, "pkt-%i",clipkt->id); + cli_print(cli, "Allocated new packet %s at slot %i",clipkt->packet_name, clipkt->id); + // mops_set_defaults(clipkt); //// implicitly done by mops_alloc_packet + } else if ( (strcmp(argv[argc-1],"?")==0) || (argc>1) ) { + cli_print(cli, " create a new packet slot\r"); + cli_print(cli, "NAME enter packet slot of packet with name NAME\r"); + cli_print(cli, "ID enter packet slot of packet with number ID\n"); + return CLI_OK; + } else { // user specified a unique packet_name + if ( (clipkt = mops_search_name (mp_head, argv[0]))==NULL) { // packet name does not exist + if ( (clipkt = mops_search_id (mp_head, (int) str2int(argv[0])))==NULL) { // packet id does not exist + cli_print(cli, "Packet does not exist\n"); + return CLI_OK; + } + } + if (mops_is_any_active(clipkt)) { // don't allow to configure packets which are active! + cli_print(cli, "The selected packet is currently in active state!\r"); + cli_print(cli, "In order to configure this packet, please stop the associated packet process first.\n"); + return CLI_OK; + } + snprintf(prompt, 16, "pkt-%i",clipkt->id); + cli_print(cli, "Modify packet parameters for packet %s [%i]",clipkt->packet_name, clipkt->id); + } + cli_set_configmode(cli, MZ_MODE_PACKET, prompt); + //cli_print(cli, "Packet configuration mode - called %s with %s\r\n", __FUNCTION__, command); + return CLI_OK; +} + + + + + + + +// Specify the type and enter the appropriate configuration mode +// NOTE that we also reset and create the p_desc here! +int cmd_packet_type(struct cli_def *cli, char *command, char *argv[], int argc) +{ + char prompt[16]; + int ret=0; + char wrn[] = "Error: Could not create mops extension handle\n"; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc!=1) ) + { + cli_print(cli, "Specify a packet type from the following list:\r\n"); + cli_print(cli, " arp\r"); + cli_print(cli, " bpdu\r"); +// cli_print(cli, " cdp (not supported in this version)\r"); +// cli_print(cli, " icmp (not supported in this version)\r"); + cli_print(cli, " igmp\r"); + cli_print(cli, " ip\r"); + cli_print(cli, " lldp\r"); + cli_print(cli, " rtp\r"); +// cli_print(cli, " syslog (not supported in this version)\r"); + cli_print(cli, " tcp\r"); + cli_print(cli, " udp\r"); + return CLI_OK; + } + + if (mz_strcmp(argv[0],"arp",3) == 0) + { + if (mops_ext_add_pdesc (clipkt, MOPS_ARP)) + cli_print(cli, "%s", wrn); + else + { + ret=mops_clear_layers(clipkt, + MOPS_SNAP|MOPS_MPLS|MOPS_IP|MOPS_UDP|MOPS_TCP); + clipkt->use_ETHER = 1; + clipkt->eth_type = 0x806; + sprintf(prompt, "pkt-%i-arp",clipkt->id); + cli_set_configmode(cli, MZ_MODE_PACKET_ARP, prompt); + mops_update_arp(clipkt); + mops_set_conf(clipkt); + } + + } + else if (mz_strcmp(argv[0],"dns",3) == 0) + { + if (mops_ext_add_pdesc (clipkt, MOPS_DNS)) + cli_print(cli, "%s", wrn); + else + { + ret=mops_clear_layers(clipkt, MOPS_SNAP|MOPS_TCP); + clipkt->use_ETHER = 1; + clipkt->use_IP= 1; + clipkt->use_UDP= 1; + sprintf(prompt, "pkt-%i-dns",clipkt->id); + cli_set_configmode(cli, MZ_MODE_PACKET_DNS, prompt); + mops_set_conf(clipkt); + } + } + else if (mz_strcmp(argv[0],"icmp",3) == 0) + { + if (mops_ext_add_pdesc (clipkt, MOPS_ICMP)) + cli_print(cli, "%s", wrn); + else + { + ret=mops_clear_layers(clipkt, MOPS_SNAP|MOPS_TCP|MOPS_UDP); + clipkt->use_ETHER = 1; + clipkt->use_IP= 1; + sprintf(prompt, "pkt-%i-icmp",clipkt->id); + cli_set_configmode(cli, MZ_MODE_PACKET_ICMP, prompt); + mops_set_conf(clipkt); + } + } + else if (mz_strcmp(argv[0],"igmp",3) == 0) + { + if (mops_ext_add_pdesc (clipkt, MOPS_IGMP)) + cli_print(cli, "%s", wrn); + else + { + ret=mops_clear_layers(clipkt, MOPS_SNAP|MOPS_TCP|MOPS_UDP); + clipkt->use_ETHER = 1; + clipkt->use_IP= 1; + clipkt->ip_proto = 2; + mops_ip_option_ra(clipkt, 0); // add router alert option to IP header + sprintf(prompt, "pkt-%i-igmp",clipkt->id); + cli_set_configmode(cli, MZ_MODE_PACKET_IGMP, prompt); + mops_update_igmp(clipkt); + mops_set_conf(clipkt); + } + } + + else if (mz_strcmp(argv[0],"cdp",3) == 0) + { + if (mops_ext_add_pdesc (clipkt, MOPS_CDP)) + cli_print(cli, "%s", wrn); + else + { + ret=mops_clear_layers(clipkt, MOPS_ALL); + clipkt->use_ETHER = 1; + sprintf(prompt, "pkt-%i-cdp",clipkt->id); + cli_set_configmode(cli, MZ_MODE_PACKET_CDP, prompt); + mops_set_conf(clipkt); + } + } + else if (mz_strcmp(argv[0],"bpdu",3) == 0) + { + if (mops_ext_add_pdesc (clipkt, MOPS_BPDU)) + cli_print(cli, "%s", wrn); + else + { + ret=mops_clear_layers(clipkt, MOPS_MPLS|MOPS_IP|MOPS_UDP|MOPS_TCP); + clipkt->use_ETHER = 1; + clipkt->use_SNAP = 1; + sprintf(prompt, "pkt-%i-bpdu",clipkt->id); + cli_set_configmode(cli, MZ_MODE_PACKET_BPDU, prompt); + mops_update_bpdu(clipkt); + mops_set_conf(clipkt); + } + } + else if (mz_strcmp(argv[0],"ip",2) == 0) + { + ret=mops_clear_layers(clipkt, MOPS_TCP|MOPS_UDP); + clipkt->use_ETHER = 1; + clipkt->use_IP = 1; + sprintf(prompt, "pkt-%i-ip",clipkt->id); + cli_set_configmode(cli, MZ_MODE_PACKET_IP, prompt); + mops_set_conf(clipkt); + } + else if (mz_strcmp(argv[0],"udp",3) == 0) + { + ret=mops_clear_layers(clipkt, MOPS_SNAP|MOPS_TCP); + clipkt->use_ETHER = 1; + clipkt->use_IP = 1; + clipkt->use_UDP = 1; + clipkt->ip_proto = 17; + sprintf(prompt, "pkt-%i-udp",clipkt->id); + cli_set_configmode(cli, MZ_MODE_PACKET_UDP, prompt); + mops_set_conf(clipkt); + } + else if (mz_strcmp(argv[0],"tcp",3) == 0) + { + ret=mops_clear_layers(clipkt, MOPS_SNAP|MOPS_UDP); + clipkt->use_ETHER = 1; + clipkt->use_IP = 1; + clipkt->use_TCP = 1; + clipkt->ip_proto = 6; + sprintf(prompt, "pkt-%i-tcp",clipkt->id); + cli_set_configmode(cli, MZ_MODE_PACKET_TCP, prompt); + mops_set_conf(clipkt); + } + else if (mz_strcmp(argv[0],"syslog",3) == 0) + { + if (mops_ext_add_pdesc (clipkt, MOPS_SYSLOG)) + cli_print(cli, "%s", wrn); + else + { + ret=mops_clear_layers(clipkt, MOPS_SNAP|MOPS_TCP); + clipkt->use_ETHER = 1; + clipkt->use_IP = 1; + clipkt->use_UDP = 1; + sprintf(prompt, "pkt-%i-syslog",clipkt->id); + cli_set_configmode(cli, MZ_MODE_PACKET_SYSLOG, prompt); + mops_set_conf(clipkt); + } + } + else if (mz_strcmp(argv[0],"lldp",3) == 0) + { + if (mops_ext_add_pdesc (clipkt, MOPS_LLDP)) + cli_print(cli, "%s", wrn); + else + { + ret=mops_clear_layers(clipkt, MOPS_SNAP|MOPS_IP|MOPS_UDP|MOPS_TCP); + clipkt->use_ETHER = 1; + sprintf(prompt, "pkt-%i-lldp",clipkt->id); + cli_set_configmode(cli, MZ_MODE_PACKET_LLDP, prompt); + mops_set_conf(clipkt); + } + } + else if (mz_strcmp(argv[0],"rtp",3) == 0) + { + if (mops_ext_add_pdesc (clipkt, MOPS_RTP)) + cli_print(cli, "%s", wrn); + else + { + ret=mops_clear_layers(clipkt, MOPS_SNAP|MOPS_TCP); + clipkt->use_ETHER = 1; + clipkt->use_IP = 1; + clipkt->use_UDP = 1; + sprintf(prompt, "pkt-%i-rtp",clipkt->id); + cli_set_configmode(cli, MZ_MODE_PACKET_RTP, prompt); + mops_set_conf(clipkt); + } + } + + else // wrong user input + { + cli_print(cli, "Unknown type\n"); + return CLI_OK; + } + + if (ret) { + cli_print(cli, "Note that the following layer(2) have configured information:\r"); + if (ret & 1) cli_print(cli, " - Ethernet or 802.3\r"); + if (ret & 2) cli_print(cli, " - SNAP\r"); + if (ret & 4) cli_print(cli, " - 802.1Q\r"); + if (ret & 8) cli_print(cli, " - MPLS\r"); + if (ret & 16) cli_print(cli, " - IP\r"); + if (ret & 32) cli_print(cli, " - UDP\r"); + if (ret & 64) cli_print(cli, " - TCP\r"); + } + + mops_update(clipkt); + return CLI_OK; +} + + + + + +int cmd_packet_end(struct cli_def *cli, char *command, char *argv[], int argc) +{ + cli_set_configmode(cli, MODE_CONFIG, NULL); + return CLI_OK; +} + + + + + +int cmd_packet_clone (struct cli_def *cli, char *command, char *argv[], int argc) +{ + // TODO + return CLI_OK; +} + + +// Reserved words: "all", "slot" +int cmd_packet_name (struct cli_def *cli, char *command, char *argv[], int argc) +{ + if (strncmp(argv[0], "?", 2) == 0) + { + cli_print(cli, "Assign a packet name (max 16 chars)\n"); + return CLI_OK; + } + + if (argc>1) + { + cli_print(cli, "Packet name must not contain spaces\n"); + return CLI_OK; + } + + if (strlen(argv[0])>MAX_MOPS_PACKET_NAME_LEN) + { + cli_print(cli, "Packet name is limited to %i chars. You might use the 'description' command.\n",MAX_MOPS_PACKET_NAME_LEN); + return CLI_OK; + } + + if (mz_strcmp(argv[0], "all", 3)==0) + { + cli_print(cli, "This is a reserved word. Please choose another\n"); + return CLI_OK; + } + + strncpy(clipkt->packet_name, argv[0], MAX_MOPS_PACKET_NAME_LEN); + clipkt->packet_name[MAX_MOPS_PACKET_NAME_LEN-1] = 0x00; +// cli_print(cli, "Changed packet name to '%s'\n", clipkt->packet_name); + + return CLI_OK; +} + +int cmd_packet_description (struct cli_def *cli, char *command, char *argv[], int argc) +{ + if (strncmp(argv[argc-1], "?", 2) == 0) + { + cli_print(cli, "Assign a packet description (max %i chars)\n", MAX_MOPS_DESCRIPTION_LEN); + return CLI_OK; + } + + if (mops_pdesc_mstrings (clipkt->description, argv, argc, MAX_MOPS_DESCRIPTION_LEN)) + { + cli_print(cli, "String too long. Currently the description is limited to %i characters.\n", + MAX_MOPS_DESCRIPTION_LEN); + cli_print(cli, "Current description is:\n%s\n", clipkt->description); + } + + return CLI_OK; +} + +int cmd_packet_count (struct cli_def *cli, char *command, char *argv[], int argc) +{ + + if (strncmp(argv[argc-1], "?", 2) == 0) + { + cli_print(cli,"Specify the packet count. Zero means infinity.\n"); + return CLI_OK; + } + else if (argc) + { + clipkt->count = (unsigned long) str2int(argv[0]); + if (clipkt->count) + { + clipkt->cntx = clipkt->count; // count is finite: cntx will count down + } + else + { + clipkt->cntx = 0; // infinity: cntx will count up + } + return CLI_OK; + } + cli_print(cli,"Specify a packet count.\n"); + return CLI_OK; +} + + +int cmd_packet_delay (struct cli_def *cli, char *command, char *argv[], int argc) +{ + int ret=0; + char str[100]; + + if (strncmp(argv[argc-1], "?", 2) == 0) { + cli_print(cli, "delay [hour | min | sec | msec | usec | nsec]\n"); + cli_print(cli, "Specify the inter-packet delay in hours, minutes, seconds, milliseconds, microseconds,\r"); + cli_print(cli, "or nanoseconds. The default unit is milliseconds (i. e. when no unit is given).\n"); + return CLI_OK; + } + + switch (argc) { + case 1: // only one argument, but may contain an unit (such as '314sec') + ret = delay_parse(&clipkt->ndelay, argv[0], NULL); + break; + + case 2: // user specified two arguments such as '100 msec' + ret = delay_parse(&clipkt->ndelay, argv[0], argv[1]); + break; + default: + cli_print(cli, "Too many arguments! Expected delay value and unit, such as '10 msec'\n"); + return CLI_OK; + } + + switch (ret) { + case 1: + cli_print(cli, "Invalid unit! Use one of {nsec, usec, msec, sec, min, hours}\n"); + return CLI_OK; + break; + case 2: + cli_print(cli, "Value too large! Supported range is from 0 to 999999999\n"); + return CLI_OK; + break; + } + sprintf(str, "Inter-packet delay set to %lu sec and %lu nsec", clipkt->ndelay.tv_sec, clipkt->ndelay.tv_nsec); + cli_print(cli, "%s\n", str); + + return CLI_OK; +} + + + +int cmd_packet_bind (struct cli_def *cli, char *command, char *argv[], int argc) +{ + int i; + + if (strncmp(argv[argc-1], "?", 2) == 0) + { + cli_print(cli," Change the packet's network interface\r"); + cli_print(cli,"default Use interface settings as packet default\n"); + return CLI_OK; + } + else if (argc) + { + if (mz_strcmp(argv[0], "default", 3)==0) + { + i = mops_get_device_index(clipkt->device); + // Copy device_list[i].ip_mops and .mac_mops to clipkt->ip_src and ->eth_src + memcpy((void *) &clipkt->eth_src, (void *) &device_list[i].mac_mops[0], 6); + memcpy((void *) &clipkt->ip_src, (void *) &device_list[i].ip_mops[0], 4); + } + else + { + i = mops_get_device_index(argv[0]); + + if (i != -1) + { + strncpy(clipkt->device, argv[0], 16); // assign device to this mops + mops_use_device(clipkt, i); + } + else + cli_print(cli, "Unknown device, will stick on %s\n", clipkt->device); + } + } + else + cli_print(cli, "Nothing specified, will stick on %s\n", clipkt->device); + + return CLI_OK; +} + + + + + + + +// FORMAT: : such as: "100:3 17:5 ..." +// NOTE: LEFTMOST TAG = OUTER TAG IN FRAME +// CFI is set/unset separately (see ? below) +// Transmission format: 0x8100 plus CoS (3) CFI(1) VLAN(12) +int cmd_packet_dot1q (struct cli_def *cli, char *command, char *argv[], int argc) +{ + int i, j, k=0; + int n; + char Vlan[64], CoS[64]; + u_int16_t v,c; + + if (strcmp(argv[argc-1],"?")==0) + { + cli_print(cli, "Configure 802.1Q tags:\n"); + cli_print(cli, " VLAN[:CoS] [VLAN[:CoS]] ... The leftmost tag is the outer tag in the frame\r"); + cli_print(cli, " remove | all Remove one or more tags ( starts with 1),\r"); + cli_print(cli, " by default the first (=leftmost,outer) tag is removed,\r"); + cli_print(cli, " keyword 'all' can be used instead of tag numbers.\r"); + cli_print(cli, " cfi | nocfi [] Set or unset the CFI-bit in any tag (by default\r"); + cli_print(cli, " assuming the first tag).\n"); + return CLI_OK; + } + + if (argc==0) + { + cli_print(cli, "Specify one or more VLAN-IDs, optionally with CoS values\n"); + return CLI_OK; + } + + n = clipkt->dot1Q_s/4; // n = number of tags + +////////////////////////////////////////// + if (mz_strcmp(argv[0], "remove", 2)==0) + { + + if (argc>2) + { + cli_print(cli, "Too many arguments!\n"); + return CLI_OK; + } + + if (n==0) + { + cli_print(cli, "No 802.1Q tags present. None to be removed.\n"); + return CLI_OK; + } + + if ((argc==2) && (mz_strcmp(argv[1], "all", 1)==0)) + { + mops_dot1Q_remove(clipkt, 0); + return CLI_OK; + } + + if (argc==1) // no tag-nr specified => assume first tag + { + j=1; + } + else + { + j = (unsigned int) str2int(argv[1]); // take first argument + if (j==0) + { + cli_print(cli, "The tag-nr must be within {1..%i}\n", n); + return CLI_OK; + } + } + + // now remove tag + if (mops_dot1Q_remove(clipkt, j)) + { + cli_print(cli, "The packet only consists of %i tag(s)!\n", n); + } + return CLI_OK; + } + + +///////////////////////////////////////// + if (mz_strcmp(argv[0], "nocfi", 2)==0) + { + if (n==0) + { + cli_print(cli, "There are no 802.1Q tags yet!\n"); + return CLI_OK; + } + + if (argc>2) + { + cli_print(cli, "Invalid format!\n"); + return CLI_OK; + } + + if (argc==1) // no tag-nr specified => assume first tag + { + j=1; + } + else + { + j = (unsigned int) str2int(argv[1]); + } + + if (mops_dot1Q_nocfi(clipkt, j)) + { + cli_print(cli, "The packet only consists of %i tags!\n",k); + } + return CLI_OK; + } + +/////////////////////////////////////// + if (mz_strcmp(argv[0], "cfi", 2)==0) + { + if (n==0) + { + cli_print(cli, "There are no 802.1Q tags yet!\n"); + return CLI_OK; + } + + if (argc>2) + { + cli_print(cli, "Invalid format!\n"); + return CLI_OK; + } + + if (argc==1) // no tag-nr specified => assume first tag + { + j=1; + } + else + { + j = (unsigned int) str2int(argv[1]); + } + + if (mops_dot1Q_cfi(clipkt, j)) + { + cli_print(cli, "The packet only consists of %i tags!\n",k); + } + return CLI_OK; + } + + +///////////////////////// + for (i=0; i4095) + { + cli_print(cli, "[tag %i] VLAN number must not exceed 4095.\n", i+1); + return CLI_OK; + } + } + + if (CoS[0]==0x00) + { + c=0; + } + else + { + c = (u_int16_t) str2int(CoS); + if (c>7) + { + cli_print(cli, "[tag %i] CoS must not exceed 7.\n", i+1); + return CLI_OK; + } + } + + mops_dot1Q (clipkt, i, 1, v, c); // 3rd param '1' means 'new stack, also set dot1Q_s' + } + return CLI_OK; +} + + +// MPLS transmission format: Label(20) EXP(3) BoS(1) TTL(8) +// -- where BoS=0 indicate MORE labels, BoS=1 means last (bottom) label +// +// NOTE: The EtherType must be 0x8847 which identifies 'IP over MPLS' that is +// we do NOT need to set 0x800 for IP somewhere! Instead, mops_update() +// will always correctly set the single EtherType, if necessary after +// 802.1Q tags. For example when VLAN tags are present, the frame looks +// like this:----------------------------------vvvv----------------------- +// DMAC-SMAC-8100VLAN1-...-8100VLANn-EtherType(8847)-MPLS1-...-MPLSn-IP... +// +// MPLS Multicast packets are indicated by EtherType 8848 (!) +// See also RFC 5332 which allows both 'Codepoints' to carry MPLS multicast +// while 0x8848 only indicates multiaccess media. +// +// NOTE: If all MPLS labels are removed again, the original EtherType is restored again! +// The original EtherType is stored in mp->eth_type_backup +int cmd_packet_mpls (struct cli_def *cli, char *command, char *argv[], int argc) +{ + int a=0,i,j=0,k; + char LabelS[64], ExpS[64], TTLS[64]; + u_int32_t Label; + u_int8_t Exp; + u_int8_t TTL; + + + if ( (strcmp(argv[argc-1],"?")==0) || (argc==0) ) + { + cli_print(cli, "Configure one or more MPLS labels:\r"); + cli_print(cli, " LABEL[:EXP[:TTL]] [LABEL[:EXP[:TTL]]] ... The leftmost tag is the outer tag in frame\r"); + cli_print(cli, " remove | all Remove tag with number (starts with 1) or all.\r"); + cli_print(cli, " bos | nobos [] Set/unset BoS flag, by default in last (rightmost) label\r"); + cli_print(cli, " unicast|multicast Choose EtherType 0x8847 or 0x8848 respectively\n"); + cli_print(cli, "Examples:\r"); + cli_print(cli, " tag mpls 100 200 300 Specify three tags, 100,200,300 \r"); + cli_print(cli, " tag mpls 100:5 200:5:1 Let first tag have CoS 5, second tag additionally uses TTL=1\r"); + cli_print(cli, " tag mpls 100::8 Let first tag have TTL=8\n"); + cli_print(cli, "Reserved label numbers:\r"); + cli_print(cli, " 0 ... explicit NULL (IPv4)\r"); + cli_print(cli, " 1 ... Router Alert\r"); + cli_print(cli, " 2 ... explicit NULL (IPv6)\r"); + cli_print(cli, " 3 ... implicit NULL (only announced within LDP)\r"); + cli_print(cli, " 14 ... OAM Alert (ITU-T Y.1711, RFC 3429)\n"); + return CLI_OK; + } + +/////////////////////////////////////////// + if (mz_strcmp(argv[0], "unicast", 2)==0) + { + if (clipkt->use_MPLS==0) + { + cli_print(cli, "First configure an MPLS label stack."); + return CLI_OK; + } + + if (argc>1) + { + cli_print(cli, "This command does not support any argument.\n"); + } + + clipkt->eth_type = 0x8847; + return CLI_OK; + } + +///////////////////////////////////////////// + if (mz_strcmp(argv[0], "multicast", 2)==0) + { + if (clipkt->use_MPLS==0) + { + cli_print(cli, "First configure an MPLS label stack."); + return CLI_OK; + } + + if (argc>1) + { + cli_print(cli, "This command does not support any argument.\n"); + } + + clipkt->eth_type = 0x8848; + return CLI_OK; + } + + k = clipkt->mpls_s/4; // number of available tags + +////////////////////////////////////////// + if (mz_strcmp(argv[0], "remove", 2)==0) + { + if (argc>2) + { + cli_print(cli, "Too many arguments!\n"); + return CLI_OK; + } + + if ((argc==2) && (mz_strcmp(argv[1], "all", 1)==0)) + { + if (mops_mpls_remove(clipkt, 0)) + cli_print(cli, "No MPLS label stack present. Nothing removed\n"); + return CLI_OK; + } + + if (argc==1) // no tag-nr specified => assume first tag + { + if (k==0) + cli_print(cli, "Currently the packet has no tag that can be removed.\n"); + else + j=1; + } + else + { + j = (unsigned int) str2int(argv[1]); // take first argument + } + if (mops_mpls_remove(clipkt, j)) + cli_print(cli, "The tag number must be within 1..%i\n",k); + + return CLI_OK; + } + +/////////////////////////////////////// + if (mz_strcmp(argv[0], "bos", 2)==0) + { + if (argc>2) + { + cli_print(cli, "Too many arguments\n"); + return CLI_OK; + } + if (argc==2) + { + i = (int) str2int(argv[1]); + if (i>k) + { + cli_print(cli, "Tag number exceeds actual number of tags (%i)\n",a); + return CLI_OK; + } + } + else // argc==1 (no tag number specified) + { + i = k; // default: last tag! + } + + mops_mpls_bos (clipkt, i); + return CLI_OK; + } + + if (mz_strcmp(argv[0], "nobos", 2)==0) + { + if (argc>2) + { + cli_print(cli, "Too many arguments\n"); + return CLI_OK; + } + if (argc==2) + { + i = (int) str2int(argv[1]); + if (i>k) + { + cli_print(cli, "Tag number exceeds actual number of tags (%i)\n",k); + return CLI_OK; + } + } + else // argc==1 (no tag number specified) + { + i = k; // default: last tag! + } + mops_mpls_nobos (clipkt, i); + return CLI_OK; + } + + +//////////////////////////////////////////// + for (i=0;i 1048575) + { + cli_print(cli, "[Tag %i] Label value cannot exceed 1048575\n", i+1); + return CLI_OK; + } + } + // Get EXP + if (ExpS[0]==0x00) + { + Exp=0; + } + else + { + Exp = (u_int8_t) str2int(ExpS); + if (Exp>7) + { + cli_print(cli, "[Tag %i] EXP value must be within range 0..7\n", i+1); + return CLI_OK; + } + } + + // Get TTL + if (TTLS[0]==0x00) + { + TTL=255; + } + else + { + if (str2int(TTLS)>255) + { + cli_print(cli, "[Tag%i] TTL value must be within range 0..255\n", i+1); + return CLI_OK; + } + TTL = (u_int8_t) str2int(TTLS); + } + + // Now add MPLS tag: + mops_mpls(clipkt, i, argc, Label, Exp, TTL); + } + + return CLI_OK; +} + + +// SYNTAX: +// +// payload hex ff:00:01:02:aa:bb:cc:dd aa:bb:cc +// payload hex file tmp/dump.dat +// +int cmd_packet_payload_hex (struct cli_def *cli, char *command, char *argv[], int argc) +{ + char str[MAX_MOPS_MSG_SIZE*3]; + int len, i; + + if (strncmp(argv[argc-1], "?", 2)==0) + { + cli_print(cli, "Specify a payload in hexadecimal format:\n"); + cli_print(cli, " XX:XX:XX:... Either directly as sequence of digits, separated by colon or space\r"); + cli_print(cli, " file Or specify a filename with hexadecimal digits as content\r"); + cli_print(cli, " (Also in the file the separator can be either a colon or a space)\n"); + cli_print(cli, "Example: \r"); + cli_print(cli, "payload hex ff:ff:ff:ff:ff:ff 00:12:34:56:67:89 08:00 ca:fe:ba:be\n"); + return CLI_OK; + } + + if (argc==0) + { + cli_print(cli, "Specify an ascii payload\n"); + return CLI_OK; + } + + + if (mz_strcmp(argv[0],"file", 2)==0) + { + // > > > > > ******* TODO: Open file and configure mops with filepointer ******** < < < < < < < < + cli_print(cli, "This feature is currently not supported.\n"); + return CLI_OK; + } + + // Get byte sequence - first copy into str + if (mops_pdesc_mstrings (str, argv, argc, MAX_MOPS_MSG_SIZE*3)) + { + cli_print(cli, "Payload too long (limited to %i bytes).\n", MAX_MOPS_MSG_SIZE); + } + else // str contains byte sequence now - convert into msg and set msg_s + { + len = strlen(str); + for (i=0; imsg, MAX_MOPS_MSG_SIZE); + if (len==-1) + { + cli_print(cli, "Invalid byte sequence. Each byte must be specified with two hexadecimal digits.\n"); + return CLI_OK; + } + + clipkt->msg_s = (u_int32_t) len; + } + + return CLI_OK; +} + + + +int cmd_packet_payload_ascii (struct cli_def *cli, char *command, char *argv[], int argc) +{ + char str[MAX_MOPS_MSG_SIZE*3]; + int len, i; + + if (strncmp(argv[argc-1], "?", 2)==0) + { + cli_print(cli, "Specify a payload in ascii format.\r"); + cli_print(cli, "Note that multiple white spaces are replaced by a single white space. If you\r"); + cli_print(cli, "really want to specify multiple white spaces then use a dash '-' instead of\r"); + cli_print(cli, "a white space. If you want to specify a dash then use a caret '^' as escape\r"); + cli_print(cli, "character.\n"); + + return CLI_OK; + } + + if (argc==0) + { + cli_print(cli, "Specify an ascii payload\n"); + return CLI_OK; + } + + + if (mz_strcmp(argv[0],"file", 2)==0) + { + // > > > > > ******* TODO: Open file and configure mops with filepointer ******** < < < < < < < < + cli_print(cli, "This feature is currently not supported.\n"); + return CLI_OK; + } + + // Get byte sequence - first copy into str + if (mops_pdesc_mstrings (str, argv, argc, MAX_MOPS_MSG_SIZE)) + { + cli_print(cli, "Payload too long (limited to %i bytes).\n", MAX_MOPS_MSG_SIZE); + } + else // str contains byte sequence now - convert into msg and set msg_s + { + len = strlen(str); + for (i=0; i0) && (str[i-1]=='^')) + { + memcpy((void*) &str[i-1], (void*) &str[i], len-i+1); + i--; len--; + } + else + { + str[i]=' '; + } + } + } + len--; // to eliminate the trailing space (created by mops_pdesc_mstring) + memcpy((void*) clipkt->msg, (void*) str, len); + clipkt->msg_s = len; + } + return CLI_OK; +} + + +int cmd_packet_payload_raw (struct cli_def *cli, char *command, char *argv[], int argc) +{ + + return CLI_OK; +} + + + +int cmd_packet_interval (struct cli_def *cli, char *command, char *argv[], int argc) +{ + unsigned long long iv, tv_sec=0; + + if (strncmp(argv[argc-1], "?", 1)==0) { + cli_print(cli, "Configure a greater packet interval in days, hours, minutes, or seconds\n"); + cli_print(cli, "Arguments: \n"); + cli_print(cli, "Use a zero value to disable an interval.\n"); + return CLI_OK; + } + + if (argc!=2) { + cli_print(cli,"Enter a value and an unit\n"); + return CLI_OK; + } + + + if (mz_strisnum(argv[0])==0) { + cli_print(cli,"Invalid value\n"); + return CLI_OK; + } + + iv = str2lint(argv[0]); + + if (iv==0) { + cli_print(cli,"Interval disabled.\n"); + clipkt->interval_used = 0; + return CLI_OK; + } + + if (mz_strcmp(argv[1], "days", 1)==0) { + if (iv>365) { + cli_print(cli, "Supported range: 1..365 days\n"); + return CLI_OK; + } + tv_sec = 86400 * iv; + } + + if (mz_strcmp(argv[1], "hours", 1)==0) { + if (iv>1000) { + cli_print(cli, "Supported range: 1..1000 hours\n"); + return CLI_OK; + } + tv_sec = 3600 * iv; + } + + if (mz_strcmp(argv[1], "minutes", 1)==0) { + if (iv>1000) { + cli_print(cli, "Supported range: 1..1000 minutes\n"); + return CLI_OK; + } + tv_sec = 60 * iv; + } + + if (mz_strcmp(argv[1], "seconds", 1)==0) { + if (iv>999999) { + cli_print(cli, "Supported range: 1..999999 seconds\n"); + return CLI_OK; + } + tv_sec = iv; + } + + if (clipkt->count==0) { + cli_print(cli, "Note: reconfigured count value from 0 (infinity) to 1.\n"); + clipkt->count=1; + } + + if ((clipkt->count * clipkt->ndelay.tv_sec)>tv_sec) { + cli_print(cli, "Error: intervals are smaller than packet trains.\r"); + cli_print(cli, "Reduce either count or delay, or both\n"); + return CLI_OK; + } + + clipkt->interval.tv_sec = tv_sec; + clipkt->interval_used = 1; + + return CLI_OK; +} + diff --git a/src/cli_rtp.c b/src/cli_rtp.c new file mode 100644 index 0000000..b9fb9cf --- /dev/null +++ b/src/cli_rtp.c @@ -0,0 +1,343 @@ +/* + * Mausezahn - A fast versatile traffic generator + * Copyright (C) 2008-2010 Herbert Haas + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 as published by the + * Free Software Foundation. + * + * 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, see http://www.gnu.org/licenses/gpl-2.0.html + * +*/ + + + +#include "mz.h" +#include "cli.h" +#include "mops.h" + + +int cmd_rtp_version (struct cli_def *cli, char *command, char *argv[], int argc) +{ + struct mops_ext_rtp * pd = clipkt->p_desc; + int v=2; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc!=1)) { + cli_print(cli, "Set the RTP version (0..3, default: v2).\n"); + return CLI_OK; + } + if (mz_strisnum(argv[0])==0) { + cli_print(cli, "Invalid number.\n"); + return CLI_OK; + } + v = (int) str2int(argv[0]); + if (v>3) { + cli_print(cli, "Range exceeded (0..3).\n"); + return CLI_OK; + } + pd->v = v; + return CLI_OK; +} + +int cmd_rtp_padding (struct cli_def *cli, char *command, char *argv[], int argc) +{ + struct mops_ext_rtp * pd = clipkt->p_desc; + char state[8]; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc!=1)) { + cli_print(cli, "Sets or unsets the RTP padding flag (default: disabled).\n"); + cli_print(cli, "Use the keywords 'set' or 'unset'.\n"); + sprintf(state, "%s", (pd->p) ? "SET" : "UNSET"); + cli_print(cli, "Current state of the padding flag: %s\n",state); + return CLI_OK; + } + + if (mz_strcmp(argv[0], "set", 1)==0) { + pd->p = 1; + } else if (mz_strcmp(argv[0], "unset", 1)==0) { + pd->p = 0; + } + else { + cli_print(cli, "Invalid keyword. Use 'set' or 'unset'.\n"); + } + + return CLI_OK; +} + +int cmd_rtp_xten (struct cli_def *cli, char *command, char *argv[], int argc) +{ + struct mops_ext_rtp * pd = clipkt->p_desc; + char state[8]; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc!=1)) { + cli_print(cli, "Sets or unsets the RTP extension flag (default: disabled).\n"); + cli_print(cli, "NOTE: This command only sets the extension flag in the RTP header.\r"); + cli_print(cli, "If you really want an extension header use the 'extension' command.\n"); + cli_print(cli, "Use the keywords 'set' or 'unset'.\n"); + sprintf(state, "%s", (pd->x) ? "SET" : "UNSET"); + cli_print(cli, "Current state of the extension flag: %s\n",state); + return CLI_OK; + } + + if (mz_strcmp(argv[0], "set", 1)==0) { + pd->x = 1; + } else if (mz_strcmp(argv[0], "unset", 1)==0) { + pd->x = 0; + } + else { + cli_print(cli, "Invalid keyword. Use 'set' or 'unset'.\n"); + } + + return CLI_OK; +} + + +int cmd_rtp_marker (struct cli_def *cli, char *command, char *argv[], int argc) +{ + struct mops_ext_rtp * pd = clipkt->p_desc; + char state[8]; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc!=1)) { + cli_print(cli, "Sets or unsets the RTP marker flag (default: disabled).\n"); + cli_print(cli, "Use the keywords 'set' or 'unset'.\n"); + sprintf(state, "%s", (pd->m) ? "SET" : "UNSET"); + cli_print(cli, "Current state of the marker flag: %s\n",state); + return CLI_OK; + } + if (mz_strcmp(argv[0], "set", 1)==0) { + pd->m = 1; + } else if (mz_strcmp(argv[0], "unset", 1)==0) { + pd->m = 0; + } + else { + cli_print(cli, "Invalid keyword. Use 'set' or 'unset'.\n"); + } + return CLI_OK; +} + + +int cmd_rtp_cc (struct cli_def *cli, char *command, char *argv[], int argc) +{ + struct mops_ext_rtp * pd = clipkt->p_desc; + int cc=0; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc!=1)) { + cli_print(cli, "Configure the RTP CSRC count (0..15, default: 0).\n"); + cli_print(cli, "NOTE: This command only configures the CSRC value in the RTP header.\r"); + cli_print(cli, "If you want to add a valid CSRC list use the 'csrc-list' command.\r"); + cli_print(cli, "The main purpose of this command is to create an invalid RTP packet.\r"); + return CLI_OK; + } + if (mz_strisnum(argv[0])==0) { + cli_print(cli, "Invalid number.\n"); + return CLI_OK; + } + cc = (int) str2int(argv[0]); + if (cc>15) { + cli_print(cli, "Range exceeded (0..15).\n"); + return CLI_OK; + } + pd->cc = cc; + return CLI_OK; +} + + +int cmd_rtp_pt (struct cli_def *cli, char *command, char *argv[], int argc) +{ + struct mops_ext_rtp * pd = clipkt->p_desc; + int pt=0; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc!=1)) { + cli_print(cli, "Configure the RTP payload type (0..127, default: 8 (G.711, A-law)).\n"); + // TODO: provide a list with well-known PT values + return CLI_OK; + } + if (mz_strisnum(argv[0])==0) { + cli_print(cli, "Invalid number.\n"); + return CLI_OK; + } + pt = (int) str2int(argv[0]); + if (pt>127) { + cli_print(cli, "Range exceeded (0..127).\n"); + return CLI_OK; + } + pd->pt = pt; + return CLI_OK; +} + +int cmd_rtp_ssrc (struct cli_def *cli, char *command, char *argv[], int argc) +{ + struct mops_ext_rtp * pd = clipkt->p_desc; + unsigned long long int ssrc = 0xcafefeed; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc!=1)) { + cli_print(cli, "Configure the RTP SSRC (source identifier) (0..ffffffff, default: random!).\n"); + cli_print(cli, "NOTE: The SSRC value is used by another Mausezahn receiver to identify a original\r"); + cli_print(cli, "Mausezahn RTP stream. By default, Mausezahn receivers check for the magic number\r"); + cli_print(cli, "'cafebabe' (hex). Use another number for another RTP stream (e. g. bidirectional\r"); + cli_print(cli, "measurements).\n"); + return CLI_OK; + } + + if (mz_strishex(argv[0])==0) { + cli_print(cli, "Invalid number.\n"); + return CLI_OK; + } + + ssrc = xstr2lint(argv[0]); + if (ssrc>0xffffffff) { + cli_print(cli, "Range exceeded (0..ffffffff).\n"); + return CLI_OK; + } + pd->ssrc = (u_int32_t) ssrc; + return CLI_OK; +} + + +int cmd_rtp_sqnr (struct cli_def *cli, char *command, char *argv[], int argc) +{ + struct mops_ext_rtp * pd = clipkt->p_desc; + unsigned long long int sqnr = 0; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc!=1)) { + cli_print(cli, "Configure the RTP initial sequence number (0..ffffffff, default: 0).\n"); + return CLI_OK; + } + + if (mz_strishex(argv[0])==0) { + cli_print(cli, "Invalid number.\n"); + return CLI_OK; + } + sqnr = xstr2lint(argv[0]); + if (sqnr>0xffffffff) { + cli_print(cli, "Range exceeded (0..ffffffff).\n"); + return CLI_OK; + } + pd->sqnr = (u_int32_t) sqnr; + return CLI_OK; +} + + +int cmd_rtp_time (struct cli_def *cli, char *command, char *argv[], int argc) +{ + struct mops_ext_rtp * pd = clipkt->p_desc; + unsigned long long int t = 0; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc!=1)) { + cli_print(cli, "Configure the RTP initial timestamp (0..ffffffff, default: 0).\n"); + return CLI_OK; + } + + if (mz_strishex(argv[0])==0) { + cli_print(cli, "Invalid number.\n"); + return CLI_OK; + } + t = xstr2lint(argv[0]); + if (t>0xffffffff) { + cli_print(cli, "Range exceeded (0..ffffffff).\n"); + return CLI_OK; + } + pd->tst = (u_int32_t) t; + return CLI_OK; +} + + +int cmd_rtp_extension (struct cli_def *cli, char *command, char *argv[], int argc) +{ + struct mops_ext_rtp * pd = clipkt->p_desc; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc!=1)) { + cli_print(cli, "Configure an RTP extension header (default: none).\n"); + cli_print(cli, "Currently supported RTP extension headers:\n"); + cli_print(cli, "none Don't use any extension.\r"); + cli_print(cli, "mausezahn Use the new Mausezahn jitter/RTT measurement extension.\r"); + cli_print(cli, " (Note that this is incompatible with Mausezahn's direct\r"); + cli_print(cli, " mode jitter measurement.)\r"); + cli_print(cli, "\n"); + return CLI_OK; + } + + if (mz_strcmp(argv[0], "none", 1)==0) { + pd->x_type = 0; + pd->x = 0; // X bit in header + } else if (mz_strcmp(argv[0], "mausezahn", 1)==0) { + pd->x_type = 42; + pd->x = 1; // X bit in header + pd->ssrc = 0xcafefeed; + } else { + cli_print(cli, "Unknown keyword.\n"); + return CLI_OK; + + } + + mops_update_rtp (clipkt); // re-build RTP packet (for proper show commands) + return CLI_OK; +} + + + +int cmd_rtp_source (struct cli_def *cli, char *command, char *argv[], int argc) +{ +// struct mops_ext_rtp * pd = clipkt->p_desc; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc!=1)) { + cli_print(cli, "Specify a RTP media source.\n"); + return CLI_OK; + } + + // [TODO] -- Allow to use /dev/dsp or a mixer source ... + // + cli_print(cli, "Currently not supported.\n"); + + return CLI_OK; +} + + +int cmd_rtp_cclist (struct cli_def *cli, char *command, char *argv[], int argc) +{ + struct mops_ext_rtp * pd = clipkt->p_desc; + unsigned long long int csrc=0; + char str[80]; + int i=0, n=0; + + + if ((strcmp(argv[argc-1],"?")==0) || (argc==0)) { + cli_print(cli, "Specify a CSRC list consisting of 1-15 CSRC values.\r"); + cli_print(cli, "Each CSRC is a 4-byte value and must be specified in hexadecimal notation,\r"); + cli_print(cli, "hence each value must be within 0..ffffffff.\n"); + return CLI_OK; + } + + if ((n=argc)>15) { + cli_print(cli, "The CSRC list must not exceed 15 items!\n"); + return CLI_OK; + } + + for (i=0; i0xffffffff) { + sprintf(str, "Parameter %i: Range exceeded (0..ffffffff)", i); + cli_print(cli, "%s\n", str); + return CLI_OK; + } + pd->csrc[i] = (u_int32_t) csrc; + } + pd->cc = n; // this one can be accessed and modified to "wrong" values by the user + pd->cc_real = n; + + return CLI_OK; +} + + + diff --git a/src/cli_sequence.c b/src/cli_sequence.c new file mode 100644 index 0000000..1a533e5 --- /dev/null +++ b/src/cli_sequence.c @@ -0,0 +1,263 @@ +/* + * Mausezahn - A fast versatile traffic generator + * Copyright (C) 2010 Herbert Haas + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 as published by the + * Free Software Foundation. + * + * 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, see http://www.gnu.org/licenses/gpl-2.0.html + * +*/ + + +#include "mz.h" +#include "cli.h" +#include "mops.h" +#include "llist.h" + +// PURPOSE: Enter sequence configuration mode +// either a) create new or b) edit old or c) delete old sequence +// +// # sequence MY_SEQUENCE +// # sequence OLD_SEQUENCE delete +// +int conf_sequence (struct cli_def *cli, char *command, char *argv[], int argc) +{ + struct mz_ll *cur; + char str[512]; + int ret=0; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc<1) || (argc>2)) { + + cli_print(cli, "Configure a sequence of packets.\n"); + cli_print(cli, "ARGUMENTS: [delete]\n"); + + cli_print(cli, "Current list of packet sequences:\n"); + while (mops_dump_sequence(str)) cli_print(cli, "%s\r", str); + return CLI_OK; + } + + switch (argc) { + case 1: + cur = mz_ll_search_name (packet_sequences, argv[0]); + if (cur==NULL) { // create NEW sequence + cli_print(cli, "Sequence does not exist; creating new sequence named '%s'\n", argv[0]); + cur = mops_create_sequence(argv[0]); + if (cur==NULL) { + cli_print(cli, "ERROR: Cannot allocate another sequence!\n"); + return CLI_OK; + } + } // else ENTER EXISTING (cur already points to it) + cli_seq = cur; + cli_set_configmode(cli, MZ_MODE_SEQUENCE, "config-seq"); + break; + + case 2: // otherwise DELETE? + if (mz_strcmp(argv[1], "delete", 3)==0) { + ret = mops_delete_sequence(argv[0]); + switch (ret) { + case 1: + cli_print(cli, "Sequence '%s' does not exist\n", argv[0]); + break; + case 2: + cli_print(cli, "Sequence '%s' is currently active! Cannot delete it.\n", argv[0]); + break; + default: + cli_print(cli, "Sequence '%s' deleted.\n", argv[0]); + } + } + break; + default: + // nothing + break; + } + return CLI_OK; +} + + +// add packet to current sequence +int sequence_add (struct cli_def *cli, char *command, char *argv[], int argc) +{ + struct mops *mp; + int ret=0; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc!=1) ) { + + cli_print(cli, "Add a packet to the current sequence.\n"); + cli_print(cli, "ARGUMENT: OR \n"); + return CLI_OK; + } + + // first assume argument is a name + mp = mops_search_name (mp_head, argv[0]); + if (mp==NULL) { // but packet name does not exist + if (mz_strisnum(argv[0])!=0) // arg is really a number? + mp = mops_search_id (mp_head, (int) str2int(argv[0])); + if (mp==NULL) { // also packet ID not found + cli_print(cli, "Packet does not exist!\n"); + return CLI_OK; + } + } + + // packet found, so add to current sequence + ret = mops_add_packet_to_sequence (cli_seq, mp); + if (ret==1) cli_print(cli, "Cannot add packet (unknown error, maybe report this)!\n"); + if (ret==-1) cli_print(cli, "Cannot add packet: sequence already full!\n"); + if (ret==-2) cli_print(cli, "Cannot add packet with infinite count!\n"); + return CLI_OK; +} + + +// add a delay +int sequence_delay (struct cli_def *cli, char *command, char *argv[], int argc) +{ + int ret=0, ret2=0; + struct timespec t; + char str[128]; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc<1) || (argc>2)) { + cli_print(cli, "Add a delay to the current sequence.\n"); + cli_print(cli, "ARGUMENTS: [hour | min | sec | msec | usec | nsec]\n"); + cli_print(cli, "The default unit is milliseconds (i. e. when no unit is given).\n"); + return CLI_OK; + } + + switch (argc) { + case 1: // only one argument, but may contain an unit (such as '314sec') + ret = delay_parse(&t, argv[0], NULL); + break; + + case 2: // user specified two arguments such as '100 msec' + ret = delay_parse(&t, argv[0], argv[1]); + break; + default: + cli_print(cli, "Too many arguments! Expected delay value and unit, such as '10 msec'\n"); + return CLI_OK; + } + + switch (ret) { + case 1: + cli_print(cli, "Invalid unit! Use one of {nsec, usec, msec, sec, min, hours}\n"); + return CLI_OK; + break; + case 2: + cli_print(cli, "Value too large! Supported range is from 0 to 999999999\n"); + return CLI_OK; + break; + } + + + ret2 = mops_add_delay_to_sequence (cli_seq, &t); + if (ret2==-1) { + cli_print(cli, "You must add a packet first.\n"); + return CLI_OK; + } + if (ret2==-2) { + cli_print(cli, "Cannot add delay (array full).\n"); + return CLI_OK; + } + + sprintf(str, "Delay set to %lu sec and %lu nsec", + ((struct pseq*) cli_seq->data)->gap[ret2].tv_sec, + ((struct pseq*) cli_seq->data)->gap[ret2].tv_nsec); + cli_print(cli, "%s\n", str); + + return CLI_OK; +} + + +// remove one packet +int sequence_remove (struct cli_def *cli, char *command, char *argv[], int argc) +{ + int ret=0; + int i=0; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc!=1)) { + cli_print(cli, "Remove a packet (and any associated pause configuration) from the current sequence.\n"); + cli_print(cli, "ARGUMENT: | last | all\n"); + cli_print(cli, "FYI: Use the 'show' command to see the current packet list with indexes.\n"); + return CLI_OK; + } + + if (mz_strcmp(argv[0], "last", 1)==0) { + ret = mops_delete_packet_from_pseq (cli_seq, -1); + } else if (mz_strcmp(argv[0], "all", 1)==0) { + ret = mops_delete_all_packets_from_pseq (cli_seq); + i=1; + } else { // index number given + if (mz_strisnum(argv[0])==0) { + cli_print(cli, "Invalid parameter. Please specify a packet index number or 'last'\n"); + return CLI_OK; + } + ret = mops_delete_packet_from_pseq (cli_seq, (int) str2int(argv[0])); + } + switch (ret) { + case 0: + if (i) cli_print(cli, "Removed all entries.\n"); + else cli_print(cli, "Removed one entry.\n"); + break; + case 1: + cli_print(cli, "List empty or invalid packet index.\n"); + break; + case 2: + cli_print(cli, "Packet index too large.\n"); + break; + + } + return CLI_OK; +} + + +// show packet list of that sequence +int sequence_show (struct cli_def *cli, char *command, char *argv[], int argc) +{ + char str[512], name[32], layers[16], proto[16]; + struct pseq *seq; + int i; + + if (strcmp(argv[argc-1],"?")==0) { + cli_print(cli, "Shows all packets of the current sequence.\n"); + return CLI_OK; + } + + if (argc>0) { + cli_print(cli, "This command has currently no arguments!\n"); + return CLI_OK; + } + + seq = (struct pseq*) cli_seq->data; + + if (seq->count==0) { + cli_print(cli, "Current sequence '%s' has no entries.\n", cli_seq->name); + } + else { // show all packets in this sequence + cli_print(cli, "%i sequence(s) defined.\r", packet_sequences->refcount-1); // total info + snprintf(str,512, "Current sequence '%s' has %i entries:", cli_seq->name, seq->count); // num entries here + cli_print(cli, "%s\n", str); + cli_print(cli, "Nr PId PktName Layers Protocol Device"); + for (i=0; icount; i++) { + strncpy (name, seq->packet[i]->packet_name, 13); // only show first 13 chars + if (strnlen(seq->packet[i]->packet_name, MAX_MOPS_PACKET_NAME_LEN)>13) { + name[13]=0x00; + strcat(name, "..."); + } + mops_get_proto_info(seq->packet[i], layers, proto); + snprintf(str,512, "%2i %4i %-16s %s %-8s %-6s", i+1, seq->packet[i]->id, name, layers, proto, seq->packet[i]->device); + cli_print(cli, "%s\r", str); + if ((seq->gap[i].tv_sec !=0) || (seq->gap[i].tv_nsec !=0)) { // gap also defined? + timespec2str(&seq->gap[i], str); + cli_print(cli, " \\___ %s pause ___/\r", str); + } + } + } + return CLI_OK; +} + + diff --git a/src/cli_set.c b/src/cli_set.c new file mode 100644 index 0000000..04ede29 --- /dev/null +++ b/src/cli_set.c @@ -0,0 +1,350 @@ +/* + * Mausezahn - A fast versatile traffic generator + * Copyright (C) 2008 Herbert Haas + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 as published by the + * Free Software Foundation. + * + * 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, see http://www.gnu.org/licenses/gpl-2.0.html + * +*/ + + +#include "mz.h" +#include "cli.h" + + +int cmd_set(struct cli_def *cli, char *command, char *argv[], int argc) +{ + libnet_t *l; + unsigned int time_factor; + int i, cnt, found_dev; + char *dum; + unsigned char *x; + + + if (argc < 2) { + cli_print(cli, "Specify a variable to set:\r\n"); + cli_print(cli, "device specify the primary network device\r"); + + cli_print(cli, "NOTE: The following options are non-MOPS and deprecated:\n"); + + cli_print(cli, "a|sa specify a MAC source address\r"); + cli_print(cli, "b|da specify a MAC destination address\r"); + cli_print(cli, "A|SA specify a IP source address\r"); + cli_print(cli, "B|DA specify a IP destination address\r"); + cli_print(cli, "c|count specify a packet count value\r"); + cli_print(cli, "d|delay specify an interpacket delay (usec, msec, or sec)\r"); + cli_print(cli, "P|payload specify an ASCII payload\r"); + cli_print(cli, "H|hexload specify a hexadecimal payload\r"); + cli_print(cli, "p|padding specify a number of padding bytes (total for raw, added otherwise)\r"); + cli_print(cli, "Q|vlan specify one ore more 802.1Q vlan tags\r"); + cli_print(cli, "M|mpls specify one ore more MPLS labels\r"); + cli_print(cli, "\n"); + return CLI_OK; + } + + // set primary device + if (strncmp(argv[0], "device", 2)==0) + { + if (strncmp(argv[1],"?",1)==0) + { + cli_print(cli,"Specify the primary network device (use 'show dev' for a list)\n"); + } + else + { + if (strlen(argv[1])) + { + found_dev = 0; + for (i=0; i MAX_PAYLOAD_SIZE) + { + cli_print(cli, "Note: Padding too big! However, let's try and see what happens...\n"); + } + } + } + + + + // DEFAULT ANSWER: + else + { + cli_print(cli, "Unknown variable '%s'\n",argv[0]); + } + + return CLI_OK; +} diff --git a/src/cli_tcp.c b/src/cli_tcp.c new file mode 100644 index 0000000..24938eb --- /dev/null +++ b/src/cli_tcp.c @@ -0,0 +1,679 @@ +/* + * Mausezahn - A fast versatile traffic generator + * Copyright (C) 2008-2010 Herbert Haas + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 as published by the + * Free Software Foundation. + * + * 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, see http://www.gnu.org/licenses/gpl-2.0.html + * +*/ + + +#include "mz.h" +#include "cli.h" +#include "mops.h" + + + +// NOTE: The port numbers are maintained for both TCP and UDP. +// See cli_udp.c. + + +int cmd_tcp_seqnr (struct cli_def *cli, char *command, char *argv[], int argc) +{ + u_int32_t txs; + unsigned long long int tmp; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc>3) ) { + cli_print(cli, "Specify the TCP sequence number (0-4294967295)\n"); + cli_print(cli, "You may specify up to three parameters:\n"); + cli_print(cli, " \r"); + cli_print(cli, " \r"); + cli_print(cli, " \n"); + cli_print(cli, "If a range is specified without step size 'sqnr_delta' (2nd case)\r"); + cli_print(cli, "then sqnr_delta is per default set to one.\r"); + cli_print(cli, "\n"); + return CLI_OK; + } + + tmp = str2lint(argv[0]); + if (tmp<=0xffffffff) + clipkt->tcp_seq = (u_int32_t) tmp; + else { + cli_print(cli, "Argument 1 must not exceed 4294967295\n"); + return CLI_OK; + } + clipkt->tcp_seq_delta = 0; + + if (argc>1) { + tmp = str2lint(argv[1]); + if (tmp<=0xffffffff) { + clipkt->tcp_seq_start = clipkt->tcp_seq; + clipkt->tcp_seq_stop = (u_int32_t) tmp; + } else { + cli_print(cli, "Argument 2 must not exceed 4294967295\n"); + return CLI_OK; + } + clipkt->tcp_seq_delta = 1; + } + + if (argc>2) { + tmp = str2lint(argv[2]); + if (tmp<=0xffffffff) { + clipkt->tcp_seq_delta = (u_int32_t) tmp; + } else { + cli_print(cli, "Argument 3 must not exceed 4294967295\n"); + return CLI_OK; + } + + if (argv[2]==0) { + cli_print(cli, "Note that a zero step size disables the range feature\n"); + return CLI_OK; + } + } + + txs = mops_tcp_complexity_sqnr (clipkt); + cli_print(cli, "FYI: Packet runs through %lu sequence numbers\n", (long unsigned int) txs); + + return CLI_OK; +} + + + + + +int cmd_tcp_acknr (struct cli_def *cli, char *command, char *argv[], int argc) +{ + u_int32_t txs; + unsigned long long int tmp; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc>3) ) { + cli_print(cli, "Specify the TCP acknowledgement number (0-4294967295)\n"); + cli_print(cli, "You may specify up to three parameters:\n"); + cli_print(cli, " \r"); + cli_print(cli, " \r"); + cli_print(cli, " \n"); + cli_print(cli, "If a range is specified without step size 'acknr_delta' (2nd case)\r"); + cli_print(cli, "then acknr_delta is per default set to one.\r"); + cli_print(cli, "\n"); + return CLI_OK; + } + + tmp = str2lint(argv[0]); + if (tmp<=0xffffffff) + clipkt->tcp_ack = (u_int32_t) tmp; + else { + cli_print(cli, "Argument 1 must not exceed 4294967295\n"); + return CLI_OK; + } + clipkt->tcp_ack_delta = 0; + + if (argc>1) { + tmp = str2lint(argv[1]); + if (tmp<=0xffffffff) { + clipkt->tcp_ack_start = clipkt->tcp_ack; + clipkt->tcp_ack_stop = (u_int32_t) tmp; + } else { + cli_print(cli, "Argument 2 must not exceed 4294967295\n"); + return CLI_OK; + } + clipkt->tcp_ack_delta = 1; + } + + if (argc>2) { + tmp = str2lint(argv[2]); + if (tmp<=0xffffffff) { + clipkt->tcp_ack_delta = (u_int32_t) tmp; + } else { + cli_print(cli, "Argument 3 must not exceed 4294967295\n"); + return CLI_OK; + } + + if (argv[2]==0) { + cli_print(cli, "Note that a zero step size disables the range feature\n"); + return CLI_OK; + } + } + + txs = mops_tcp_complexity_acknr (clipkt); + cli_print(cli, "FYI: Packet runs through %lu acknowledge numbers\n", (long unsigned int) txs); + + + return CLI_OK; +} + + + + + + +int cmd_tcp_offset (struct cli_def *cli, char *command, char *argv[], int argc) +{ + unsigned int tmp; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc>1) ) + { + cli_print(cli, "Specify the TCP offset (=header length, 0..15) \r"); + cli_print(cli, "\n"); + return CLI_OK; + } + + tmp = (unsigned int) str2int(argv[0]); + if (tmp<=15) + clipkt->tcp_offset = (u_int8_t) tmp; + else + { + cli_print(cli, "The TCP offset must not exceed 15\n"); + } + + return CLI_OK; +} + + + + +int cmd_tcp_res (struct cli_def *cli, char *command, char *argv[], int argc) +{ + int tmp; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc>1) ) + { + cli_print(cli, "Specify the TCP reserved field in binary format (4 bits)\n"); + cli_print(cli, "\n"); + return CLI_OK; + } + + tmp = str2bin8 (argv[0]); + if ((tmp==-1)||(tmp>15)) + { + cli_print(cli, "Invalid binary value! Allowed range: 0000 - 1111\n"); + } + else + clipkt->tcp_res = (u_int8_t) tmp; + + return CLI_OK; +} + + +int cmd_tcp_flags (struct cli_def *cli, char *command, char *argv[], int argc) +{ + int i, j=0; + char str[64]; + + if (strcmp(argv[argc-1],"?")==0) + { + cli_print(cli, "Configure a combination of TCP flags at once. All mentioned \r"); + cli_print(cli, "flags are set, while not mentioned flags remain unset.\r"); + cli_print(cli, "Flag keywords: cwr, ece, urg, ack, psh, rst, syn, fin.\r"); + cli_print(cli, "NOTE: The flags command alone resets all flags to zero!\n"); + cli_print(cli, "Example:\n"); + cli_print(cli, " mz(config-pkt-1-tcp)# flags syn fin ack \n"); + cli_print(cli, "\n"); + mops_tcp_flags2str (clipkt, str); + cli_print(cli,"Current setting is: %s\n",str); + return CLI_OK; + } + + if (argc>8) + { + cli_print(cli, "Up to 8 arguments are allowed using the keywords:\r"); + cli_print(cli, "cwr, ece, urg, ack, psh, rst, syn, fin.\n"); + return CLI_OK; + } + + clipkt->tcp_ctrl_CWR = 0; + clipkt->tcp_ctrl_ECE = 0; + clipkt->tcp_ctrl_URG = 0; + clipkt->tcp_ctrl_ACK = 0; + clipkt->tcp_ctrl_PSH = 0; + clipkt->tcp_ctrl_RST = 0; + clipkt->tcp_ctrl_SYN = 0; + clipkt->tcp_ctrl_FIN = 0; + + + + for (i=0; itcp_ctrl_CWR = 1; + j=1; + } + + if (mz_strcmp(argv[i], "ece", 1)==0) { + clipkt->tcp_ctrl_ECE = 1; + j=1; + } + + if (mz_strcmp(argv[i], "urg", 1)==0) { + clipkt->tcp_ctrl_URG = 1; + j=1; + } + + if (mz_strcmp(argv[i], "ack", 1)==0) { + clipkt->tcp_ctrl_ACK = 1; + j=1; + } + + if (mz_strcmp(argv[i], "psh", 1)==0) { + clipkt->tcp_ctrl_PSH = 1; + j=1; + } + + if (mz_strcmp(argv[i], "rst", 1)==0) { + clipkt->tcp_ctrl_RST = 1; + j=1; + } + + if (mz_strcmp(argv[i], "syn", 1)==0) { + clipkt->tcp_ctrl_SYN = 1; + j=1; + } + + if (mz_strcmp(argv[i], "fin", 1)==0) { + clipkt->tcp_ctrl_FIN = 1; + j=1; + } + + if (!j) { + cli_print(cli, "Unknown keyword at position %i\n", i+1); + return CLI_OK; + } + else { // flag matched, continue + j=0; + } + } + + mops_tcp_flags2str (clipkt, str); + cli_print(cli,"Current setting is: %s\n",str); + + return CLI_OK; +} + + + +int cmd_tcp_cwr (struct cli_def *cli, char *command, char *argv[], int argc) +{ + char str[64]; + + if (strcmp(argv[argc-1],"?")==0) + { + cli_print(cli, "Set or unset the TCP Congestion Window Reduced flag (CWR)\r"); + mops_tcp_flags2str (clipkt, str); + cli_print(cli,"Current setting is: %s\n",str); + return CLI_OK; + } + + if (argc!=1) + { + cli_print(cli, "Use the 'set' or 'unset' keywords.\n"); + return CLI_OK; + } + + + if (mz_strcmp(argv[0], "set", 1)==0) + clipkt->tcp_ctrl_CWR = 1; + else if (mz_strcmp(argv[0], "unset", 1)==0) + clipkt->tcp_ctrl_CWR = 0; + else + cli_print(cli, "Unknown keyword. Use the 'set' or 'unset' keywords.\n"); + + mops_tcp_flags2str (clipkt, str); + cli_print(cli,"Current setting is: %s\n",str); + + return CLI_OK; +} + + + +int cmd_tcp_ece (struct cli_def *cli, char *command, char *argv[], int argc) +{ + char str[64]; + + if (strcmp(argv[argc-1],"?")==0) { + cli_print(cli, "Set or unset the TCP ECN-Echo flag (ECE)\r"); + mops_tcp_flags2str (clipkt, str); + cli_print(cli,"Current setting is: %s\n",str); + return CLI_OK; + } + + if (argc!=1) { + cli_print(cli, "Use the 'set' or 'unset' keywords.\n"); + return CLI_OK; + } + + if (mz_strcmp(argv[0], "set", 1)==0) + clipkt->tcp_ctrl_ECE = 1; + else if (mz_strcmp(argv[0], "unset", 1)==0) + clipkt->tcp_ctrl_ECE = 0; + else + cli_print(cli, "Unknown keyword. Use the 'set' or 'unset' keywords.\n"); + + mops_tcp_flags2str (clipkt, str); + cli_print(cli,"Current setting is: %s\n",str); + + return CLI_OK; +} + + + +int cmd_tcp_urg (struct cli_def *cli, char *command, char *argv[], int argc) +{ + char str[64]; + + if (strcmp(argv[argc-1],"?")==0) { + cli_print(cli, "Set or unset the TCP urgent flag (URG)\r"); + mops_tcp_flags2str (clipkt, str); + cli_print(cli,"Current setting is: %s\n",str); + return CLI_OK; + } + + if (argc!=1) { + cli_print(cli, "Use the 'set' or 'unset' keywords.\n"); + return CLI_OK; + } + + if (mz_strcmp(argv[0], "set", 1)==0) + clipkt->tcp_ctrl_URG = 1; + else if (mz_strcmp(argv[0], "unset", 1)==0) + clipkt->tcp_ctrl_URG = 0; + else + cli_print(cli, "Unknown keyword. Use the 'set' or 'unset' keywords.\n"); + + mops_tcp_flags2str (clipkt, str); + cli_print(cli,"Current setting is: %s\n",str); + + return CLI_OK; +} + + + + +int cmd_tcp_ack (struct cli_def *cli, char *command, char *argv[], int argc) +{ + char str[64]; + + if (strcmp(argv[argc-1],"?")==0) { + cli_print(cli, "Set or unset the TCP acknowledgement flag (ACK)\r"); + mops_tcp_flags2str (clipkt, str); + cli_print(cli,"Current setting is: %s\n",str); + return CLI_OK; + } + + if (argc!=1) { + cli_print(cli, "Use the 'set' or 'unset' keywords.\n"); + return CLI_OK; + } + + + if (mz_strcmp(argv[0], "set", 1)==0) + clipkt->tcp_ctrl_ACK = 1; + else if (mz_strcmp(argv[0], "unset", 1)==0) + clipkt->tcp_ctrl_ACK = 0; + else + cli_print(cli, "Unknown keyword. Use the 'set' or 'unset' keywords.\n"); + + mops_tcp_flags2str (clipkt, str); + cli_print(cli,"Current setting is: %s\n",str); + + return CLI_OK; +} + + + +int cmd_tcp_psh (struct cli_def *cli, char *command, char *argv[], int argc) +{ + char str[64]; + + if (strcmp(argv[argc-1],"?")==0) { + cli_print(cli, "Set or unset the TCP push flag (PSH)\r"); + mops_tcp_flags2str (clipkt, str); + cli_print(cli,"Current setting is: %s\n",str); + return CLI_OK; + } + + if (argc!=1) { + cli_print(cli, "Use the 'set' or 'unset' keywords.\n"); + return CLI_OK; + } + + if (mz_strcmp(argv[0], "set", 1)==0) + clipkt->tcp_ctrl_PSH = 1; + else if (mz_strcmp(argv[0], "unset", 1)==0) + clipkt->tcp_ctrl_PSH = 0; + else + cli_print(cli, "Unknown keyword. Use the 'set' or 'unset' keywords.\n"); + + mops_tcp_flags2str (clipkt, str); + cli_print(cli,"Current setting is: %s\n",str); + + return CLI_OK; +} + + + +int cmd_tcp_rst (struct cli_def *cli, char *command, char *argv[], int argc) +{ + char str[64]; + + if (strcmp(argv[argc-1],"?")==0) { + cli_print(cli, "Set or unset the TCP reset flag (RST)\r"); + mops_tcp_flags2str (clipkt, str); + cli_print(cli,"Current setting is: %s\n",str); + return CLI_OK; + } + + if (argc!=1) { + cli_print(cli, "Use the 'set' or 'unset' keywords.\n"); + return CLI_OK; + } + + if (mz_strcmp(argv[0], "set", 1)==0) + clipkt->tcp_ctrl_RST = 1; + else if (mz_strcmp(argv[0], "unset", 1)==0) + clipkt->tcp_ctrl_RST = 0; + else + cli_print(cli, "Unknown keyword. Use the 'set' or 'unset' keywords.\n"); + + mops_tcp_flags2str (clipkt, str); + cli_print(cli,"Current setting is: %s\n",str); + + return CLI_OK; +} + + + +int cmd_tcp_syn (struct cli_def *cli, char *command, char *argv[], int argc) +{ + char str[64]; + + if (strcmp(argv[argc-1],"?")==0) { + cli_print(cli, "Set or unset the TCP synchronisation flag (SYN)\r"); + mops_tcp_flags2str (clipkt, str); + cli_print(cli,"Current setting is: %s\n",str); + return CLI_OK; + } + + if (argc!=1) { + cli_print(cli, "Use the 'set' or 'unset' keywords.\n"); + return CLI_OK; + } + + if (mz_strcmp(argv[0], "set", 1)==0) + clipkt->tcp_ctrl_SYN = 1; + else if (mz_strcmp(argv[0], "unset", 1)==0) + clipkt->tcp_ctrl_SYN = 0; + else + cli_print(cli, "Unknown keyword. Use the 'set' or 'unset' keywords.\n"); + + mops_tcp_flags2str (clipkt, str); + cli_print(cli,"Current setting is: %s\n",str); + + return CLI_OK; +} + + + +int cmd_tcp_fin (struct cli_def *cli, char *command, char *argv[], int argc) +{ + char str[64]; + + if (strcmp(argv[argc-1],"?")==0) { + cli_print(cli, "Set or unset the TCP finalisation flag (FIN)\r"); + mops_tcp_flags2str (clipkt, str); + cli_print(cli,"Current setting is: %s\n",str); + return CLI_OK; + } + + if (argc!=1) { + cli_print(cli, "Use the 'set' or 'unset' keywords.\n"); + return CLI_OK; + } + + if (mz_strcmp(argv[0], "set", 1)==0) + clipkt->tcp_ctrl_FIN = 1; + else if (mz_strcmp(argv[0], "unset", 1)==0) + clipkt->tcp_ctrl_FIN = 0; + else + cli_print(cli, "Unknown keyword. Use the 'set' or 'unset' keywords.\n"); + + mops_tcp_flags2str (clipkt, str); + cli_print(cli,"Current setting is: %s\n",str); + + return CLI_OK; +} + + + +int cmd_tcp_window (struct cli_def *cli, char *command, char *argv[], int argc) +{ + unsigned long int tmp; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc>1) ) + { + cli_print(cli, "Specify the TCP window size (0..65535)\r"); + cli_print(cli, "\n"); + return CLI_OK; + } + + tmp = (unsigned long int) str2int (argv[0]); + if (tmp<65535) + { + clipkt->tcp_win = (u_int16_t) tmp; + } + else + { + cli_print(cli, "The TCP window size must not exceed 65535\n"); + } + + return CLI_OK; +} + + + +int cmd_tcp_sum (struct cli_def *cli, char *command, char *argv[], int argc) +{ + int sum; + + if (strncmp(argv[argc-1], "?", 2)==0) + { + cli_print(cli, "Specify the TCP checksum in hexadecimal or use the keyword 'auto'.\r"); + cli_print(cli, "By default, the checksum is computed automatically.\n"); + return CLI_OK; + } + + if (mz_strcmp(argv[0], "auto", 2)==0) + { + clipkt->tcp_sum_false=0; + return CLI_OK; + } + + sum = (int) xstr2int(argv[0]); + + if (sum>0xffff) + { + cli_print(cli, "The checksum must be within range 0..ffff\n"); + return CLI_OK; + } + + clipkt->tcp_sum = (u_int16_t) sum; + clipkt->tcp_sum_false=1; + + return CLI_OK; +} + + + +int cmd_tcp_urgptr(struct cli_def *cli, char *command, char *argv[], int argc) +{ + + unsigned long int tmp; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc>1) ) + { + cli_print(cli, "Specify the TCP urgent pointer (0..65535)\r"); + cli_print(cli, "\n"); + return CLI_OK; + } + + tmp = (unsigned long int) str2int (argv[0]); + if (tmp<65535) + { + clipkt->tcp_urg = (u_int16_t) tmp; + } + else + { + cli_print(cli, "The TCP urgent pointer must not exceed 65535\n"); + } + + return CLI_OK; +} + + + +int cmd_tcp_options (struct cli_def *cli, char *command, char *argv[], int argc) +{ + int mss=0, sack=0, scale=0; + u_int32_t tsval=0, tsecr=0; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc>1) ) { + cli_print(cli, "Specify TCP options\n"); + cli_print(cli, "Option parameters:\n"); + cli_print(cli, "[ mss <0..65535> ] [sack] [tsval <0..4294967295> [tsecr <0..4294967295>]] [nop] [scale <0..14>]\n"); + cli_print(cli, "NOTE: Only a set of default options are supported in this version\r"); + cli_print(cli, "(20 bytes, consisting of MSS=1452 bytes, SACK permitted, a Timestamp,\r"); + cli_print(cli, "NOP, and Window Scale 5)\r"); + cli_print(cli, "\n"); + return CLI_OK; + } + + if (clipkt->tcp_option_used) { + // turn off + clipkt->tcp_option_used = 0; + } else { + // turn on + mops_tcp_add_option (clipkt, mss, sack, scale, tsval, tsecr); + + cli_print(cli, "NOTE: Only a set of default options are supported in this version\r"); + cli_print(cli, "(20 bytes, consisting of MSS=1452 bytes, SACK permitted, a Timestamp,\r"); + cli_print(cli, "NOP, and Window Scale 5)\n"); + } + + return CLI_OK; +} + + + +int cmd_tcp_end(struct cli_def *cli, char *command, char *argv[], int argc) +{ + char prompt[16]; + sprintf(prompt, "pkt-%i",clipkt->id); + cli_set_configmode(cli, MZ_MODE_PACKET, prompt); + return CLI_OK; +} diff --git a/src/cli_tools.c b/src/cli_tools.c new file mode 100644 index 0000000..20ce50e --- /dev/null +++ b/src/cli_tools.c @@ -0,0 +1,40 @@ +/* + * Mausezahn - A fast versatile traffic generator + * Copyright (C) 2008-2010 Herbert Haas + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 as published by the + * Free Software Foundation. + * + * 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, see http://www.gnu.org/licenses/gpl-2.0.html + * +*/ + + + + + +#include "mz.h" + + +// Returns a nice string with default and current value of a given variable +// +// EXAMPLE: +// +// char mystring[256]; +// mz_def16 ("20 seconds", pd->max_age, mystring) +// +int mz_def16 (char *def, u_int16_t val, char *str256) +{ + str256[0]=0x00; + sprintf(str256, "The default value is %s. The current value is %u (0x%04x).", def, val, val); + return 0; +} + + diff --git a/src/cli_udp.c b/src/cli_udp.c new file mode 100644 index 0000000..1bc108b --- /dev/null +++ b/src/cli_udp.c @@ -0,0 +1,204 @@ +/* + * Mausezahn - A fast versatile traffic generator + * Copyright (C) 2008-2010 Herbert Haas + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 as published by the + * Free Software Foundation. + * + * 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, see http://www.gnu.org/licenses/gpl-2.0.html + * +*/ + + +#include "mz.h" +#include "cli.h" +#include "mops.h" + + + +int cmd_port_source (struct cli_def *cli, char *command, char *argv[], int argc) +{ + u_int32_t t32=0; + int validport=0; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc>2) ) { + cli_print(cli, "Specify the source port number:\n"); + cli_print(cli, " []\r"); + cli_print(cli, " random [norandom]\r"); + cli_print(cli, "\n"); + return CLI_OK; + } + + if (mz_strcmp(argv[0], "random",1)==0) { + clipkt->sp_isrand = 1; + clipkt->sp_isrange = 0; + } else if (mz_strcmp(argv[0], "norandom",1)==0) { + clipkt->sp_isrand = 0; + } else { + if (!mz_strisnum(argv[0])) { + cli_print(cli, "Unknown keyword\n"); + return CLI_OK; + } + t32 = str2int(argv[0]); + if (t32>65535) { + cli_print(cli, "Port number cannot exceed 65535\n"); + return CLI_OK; + } else { + clipkt->sp= (u_int16_t) t32; + validport=1; + clipkt->sp_isrange = 0; + } + } + + if ((argc==2) && (validport)) { + if (!mz_strisnum(argv[1])) { + cli_print(cli, "Invalid number\n"); + return CLI_OK; + } + t32 = str2int(argv[1]); + if (t32>65535) { + cli_print(cli, "Port number cannot exceed 65535\n"); + } else { + clipkt->sp_start = clipkt->sp; + clipkt->sp_stop = (u_int16_t) t32; + clipkt->sp_isrange = 1; + } + } + + return CLI_OK; +} + + + + +int cmd_port_destination (struct cli_def *cli, char *command, char *argv[], int argc) +{ + u_int32_t t32=0; + int validport=0; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc>2) ) { + cli_print(cli, "Specify the destination port number\r"); + cli_print(cli, " []\r"); + cli_print(cli, " random [norandom]\r"); + cli_print(cli, "\n"); + return CLI_OK; + } + + if (mz_strcmp(argv[0], "random",1)==0) { + clipkt->dp_isrand = 1; + clipkt->dp_isrange = 0; + } else if (mz_strcmp(argv[0], "norandom",1)==0) { + clipkt->dp_isrand = 0; + } else { + if (!mz_strisnum(argv[0])) { + cli_print(cli, "Unknown keyword\n"); + return CLI_OK; + + } + t32 = str2int(argv[0]); + if (t32>65535) { + cli_print(cli, "Port number cannot exceed 65535\n"); + return CLI_OK; + } else { + clipkt->dp= (u_int16_t) t32; + validport=1; + clipkt->dp_isrange = 0; + } + } + + if ((argc==2) && (validport)) { + if (!mz_strisnum(argv[1])) { + cli_print(cli, "Invalid number\n"); + return CLI_OK; + } + t32 = str2int(argv[1]); + if (t32>65535) { + cli_print(cli, "Port number cannot exceed 65535\n"); + } else { + clipkt->dp_start = clipkt->dp; + clipkt->dp_stop = (u_int16_t) t32; + clipkt->dp_isrange = 1; + } + } + + return CLI_OK; +} + + + +int cmd_udp_sum (struct cli_def *cli, char *command, char *argv[], int argc) +{ + int sum; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc>1) ) + { + cli_print(cli, "Specify the UDP checksum:\n"); + cli_print(cli, " - either in hexadecimal format (0-ffff)\r"); + cli_print(cli, " - or use the keyword 'auto' (default)\r"); + cli_print(cli, " - or use the keyword 'unset'\r"); + cli_print(cli, "\r"); + cli_print(cli, "By default, the checksum is computed automatically. The keyword\r"); + cli_print(cli, "'unset' signals the receiver that the checksum has not be computed\r"); + cli_print(cli, "and should be ignored.\n"); + return CLI_OK; + } + + if (mz_strcmp(argv[0], "auto", 2)==0) + { + clipkt->udp_sum_false=0; + return CLI_OK; + } + + if (mz_strcmp(argv[0], "unset", 2)==0) + { + clipkt->udp_sum_false=1; + clipkt->udp_sum = 0xffff; + return CLI_OK; + } + + sum = (int) xstr2int(argv[0]); + + if (sum>0xffff) + { + cli_print(cli, "The checksum must be within range 0..ffff\n"); + return CLI_OK; + } + + clipkt->udp_sum = (u_int16_t) sum; + clipkt->udp_sum_false=1; + + return CLI_OK; +} + + + +int cmd_udp_len (struct cli_def *cli, char *command, char *argv[], int argc) +{ + + if ( (strcmp(argv[argc-1],"?")==0) || (argc>1) ) + { + cli_print(cli, "Specify the UDP length\r"); + cli_print(cli, "\n"); + return CLI_OK; + } + + cli_print(cli, "Not supported in this version.\n"); + + return CLI_OK; +} + + +int cmd_udp_end(struct cli_def *cli, char *command, char *argv[], int argc) +{ + char prompt[16]; + sprintf(prompt, "pkt-%i",clipkt->id); + cli_set_configmode(cli, MZ_MODE_PACKET, prompt); + return CLI_OK; +} diff --git a/src/directmops.c b/src/directmops.c new file mode 100644 index 0000000..5d95bd3 --- /dev/null +++ b/src/directmops.c @@ -0,0 +1,30 @@ +/* + * Mausezahn - A fast versatile traffic generator + * Copyright (C) 2008-2010 Herbert Haas + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 as published by the + * Free Software Foundation. + * + * 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, see http://www.gnu.org/licenses/gpl-2.0.html + * +*/ + + +#include "mz.h" +#include "cli.h" +#include "mops.h" + + +int mops_direct(char* dev, int mops_type, char* argstring) +{ + printf("Got device {%s} type {%i} and argstring {%s}\n", dev, mops_type, argstring); + + return 0; +} diff --git a/src/dns.c b/src/dns.c new file mode 100644 index 0000000..5f9203c --- /dev/null +++ b/src/dns.c @@ -0,0 +1,817 @@ +/* + * Mausezahn - A fast versatile traffic generator + * Copyright (C) 2008 Herbert Haas + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 as published by the + * Free Software Foundation. + * + * 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, see http://www.gnu.org/licenses/gpl-2.0.html + * +*/ + + + +//////////////////////////////////////////////////////////////////// +// +// DNS: Only UDP-based here +// +//////////////////////////////////////////////////////////////////// + +#include "mz.h" +#include "cli.h" + + +#define MZ_DNS_HELP \ + "| DNS type: Send Domain Name System Messages.\n" \ + "|\n" \ + "| Generally there are two interesting general DNS messages: queries and answers. The easiest\n" \ + "| way is to use the following syntax:\n" \ + "|\n" \ + "| query|q = [:] ............. where type is per default \"A\"\n" \ + "| (and class is always \"IN\")\n" \ + "|\n" \ + "| answer|a = [::] ...... ttl is per default 0.\n" \ + "| = [::]/[::]/...\n" \ + "|\n" \ + "| Note: If you only use the 'query' option then a query is sent. If you additonally add\n" \ + "| an 'answer' option then an answer is sent.\n" \ + "|\n" \ + "| Examples: \n" \ + "|\n" \ + "| q = www.xyz.com\n" \ + "| q = www.xyz.com, a=192.168.1.10\n" \ + "| q = www.xyz.com, a=A:3600:192.168.1.10\n" \ + "| q = www.xyz.com, a=CNAME:3600:abc.com/A:3600:192.168.1.10\n" \ + "|\n" \ + "| Note: can be: A, CNAME, or any integer\n" \ + "|\n" \ + "|\n" \ + "| OPTIONAL parameter hacks: (if you don't know what you do this might cause invalid packets)\n" \ + "|\n" \ + "| Parameter Description query / reply)\n" \ + "| -------------------------------------------------------------------------------------\n" \ + "|\n" \ + "| request/response|reply ..... flag only request / n.a. \n" \ + "| id ......................... packet id (0-65535) random / random\n" \ + "| opcode (or op) ............. accepts values 0..15 or one of std / 0 \n" \ + "| these keywords: \n" \ + "| = std ................... Standard Query\n" \ + "| = inv ................... Inverse Query\n" \ + "| = sts ................... Server Status Request\n" \ + "| aa or !aa .................. Authoritative Answer UNSET / SET\n" \ + "| tc or !tc .................. Truncation UNSET / UNSET\n" \ + "| rd or !rd .................. Recursion Desired SET / SET\n" \ + "| ra or !ra .................. Recursion Available UNSET / SET\n" \ + "| z .......................... Reserved (takes values 0..7) 0 / 0 \n" \ + "| (z=2...authenticated)\n" \ + "| rcode ...................... Response Code (0..15); interesting 0 / 0 \n" \ + "| values are:\n" \ + "| = 0 ...................... No Error Condition\n" \ + "| = 1 ...................... Unable to interprete query due to format error\n" \ + "| = 2 ...................... Unable to process due to server failure\n" \ + "| = 3 ...................... Name in query does not exist\n" \ + "| = 4 ...................... Type of query not supported\n" \ + "| = 5 ...................... Query refused\n" \ + "|\n" \ + "| Count values (values 0..65535) will be set automatically! You should not set these\n" \ + "| values manually except you are interested in invalid packets.\n" \ + "| qdcount (or qdc) ........... Number of entries in question section 1 / 1\n" \ + "| ancount (or anc) ........... Number of RRs in answer records section 0 / 1\n" \ + "| nscount (or nsc) ........... Number of name server RRs in authority 0 / 0\n" \ + "| records section\n" \ + "| arcount (or arc) ........... Number of RRs in additional records section 0 / 0\n" \ + "\n" + + +int dns_get_query (char* argval); +int dns_get_answer (char* argval); + + + +// Note: I do NOT use libnet here (had problems with bugs there...) +int create_dns_packet () +{ + + char *token, *tokenptr, argval[MAX_PAYLOAD_SIZE]; + + int i=0,j=0; + + unsigned char *x; + u_int16_t tmp; + + + // 16 bit values: + u_int8_t + dns_id0 =0, // DNS packet ID + dns_id1 =0, + dns_flags0 =0, // consists of the flags below + dns_flags1 =0, + dns_num_q0 =0, // number of questions + dns_num_q1 =0, + dns_num_ans0 =0, // number of answer resource records + dns_num_ans1 =0, + dns_num_aut0 =0, // number of authority resource records + dns_num_aut1 =0, + dns_num_add0 =0, // number of additional resource records + dns_num_add1 =0, + dns_type0 =0, + dns_type1 =0; + + + // bit fields for dns_flags1: Q/R(1), OPCODE(4), AA(1), TC(1), RD(1) + // bit fields for dns_flags0: RA(1), Z(3), RCODE(4) + u_int8_t + dns_flags_qr, // 1 bit + dns_flags_opcode, // 4 bits + dns_flags_aa, // 1 bit + dns_flags_tc, // 1 bit + dns_flags_rd, // 1 bit + // ---- next byte ----- + dns_flags_ra, // 1 bit + dns_flags_z, // 3 bits + dns_flags_rcode; // 4 bits + + + u_int8_t + dns_packet[MAX_PAYLOAD_SIZE], // finally the whole packet with all sections + section[MAX_PAYLOAD_SIZE]; // contains only a section (intermediately) + u_int32_t + dns_packet_s; + + + + if ( (getarg(tx.arg_string,"help", NULL)==1) && (mode==DNS) ) + { + if (mz_port) + { + cli_print(gcli, "%s", MZ_DNS_HELP); + return -1; + } + else + { + fprintf(stderr,"\n" + MAUSEZAHN_VERSION + "\n%s", MZ_DNS_HELP); + exit(0); + } + } + + + // general defaults: + // TODO: define globals in case dns is called by external functions!) + // MOST SAFEST AND EASIEST METHOD: define tx.dns_xxxx for default-initialization + // + dns_id0 = 0x42; // dns_id0 = (u_int8_t) ( ((float) rand()/RAND_MAX)*255); + dns_id1 = 0x42; + + dns_flags_qr = 0; // request + dns_flags_opcode = 0; // 'standard query' (also for response!) + + dns_type0 = 1; // A record + dns_type1 = 0; + + + i=0; + + + ///////////////////////////////////////////////////////////////////////////////// + // Evaluate CLI parameters: + + + // Handle the query // + + if ( (getarg(tx.arg_string,"query", argval)==1) || + (getarg(tx.arg_string,"q", argval)==1) ) + { + + (void) dns_get_query (argval); // returns the length in byte dns_num_q0=1; + + // copy the result from gbuf to our local buffer 'section': + for (j=0;j[:[:]]/[:]\n" + if ( (getarg(tx.arg_string,"answer", argval)==1) || + (getarg(tx.arg_string,"a", argval)==1) ) + { + + // In case there are multiple answer sections seperate them with / or | + token = strtok_r(argval,"/|",&tokenptr); + do + { + //then the corresponding answer section: + //first create a pointer to the : + section[i]=0xc0; // a pointer always starts with MSB=11xxxxxx xxxxxxx = 0xc0 + i++; + section[i]=0x0c; // this number always points to the first query entry + i++; + //then add rdata + dns_num_ans0 += dns_get_answer (token); + //NOTE: 'i' points to the next free byte in section[] (see the query handling above) + for (j=0;j 15) + { + if (!quiet) + { + fprintf(stderr, "mz/dns: [Warning] Opcode cannot exceed 15 => will reduce to 15!\n"); + } + dns_flags_opcode = 15; + } + } + } + + + + + if (getarg(tx.arg_string,"aa",NULL)==1) + { + dns_flags_aa = 1; + } + + if (getarg(tx.arg_string,"!aa",NULL)==1) + { + dns_flags_aa = 0; + } + + if (getarg(tx.arg_string,"tc",NULL)==1) + { + dns_flags_tc = 1; + } + + if (getarg(tx.arg_string,"!tc",NULL)==1) + { + dns_flags_tc = 0; + } + + if (getarg(tx.arg_string,"rd",NULL)==1) + { + dns_flags_rd = 1; + } + + if (getarg(tx.arg_string,"!rd",NULL)==1) + { + dns_flags_rd = 0; + } + + if (getarg(tx.arg_string,"ra",NULL)==1) + { + dns_flags_ra = 1; + } + + if (getarg(tx.arg_string,"!ra",NULL)==1) + { + dns_flags_ra = 0; + } + + if (getarg(tx.arg_string,"z", argval)==1) + { + dns_flags_z = (u_int8_t) str2int (argval); + if (dns_flags_z > 7) + { + if (!quiet) + { + fprintf(stderr, "mz/dns: [Warning] z cannot exceed 7 => will reduce to 7!\n"); + } + dns_flags_z = 7; + } + } + + + + if (getarg(tx.arg_string,"rcode", argval)==1) + { + dns_flags_rcode = (u_int8_t) str2int (argval); + if (dns_flags_rcode > 15) + { + if (!quiet) + { + fprintf(stderr, "mz/dns: [Warning] rcode cannot exceed 15 => will reduce to 15!\n"); + } + dns_flags_rcode = 7; + } + } + + + if ( (getarg(tx.arg_string,"qdcount", argval)==1) || + (getarg(tx.arg_string,"qdc", argval)==1) || + (getarg(tx.arg_string,"qc", argval)==1) ) + + { + tmp = (u_int16_t) str2int (argval); + x = (unsigned char*) &tmp; + dns_num_q1 = *x; + x++; + dns_num_q0 = *x; + } + + if ( (getarg(tx.arg_string,"ancount", argval)==1) || + (getarg(tx.arg_string,"anc", argval)==1) ) + { + tmp = (u_int16_t) str2int (argval); + x = (unsigned char*) &tmp; + dns_num_ans1 = *x; + x++; + dns_num_ans0 = *x; + } + + if ( (getarg(tx.arg_string,"nscount", argval)==1) || + (getarg(tx.arg_string,"nsc", argval)==1) ) + { + tmp = (u_int16_t) str2int (argval); + x = (unsigned char*) &tmp; + dns_num_aut1 = *x; + x++; + dns_num_aut0 = *x; + } + + if ( (getarg(tx.arg_string,"arcount", argval)==1) || + (getarg(tx.arg_string,"arc", argval)==1) ) + { + tmp = (u_int16_t) str2int (argval); + x = (unsigned char*) &tmp; + dns_num_add1 = *x; + x++; + dns_num_add0 = *x; + } + + // + // End of optional parameter handling + // + /////////////////////////////////////////////////////////////////////////////////////////////// + + + + + ///////////////////////////////////////////////////////// + // Now put all together i. e. create the UDP payload + // + // bit fields for dns_flags1: Q/R(1), OPCODE(4), AA(1), TC(1), RD(1) + // bit fields for dns_flags0: RA(1), Z(3), RCODE(4) + // + // 7 6 5 4 3 2 1 0 + // +--+--+--+--+--+--+--+--+ + // |QR| OPCODE |AA|TC|RD| + // +--+--+--+--+--+--+--+--+ + // + // + // 7 6 5 4 3 2 1 0 + // +--+--+--+--+--+--+--+--+ + // |RA| Z | RCODE | + // +--+--+--+--+--+--+--+--+ + // + + //// Flags: MSB + dns_flags_qr <<= 7; + dns_flags1 |= dns_flags_qr; + + dns_flags_opcode <<= 3; + dns_flags1 |= dns_flags_opcode; + + dns_flags_aa <<= 2; + dns_flags1 |= dns_flags_aa; + + dns_flags_tc <<= 1; + dns_flags1 |= dns_flags_tc; + + dns_flags1 |= dns_flags_rd; + + //// Flags: LSB + + dns_flags_ra <<= 7; + dns_flags0 |= dns_flags_ra; + + dns_flags_z <<= 4; + dns_flags0 |= dns_flags_z; + + dns_flags0 |= dns_flags_rcode; + + //// Add header bytes to dns_packet: + + dns_packet[0]=dns_id1; + dns_packet[1]=dns_id0; + + dns_packet[2]=dns_flags1; + dns_packet[3]=dns_flags0; + + dns_packet[4]=dns_num_q1; + dns_packet[5]=dns_num_q0; + + dns_packet[6]=dns_num_ans1; + dns_packet[7]=dns_num_ans0; + + dns_packet[8]=dns_num_aut1; + dns_packet[9]=dns_num_aut0; + + dns_packet[10]=dns_num_add1; + dns_packet[11]=dns_num_add0; + + //// Add sections to dns_packet: + + + for (j=0; j[:]\n" +// Return value: +// number of queries (currently only 1 query accepted, +// hence return value is 1 on success or 0 upon failure +// +int dns_get_query(char* argval) +{ + char *token, *field, *saveptr1=NULL, *saveptr2=NULL; + int i,j, cnt; + u_int16_t tmp; + unsigned char *x; + + i=0; + + // now get first field: + field = strtok_r(argval, ":", &saveptr1); + + // decompose into labels: + token = strtok_r(field, ".", &saveptr2); + + do // loop through all labels + { + cnt = strlen(token); + gbuf[i] = cnt; + i++; + for (j=i; j<(i+cnt);j++) + { + gbuf[j] = *token; + token++; + } + i+=cnt; + + } while ( (token = strtok_r(NULL, ".", &saveptr2)) != NULL); + + gbuf[i]=0x00; + i++; // (always point to next empty byte) + + + // lets see if has also been specified: + if ( (field = strtok_r(NULL, ":", &saveptr1)) !=NULL) + { + if ( (strncmp(field, "A",1)==0) || (strncmp(field, "a",1)==0) ) + { + tmp = 1; + } + else + { + tmp = (u_int16_t) str2int (field); + } + + x = (unsigned char*) &tmp; + + gbuf[i] = *(x+1); + i++; + gbuf[i] = *x; + i++; + } + else // use default type=A + { + gbuf[i] = 0x00; i++; + gbuf[i] = 0x01; i++; + } + + // finally add the class=IN: + gbuf[i] = 0x00; i++; + gbuf[i] = 0x01; i++; + + // this is the number of used bytes: + gbuf_s = i; + + //////// TEST + /* + for (j=0; j Depending on type the rdata must be handled differently +// A:86400:192.168.1.33 => Up to 3 parameters +// A:192.168.1.33 => TTL may be omitted, then TTL=0 +// 192.168.1.44 => Single parameter can only be an A record +// +// Other TYPES than A and CNAME are currently not supported and therefore the user must +// specify RDATA in hex. +// + +int dns_get_answer(char* argval) +{ + char *field, *saveptr1=NULL; + char field1[512], field2[512], field3[512]; + int i, len, num_params; + u_int16_t TYPE=1; // A + u_int8_t *ptrTYPE; + u_int32_t TTL=0; + u_int8_t *ptrTTL; + u_int16_t RDLEN; + u_int8_t *ptrRDLEN; + u_int8_t rdata[512]; + + field1[0]='\0'; + field2[0]='\0'; + field3[0]='\0'; + + len = strlen (argval); + + // determine number of occurences of ':' + num_params=1; + for (i=0; i3) return 0; // Error! + + // now get the fields (type, ttl, rdata) + field = strtok_r(argval, ":", &saveptr1); + strncpy(field1, field, 512); + if (num_params>1) // 2 or 3 + { + field = strtok_r(NULL, ":", &saveptr1); + strncpy(field2, field, 512); + if (num_params==3) + { + field = strtok_r(NULL, ":", &saveptr1); + strncpy(field3, field, 512); + } + } + + + // Now we have all parameters in field1, field2, and field3. + // But field2 and/or field3 might be empty. + + switch (num_params) + { + case 1: // only RDATA specified + strncpy(field3, field1, 512); + strcpy(field1, "A"); + strcpy(field2, "0"); + break; + case 2: // TYPE and RDATA + strncpy(field3, field2, 512); + strcpy(field2, "0"); + break; + } + + //CHECK: + //printf("fields: [%s] [%s] [%s]\n",field1,field2,field3); + + ////////////////////////////////////////////////////////////////////// + // Now create the whole answer section: Type, Class, TTL, RDLEN, RDATA + + //// TYPE + if ( (strcmp(field1,"CNAME")==0) || + (strcmp(field1,"cname")==0) ) + { + TYPE=5; + gbuf[0]=0x00; + gbuf[1]=0x05; + } + else if ( (strcmp(field1,"A")==0) || + (strcmp(field1,"a")==0) ) + { + TYPE=1; + gbuf[0]=0x00; + gbuf[1]=0x01; + } + else // type must be given as number + { + TYPE = (u_int16_t) str2int(field1); + ptrTYPE = (u_int8_t*) &TYPE; + gbuf[0]=*(ptrTYPE+1); + gbuf[1]=*(ptrTYPE); + } + + + //// CLASS = IN = 0x00 01 + gbuf[2]= 0x00; gbuf[3]=0x01; + + //// TTL + TTL = (u_int32_t) str2int(field2); + ptrTTL = (u_int8_t*) &TTL; + gbuf[4]= *(ptrTTL+3); + gbuf[5]= *(ptrTTL+2); + gbuf[6]= *(ptrTTL+1); + gbuf[7]= *(ptrTTL+0); + + + //// RDLEN and RDATA + if (TYPE==1) // A + { + RDLEN = num2hex(field3, rdata); // should be 4 if IP address + if (RDLEN!=4) + { + fprintf(stderr," mz/dns_get_answer: [WARNING] RDATA of A record should contain an IPv4 address (4 bytes).\n"); + } + } + else if (TYPE==5) // CNAME + { + RDLEN = dns_process_label (field3, rdata); + if (RDLEN==0) + { + fprintf(stderr," mz/dns_get_answer: [WARNING] RDATA must contain a domain name.\n"); + } + } + else // Any other type + { + RDLEN = str2hex(field3, rdata, 512); // should be 4 if IP address + } + + ptrRDLEN = (u_int8_t*) &RDLEN; + gbuf[8] = *(ptrRDLEN+1); + gbuf[9] = *(ptrRDLEN+0); + + + // finally write rdata + for (i=0; i0xff) return 1; + addr[i]=(u_int8_t) strtol (hs, NULL, 16); + hs = strtok(NULL,"-:., "); + if ( (hs == NULL ) && (i!=5) ) + { + // Not a valid MAC address + return 1; + } + } + + if (hs!=NULL) return 1; // more than 6 bytes + + return 0; +} + + + + +// Converts ascii hex values (string) into integer array +// For example "1a 00:00-2f" will be converted to {26, 0, 0, 47} +// +// NOTE: n ist the max number of bytes to be converted +// +// RETURN VALUE: number of bytes converted +// or -1 upon failure +// +int str2hex(char* str, u_int8_t *hp, int n) +{ + char *hs; + int curval,i; + + + if (strlen(str)==0) return 0; + + char tmp[8192]=""; //for very long payloads + + strncpy(tmp,str,8191); // necessary because strtok cannot operate on fixed strings + + hs=(char*)strtok(tmp,"-:., "); + + i=0; + do + { n--; + curval=strtol(hs,NULL,16); + if (curval>0xff) return -1; + hp[i]=(u_int8_t) curval; + i++; + } + while ((n) && ((hs=(char*)strtok(NULL,"-:., "))!= NULL)); + + return i; // return the length of the array +} + + + +// Converts ascii numbers (terminated string) into integer array +// Every byte can be specified as integers {0..255} +// For example "192.16.1.1" will be converted to {C0, 10, 01, 01} +// +// NOTE: Returns the number of converted bytes! +int num2hex(char* str, u_int8_t *hp) +{ + char *hs; + int i; + unsigned int curval; + + if (strlen(str)==0) return 0; + + char tmp[8192]=""; //for very long payloads + + strncpy(tmp,str,8192); // necessary because strtok cannot operate on fixed strings + + hs = (char*) strtok (tmp,"-:., "); + + i=0; + do + { + curval = (unsigned int) str2int(hs); + if (curval<256) + { + hp[i] = (u_int8_t) curval; + i++; + } + } + while ((hs=(char*)strtok(NULL,"-:., "))!= NULL); + //hp[i]='\0'; // termination not necessary + + return i; +} + + + +// Convert array of integers into string of hex +// E.g. {0,1,10} => "00-01-0A" +// Useful for verification messages. +int bs2str(u_int8_t *bs, char* str, int len) +{ + int i; + char t[4]; + + str[0]='\0'; + + for (i=0; i 3232235521 +u_int32_t str2ip32 (char* str) +{ + u_int32_t ip = 0; + unsigned int a,b,c,d; + int r; + + // check whether str really contains an IP address + if (strlen(str)<3) return 0; + if (str==NULL) return 0; + + if ((r=sscanf(str,"%i.%i.%i.%i",&a,&b,&c,&d))==0) return 0; + if (r==EOF) return 0; + + /* or an alternative method... + // these are the four bytes of a dotted decimal notation IP address: + a = (unsigned int) strtol(strtok(str,"."), (char **)NULL, 10); + b = (unsigned int) strtol(strtok(NULL,"."), (char **)NULL, 10); + c = (unsigned int) strtol(strtok(NULL,"."), (char **)NULL, 10); + d = (unsigned int) strtol(strtok(NULL,"."), (char **)NULL, 10); + */ + + if ((a>255)||(b>255)||(c>255)||(d>255)) return 0; + + ip = d + 256*c + 256*256*b + 256*256*256*a; + + //check with: + //printf("str2ip32 got 4 bytes: %i %i %i %i\n",a,b,c,d); + //printf("str2ip32 returned %u\n",ip); + + return ip; +} + + +// Converts an IP address given in 'dotted decimal' into an unsigned 32-bit integer +// This version does the same as str2ip32() but in 'network byte order' +u_int32_t str2ip32_rev (char* str) +{ + u_int32_t ip = 0; + unsigned int a,b,c,d; + int r; + + // check whether str really contains an IP address + if (strlen(str)<3) return 0; + if (str==NULL) return 0; + + if ((r=sscanf(str,"%i.%i.%i.%i",&a,&b,&c,&d))==0) return 0; + if (r==EOF) return 0; + + /* or an alternative method... + // these are the four bytes of a dotted decimal notation IP address: + a = (unsigned int) strtol(strtok(str,"."), (char **)NULL, 10); + b = (unsigned int) strtol(strtok(NULL,"."), (char **)NULL, 10); + c = (unsigned int) strtol(strtok(NULL,"."), (char **)NULL, 10); + d = (unsigned int) strtol(strtok(NULL,"."), (char **)NULL, 10); + */ + + if ((a>255)||(b>255)||(c>255)||(d>255)) return 0; + + ip = a + b*256 + c*256*256 + d*256*256*256; + + //check with: + //printf("str2ip32 got 4 bytes: %i %i %i %i\n",a,b,c,d); + //printf("str2ip32 returned %u\n",ip); + + return ip; +} + + +// Converts a 2-byte value (e. g. a EtherType field) +// into a nice string using hex notation. +// Useful for verification messages. +// Example: type2str (tx.eth_type, msg) may result in msg="08:00" +// Return value: how many hex digits have been found. +int type2str(u_int16_t type, char *str) +{ + char hex[8]; + int i=0; + + (void) sprintf (hex, "%x",type); + i=strlen(hex); + + switch (i) + { + case 1: + str[0]='0'; + str[1]='0'; + str[2]=':'; + str[3]='0'; + str[4]=hex[0]; + str[5]='\0'; + break; + case 2: + str[0]='0'; + str[1]='0'; + str[2]=':'; + str[3]=hex[0]; + str[4]=hex[1]; + str[5]='\0'; + break; + case 3: + str[0]='0'; + str[1]=hex[0]; + str[2]=':'; + str[3]=hex[1]; + str[4]=hex[2]; + str[5]='\0'; + break; + case 4: + str[0]=hex[0]; + str[1]=hex[1]; + str[2]=':'; + str[3]=hex[2]; + str[4]=hex[3]; + str[5]='\0'; + break; + + } + return i; +} + diff --git a/src/init.c b/src/init.c new file mode 100644 index 0000000..0ab21cb --- /dev/null +++ b/src/init.c @@ -0,0 +1,662 @@ +/* + * Mausezahn - A fast versatile traffic generator + * Copyright (C) 2008-2010 Herbert Haas + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 as published by the + * Free Software Foundation. + * + * 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, see http://www.gnu.org/licenses/gpl-2.0.html + * +*/ + + + + +// **************************************************************************** +// Two features here: +// 1. Initialize globals +// 2. Handle arguments in *argv[] and make a mode decision +// **************************************************************************** + +#include "mz.h" +#include "cli.h" +#include "mops.h" + +// Purpose: reset globals, global structs, etc. +int reset() +{ + int i; + time_t t; + + // Determine platform type sizes: + MZ_SIZE_LONG_INT = sizeof(long int); + + mz_default_config_path[0] = 0x00; + mz_default_log_path[0] = 0x00; + + // Reset globals: + quiet = 0; + verbose = 0; + simulate = 0; + filename[0] = '\0'; + path[0] = '\0'; + gind=0; + gind_max = TIME_COUNT; + fp = NULL; + fp2 = NULL; + mz_port = 0; + mz_rand = 0; + mp_head = NULL; + + for (i=0;i10000) { + fprintf(stderr, " Warning: Padding must not exceed 10000!\n"); + return -1; + } + break; + case 't': + packet_type = optarg; // analyzed below + break; + case 'X': + mops_type = optarg; // MOPS TRANSITION STRATEGY -- analyzed below + break; + case 'T': + packet_type = optarg; + RX = 1; + break; + case 'r': + mz_rand = 1; + break; + case 'M': + if (strncmp(optarg,"help",4)==0) { + (void) get_mpls_params("help "); + } + else { + strncpy (tx.mpls_txt, optarg, 128); + tx.eth_type = ETHERTYPE_MPLS; + tx.packet_mode = 0; + tx.mpls=1; + } + break; + case 'P': // ASCII payload + strncpy((char*)tx.ascii_payload, optarg, MAX_PAYLOAD_SIZE); + tx.ascii = 1; + break; + case 'f': // ASCII payload in FILE + afp = fopen(optarg, "r"); + if (fgets((char*)tx.ascii_payload, MAX_PAYLOAD_SIZE, afp) == NULL) + fprintf(stderr, " mz/getopts: File empty?\n"); + fclose(afp); + tx.ascii = 1; + break; + case 'F': // HEX payload in FILE + afp = fopen(optarg, "r"); + i=0; + while ( (hexpld[i]=fgetc(afp))!=EOF ) { + if (isspace(hexpld[i])) { + hexpld[i]=':'; + } + i++; + } + hexpld[i]='\0'; + fclose(afp); + hexpld_specified=1; + break; + case 'Q': // VLAN TAG + if (strncmp(optarg,"help",4)==0) { + print_dot1Q_help(); // ugly but most simple and safe solution + } + else { + strncpy (tx.dot1Q_txt, optarg, 32); + tx.dot1Q=1; + // determine number of VLAN tags + for (i=0; i2) { // number of remaining arguments + fprintf(stderr," mz/getopts: Too many arguments!\n"); + return -1; + } + + + // There can be 0-2 additional arguments + switch (rargs) { + case 0: + if (lookupdev()) { // no device found + if (verbose) fprintf(stderr, " mz: no active interfaces found!\n"); + strcpy(tx.device, "lo"); + } + if (verbose) // device found + fprintf(stderr," mz: device not given, will use %s\n",tx.device); + break; + case 1: // arg_string OR device given => find out! + if ( (strncmp(argv[optind],"eth",3)==0) + || (strncmp(argv[optind],"ath",3)==0) + || ((strncmp(argv[optind],"lo",2)==0)&&(strncmp(argv[optind],"log",3)!=0)) + || (strncmp(argv[optind],"vmnet",5)==0) + || (strncmp(argv[optind],"wifi",4)==0) ) { + // device has been specified! + strncpy (tx.device, argv[optind], 16); + } + else { /// arg_string given => no device has been specified -- let's find one! + strncpy (tx.arg_string, argv[optind], MAX_PAYLOAD_SIZE); + if (lookupdev()) { // no device found + if (verbose) fprintf(stderr, " mz: no active interfaces found!\n"); + strcpy(tx.device, "lo"); + } + if (verbose) + fprintf(stderr," mz: device not given, will use %s\n",tx.device); + } + break; + case 2: // both device and arg_string given + strncpy (tx.device, argv[optind], 16); + strncpy (tx.arg_string, argv[optind+1], MAX_PAYLOAD_SIZE); + break; + default: + fprintf(stderr," mz/getopts: Unknown argument problem!\n"); + return 1; + } + + if (hexpld_specified) { + strcat(tx.arg_string, ",p="); + strcat(tx.arg_string, hexpld); + } + + + ////////////////////////////////////////////////////////////////////////// + // + // Initialize MAC and IP Addresses. + // + // - tx.eth_src = own interface MAC + // - tx.ip_src = own interface IP or user specified + // - tx.ip_dst = 255.255.255.255 or user specified (can be a range) + // - tx.ip_src_rand ... is set if needed. + // + + // Get own device MAC address: + // Don't open context if only a help text is requested + if (getarg(tx.arg_string,"help", NULL)!=1) { + l = libnet_init (LIBNET_LINK_ADV, tx.device, err_buf ); + if (l == NULL) { + fprintf(stderr, " mz/getopts: libnet_init() failed (%s)", err_buf); + return -1; + } + mymac = libnet_get_hwaddr(l); + for (i=0; i<6; i++) { + tx.eth_src[i] = mymac->ether_addr_octet[i]; + tx.eth_mac_own[i] = mymac->ether_addr_octet[i]; + } + + // Set source IP address: + if (strlen(tx.ip_src_txt)) { // option -A has been specified + if (mz_strcmp(tx.ip_src_txt, "bcast", 2)==0) { + tx.ip_src = libnet_name2addr4 (l, "255.255.255.255", LIBNET_DONT_RESOLVE); + } else if (strcmp(tx.ip_src_txt, "rand") == 0) { + tx.ip_src_rand = 1; + tx.ip_src_h = (u_int32_t) ( ((float) rand()/RAND_MAX)*0xE0000000); //this is 224.0.0.0 + } + else if (get_ip_range_src(tx.ip_src_txt)) { // returns 1 when no range has been specified + // name2addr4 accepts a DOTTED DECIMAL ADDRESS or a FQDN: + tx.ip_src = libnet_name2addr4 (l, tx.ip_src_txt, LIBNET_RESOLVE); + } + } + else { // no source IP specified: by default use own IP address + tx.ip_src = libnet_get_ipaddr4(l); + } + + // Set destination IP address: + if (strlen(tx.ip_dst_txt)) { // option -B has been specified + if (mz_strcmp(tx.ip_dst_txt, "rand", 2)==0) { + fprintf(stderr, "Option -B does not support random destination IP addresses currently.\n"); + return 1; + } + + if (mz_strcmp(tx.ip_dst_txt, "bcast", 2)==0) { + tx.ip_dst = libnet_name2addr4 (l, "255.255.255.255", LIBNET_DONT_RESOLVE); + } else if (get_ip_range_dst(tx.ip_dst_txt)) { // returns 1 when no range has been specified + // name2addr4 accepts a DOTTED DECIMAL ADDRESS or a FQDN: + tx.ip_dst = libnet_name2addr4 (l, tx.ip_dst_txt, LIBNET_RESOLVE); + } + } + else { // no destination IP specified: by default use broadcast + tx.ip_dst = libnet_name2addr4 (l, "255.255.255.255", LIBNET_DONT_RESOLVE); + } + + // Initialize tx.ip_src_h and tx.ip_dst_h which are used by 'print_frame_details()' + // in verbose mode. See 'modifications.c'. + + if (tx.ip_src_rand) { // ip_src_h already given, convert to ip_src + dum1 = (unsigned char*) &tx.ip_src_h; + dum2 = (unsigned char*) &tx.ip_src; + } + else { // ip_src already given, convert to ip_src_h + dum1 = (unsigned char*) &tx.ip_src; + dum2 = (unsigned char*) &tx.ip_src_h; + } + + *dum2 = *(dum1+3); + dum2++; + *dum2 = *(dum1+2); + dum2++; + *dum2 = *(dum1+1); + dum2++; + *dum2 = *dum1; + + dum1 = (unsigned char*) &tx.ip_dst; + dum2 = (unsigned char*) &tx.ip_dst_h; + + *dum2 = *(dum1+3); + dum2++; + *dum2 = *(dum1+2); + dum2++; + *dum2 = *(dum1+1); + dum2++; + *dum2 = *dum1; + + libnet_destroy(l); + } + + // + // END OF ADDRESS INITIALIZATION + // + ////////////////////////////////////////////////////////////////////////// + + + ////// retrieve interface parameters /////// + + for (i=0; i special packet types, stateless + // + // If -t not present then evaluate arg_string which must + // contain a byte-string in hexadecimal notation. + // + // + + // ***** NEW: MOPS TRANSITION STRATEGY ***** + if (mops_type != NULL) { + + if (mz_strcmp(mops_type,"lldp",4)==0) { + mops_direct(tx.device, MOPS_LLDP, tx.arg_string); + } + } + + + if (packet_type == NULL) { // raw hex string given + mode = BYTE_STREAM; + } + else if (strcmp(packet_type,"arp")==0) { + mode = ARP; + } + else if (strcmp(packet_type,"bpdu")==0) { + mode = BPDU; + } + else if (strcmp(packet_type,"ip")==0) { + mode = IP; + } + else if (strcmp(packet_type,"udp")==0) { + mode = UDP; + } + else if (strcmp(packet_type,"icmp")==0) { + mode = ICMP; + } + else if (strcmp(packet_type,"tcp")==0) { + mode = TCP; + } + else if (strcmp(packet_type,"dns")==0) { + mode = DNS; + } + else if (strcmp(packet_type,"cdp")==0) { + mode = CDP; + } + else if (strcmp(packet_type,"syslog")==0) { + mode = SYSLOG; + } + else if (strcmp(packet_type,"lldp")==0) { + mode = LLDP; + tx.packet_mode=0; // create whole frame by ourself + } + else if (strcmp(packet_type,"rtp")==0) { + if (RX) { + mode = RX_RTP; + } + else { + mode = RTP; + if (!count_set) tx.count = 0; + if (!delay_set) tx.delay = 20000; // 20 msec inter-packet delay for RTP + } + } + else if (strcmp(packet_type,"help")==0) { + fprintf(stderr, "\n" + MAUSEZAHN_VERSION + "\n" + "| The following packet types are currently implemented:\n" + "|\n" + "| arp ... sends ARP packets\n" + "| bpdu ... sends BPDU packets (STP or PVST+)\n" + "| cdp ... sends CDP messages\n" + "| ip ... sends IPv4 packets\n" + "| udp ... sends UDP datagrams\n" + "| tcp ... sends TCP segments\n" + "| icmp ... sends ICMP messages\n" + "| dns ... sends DNS messages\n" + "| rtp ... sends RTP datagrams\n" + "| syslog ... sends Syslog messages\n" + "|\n" + "| Of course you can build any other packet type 'manually' using the direct layer 2 mode.\n" + "| FYI: The interactive mode supports additional protocols. (Try mz -x )\n" + "\n" + ); + exit(1); + } + else { + fprintf(stderr, " mz: you must specify a valid packet type!\n"); + } + + + ////////////////////////////////////////////////////////////////////////// + + // TODO: Implement macro support + // Check macro types here + + return 0; + +} + + + + diff --git a/src/interval b/src/interval new file mode 100644 index 0000000..b484744 --- /dev/null +++ b/src/interval @@ -0,0 +1,376 @@ +cli_arp.c: struct mops_ext_arp * pd = clipkt->p_desc; +cli_arp.c: struct mops_ext_arp * pd = clipkt->p_desc; +cli_arp.c: struct mops_ext_arp * pd = clipkt->p_desc; +cli_arp.c: struct mops_ext_arp * pd = clipkt->p_desc; +cli_arp.c: struct mops_ext_arp * pd = clipkt->p_desc; +cli_arp.c: struct mops_ext_arp * pd = clipkt->p_desc; +cli_arp.c: struct mops_ext_arp * pd = clipkt->p_desc; +cli_arp.c: struct mops_ext_arp * pd = clipkt->p_desc; +cli_arp.c: struct mops_ext_arp * pd = clipkt->p_desc; +cli_arp.c: struct mops_ext_arp * pd = (MOPS_EXT_ARP) clipkt->p_desc; +cli_arp.c: sprintf(prompt, "pkt-%i",clipkt->id); +cli_bpdu.c: struct mops_ext_bpdu * pd = clipkt->p_desc; +cli_bpdu.c: struct mops_ext_bpdu * pd = clipkt->p_desc; +cli_bpdu.c: struct mops_ext_bpdu * pd = clipkt->p_desc; +cli_bpdu.c: struct mops_ext_bpdu * pd = clipkt->p_desc; +cli_bpdu.c: struct mops_ext_bpdu * pd = clipkt->p_desc; +cli_bpdu.c: struct mops_ext_bpdu * pd = clipkt->p_desc; +cli_bpdu.c: struct mops_ext_bpdu * pd = clipkt->p_desc; +cli_bpdu.c: struct mops_ext_bpdu * pd = clipkt->p_desc; +cli_bpdu.c: struct mops_ext_bpdu * pd = clipkt->p_desc; +cli_bpdu.c: struct mops_ext_bpdu * pd = clipkt->p_desc; +cli_bpdu.c: struct mops_ext_bpdu * pd = clipkt->p_desc; +cli_bpdu.c: struct mops_ext_bpdu * pd = clipkt->p_desc; +cli_bpdu.c: struct mops_ext_bpdu * pd = clipkt->p_desc; +cli_bpdu.c: sprintf(prompt, "pkt-%i",clipkt->id); +cli_cmds.c: bs2str(clipkt->eth_snap, line, clipkt->eth_snap_s); +cli_cmds.c: k = clipkt->dot1Q_s/4; // number of tags +cli_cmds.c: x0 = (unsigned char*) &clipkt->dot1Q[(j*4)+2]; +cli_cmds.c: x1 = (unsigned char*) &clipkt->dot1Q[(j*4)+3]; +cli_cmds.c: k = clipkt->mpls_s/4; // number of tags +cli_cmds.c: x0 = (unsigned char*) &clipkt->mpls[(j*4)+0]; +cli_cmds.c: x1 = (unsigned char*) &clipkt->mpls[(j*4)+1]; +cli_cmds.c: x2 = (unsigned char*) &clipkt->mpls[(j*4)+2]; +cli_cmds.c: x3 = (unsigned char*) &clipkt->mpls[(j*4)+3]; +cli_cmds.c: x0 = (unsigned char*) & clipkt->ip_src; +cli_cmds.c: if (clipkt->ip_src_isrange) +cli_cmds.c: x1 = (unsigned char*) & clipkt->ip_src_start; +cli_cmds.c: x2 = (unsigned char*) & clipkt->ip_src_stop; +cli_cmds.c: (clipkt->ip_src_israndom) ? "RANDOM" : "(not random)", +cli_cmds.c: (clipkt->ip_src_isrange) ? "RANGE:" : "(no range)", +cli_cmds.c: x0 = (unsigned char*) & clipkt->ip_dst; +cli_cmds.c: if (clipkt->ip_dst_isrange) +cli_cmds.c: x1 = (unsigned char*) & clipkt->ip_dst_start; +cli_cmds.c: x2 = (unsigned char*) & clipkt->ip_dst_stop; +cli_cmds.c: (clipkt->ip_dst_isrange) ? "RANGE:" : "(no range)", +cli_cmds.c: clipkt->ip_tos, clipkt->ip_proto, clipkt->ip_ttl, clipkt->ip_id, clipkt->ip_frag_offset, +cli_cmds.c: (clipkt->ip_flags_RS) ? "RS" : "-", +cli_cmds.c: (clipkt->ip_flags_DF) ? "DF" : "-", +cli_cmds.c: (clipkt->ip_flags_MF) ? "MF" : "-"); +cli_cmds.c: clipkt->frame[clipkt->begin_IP+2]*256+clipkt->frame[clipkt->begin_IP+3], +cli_cmds.c: (clipkt->ip_len_false) ? "false" : "correct", +cli_cmds.c: clipkt->frame[clipkt->begin_IP+10], +cli_cmds.c: clipkt->frame[clipkt->begin_IP+11], +cli_cmds.c: (clipkt->ip_sum_false) ? "false" : "correct"); +cli_cmds.c: if (clipkt->sp_isrange) +cli_cmds.c: sprintf(line2, "RANGE: %u-%u", clipkt->sp_start, clipkt->sp_stop); +cli_cmds.c: if (clipkt->dp_isrange) +cli_cmds.c: sprintf(line3, "RANGE: %u-%u", clipkt->dp_start, clipkt->dp_stop); +cli_cmds.c: clipkt->sp, +cli_cmds.c: (clipkt->sp_isrand) ? "RANDOM" : "(not random)", +cli_cmds.c: clipkt->dp, +cli_cmds.c: (clipkt->dp_isrand) ? "RANDOM" : "(not random)"); +cli_cmds.c: clipkt->udp_sum, (clipkt->udp_sum_false) ? "false" : "correct", +cli_cmds.c: clipkt->udp_len, (clipkt->udp_len_false) ? "false" : "correct"); +cli_cmds.c: if (clipkt->sp_isrange) +cli_cmds.c: sprintf(line2, "RANGE: %u-%u", clipkt->sp_start, clipkt->sp_stop); +cli_cmds.c: if (clipkt->dp_isrange) +cli_cmds.c: sprintf(line3, "RANGE: %u-%u", clipkt->dp_start, clipkt->dp_stop); +cli_cmds.c: clipkt->sp, +cli_cmds.c: (clipkt->sp_isrand) ? "RANDOM" : "(not random)", +cli_cmds.c: clipkt->dp, +cli_cmds.c: (clipkt->dp_isrand) ? "RANDOM" : "(not random)"); +cli_cmds.c: clipkt->tcp_seq, +cli_cmds.c: clipkt->tcp_seq_start, +cli_cmds.c: clipkt->tcp_seq_stop, +cli_cmds.c: clipkt->tcp_seq_delta, +cli_cmds.c: clipkt->tcp_ack, +cli_cmds.c: (clipkt->tcp_ctrl_ACK) ? "(valid)" : "(invalid)"); +cli_cmds.c: clipkt->tcp_res, +cli_cmds.c: clipkt->tcp_urg); +cli_cmds.c: sprintf(line, "Announced window size= %u", clipkt->tcp_win); +cli_cmds.c: clipkt->tcp_offset, +cli_cmds.c: (clipkt->tcp_offset_false) ? "FALSE" : "valid", +cli_cmds.c: clipkt->tcp_sum, +cli_cmds.c: (clipkt->tcp_sum_false) ? "FALSE" : "valid"); +cli_cmds.c: (clipkt->tcp_option_used) ? "TCP options attached" : "(No TCP options attached)", +cli_cmds.c: clipkt->tcp_option_s); +cli_dns.c: sprintf(prompt, "pkt-%i",clipkt->id); +cli_eth.c: clipkt->eth_src_israndom = 1; +cli_eth.c: while (strncmp(device_list[i].dev, clipkt->device, 10) && (ieth_src[j] = device_list[i].mac_mops[j]; +cli_eth.c: clipkt->eth_src_israndom = 0; +cli_eth.c: if (mops_pdesc_mac(clipkt->eth_src, argv[0])) +cli_eth.c: clipkt->eth_src_israndom = 0; +cli_eth.c: mops_pdesc_mac (clipkt->eth_dst, "ff:ff:ff:ff:ff:ff"); +cli_eth.c: mops_pdesc_mac (clipkt->eth_dst, "01:00:0C:CC:CC:CD"); +cli_eth.c: mops_pdesc_mac (clipkt->eth_dst, "01:00:0C:CC:CC:CC"); +cli_eth.c: mops_pdesc_mac (clipkt->eth_dst, "01:80:C2:00:00:00"); +cli_eth.c: if (mops_pdesc_mac(clipkt->eth_dst, argv[0])) +cli_eth.c: clipkt->eth_type = (u_int16_t) t32; +cli_eth.c: clipkt->eth_len = (u_int16_t) t32; +cli_eth.c: memcpy(&clipkt->eth_snap[0], &t8, 3); +cli_eth.c: memcpy(&clipkt->eth_snap[3], &oui, 3); +cli_eth.c: memcpy(&clipkt->eth_snap[6], &etp, 2); +cli_eth.c: clipkt->eth_snap_s = 8; +cli_igmp.c: clipkt->ip_dst = str2ip32("224.0.0.1"); +cli_igmp.c: clipkt->ip_ttl = 1; +cli_igmp.c: clipkt->ndelay.tv_sec = 125; +cli_igmp.c: clipkt->ndelay.tv_nsec = 0; +cli_igmp.c: clipkt->ip_dst = mip; +cli_igmp.c: clipkt->ip_ttl = 1; +cli_igmp.c: clipkt->ndelay.tv_sec = 125; +cli_igmp.c: clipkt->ndelay.tv_nsec = 0; +cli_igmp.c: clipkt->ip_dst = mip; +cli_igmp.c: clipkt->ip_ttl = 1; +cli_igmp.c: clipkt->ndelay.tv_sec = 1; +cli_igmp.c: clipkt->ndelay.tv_nsec = 0; +cli_igmp.c: clipkt->ip_dst = str2ip32("224.0.0.2"); +cli_igmp.c: clipkt->ip_ttl = 1; +cli_igmp.c: clipkt->ndelay.tv_sec = 1; +cli_igmp.c: clipkt->ndelay.tv_nsec = 0; +cli_igmp.c: clipkt->ip_dst = str2ip32("224.0.0.1"); +cli_igmp.c: clipkt->ip_ttl = 1; +cli_igmp.c: clipkt->ndelay.tv_sec = 125; +cli_igmp.c: clipkt->ndelay.tv_nsec = 0; +cli_igmp.c: clipkt->ip_dst = mip; +cli_igmp.c: clipkt->ip_ttl = 1; +cli_igmp.c: clipkt->ndelay.tv_sec = 1; +cli_igmp.c: clipkt->ndelay.tv_nsec = 0; +cli_ip.c: while (strncmp(device_list[i].dev, clipkt->device, 10) && (iip_src = device_list[i].ip_mops[3] +cli_ip.c: clipkt->ip_src_israndom = 0; +cli_ip.c: clipkt->ip_src_isrange = 0; +cli_ip.c: clipkt->ip_src_israndom = 1; +cli_ip.c: clipkt->ip_src_isrange = 0; +cli_ip.c: clipkt->ip_src = str2ip32(argv[0]); +cli_ip.c: clipkt->ip_src_israndom = 0; +cli_ip.c: clipkt->ip_src_isrange = 0; +cli_ip.c: clipkt->ip_src_start = str2ip32(argv[0]); +cli_ip.c: clipkt->ip_src_start = ip1; +cli_ip.c: clipkt->ip_src_stop = ip2; +cli_ip.c: clipkt->ip_src_isrange = 1; +cli_ip.c: clipkt->ip_src_israndom = 0; +cli_ip.c: if (str2ip32(argv[1]) > clipkt->ip_src_start) +cli_ip.c: clipkt->ip_src_stop = str2ip32(argv[1]); +cli_ip.c: clipkt->ip_src_isrange = 1; +cli_ip.c: clipkt->ip_src_israndom = 0; +cli_ip.c: clipkt->ip_dst = str2ip32(argv[0]); +cli_ip.c: clipkt->ip_dst_isrange = 0; +cli_ip.c: clipkt->ip_dst_start = str2ip32(argv[0]); +cli_ip.c: clipkt->ip_dst_start = ip1; +cli_ip.c: clipkt->ip_dst_stop = ip2; +cli_ip.c: clipkt->ip_dst_isrange = 1; +cli_ip.c: if (str2ip32(argv[1]) > clipkt->ip_dst_start) +cli_ip.c: clipkt->ip_dst_stop = str2ip32(argv[1]); +cli_ip.c: clipkt->ip_dst_isrange = 1; +cli_ip.c: clipkt->ip_version = ver; +cli_ip.c: clipkt->ip_ttl = ttl; +cli_ip.c: clipkt->ip_proto = proto; +cli_ip.c: clipkt->ip_IHL = ihl; +cli_ip.c: clipkt->ip_len = len; +cli_ip.c: clipkt->ip_id = id; +cli_ip.c: clipkt->ip_frag_offset = offset; +cli_ip.c: clipkt->ip_sum_false=0; +cli_ip.c: clipkt->ip_sum = (u_int16_t) sum; +cli_ip.c: clipkt->ip_sum_false=1; +cli_ip.c: clipkt->ip_tos = (u_int8_t) str2bin8 (argv[0]); +cli_ip.c: clipkt->ip_tos = (u_int8_t) xstr2int (tmp); +cli_ip.c: clipkt->ip_flags_RS = 1; +cli_ip.c: clipkt->ip_flags_RS = 0; +cli_ip.c: clipkt->ip_flags_DF = 1; +cli_ip.c: clipkt->ip_flags_DF = 0; +cli_ip.c: clipkt->ip_flags_MF = 1; +cli_ip.c: clipkt->ip_flags_MF = 0; +cli_ip.c: sprintf(str, "%s", (clipkt->auto_delivery_off) ? "DISABLED" : "ENABLED"); +cli_ip.c: clipkt->auto_delivery_off=0; +cli_ip.c: clipkt->auto_delivery_off=1; +cli_ip.c: sprintf(str, "%s", (clipkt->auto_delivery_off) ? "DISABLED" : "ENABLED"); +cli_ip.c: sprintf(prompt, "pkt-%i",clipkt->id); +cli_launch.c: strncpy (clipkt->packet_name, "sysBPDU", 7); +cli_launch.c: cli_print(cli, "Allocated new packet %s at slot %i",clipkt->packet_name, clipkt->id); +cli_launch.c: clipkt->use_ETHER = 1; +cli_launch.c: clipkt->use_SNAP = 1; +cli_launch.c: clipkt->count = 0; +cli_launch.c: clipkt->ndelay.tv_sec = 2; +cli_launch.c: clipkt->ndelay.tv_nsec = 0; +cli_launch.c: pd = clipkt->p_desc; +cli_launch.c: strncpy (clipkt->packet_name, "sysFlood_TCPSYN", 15); +cli_launch.c: cli_print(cli, "Allocated new packet %s at slot %i",clipkt->packet_name, clipkt->id); +cli_launch.c: clipkt->use_ETHER = 1; +cli_launch.c: clipkt->use_IP = 1; +cli_launch.c: clipkt->use_TCP = 1; +cli_launch.c: clipkt->ip_proto = 6; +cli_launch.c: clipkt->count = 0; +cli_launch.c: clipkt->ip_dst = str2ip32(argv[0]); +cli_launch.c: clipkt->ip_src_israndom=1; +cli_launch.c: clipkt->dp = valid_port; +cli_launch.c: clipkt->dp_isrange=1; +cli_launch.c: clipkt->dp_start=1; +cli_launch.c: clipkt->dp_stop=1023; +cli_launch.c: clipkt->ndelay.tv_sec = 0; +cli_launch.c: clipkt->ndelay.tv_nsec = 0; +cli_lldp.c: struct mops_ext_lldp * pd = clipkt->p_desc; +cli_lldp.c: struct mops_ext_lldp * pd = clipkt->p_desc; +cli_lldp.c: struct mops_ext_lldp * pd = clipkt->p_desc; +cli_lldp.c: struct mops_ext_lldp * pd = clipkt->p_desc; +cli_packet.c: snprintf(prompt, 16, "pkt-%i",clipkt->id); +cli_packet.c: cli_print(cli, "Allocated new packet %s at slot %i",clipkt->packet_name, clipkt->id); +cli_packet.c: snprintf(prompt, 16, "pkt-%i",clipkt->id); +cli_packet.c: cli_print(cli, "Modify packet parameters for packet %s [%i]",clipkt->packet_name, clipkt->id); +cli_packet.c: clipkt->use_ETHER = 1; +cli_packet.c: clipkt->eth_type = 0x806; +cli_packet.c: sprintf(prompt, "pkt-%i-arp",clipkt->id); +cli_packet.c: clipkt->use_ETHER = 1; +cli_packet.c: clipkt->use_IP= 1; +cli_packet.c: clipkt->use_UDP= 1; +cli_packet.c: sprintf(prompt, "pkt-%i-dns",clipkt->id); +cli_packet.c: clipkt->use_ETHER = 1; +cli_packet.c: clipkt->use_IP= 1; +cli_packet.c: sprintf(prompt, "pkt-%i-icmp",clipkt->id); +cli_packet.c: clipkt->use_ETHER = 1; +cli_packet.c: clipkt->use_IP= 1; +cli_packet.c: clipkt->ip_proto = 2; +cli_packet.c: sprintf(prompt, "pkt-%i-igmp",clipkt->id); +cli_packet.c: clipkt->use_ETHER = 1; +cli_packet.c: sprintf(prompt, "pkt-%i-cdp",clipkt->id); +cli_packet.c: clipkt->use_ETHER = 1; +cli_packet.c: clipkt->use_SNAP = 1; +cli_packet.c: sprintf(prompt, "pkt-%i-bpdu",clipkt->id); +cli_packet.c: clipkt->use_ETHER = 1; +cli_packet.c: clipkt->use_IP = 1; +cli_packet.c: sprintf(prompt, "pkt-%i-ip",clipkt->id); +cli_packet.c: clipkt->use_ETHER = 1; +cli_packet.c: clipkt->use_IP = 1; +cli_packet.c: clipkt->use_UDP = 1; +cli_packet.c: clipkt->ip_proto = 17; +cli_packet.c: sprintf(prompt, "pkt-%i-udp",clipkt->id); +cli_packet.c: clipkt->use_ETHER = 1; +cli_packet.c: clipkt->use_IP = 1; +cli_packet.c: clipkt->use_TCP = 1; +cli_packet.c: clipkt->ip_proto = 6; +cli_packet.c: sprintf(prompt, "pkt-%i-tcp",clipkt->id); +cli_packet.c: clipkt->use_ETHER = 1; +cli_packet.c: clipkt->use_IP = 1; +cli_packet.c: clipkt->use_UDP = 1; +cli_packet.c: sprintf(prompt, "pkt-%i-syslog",clipkt->id); +cli_packet.c: clipkt->use_ETHER = 1; +cli_packet.c: sprintf(prompt, "pkt-%i-lldp",clipkt->id); +cli_packet.c: clipkt->use_ETHER = 1; +cli_packet.c: clipkt->use_IP = 1; +cli_packet.c: clipkt->use_UDP = 1; +cli_packet.c: sprintf(prompt, "pkt-%i-rtp",clipkt->id); +cli_packet.c: strncpy(clipkt->packet_name, argv[0], MAX_MOPS_PACKET_NAME_LEN); +cli_packet.c: clipkt->packet_name[MAX_MOPS_PACKET_NAME_LEN-1] = 0x00; +cli_packet.c:// cli_print(cli, "Changed packet name to '%s'\n", clipkt->packet_name); +cli_packet.c: if (mops_pdesc_mstrings (clipkt->description, argv, argc, MAX_MOPS_DESCRIPTION_LEN)) +cli_packet.c: cli_print(cli, "Current description is:\n%s\n", clipkt->description); +cli_packet.c: clipkt->count = (unsigned long) str2int(argv[0]); +cli_packet.c: if (clipkt->count) +cli_packet.c: clipkt->cntx = clipkt->count; // count is finite: cntx will count down +cli_packet.c: clipkt->cntx = 0; // infinity: cntx will count up +cli_packet.c: ret = delay_parse(&clipkt->ndelay, argv[0], NULL); +cli_packet.c: ret = delay_parse(&clipkt->ndelay, argv[0], argv[1]); +cli_packet.c: sprintf(str, "Inter-packet delay set to %lu sec and %lu nsec", clipkt->ndelay.tv_sec, clipkt->ndelay.tv_nsec); +cli_packet.c: i = mops_get_device_index(clipkt->device); +cli_packet.c: // Copy device_list[i].ip_mops and .mac_mops to clipkt->ip_src and ->eth_src +cli_packet.c: memcpy((void *) &clipkt->eth_src, (void *) &device_list[i].mac_mops[0], 6); +cli_packet.c: memcpy((void *) &clipkt->ip_src, (void *) &device_list[i].ip_mops[0], 4); +cli_packet.c: strncpy(clipkt->device, argv[0], 16); // assign device to this mops +cli_packet.c: cli_print(cli, "Unknown device, will stick on %s\n", clipkt->device); +cli_packet.c: cli_print(cli, "Nothing specified, will stick on %s\n", clipkt->device); +cli_packet.c: n = clipkt->dot1Q_s/4; // n = number of tags +cli_packet.c: if (clipkt->use_MPLS==0) +cli_packet.c: clipkt->eth_type = 0x8847; +cli_packet.c: if (clipkt->use_MPLS==0) +cli_packet.c: clipkt->eth_type = 0x8848; +cli_packet.c: k = clipkt->mpls_s/4; // number of available tags +cli_packet.c: len = str2hex (str, clipkt->msg, MAX_MOPS_MSG_SIZE); +cli_packet.c: clipkt->msg_s = (u_int32_t) len; +cli_packet.c: memcpy((void*) clipkt->msg, (void*) str, len); +cli_packet.c: clipkt->msg_s = len; +cli_packet.c: clipkt->interval_used = 0; +cli_packet.c: if (clipkt->count==0) { +cli_packet.c: clipkt->count=1; +cli_packet.c: if ((clipkt->count * clipkt->ndelay.tv_sec)>tv_sec) { +cli_packet.c: clipkt->interval.tv_sec = tv_sec; +cli_packet.c: clipkt->interval_used = 1; +cli_rtp.c: struct mops_ext_rtp * pd = clipkt->p_desc; +cli_rtp.c: struct mops_ext_rtp * pd = clipkt->p_desc; +cli_rtp.c: struct mops_ext_rtp * pd = clipkt->p_desc; +cli_rtp.c: struct mops_ext_rtp * pd = clipkt->p_desc; +cli_rtp.c: struct mops_ext_rtp * pd = clipkt->p_desc; +cli_rtp.c: struct mops_ext_rtp * pd = clipkt->p_desc; +cli_rtp.c: struct mops_ext_rtp * pd = clipkt->p_desc; +cli_rtp.c: struct mops_ext_rtp * pd = clipkt->p_desc; +cli_rtp.c: struct mops_ext_rtp * pd = clipkt->p_desc; +cli_rtp.c: struct mops_ext_rtp * pd = clipkt->p_desc; +cli_rtp.c:// struct mops_ext_rtp * pd = clipkt->p_desc; +cli_rtp.c: struct mops_ext_rtp * pd = clipkt->p_desc; +cli_tcp.c: clipkt->tcp_seq = (u_int32_t) tmp; +cli_tcp.c: clipkt->tcp_seq_delta = 0; +cli_tcp.c: clipkt->tcp_seq_start = clipkt->tcp_seq; +cli_tcp.c: clipkt->tcp_seq_stop = (u_int32_t) tmp; +cli_tcp.c: clipkt->tcp_seq_delta = 1; +cli_tcp.c: clipkt->tcp_seq_delta = (u_int32_t) tmp; +cli_tcp.c: clipkt->tcp_ack = (u_int32_t) tmp; +cli_tcp.c: clipkt->tcp_ack_delta = 0; +cli_tcp.c: clipkt->tcp_ack_start = clipkt->tcp_ack; +cli_tcp.c: clipkt->tcp_ack_stop = (u_int32_t) tmp; +cli_tcp.c: clipkt->tcp_ack_delta = 1; +cli_tcp.c: clipkt->tcp_ack_delta = (u_int32_t) tmp; +cli_tcp.c: clipkt->tcp_offset = (u_int8_t) tmp; +cli_tcp.c: clipkt->tcp_res = (u_int8_t) tmp; +cli_tcp.c: clipkt->tcp_ctrl_CWR = 0; +cli_tcp.c: clipkt->tcp_ctrl_ECE = 0; +cli_tcp.c: clipkt->tcp_ctrl_URG = 0; +cli_tcp.c: clipkt->tcp_ctrl_ACK = 0; +cli_tcp.c: clipkt->tcp_ctrl_PSH = 0; +cli_tcp.c: clipkt->tcp_ctrl_RST = 0; +cli_tcp.c: clipkt->tcp_ctrl_SYN = 0; +cli_tcp.c: clipkt->tcp_ctrl_FIN = 0; +cli_tcp.c: clipkt->tcp_ctrl_CWR = 1; +cli_tcp.c: clipkt->tcp_ctrl_ECE = 1; +cli_tcp.c: clipkt->tcp_ctrl_URG = 1; +cli_tcp.c: clipkt->tcp_ctrl_ACK = 1; +cli_tcp.c: clipkt->tcp_ctrl_PSH = 1; +cli_tcp.c: clipkt->tcp_ctrl_RST = 1; +cli_tcp.c: clipkt->tcp_ctrl_SYN = 1; +cli_tcp.c: clipkt->tcp_ctrl_FIN = 1; +cli_tcp.c: clipkt->tcp_ctrl_CWR = 1; +cli_tcp.c: clipkt->tcp_ctrl_CWR = 0; +cli_tcp.c: clipkt->tcp_ctrl_ECE = 1; +cli_tcp.c: clipkt->tcp_ctrl_ECE = 0; +cli_tcp.c: clipkt->tcp_ctrl_URG = 1; +cli_tcp.c: clipkt->tcp_ctrl_URG = 0; +cli_tcp.c: clipkt->tcp_ctrl_ACK = 1; +cli_tcp.c: clipkt->tcp_ctrl_ACK = 0; +cli_tcp.c: clipkt->tcp_ctrl_PSH = 1; +cli_tcp.c: clipkt->tcp_ctrl_PSH = 0; +cli_tcp.c: clipkt->tcp_ctrl_RST = 1; +cli_tcp.c: clipkt->tcp_ctrl_RST = 0; +cli_tcp.c: clipkt->tcp_ctrl_SYN = 1; +cli_tcp.c: clipkt->tcp_ctrl_SYN = 0; +cli_tcp.c: clipkt->tcp_ctrl_FIN = 1; +cli_tcp.c: clipkt->tcp_ctrl_FIN = 0; +cli_tcp.c: clipkt->tcp_win = (u_int16_t) tmp; +cli_tcp.c: clipkt->tcp_sum_false=0; +cli_tcp.c: clipkt->tcp_sum = (u_int16_t) sum; +cli_tcp.c: clipkt->tcp_sum_false=1; +cli_tcp.c: clipkt->tcp_urg = (u_int16_t) tmp; +cli_tcp.c: if (clipkt->tcp_option_used) { +cli_tcp.c: clipkt->tcp_option_used = 0; +cli_tcp.c: sprintf(prompt, "pkt-%i",clipkt->id); +cli_udp.c: clipkt->sp_isrand = 1; +cli_udp.c: clipkt->sp_isrange = 0; +cli_udp.c: clipkt->sp_isrand = 0; +cli_udp.c: clipkt->sp= (u_int16_t) t32; +cli_udp.c: clipkt->sp_isrange = 0; +cli_udp.c: clipkt->sp_start = clipkt->sp; +cli_udp.c: clipkt->sp_stop = (u_int16_t) t32; +cli_udp.c: clipkt->sp_isrange = 1; +cli_udp.c: clipkt->dp_isrand = 1; +cli_udp.c: clipkt->dp_isrange = 0; +cli_udp.c: clipkt->dp_isrand = 0; +cli_udp.c: clipkt->dp= (u_int16_t) t32; +cli_udp.c: clipkt->dp_isrange = 0; +cli_udp.c: clipkt->dp_start = clipkt->dp; +cli_udp.c: clipkt->dp_stop = (u_int16_t) t32; +cli_udp.c: clipkt->dp_isrange = 1; +cli_udp.c: clipkt->udp_sum_false=0; +cli_udp.c: clipkt->udp_sum_false=1; +cli_udp.c: clipkt->udp_sum = 0xffff; +cli_udp.c: clipkt->udp_sum = (u_int16_t) sum; +cli_udp.c: clipkt->udp_sum_false=1; +cli_udp.c: sprintf(prompt, "pkt-%i",clipkt->id); +mops_ext.c:// mops_pdesc_mstrings (clipkt->description, argv, argc, 20); diff --git a/src/layer1.c b/src/layer1.c new file mode 100644 index 0000000..f671bb5 --- /dev/null +++ b/src/layer1.c @@ -0,0 +1,383 @@ +/* + * Mausezahn - A fast versatile traffic generator + * Copyright (C) 2008 Herbert Haas + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 as published by the + * Free Software Foundation. + * + * 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, see http://www.gnu.org/licenses/gpl-2.0.html + * +*/ + + + +// **************************************************************************** +// +// This section contains functions to send an arbitrary byte stream out of +// the network card. Currently it works perfect for Ethernet cards. +// +// TODO: Access to the 802.11 header +// +// **************************************************************************** + +#include "mz.h" +#include "cli.h" + +int send_eth() +{ + // Tasks: + // 1. Check 'eth_src_txt' and 'eth_dst_txt' which contain either a MAC address or a keyword + // 'eth_dst' can be set without having 'eth_src_txt' specified (the next 6 bytes of the + // 'arg_string' will be used). But if 'eth_src_txt' is given then also 'eth_dst_txt' + // should have been specified, otherwise a default (ff-ff-ff-ff-ff-ff) will be used. + // 2. Check whether 'arg_string' contains a hex-string. If YES then convert it into an + // 'eth_payload' and extract eth_type. + // 3. Apply 'padding' if specified + // 4. Check if frame has at least minimum length (14 Bytes). + // 5. Send frame 'count' times and + // 6. Apply 'delay' (make precautions for better delay functions) + + int + src, // flag telling whether user has specified a source address + dst, // flag telling whether user has specified a destination address + src_random=0, + dst_random=0, + byte_ptr=1, + bytestring_s=0, + min_size=15, + pad=0, + repeat, loop, update, + i=0, + j=0; + char + err_buf[LIBNET_ERRBUF_SIZE], + message[MAX_PAYLOAD_SIZE*3]; + + u_int8_t bytestring[MAX_PAYLOAD_SIZE]; + libnet_ptag_t t; + libnet_t *l; + + + + if (tx.dot1Q) + { + fprintf(stderr," Note: raw layer 2 mode does not support 802.1Q builder.\n" + " If you want to create VLAN tags then you must do it by hand.\n"); + exit(1); + } + + if (tx.mpls) + { + fprintf(stderr," Note: raw layer 2 mode does not support MPLS builder.\n" + " If you want to create MPLS labels then you must do it by hand.\n"); + exit(1); + } + + + + + // So other functions can use this function for sending Ethernet frames + // These other functions must set dst, src, type and payload! + if (tx.eth_params_already_set) goto ALL_SPECIFIED; + + + if ((tx.padding) && (tx.padding<15)) // Note: ignored if padding==0 + { + tx.padding=15; + if (mz_port) { + cli_print(gcli, "Note: Set padding to 15 bytes (total length)\n"); + } else + fprintf(stderr, " mz/send_eth: [Note] adjusted minimum frame size to 15 bytes.\n"); + } + + // Create a temporal, local bytestring: + // + for (i=0; i=6) { + (void) getbytes (bytestring, tx.eth_src, byte_ptr, byte_ptr+5); + byte_ptr=7; // now points to eth_type within bytestring + } + } + + // FINALLY: If both dst and src have NOT been specified: + // + if ((!dst) && (!src)) + { + if (bytestring_s>=6) { + (void) getbytes (bytestring, tx.eth_dst, byte_ptr, byte_ptr+5); + byte_ptr=7; + } + + if (bytestring_s>=12) { + (void) getbytes (bytestring, tx.eth_src, byte_ptr, byte_ptr+5); + byte_ptr=13; // points to eth_type + } + } + + // Set eth_type: + // + if (bytestring_s>=2) { + tx.eth_type = 256 * bytestring[byte_ptr-1] + bytestring[byte_ptr]; // don't forget: byte_ptr counts from 1 not 0 + byte_ptr+=2; // points to first payload byte (if available) + } + + + // Get remaining payload: + // + if ( (tx.eth_payload_s = bytestring_s - byte_ptr +1) > 0 ) // if there are any remaining bytes + { + (void) getbytes (bytestring, tx.eth_payload, byte_ptr, bytestring_s); + } + + + + // Add padding if desired. + // Note: padding means 'extend to given length' (not 'add these number of bytes') + if (tx.padding) + { + pad = tx.padding - (14 + tx.eth_payload_s); // number of additonal pad bytes required + for (i=0; i []\n" \ + "| | |\n" \ + "| help, request, reply --+ |\n" \ + "| +-- sendermac, senderip, targetmac, targetip\n" \ + "| smac sip tmac tip\n" \ + "|\n" \ + "| EXAMPLES:\n" \ + "| 1. Legitimate ARP response to broadcast:\n" \ + "| # mz eth0 -t arp \"reply\"\n" \ + "| 2. ARP cache poisoning, claiming to be 192.168.0.1, telling a target PC:\n" \ + "| # mz eth0 -t arp \"reply, senderip=192.168.0.1, targetmac=00:00:0c:01:02:03, targetip=172.16.1.50\"\n" \ + "\n" + + +#define MZ_BPDU_HELP \ + "| BPDU type: Send arbitrary BPDU packets (spanning tree).\n" \ + "|\n" \ + "| ARGUMENT SYNTAX: []\n" \ + "| | \n" \ + "| conf, tcn --+ \n" \ + "| \n" \ + "| Parameters:\n" \ + "|\n" \ + "| id = 0-65535 ..... default: 0, identifies 'Spanning Tree Protocol'\n" \ + "| version = 0-255 ..... default: 0\n" \ + "| type = 0-255 ..... BPDU Type: 0=CONF, 1=TCN (default: CONF)\n" \ + "| flags = 0-255 ..... 1=TC, 128=ACK (default: 0 = No TC, No ACK)\n" \ + "| rootid = : ..... 8 byte Root-ID (default: 00:00:)\n" \ + "| rootpc = 0-4294967295 ..... root path cost (default: 0)\n" \ + "| bid = ..... 6 byte MAC address (default: own-mac)\n" \ + "| pid = 0-65535 ..... port identifier (default: 0)\n" \ + "| age = 0-65535 ..... message age (default: 0)\n" \ + "| maxage = 0-65535 ..... max age (default: 20)\n" \ + "| hello = 0-65535 ..... hello time (default: 2)\n" \ + "| fwd = 0-65535 ..... forward delay (default: 15)\n" \ + "| tag - ..... Keyword to enforce 802.1Q VLAN tag; use this\n" \ + "| together with the 'vlan' parameter below.\n" \ + "|\n" \ + "| PVST+ extensions:\n" \ + "|\n" \ + "| vlan ..... VLAN number (default: 0)\n" \ + "| pri ..... 802.1P-Priority (0-7, default: 0)\n" \ + "| notag ..... Omit 802.1Q VLAN tag\n" \ + "| \n" \ + "|\n" \ + "| DEFAULTS: mz sends standard IEEE 802.1d (CST) BPDUs and assumes that your computer\n" \ + "| wants to become the root bridge (rid=bid). Configuration BPDUs are the default but\n" \ + "| can be changed using the 'tcn' keyword. Optionally the 802.3 source and destination\n" \ + "| MAC addresses can be specified using the -a and -b options. Per default, the correct\n" \ + "| STP or PVST+ destination addresses are used (same as '-b stp' or '-b pvst', \n" \ + "| respectively).\n" \ + "| \n" \ + "| Note that the parameter 'vlan' only selects the PVST+ mode if the parameter 'tag' is\n" \ + "| NOT used.\n" \ + "\n" + + + +// Send arbitrary ARP packets. +// Note: +// - The Ethernet dst and src MAC addresses can be specified, +// the eth_src_txt can be 'rand' +// - If eth_dst and eth_src are NOT specified then practical defaults are used +// +// arg_string syntax: , , ... , +// - commands: 'request' OR 'reply' +// - params: 'sendermac', 'senderip', 'targetmac', 'targetip' +// +// Example arg_string for ARP cache poisoning: +// "reply, senderip=192.168.0.1, targetmac=00:00:0c:01:02:03, targetip=172.16.1.50" +// where sendermac will be automatically replaced by own mac, +// senderip is the spoofed IP, +// targetmac and targetip identifies the receiver. +// +int send_arp () +{ + libnet_t *l; + libnet_ptag_t t; + + char + argval[64], + t1[64], + t2[64], + src, + dst, + errbuf[LIBNET_ERRBUF_SIZE]; + + int + i, + arpmode=0, + arpop=0, + loop, + tm=0; + + u_int8_t + *packet, + sendermac[6], + targetmac[6]; + + + + u_int32_t + packet_s, + senderip=0, + targetip=0; + + + if (tx.dot1Q) + { + fprintf(stderr," Note: ARP mode does not support 802.1Q builder.\n"); + exit(1); + } + + if (tx.mpls) + { + fprintf(stderr," Note: ARP mode does not support MPLS builder.\n"); + exit(1); + } + + if (getarg(tx.arg_string,"help", NULL)==1) + { + if (mz_port) + { + cli_print(gcli, "%s", MZ_ARP_HELP); + return -1; + } + else + { + fprintf(stderr,"\n" + MAUSEZAHN_VERSION + "\n%s", MZ_ARP_HELP); + exit(0); + } + } + + + // Set the flags to shorten subsequent decisions: + src = strlen(tx.eth_src_txt); + dst = strlen(tx.eth_dst_txt); + + l = libnet_init(LIBNET_LINK_ADV, tx.device, errbuf); + + if (l == NULL) + { + fprintf(stderr, "%s", errbuf); + exit(EXIT_FAILURE); + } + + + + if (getarg(tx.arg_string,"request", NULL)==1) + { + arpmode=1; + arpop = ARPOP_REQUEST; + } + else + if (getarg(tx.arg_string, "reply", NULL)==1) + { + arpmode=2; + arpop = ARPOP_REPLY; + } + else + { // Default: + arpmode=2; + arpop = ARPOP_REPLY; + } + + + + if ( (getarg(tx.arg_string,"sendermac", argval)==1) || (getarg(tx.arg_string,"smac", argval)==1) ) + { + //TODO: Allow 'rand' as sendermac + str2hex(argval,sendermac,6); + } + else + { + // sendermac is usually ALWAYS own MAC: + getbytes(tx.eth_src, sendermac,1,6); + } + + + if ( (getarg(tx.arg_string,"targetmac", argval)==1) || (getarg(tx.arg_string,"tmac", argval)==1) ) + { + str2hex(argval,targetmac,6); + tm=1; + } + else + { + // targetmac is either zero (request) or bcast (reply=>gratitious ARP) + if (arpmode==1) //request + str2hex("00:00:00:00:00:00",targetmac, 6); + else //reply + str2hex("ff:ff:ff:ff:ff:ff",targetmac, 6); + } + + + if ( (getarg(tx.arg_string,"senderip", argval)==1) || (getarg(tx.arg_string,"sip", argval)==1) ) + { + senderip = str2ip32_rev(argval); + } + else + { + // senderip is usually ALWAYS the own IP + senderip = libnet_get_ipaddr4(l); // TODO - use tx.ip_src + } + + + + if ( (getarg(tx.arg_string,"targetip", argval)==1) || (getarg(tx.arg_string,"tip", argval)==1) ) + { + targetip = str2ip32_rev(argval); + } + else + { + // if targetip is missing also use own IP because it may be used for duplicate IP detection + targetip = libnet_get_ipaddr4(l); + } + + + + // NOTE: Now all ARP parameters are set (possibly defaults used!) + + bs2str(sendermac,t1,6); + bs2str(targetmac,t2,6); + //Check: + //printf("-- sendermac=%s targetmac=%s senderip=%u targetip=%u\n",t1,t2,senderip,targetip); + + + + // Build the ARP header + + t = libnet_autobuild_arp(arpop, /* operation type */ + sendermac, /* sender hardware addr */ + (u_int8_t *)&senderip, /* sender protocol addr */ + targetmac, /* target hardware addr */ + (u_int8_t *)&targetip, /* target protocol addr */ + l); /* libnet context */ + + if (t == -1) + { + fprintf(stderr, " mz/send_arp: Can't build ARP header: %s\n", libnet_geterror(l)); + exit(EXIT_FAILURE); + } + + + // Finally build the Ethernet header + + if ((!dst) && (!src)) // ... user does not care about addresses (both eth_dst and eth_src NOT specified) + { + if (arpmode==1) + str2hex("ff:ff:ff:ff:ff:ff", tx.eth_dst, 6); + else + getbytes(targetmac, tx.eth_dst, 1, 6); // either also bcast or specific MAC + + t = libnet_autobuild_ethernet(tx.eth_dst, /* ethernet destination */ + ETHERTYPE_ARP, /* protocol type */ + l); /* libnet handle */ + + if (t == -1) + { + fprintf(stderr, " mz/send_arp: Can't build ethernet header: %s\n", + libnet_geterror(l)); + exit(EXIT_FAILURE); + } + } + else // EITHER eth_dst OR eth_src OR BOTH specified: + { + if (!dst) + { + if (arpmode==1) + str2hex("ff:ff:ff:ff:ff:ff", tx.eth_dst, 6); + else + getbytes(targetmac, tx.eth_dst, 1, 6); // either also bcast when reply or specific MAC + } + else // eth_dst specified + { + if (check_eth_mac_txt(ETH_DST)) // if true then problem! + { + str2hex("ff:ff:ff:ff:ff:ff",tx.eth_dst, 6); // the default + } + } + + + if (!src) + { + // tx.eth_src contains own MAC by default! + } + else // use specified source MAC address + { + if (check_eth_mac_txt(ETH_SRC)) // if true then problem! + { + str2hex("ff:ff:ff:ff:ff:ff",tx.eth_src, 6); // the default + } + } + + t = libnet_build_ethernet (tx.eth_dst, tx.eth_src, ETHERTYPE_ARP, NULL, 0, l, 0); // Note: payload=NULL, payload_s=0 + } + + if (libnet_adv_cull_packet(l, &packet, &packet_s) == -1) + { + fprintf(stderr, "%s", libnet_geterror(l)); + } + else + { + libnet_adv_free_packet(l, packet); + } + + // this is for the statistics: + mz_start = clock(); + total_d = tx.count; + + + again: + + if (tx.count==0) + loop=1000000; + else + loop=tx.count; + + for (i=1; i<=loop; i++) + { + + if (!simulate) libnet_write(l); + + if (verbose) + { + fprintf(stderr," sent ARP: %s smac=%s sip=%s tmac=%s tip=%s\n", + (arpmode==1) ? "request" : "reply", + t1, + libnet_addr2name4(senderip,LIBNET_DONT_RESOLVE), + t2, + libnet_addr2name4(targetip,LIBNET_DONT_RESOLVE)); + } + + + if (tx.delay) SLEEP (tx.delay); + } + + if (tx.count==0) + { + goto again; + } + + + libnet_destroy(l); + + return 0; +} + + + + + + + + + + + + + + + +/////////////////////////////////////////////////////////////////////////////////////// +// Send arbitrary BPDU frames. +// +// commands: +// conf|tcn ...when specifying everything yourself +// +// params: +// id, version, type, flags, rootid, rootpc, bid, pid, age, maxage, hello, fwd, +// vlan +// +// defaults: +// mz assumes you want to become root bridge! (rid=bid) +// +int send_bpdu () +{ + + // BPDU parameters: + u_int16_t + id=0; + u_int8_t + version=0, + bpdu_type=0, // 0=conf, 1=topology change (actually in big endian!) + flags=0, // 1=TC, 128=TCAck + root_id[8], // Root BID + bridge_id[8]; // Own BID + u_int32_t + root_pc=0; // Root Path Cost + u_int16_t + port_id=0, // Port Identifier + message_age=0, // All timers are multiples of 1/256 sec. Thus times range from 0 to 256 seconds. + max_age=20, + hello_time=2, // + f_delay=15; + + // LLC Parameters: + u_int8_t + dsap=0x42, + ssap=0x42, + control=0x3; + + // Optional payload (needed for PVST+) + u_int8_t + bpdu_payload[64], + snap_oui[3]; + u_int32_t + bpdu_payload_s=0; + u_int16_t + vlan=0; + u_int8_t + priority=0x00, + *x; + int + tag=0; + + + // Standard libnet variables: + libnet_t *l; + libnet_ptag_t t; + char errbuf[LIBNET_ERRBUF_SIZE]; + + // Other variables: + unsigned int i, loop; + int bpdumode=0; + char argval[64]; + char dum1[32], dum2[32]; + + + if (tx.dot1Q) + { + fprintf(stderr," Note: BPDU mode does not support 802.1Q builder.\n"); + exit(1); + } + + if (tx.mpls) + { + fprintf(stderr," Note: BPDU mode does not support MPLS builder.\n"); + exit(1); + } + + + // HELP TEXT + if (getarg(tx.arg_string,"help", NULL)==1) + { + if (mz_port) + { + cli_print(gcli, "%s", MZ_BPDU_HELP); + return -1; + } + else + { + fprintf(stderr,"\n" + MAUSEZAHN_VERSION + "\n%s", MZ_BPDU_HELP); + exit(0); + } + } + + ///////////////////////////////////////////////////////// + // Default Destination Address + if (check_eth_mac_txt(ETH_DST)) // if true then problem! + { + str2hex("01:80:C2:00:00:00",tx.eth_dst, 6); // if '1' then user did not set MAC address (or problem occurred) + } + + // Default Bridge-ID + bridge_id[0]=0x00; + bridge_id[1]=0x00; + for (i=0; i<6; i++) bridge_id[2+i]=tx.eth_src[i]; + for (i=0; i<8; i++) root_id[i]=bridge_id[i]; + ///////////////////////////////////////////////////////// + + + + + // determine BPDU type: + if (getarg(tx.arg_string,"conf", NULL)==1) + { + bpdumode=1; + tx.eth_len = LIBNET_802_2_H + LIBNET_STP_CONF_H; + } + else + if (getarg(tx.arg_string, "tcn", NULL)==1) + { + bpdumode=2; + tx.eth_len = LIBNET_802_2_H + LIBNET_STP_TCN_H; + bpdu_type=0x80; + } + else // default + { + bpdumode=1; + tx.eth_len = LIBNET_802_2_H + LIBNET_STP_CONF_H; + } + + +// Commands summary: +// id, version, type, flags, rid, rootpc, bid, pid, age, maxage, hello, fwd + + if (getarg(tx.arg_string,"id", argval)==1) + { + id = (u_int16_t) str2int(argval); + } + + if (getarg(tx.arg_string,"version", argval)==1) + { + version = (u_int8_t) str2int(argval); + } + + if (getarg(tx.arg_string,"bpdu_type", argval)==1) + { + bpdu_type = (u_int8_t) str2int(argval); + } + + if (getarg(tx.arg_string,"flags", argval)==1) + { + flags = (u_int8_t) str2int(argval); + } + + if (getarg(tx.arg_string,"rid", argval)==1) + { + if (str2hex(argval,root_id, 8)!=8) + { + fprintf(stderr," mz/send_bpdu: [ERROR] The root-id must be exactly 8 bytes!\n"); + exit (-1); + } + } + + if (getarg(tx.arg_string,"rootpc", argval)==1) + { + root_pc = (u_int32_t) str2int(argval); + } + + if (getarg(tx.arg_string,"bid", argval)==1) + { + if (str2hex(argval,bridge_id, 6)!=6) + { + fprintf(stderr," mz/send_bpdu: [ERROR] The bridge-id must be exactly 6 bytes!\n"); + exit (-1); + } + } + + if (getarg(tx.arg_string,"pid", argval)==1) + { + port_id = (u_int16_t) str2int(argval); + } + + if (getarg(tx.arg_string,"age", argval)==1) + { + message_age = (u_int16_t) str2int(argval); + } + + if (getarg(tx.arg_string,"maxage", argval)==1) + { + max_age = (u_int16_t) str2int(argval); + } + + if (getarg(tx.arg_string,"hello", argval)==1) + { + hello_time = (u_int16_t) str2int(argval); + } + + if (getarg(tx.arg_string,"fwd", argval)==1) + { + f_delay = (u_int16_t) str2int(argval); + } + + + + if (getarg(tx.arg_string,"vlan", argval)==1) + { + // PVST+ uses TLVs of type=0x00, len=0x02, and Value=0xVV which is the VLAN ID + // The DA must be 0100.0ccc.cccd instead of the standard 0180.c200.0000 + // + if (check_eth_mac_txt(ETH_DST)) // if '1' then user did not set MAC address (or problem occurred) + { + str2hex("01:00:0C:CC:CC:CD",tx.eth_dst, 6); // Cisco PVST+ address + } + +/* // OLD TLV, maybe wrong, maybe obsolete, I don't know. + + bpdu_payload[0] = 0x34; + bpdu_payload[1] = 0x00; + bpdu_payload[2] = 0x02; + vlan = (u_int16_t) str2int(argval); + + x = (u_int8_t*) &vlan; + bpdu_payload[3] = *(x+1); + bpdu_payload[4] = *(x); + bpdu_payload[5] = 0x00; + bpdu_payload[6] = 0x00; + bpdu_payload_s = 7; +*/ + // Updated PVST+ TLV: + bpdu_payload[0] = 0x00; + bpdu_payload[1] = 0x00; + bpdu_payload[2] = 0x00; + bpdu_payload[3] = 0x00; + bpdu_payload[4] = 0x02; + vlan = (u_int16_t) str2int(argval); + x = (u_int8_t*) &vlan; + bpdu_payload[5] = *(x+1); + bpdu_payload[6] = *(x); + bpdu_payload_s = 7; + + tag=1; // set the default: Use 802.1Q tag !!! + } + else // even a normal BPDU must be padded to 60 bytes (total) + { + bpdu_payload[0] = 0x00; + bpdu_payload[1] = 0x00; + bpdu_payload[2] = 0x00; + bpdu_payload[3] = 0x00; + bpdu_payload[4] = 0x00; + bpdu_payload[5] = 0x00; + bpdu_payload[6] = 0x00; + bpdu_payload[7] = 0x00; + bpdu_payload_s = 8; + + tag=0; // set the default: send untagged !!! + } + + + // Note: The order is important because above the defaults for 'tag' has been set. + // + if (getarg(tx.arg_string,"notag", NULL)==1) + { + tag=0; + } + + + // Send normal BPDU with VLAN tag + if (getarg(tx.arg_string,"tag", NULL)==1) + { + tag=2; + bpdu_payload[0] = 0x00; + bpdu_payload[1] = 0x00; + bpdu_payload[2] = 0x00; + bpdu_payload[3] = 0x00; + bpdu_payload[4] = 0x00; + bpdu_payload[5] = 0x00; + bpdu_payload[6] = 0x00; + bpdu_payload[7] = 0x00; + bpdu_payload_s = 8; + + // Rewrite to standard 0180.c200.0000 + // + if (check_eth_mac_txt(ETH_DST)) // if '1' then user did not set MAC address (or problem occurred) + { + str2hex("01:80:C2:00:00:00",tx.eth_dst, 6); + } + vlan = (u_int16_t) str2int(argval); + } + + + if (getarg(tx.arg_string,"pri", argval)==1) + { + priority = (u_int8_t) str2int(argval); + if (priority>7) + { + fprintf(stderr, " mz/send_bpdu: Priority must be between 0 and 7.\n"); + exit(1); + } + + if (tag==0) + { + fprintf(stderr, " mz/send_bpdu: Priority cannot be used together with the 'notag' keyword.\n"); + exit(1); + } + } + + + // Open the link - get libnet handle + l = libnet_init(LIBNET_LINK_ADV, tx.device, errbuf); + + if (l == NULL) + { + fprintf(stderr, "%s", errbuf); + exit(EXIT_FAILURE); + } + + + if (bpdumode==1) // Prepare CONFIGURATION BPDU: + { + + t = libnet_build_stp_conf (id, + version, + bpdu_type, + flags, + root_id, + root_pc, + bridge_id, + port_id, + message_age, + max_age, + hello_time, + f_delay, + (bpdu_payload_s) ? bpdu_payload : NULL, + bpdu_payload_s, + l, + 0); + + if (t == -1) + { + fprintf(stderr, " mz/send_bpdu: Can't build BPDU header: %s\n", + libnet_geterror(l)); + exit(EXIT_FAILURE); + } + } + else // Topology Change BPDU + { + t = libnet_build_stp_tcn(id, + version, + bpdu_type, + (bpdu_payload_s) ? bpdu_payload : NULL, + bpdu_payload_s, + l, + 0); + if (t == -1) + { + + fprintf(stderr, " mz/send_bpdu: Can't build BPDU header: %s\n", + libnet_geterror(l)); + exit(EXIT_FAILURE); + } + } + + + + if ( (vlan==0) || (tag==2) ) // normal BPDU + { + // normal LLC without SNAP + t = libnet_build_802_2 (dsap, + ssap, + control, + NULL, + 0, + l, + 0); + + if (t == -1) + { + fprintf(stderr, " mz/send_bpdu: Can't build LLC header: %s\n", + libnet_geterror(l)); + exit(EXIT_FAILURE); + } + } + else // PVST+ => LLC with SNAP + { + snap_oui[0]=0x00; + snap_oui[1]=0x00; + snap_oui[2]=0x0c; + + // requires a SNAP header with oui=0x00000c and type=0x010b + t = libnet_build_802_2snap(0xAA, + 0xAA, + 0x03, + snap_oui, + 0x010b, + NULL, + 0, + l, + 0); + + if (t == -1) + { + fprintf(stderr, " mz/send_bpdu: Can't build SNAP header: %s\n", + libnet_geterror(l)); + exit(EXIT_FAILURE); + } + } + + + if (tag==0) + { + // Normal 802.3 header without VLAN tag + t = libnet_build_802_3 (tx.eth_dst, + tx.eth_src, + (vlan) ? 0x36 : tx.eth_len, // NOTE the LENGTH field => 802.3 header! + NULL, + 0, + l, + 0); + + } + else // PVST+ => 802.3 with 802.1Q + { + t = libnet_build_802_1q(tx.eth_dst, + tx.eth_src, + 0x8100, + priority, + 0x00, // CFI + vlan, + 0x32, //tx.eth_len, + NULL, + 0, + l, + 0); + } + + + if (t == -1) + { + fprintf(stderr, " mz/send_bpdu: Can't build 802.3 header: %s\n", + libnet_geterror(l)); + exit(EXIT_FAILURE); + } + + + // This is ugly but it works good ;-) + if (tx.count==0) + loop=1000000; + else + loop=tx.count; + + // this is for the statistics: + mz_start = clock(); + total_d = tx.count; + + + again: + + for (i=1; i<=loop; i++) + { + if (!simulate) libnet_write(l); + + if (verbose) + { + bs2str(root_id,dum1,8); + bs2str(bridge_id,dum2,8); + fprintf(stderr," sent BPDU: "); + fprintf(stderr,"%s ", (bpdumode==1) ? "conf" : "tcn "); + fprintf(stderr," id=%u ver=%u flags=%x rid=%s bid=%s\n" + " rpc=%u pid=%u age=%u maxage=%u hello=%u fwd_delay=%u\n", + id, + version, + flags, + dum1, + dum2, + root_pc, + port_id, + message_age, + max_age, + hello_time, + f_delay); + + fprintf(stderr,"\n"); + } + + + if (tx.delay) SLEEP (tx.delay); + } + + if (tx.count==0) + { + goto again; + } + + + libnet_destroy(l); + + return 0; +} + diff --git a/src/layer3.c b/src/layer3.c new file mode 100644 index 0000000..a928383 --- /dev/null +++ b/src/layer3.c @@ -0,0 +1,417 @@ +/* + * Mausezahn - A fast versatile traffic generator + * Copyright (C) 2008-2010 Herbert Haas + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 as published by the + * Free Software Foundation. + * + * 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, see http://www.gnu.org/licenses/gpl-2.0.html + * +*/ + + + +// *************************************************************************** +// This sections contains functions to send various L3-based PDUs such as +// +// * IP +// +// (ahem, yes this is currently all here...) +// +// *************************************************************************** + +#include "mz.h" +#include "cli.h" + +#define MZ_IP_HELP \ + "| IP type: Send raw IP packets.\n" \ + "|\n" \ + "| Supports L3 mode (automatic L2 creation) or 'L2-L3' mode (MAC addresses must be provided).\n" \ + "| In L3 mode the IP checksum and length cannot be manipulated to wrong values (currently).\n" \ + "| The L2-L3 mode is activated when specifying any MAC addresses on the command line\n" \ + "| (options -a, -b). \n" \ + "|\n" \ + "| The IP addresses can be specified via the -A and -B options, which identify the source\n" \ + "| and destination addresses, respectively. A dotted decimal notation, an IP range, or a\n" \ + "| FQDN can be used. The source address can also be random (-A rand).\n" \ + "|\n" \ + "| ARGUMENT SYNTAX: []\n" \ + "|\n" \ + "| Parameters:\n" \ + "|\n" \ + "| len 0-65535 Only accessible in L2 mode\n" \ + "| sum 0-65535 Only accessible in L2 mode (0 means auto-calculation)\n" \ + "| tos 00-ff Full 8-bit control via hex input (use this also for ECN bits).\n" \ + "| dscp 0-63 Allows easier specification of DSCP (PHB and Drop Propability)\n" \ + "| ttl 0-255\n" \ + "| proto 0-255\n" \ + "| frag 0-65535 Includes flags (MSB) and offset (LSB)\n" \ + "| df Sets the \"Don't Fragment\" flag\n" \ + "| mf Sets the \"More Fragments\" flag\n" \ + "| rf Sets the reserved flag.\n" \ + "| id 0-65535\n" \ + "| loose Loose Source Route (LSR) option; specify a sequence of hops\n" \ + "| using the notation: 1.1.1.1+2.2.2.2+3.3.3.3+...\n" \ + "| strict Strict Source Route (SSR) option; same address notation as above\n" \ + "| option Specify any IP option using a hexadecimal string (aa:bb:cc:...)\n" \ + "|\n" \ + "| Additionally the Ethertype can be specified:\n" \ + "|\n" \ + "| type 00:00-ff:ff Only accessible in L2 mode (default = 08:00 = IP)\n" \ + "| \n" + + + +// Only used to simplify initialization of libnet +// Return pointer to context +libnet_t* get_link_context() +{ + libnet_t * l; + char errbuf[LIBNET_ERRBUF_SIZE]; + + // Don't open context if only a help text is requested + if (getarg(tx.arg_string,"help", NULL)==1) + { + return NULL; + } + + + if (tx.packet_mode) + { // Let libnet create an appropriate Ethernet frame + l = libnet_init (LIBNET_RAW4_ADV, tx.device, errbuf); + } + else // User specified Ethernet header details (src or dst) + { + l = libnet_init (LIBNET_LINK_ADV, tx.device, errbuf); + } + + if (l == NULL) + { + fprintf(stderr, "%s", errbuf); + exit(EXIT_FAILURE); + } + return l; +} + + +////////////////////////////////////////////////////////////////////////////// +// Prepare IP packet +libnet_ptag_t create_ip_packet (libnet_t *l) +{ + libnet_ptag_t t; + char argval[MAX_PAYLOAD_SIZE]; + int i, T; // only an abbreviation for tx.packet_mode + + // Default IP header fields + tx.ip_len = LIBNET_IPV4_H; // Don't forget to add payload length + tx.ip_id = 0; + tx.ip_frag = 0; // Flags and Offset !!! + tx.ip_sum = 0; // default: automatically calculate checksum + tx.ip_tos = 0; + tx.ip_ttl = 255; + + + // temporary variables + unsigned int dummy; + size_t len; + char *s; + + + T = tx.packet_mode; // >0 means automatic L2 creation + + if ( (getarg(tx.arg_string,"help", NULL)==1) && (mode==IP) ) + { + if (mz_port) + { + cli_print(gcli, "%s", MZ_IP_HELP); + return -1; + } + else + { + + fprintf(stderr,"\n" + MAUSEZAHN_VERSION + "\n%s", MZ_IP_HELP); + + exit(0); + } + } + + // Check if hex_payload already specified (externally) + if (tx.hex_payload_s) + { + memcpy( (void*) tx.ip_payload, (void*) tx.hex_payload, tx.hex_payload_s); + tx.ip_payload_s = tx.hex_payload_s; + } + + + // Evaluate CLI parameters: + + if ( (getarg(tx.arg_string,"payload", argval)==1) || (getarg(tx.arg_string,"p", argval)==1)) + { + tx.ip_payload_s = str2hex (argval, tx.ip_payload, MAX_PAYLOAD_SIZE); + } + // else payload has been specified as ASCII text via -P option + + + // NOTE: If 'mode' is NOT IP (e. g. UDP or TCP or something else) + // then the argument 'len' and 'sum' is NOT meant for the IP header! + // Instead the user can use 'iplen' and 'ipsum'. + if (mode==IP) + { + if (getarg(tx.arg_string,"len", argval)==1) + { + if (T) fprintf(stderr, " IP_Warning: 'len' cannot be set in this mode.\n"); + tx.ip_len = (u_int16_t) str2int(argval); + } + else + { + tx.ip_len = LIBNET_IPV4_H + tx.ip_payload_s; + } + + if (getarg(tx.arg_string,"sum", argval)==1) + { + if (T) fprintf(stderr, " IP_Warning: 'sum' cannot be set in this mode.\n"); + tx.ip_sum = (u_int16_t) str2int(argval); + } + } + else // mode is NOT IP + { + if (getarg(tx.arg_string,"iplen", argval)==1) + { + if (T) fprintf(stderr, " IP_Warning: 'len' cannot be set in this mode.\n"); + tx.ip_len = (u_int16_t) str2int(argval); + } + else + { + tx.ip_len = LIBNET_IPV4_H + tx.ip_payload_s; + } + + if (getarg(tx.arg_string,"ipsum", argval)==1) + { + if (T) fprintf(stderr, " IP_Warning: 'sum' cannot be set in this mode.\n"); + tx.ip_sum = (u_int16_t) str2int(argval); + } + } + + + if (getarg(tx.arg_string,"tos", argval)==1) + { + tx.ip_tos = (u_int8_t) strtol(argval,NULL,16); + dummy = (unsigned int) strtol(argval,NULL,16); + if (dummy > 255) fprintf(stderr, " IP_Warning: 'tos' too big, adjusted to LSBs\n"); + } + + if (getarg(tx.arg_string,"dscp", argval)==1) + { + dummy = (unsigned int) str2int(argval); + if (dummy > 63) + { + fprintf(stderr, " IP_Warning: 'dscp' too big, adjusted to 63\n"); + dummy = 63; + } + tx.ip_tos = (u_int8_t) dummy*4; + } + + if (getarg(tx.arg_string,"id", argval)==1) + { + tx.ip_id = (u_int16_t) str2int(argval); + } + + if (getarg(tx.arg_string,"frag", argval)==1) + { + tx.ip_frag = (u_int16_t) str2int(argval); + } + + if (getarg(tx.arg_string,"df", NULL)==1) + { + tx.ip_frag |= 0x4000; + } + + if (getarg(tx.arg_string,"mf", NULL)==1) + { + tx.ip_frag |= 0x2000; + } + + if (getarg(tx.arg_string,"rf", NULL)==1) + { + tx.ip_frag |= 0x8000; + } + + + if (getarg(tx.arg_string,"ttl", argval)==1) + { + tx.ip_ttl = (u_int8_t) str2int(argval); + } + + if (getarg(tx.arg_string,"proto", argval)==1) + { + tx.ip_proto = (u_int8_t) str2int(argval); + } + + + if ((tx.ascii)&&(mode==IP)) // ASCII PAYLOAD overrides hex payload + { + strncpy((char *)tx.ip_payload, (char *)tx.ascii_payload, MAX_PAYLOAD_SIZE); + tx.ip_payload_s = strlen((char *)tx.ascii_payload); + tx.ip_len += tx.ip_payload_s; + } + + + ///////// + // Want some padding? The specified number of padding bytes are ADDED to the + // payload. Note that this is only evaluated if we are in IP mode because + // UDP and TCP already might have been padded and set the ip_payload_s. + // (Note the difference in send_eth() where you specified the total number + // of bytes in the frame) + // + if ((tx.padding)&&(mode==IP)) + { + for (i=0; i"); + do + { + len--; + tx.ip_option[tx.ip_option_s] = (u_int8_t) str2int(s); + tx.ip_option_s++; + } while ( (s=strtok(NULL, ".+-:;/>")) != NULL ); + + // add empty space for record route: //// NONSENSE? ///// + /* + for (i=0; i<(4*dummy); i++) + { + tx.ip_option[tx.ip_option_s] = 0x00; + tx.ip_option_s++; + } + */ + } + + + + // Allow any IP option specified as hex string + // An option can be a single byte or consist of multiple bytes in which case + // a length field is needed, see RFC 791. + if (getarg(tx.arg_string,"option", argval)==1) + { + // check if conflicting with argument "loose" or "strict" + if (tx.ip_option_s) + { + fprintf(stderr, " IP_Error: Another IP option already specified. Please check your arguments.\n"); + exit(1); + } + + tx.ip_option_s = str2hex (argval, tx.ip_option, 1023); + } + + + + if (tx.ip_option_s) + { + t = libnet_build_ipv4_options (tx.ip_option, + tx.ip_option_s, + l, + 0); + } + + + /////// + // Did the user specify ANY payload? We require at least one byte! + /* + if (!tx.ip_payload_s) + { + tx.ip_payload[0] = 0x42; + tx.ip_payload_s = 1; + } + */ + + t = libnet_build_ipv4 (tx.ip_len, + tx.ip_tos, + tx.ip_id, + tx.ip_frag, + tx.ip_ttl, + tx.ip_proto, + tx.ip_sum, + tx.ip_src, // init.c defaults this to own SA + tx.ip_dst, // init.c defaults this to 255.255.255.255 + (mode==IP) ? (tx.ip_payload_s) ? tx.ip_payload : NULL : NULL, // if e.g. mode=UDP ignore payload argument + (mode==IP) ? tx.ip_payload_s : 0, + + /* + (mode==IP) ? tx.ip_payload : NULL, // if e.g. mode=UDP ignore payload argument + (mode==IP) ? tx.ip_payload_s : 0, + */ + l, + 0); + + + if (t == -1) + { + fprintf(stderr, " mz/create_ip_packet: Can't build IP header: %s\n", libnet_geterror(l)); + exit (0); + } + + + return t; + +} + + + + + + diff --git a/src/layer4.c b/src/layer4.c new file mode 100644 index 0000000..55a73b6 --- /dev/null +++ b/src/layer4.c @@ -0,0 +1,731 @@ +/* + * Mausezahn - A fast versatile traffic generator + * Copyright (C) 2008-2010 Herbert Haas + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 as published by the + * Free Software Foundation. + * + * 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, see http://www.gnu.org/licenses/gpl-2.0.html + * +*/ + + + +//////////////////////////////////////////////////////////////////// +// +// Layer 4 packet types +// +// 1. UDP +// 2. ICMP +// 3. TCP +// +//////////////////////////////////////////////////////////////////// + +#include "mz.h" +#include "cli.h" + + +#define MZ_UDP_HELP \ + "| UDP type: Send raw UDP packets.\n" \ + "|\n" \ + "| Parameters: \n" \ + "|\n" \ + "| sp 0-65535\n" \ + "| dp 0-65535\n" \ + "| len 0-65535\n" \ + "| sum 0-65535\n" \ + "| payload|p \n" \ + "|\n" \ + "| Optionally the port numbers can be specified as ranges, e. g. \"dp=1023-33700\",\n" \ + "| in which case one packet per port number is sent.\n" \ + "|\n" \ + "| Note that the UDP length must include the header length. If you do NOT specify the len\n" \ + "| parameter (or specify len=0) then Mausezahn will compute the correct length.\n" \ + "|\n" \ + "| Note that all IP parameters can be modified (see IP help, i. e. '-t ip \"help\")\n" \ + "| except that (to avoid confusion) the IP length is 'iplen' and the IP checksum is 'ipsum'.\n" \ + "| Of course all Ethernet fields can also be accessed.\n" \ + "|\n" \ + "\n" + + +#define MZ_ICMP_HELP \ + "| ICMP type: Send raw ICMP packets.\n" \ + "|\n" \ + "| ARGUMENT SYNTAX: [type] \n" \ + "| \n" \ + "| Per default an echo reply is sent (type=0, code=0)\n" \ + "|\n" \ + "| TYPE OPTIONAL PARAMETERS\n" \ + "| =========== ====================================================================\n" \ + "| Ping: \"ping\" or \"echoreq\" \n" \ + "| 'id' (0-65535) is the optional identification number\n" \ + "| 'seq' (0-65535) is the optional packet sequence number\n" \ + "|\n" \ + "| Redirect: \"redir, code=0, gw=192.168.1.10, p=aa:bb:cc\"\n" \ + "| 'gw' (or 'gateway') is the announced gateway, by default your own\n" \ + "| IP address.\n" \ + "| 'code' can be:\n" \ + "| 0 ... redirect datagram for the network\n" \ + "| 1 ... redirect datagram for the host\n" \ + "| 2 ... redirect datagram for ToS and network\n" \ + "| 3 ... redirect datagram for ToS and host\n" \ + "| 'p' (or 'payload') is the payload of the ICMP packet, tpyically an IP\n" \ + "| header. Note that - at the moment - you must prepare this payload by\n" \ + "| yourself.\n" \ + "|\n" \ + "| Unreachable \"unreach, code=2\"\n" \ + "| 'code' can be:\n" \ + "| 0 ... network unreachable\n" \ + "| 1 ... host unreachable\n" \ + "| 2 ... protocol unreachable\n" \ + "| 3 ... port unreachable\n" \ + "| 4 ... fragmentation needed but DF-bit is set\n" \ + "| 5 ... source route failed\n" \ + "|\n" \ + "|\n" \ + "| (other ICMP types will follow)\n" \ + "|\n" \ + "\n" + +#define MZ_TCP_HELP \ + "| TCP type: Send raw TCP packets.\n" \ + "|\n" \ + "| Parameters Values Explanation \n" \ + "| ---------- ------------------------------------ -------------------\n" \ + "| sp 0-65535 Source Port\n" \ + "| dp 0-65535 Destination Port\n" \ + "| flags fin|syn|rst|psh|ack|urg|ecn|cwr\n" \ + "| s 0-4294967295 Sequence Nr.\n" \ + "| a 0-4294967295 Acknowledgement Nr.\n" \ + "| win 0-65535 Window Size\n" \ + "| urg 0-65535 Urgent Pointer\n" \ + "| sum 0-65535 Checksum\n" \ + "|\n" \ + "| The port numbers can be specified as ranges, e. g. \"dp=1023-33700\".\n" \ + "| Multiple flags can be specified such as \"flags=syn|ack|urg\".\n" \ + "|\n" \ + "| Also the sequence number can be specified as a range, for example:\n" \ + "|\n" \ + "| s=10000-50000 ... send 40000 packets with SQNRs in that range. If the second\n" \ + "| value is lower than the first then it is assumed that the\n" \ + "| SQNRs should 'wrap around'.\n" \ + "| ds=30000 ........ use this increment within a SQNR-range.\n" \ + "|\n" \ + "| Note that all IP parameters can be modified (see IP help, i. e. '-t ip \"help\")\n" \ + "| except that (to avoid confusion) the IP length is 'iplen' and the IP checksum is 'ipsum'.\n" \ + "| Of course all Ethernet fields can also be accessed.\n"\ + "|\n" + + + +// Note: If another function specified tx.udp_payload then it must also +// set tx.udp_payload_s AND tx.udp_len = tx.udp_payload_s + 8 +libnet_ptag_t create_udp_packet (libnet_t *l) +{ + libnet_ptag_t t; + char argval[MAX_PAYLOAD_SIZE]; + int T; // only an abbreviation for tx.packet_mode + int i; + + ///////////////////////////// + // Default UDP header fields + // Already reset in init.c + ///////////////////////////// + + T = tx.packet_mode; // >0 means automatic L2 creation + + if ( (getarg(tx.arg_string,"help", NULL)==1) && (mode==UDP) ) + { + if (mz_port) + { + cli_print(gcli, "%s", MZ_UDP_HELP); + return -1; + } + else + { + + fprintf(stderr,"\n" + MAUSEZAHN_VERSION + "\n%s", MZ_UDP_HELP); + + exit(0); + } + + } + + + // Evaluate CLI parameters: + + if (getarg(tx.arg_string,"dp", argval)==1) + { + if (get_port_range (DST_PORT, argval)) // problem + { + tx.dp = 0; + } + } + + if (getarg(tx.arg_string,"sp", argval)==1) + { + if (get_port_range (SRC_PORT, argval)) // problem + { + tx.sp = 0; + } + } + + + // Check if hex_payload already specified (externally) + if (tx.hex_payload_s) + { + memcpy( (void*) tx.udp_payload, (void*) tx.hex_payload, tx.hex_payload_s); + tx.udp_payload_s = tx.hex_payload_s; + } + + if ( (getarg(tx.arg_string,"payload", argval)==1) || (getarg(tx.arg_string,"p", argval)==1)) + { + tx.udp_payload_s = str2hex (argval, tx.udp_payload, MAX_PAYLOAD_SIZE); + } + + + + if (getarg(tx.arg_string,"sum", argval)==1) + { + if (T) fprintf(stderr, " IP_Warning: 'sum' cannot be set in this mode.\n"); + tx.ip_sum = (u_int16_t) str2int(argval); + } + + + if (tx.ascii) // ASCII PAYLOAD overrides hex payload + { + strncpy((char *)tx.udp_payload, (char *)tx.ascii_payload, MAX_PAYLOAD_SIZE); + tx.udp_payload_s = strlen((char *)tx.ascii_payload); + printf("[%s]\n", tx.ascii_payload); + } + + + ///////// + // Want some padding? The specified number of padding bytes are ADDED to the + // payload. + // (Note the difference in send_eth() where you specified the total number + // of bytes in the frame) + // + if (tx.padding) + { + for (i=0; i0 means automatic L2 creation + + if ( (getarg(tx.arg_string,"help", NULL)==1) && (mode==TCP) ) + { + if (mz_port) + { + cli_print(gcli, "%s", MZ_TCP_HELP); + return -1; + } + else + { + fprintf(stderr,"\n" + MAUSEZAHN_VERSION + "\n%s", MZ_TCP_HELP); + exit(0); + } + } + + + // Evaluate CLI parameters: + + if (getarg(tx.arg_string,"dp", argval)==1) + { + if (get_port_range (DST_PORT, argval)) // problem + { + tx.dp = 0; + } + } + + + if (getarg(tx.arg_string,"sp", argval)==1) + { + if (get_port_range (SRC_PORT, argval)) // problem + { + tx.sp = 0; + } + } + + + if (getarg(tx.arg_string,"s", argval)==1) + { + //check whether a range has been specified: + dummy1 = strtok(argval, "-"); + tx.tcp_seq = (u_int32_t) str2int (dummy1); + if ( (dummy2 = strtok(NULL, "-")) == NULL ) // no additional value + { + tx.tcp_seq_stop = tx.tcp_seq; + } + else // range + { + tx.tcp_seq_stop = (u_int32_t) str2int (dummy2); + tx.tcp_seq_start = tx.tcp_seq; // initially tcp_seq = tcp_seq_start + tx.tcp_seq_delta = 1; // an initialization only in case 'ds' not specified + } + } + + if (getarg(tx.arg_string,"ds", argval)==1) + { + tx.tcp_seq_delta = (u_int32_t) str2int (argval); + } + + if (getarg(tx.arg_string,"a", argval)==1) + { + tx.tcp_ack = (u_int32_t) str2int (argval); + } + + if (getarg(tx.arg_string,"win", argval)==1) + { + tx.tcp_win = (u_int16_t) str2int (argval); + } + + if (getarg(tx.arg_string,"urg", argval)==1) + { + tx.tcp_urg = (u_int16_t) str2int (argval); + } + + + if ( (getarg(tx.arg_string,"flags", argval)==1) || + (getarg(tx.arg_string,"flag", argval)==1) ) // because everybody confuses this + { + if (get_tcp_flags(argval)) // problem + { + tx.tcp_control=2; // Assume SYN as default + } + } + + + // Check if hex_payload already specified (externally) + if (tx.hex_payload_s) + { + memcpy( (void*) tx.tcp_payload, (void*) tx.hex_payload, tx.hex_payload_s); + tx.tcp_payload_s = tx.hex_payload_s; + } + + + if ( (getarg(tx.arg_string,"payload", argval)==1) || (getarg(tx.arg_string,"p", argval)==1)) + { + tx.tcp_payload_s = str2hex (argval, tx.tcp_payload, MAX_PAYLOAD_SIZE); + } + + + if (tx.ascii) // ASCII PAYLOAD overrides hex payload + { + strncpy((char *)tx.tcp_payload, (char *)tx.ascii_payload, MAX_PAYLOAD_SIZE); + tx.tcp_payload_s = strlen((char *)tx.ascii_payload); + tx.tcp_len = 20 + tx.tcp_payload_s; // only needed by libnet to calculate checksum + tx.ip_payload_s = tx.tcp_len; // for create_ip_packet + } + + + + ///////// + // Want some padding? The specified number of padding bytes are ADDED to the + // payload. + // (Note the difference in send_eth() where you specified the total number + // of bytes in the frame) + // + if (tx.padding) + { + for (i=0; irefcount always contains the number of elements. + * + * Each element has a unique index number. + * + * The user must assign her/his data to (void*) elem->data. + * + */ + + +// Create new list element - may be the first one (list==NULL) +// +struct mz_ll * mz_ll_create_new_element(struct mz_ll *list) +{ + struct mz_ll *new_element; + new_element = (struct mz_ll*) malloc (sizeof(struct mz_ll)); + if (new_element==NULL) return NULL; + _mz_ll_set_default(new_element); + if (list==NULL) { + new_element->next=new_element; + new_element->prev=new_element; + new_element->head=new_element; + new_element->refcount=1; + new_element->index=0; + new_element->index_last=0; + } else { + new_element->prev=list->prev; + new_element->next=list; + new_element->prev->next=new_element; + list->prev = new_element; + new_element->head=list; + list->refcount++; + list->index_last++; + new_element->index=list->index_last; + } + + return new_element; +} + +// Delete ONE list element. +int mz_ll_delete_element (struct mz_ll *cur) +{ + if ((cur==NULL)||(cur==cur->head)) return -1; // don't delete head! + if (cur->data!=NULL) { free(cur->data); cur->data=NULL; } + + if ((cur->next!=cur)&&(cur->prev!=cur)) { + cur->prev->next=cur->next; + cur->next->prev=cur->prev; + } + cur->head->refcount--; + if (cur!=NULL) { free(cur); cur=NULL; } + return 0; +} + + +int mz_ll_delete_list (struct mz_ll *list) +{ + struct mz_ll *cur=list, + *tmp; + + if (cur==NULL) return 1; + while (cur!=cur->next) { + tmp=cur->next; + mz_ll_delete_element(cur); + cur=tmp; + } + // Finally free list head: + if (list->data!=NULL) { free(list->data); list->data=NULL; } + free(list); + list=NULL; + return 0; +} + +struct mz_ll * mz_ll_search_name (struct mz_ll *list, char *str) +{ + struct mz_ll *cur=list; + do { + if (strncmp(cur->name, str, MZ_LL_NAME_LEN)==0) return cur; + cur=cur->next; + } + while (cur!=list); + return NULL; +} + +struct mz_ll * mz_ll_search_index (struct mz_ll *list, int i) +{ + struct mz_ll *cur=list; + do { + if (cur->index==i) return cur; + cur=cur->next; + } + while (cur!=list); + return NULL; +} + +int mz_ll_size(struct mz_ll *list) +{ + int i=0; + struct mz_ll *cur=list; + + if (list==NULL) return 0; + + do { + i++; + cur=cur->next; + } + while (cur!=list); + if (i!=list->refcount) fprintf(stderr, "MZ_LL_SIZE: Anomalous situation. Report this.\n"); + return i; +} + + +int mz_ll_dump_all(struct mz_ll *list) +{ + int i=0; + struct mz_ll *cur=list; + + if (list==NULL) return 0; + + do { + i++; + fprintf(stdout, "Element %i: '%s', index=%i\n",i,cur->name, cur->index); + cur=cur->next; + } + while (cur!=list); + return i; +} + + + +// ------ PRIVATE: initialize list-element +void _mz_ll_set_default (struct mz_ll *cur) +{ + cur->refcount = 0; + cur->data = NULL; + cur->name[0]='\0'; + cur->index=0; + cur->state=0; +} + + + + diff --git a/src/llist.h b/src/llist.h new file mode 100644 index 0000000..49a87c7 --- /dev/null +++ b/src/llist.h @@ -0,0 +1,75 @@ +/* + * Mausezahn - A fast versatile traffic generator + * Copyright (C) 2010 Herbert Haas + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 as published by the + * Free Software Foundation. + * + * 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, see http://www.gnu.org/licenses/gpl-2.0.html + * +*/ + + + +#ifndef MZ_LINKED_LIST +#define MZ_LINKED_LIST + +#define MAX_PACKET_SEQUENCE_LEN 20 // how many packets can be defined in a sequence at maximum + +// A packet sequence -- this is the list data (each list element corresponds to one sequence) +struct pseq { + struct mops *packet[MAX_PACKET_SEQUENCE_LEN]; // pointer to the packets + struct timespec gap[MAX_PACKET_SEQUENCE_LEN]; // optional delay between different packets + int count; // total number of current members (=packets) +}; + + +// --------------- Mausezahn Multipurpose Linked List: ------------------- + +#define MZ_LL_NAME_LEN 64 + +// one list element +struct mz_ll { + struct mz_ll *prev; + struct mz_ll *next; + struct mz_ll *head; // always points to head element + int refcount; // head element: total number of list items! (Otherwise can be used as refcount.) + char name[MZ_LL_NAME_LEN]; + pthread_t sequence_thread; + int state; // 0 = inactive, 1 = active + int index; // monotonically increasing; + int index_last; //head always stores the last value! + void *data; // points to your data +}; + +struct mz_ll *packet_sequences; +struct mz_ll *cli_seq; // currently edited packet sequence used by CLI + +// prototypes +struct mz_ll * mz_ll_create_new_element(struct mz_ll *list); +int mz_ll_delete_element (struct mz_ll *cur); +int mz_ll_delete_list(struct mz_ll *list); +struct mz_ll * mz_ll_search_name (struct mz_ll *list, char *str); +void _mz_ll_set_default (struct mz_ll *cur); +int mz_ll_dump_all(struct mz_ll *list); +int mops_tx_sequence (struct mz_ll *seq); + +// convenience functions using the above in a more intelligent way +int mops_delete_sequence(char *name); +struct mz_ll * mops_create_sequence (char *name); +int mops_dump_sequence (char* str); +int mops_add_packet_to_sequence (struct mz_ll *seq, struct mops *mp); +int mops_add_delay_to_sequence (struct mz_ll *seq, struct timespec *t); +int mops_delete_packet_from_pseq (struct mz_ll *seq, int index); +int mops_delete_all_packets_from_pseq (struct mz_ll *seq); +int stop_sequence (char *name); +int stop_all_sequences (); +#endif + diff --git a/src/lookupdev.c b/src/lookupdev.c new file mode 100644 index 0000000..dfca239 --- /dev/null +++ b/src/lookupdev.c @@ -0,0 +1,357 @@ +/* + * Mausezahn - A fast versatile traffic generator + * Copyright (C) 2008-2010 Herbert Haas + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 as published by the + * Free Software Foundation. + * + * 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, see http://www.gnu.org/licenses/gpl-2.0.html + * +*/ + + + + +#include "mz.h" +#include "mops.h" + +#include +#include + + +// PURPOSE: Find usable network devices +// +// NOTE: +// +// 1. Ignores devices without IP address +// 2. Ignores loopback (etc) +// +// RETURN VALUES: +// +// 0 if usable device found (device_list[] and tx.device set) +// 1 if no usable device found +// +int lookupdev() +{ + // char *tx.device is global, see as.h + + char + ipaddress[IPADDRSIZE+1], + errbuf[PCAP_ERRBUF_SIZE]; + + pcap_if_t + *alldevs, + *index = NULL; + + pcap_addr_t *pcap_addr; + + int i=0; + + + // FIRST get a list of all available devices + // + if (pcap_findalldevs(&alldevs, errbuf) == -1) + { + fprintf(stderr," mz: %s\n",errbuf); + return 1; + } + + index = (pcap_if_t *) alldevs; + + while (index) + { + if (index->addresses) + { + pcap_addr = index->addresses; + while(pcap_addr) + { + if (pcap_addr->addr && (pcap_addr->addr->sa_family==AF_INET)) + { + if (inet_ntop(pcap_addr->addr->sa_family, + (void *)&pcap_addr->addr->sa_data[2], + ipaddress, + IPADDRSIZE)) + { + if (verbose) + { + fprintf(stderr," mz: device %s got assigned %s ", + index->name, ipaddress); + } + + if (strncmp(ipaddress, "127", 3)==0) + { + if (verbose) fprintf(stderr, "(loopback)\n"); + strncpy(device_list[i].dev, index->name, 9); + strncpy(device_list[i].ip_str, ipaddress, IPADDRSIZE); + device_list[i].phy=0; + get_if_addr(index->name, device_list[i].ip, device_list[i].mac); + get_if_addr(index->name, device_list[i].ip_mops, device_list[i].mac_mops); + i++; + } + else if (strncmp(ipaddress, "169.254", 7)==0) + { + if (verbose) fprintf(stderr, "but IGNORED (cause: host-scope address)\n"); + } + else // FOUND VALID INTERFACE + { + if (verbose) fprintf(stderr, "and is a possible candidate.\n"); + strncpy(device_list[i].dev, index->name, 9); + strncpy(device_list[i].ip_str, ipaddress, IPADDRSIZE); + device_list[i].phy=1; + get_if_addr(index->name, device_list[i].ip, device_list[i].mac); + get_if_addr(index->name, device_list[i].ip_mops, device_list[i].mac_mops); + i++; + } + + // Select only interfaces with IP addresses + // but avoid those that start with 127 or 169.254 + // Put the remaining on a list. If this list has more than one entry + // ask the user which interface to listen to. + } + else + { + return 1; + } + } + pcap_addr = pcap_addr->next; + } // closes while(pcap_addr) + } + index = index->next; + } // closes while (index) + + device_list_entries = i; + + /* + if (verbose) + { + for (i=0; i=0) { + close(device_list[devind].ps); + device_list[devind].ps=-1; + } + + if (device_list[devind].ps<0) { + ps = socket (PF_PACKET, SOCK_RAW, htons(ETH_P_IP)); //ETH_P_ALL, ETH_P_802_3); + if (ps<0) { + fprintf(stderr, " Warning: [lookupdev.c get_dev_params()] Cannot open socket!\n"); + return 1; + } + + // Get device index + strncpy(si.ifr_name, name, IFNAMSIZ); + if (ioctl(ps, SIOCGIFINDEX, &si)==-1) { + perror("ioctl"); + close(ps); + return 1; + } + index=si.ifr_ifindex; + + // Get MTU + if (ioctl(ps, SIOCGIFMTU, &si)==-1) { + perror("ioctl"); + close(ps); + return 1; + } + mtu = si.ifr_mtu; + + // ***** bind socket for later TX and RX **** + psock.sll_family = AF_PACKET; // evident + // psock.sll_protocol = 0; // unsigned short - Physical layer protocol + psock.sll_ifindex = index; // int - Interface number + psock.sll_hatype = 0; // unsigned short - Header type //ARPHRD_ETHER + psock.sll_pkttype = 0; // unsigned char - Packet type + psock.sll_halen = 6; // unsigned char - Length of address + bind(ps, (const struct sockaddr *) &psock, sizeof(psock)); // <= !!! + device_list[devind].ps = ps; // Note that close(ps) must be done upon termination + } + + // Get MAC of default gateway + service_arp(name, device_list[devind].ip_gw, device_list[devind].mac_gw); + + usleep(200); // this is a VERY short delay but it usually works in today's LANs + cur=device_list[devind].arp_table; + while(cur!=NULL) { + if ((cur->sip[0]==dgw[0]) && + (cur->sip[1]==dgw[1]) && + (cur->sip[2]==dgw[2]) && + (cur->sip[3]==dgw[3])) { // entry found! + for (i=0; i<6; i++) { + device_list[devind].mac_gw[i] = cur->smac[i]; + } + } + cur=cur->next; + } + + // FINALLY: Copy findings in device_list + + if (device_list[devind].phy) { + for (i=0; i<4; i++) { + device_list[devind].net[i] = net[i]; + device_list[devind].mask[i] = mask[i]; + device_list[devind].ip_gw[i] = dgw[i]; + } + } + else { + for (i=0; i<4; i++) { + device_list[devind].net[i] = 0; + device_list[devind].mask[i] = 0; + device_list[devind].ip_gw[i] = 0; + } + } + + device_list[devind].index = index; + device_list[devind].mtu = mtu; + + return 0; +} + diff --git a/src/modifications.c b/src/modifications.c new file mode 100644 index 0000000..e7119fd --- /dev/null +++ b/src/modifications.c @@ -0,0 +1,542 @@ +/* + * Mausezahn - A fast versatile traffic generator + * Copyright (C) 2008-2010 Herbert Haas + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 as published by the + * Free Software Foundation. + * + * 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, see http://www.gnu.org/licenses/gpl-2.0.html + * +*/ + + + + +// *************************************************************************** +// +// This sections contains functions to manipulate headers of +// Eth, MPLS, 802.1Q, IP, UDP, and TCP: +// +// int update_Eth_SA (libnet_t *l, libnet_ptag_t t) +// int update_IP_SA (libnet_t *l, libnet_ptag_t t) +// int update_IP_DA (libnet_t *l, libnet_ptag_t t) +// int update_DPORT (libnet_t *l, libnet_ptag_t t) +// int update_SPORT (libnet_t *l, libnet_ptag_t t) +// int update_TCP_SQNR (libnet_t *l, libnet_ptag_t t) +// +// and finally: +// +// int print_frame_details() +// +// *************************************************************************** + +#include "mz.h" +#include "mops.h" + +/////////////////////////////////////////////////////////////////////////// +// Applies another random Ethernet source address to a given Ethernet-PTAG. +// (The calling function should check 'tx.eth_src_rand' whether the SA +// should be randomized.) +// +int update_Eth_SA(libnet_t *l, libnet_ptag_t t) +{ + tx.eth_src[0] = (u_int8_t) ( ((float) rand()/RAND_MAX)*256) & 0xFE; // keeps bcast-bit zero + tx.eth_src[1] = (u_int8_t) ( ((float) rand()/RAND_MAX)*256); + tx.eth_src[2] = (u_int8_t) ( ((float) rand()/RAND_MAX)*256); + tx.eth_src[3] = (u_int8_t) ( ((float) rand()/RAND_MAX)*256); + tx.eth_src[4] = (u_int8_t) ( ((float) rand()/RAND_MAX)*256); + tx.eth_src[5] = (u_int8_t) ( ((float) rand()/RAND_MAX)*256); + + t = libnet_build_ethernet (tx.eth_dst, + tx.eth_src, + tx.eth_type, + NULL, // the payload + 0, + l, + t); + + if (t == -1) + { + fprintf(stderr, " mz/update_Eth_SA: Can't build Ethernet header: %s\n", + libnet_geterror(l)); + exit(EXIT_FAILURE); + } + + return 0; +} + + +// Update official timestamp, own timestamp and sequence number in the RTP header. +// The actual RTP message is stored in tx.udp_payload. +int update_RTP(libnet_t *l, libnet_ptag_t t) +{ + u_int8_t *ptr; + struct mz_timestamp ts; + + tx.rtp_sqnr++; + tx.rtp_stmp+=160; // TODO: different values for different codecs + + // update SQNR + ptr = (u_int8_t*) &tx.rtp_sqnr; + tx.udp_payload[2] = *(ptr+1); + tx.udp_payload[3] = *ptr; + + // update official timestamp + ptr = (u_int8_t*) &tx.rtp_stmp; + tx.udp_payload[4] = *(ptr+3); + tx.udp_payload[5] = *(ptr+2); + tx.udp_payload[6] = *(ptr+1); + tx.udp_payload[7] = *ptr; + + + // update own timestamp + getcurtime(&ts); // Now add TX timestamp: + mops_hton4 ((u_int32_t*) &ts.sec, &tx.udp_payload[16]); + mops_hton4 ((u_int32_t*) &ts.nsec, &tx.udp_payload[20]); + + t = libnet_build_udp(tx.sp, + tx.dp, + tx.udp_len, + tx.udp_sum, + tx.udp_payload, + tx.udp_payload_s, + l, + t); + + if (t == -1) { + fprintf(stderr," mz/send_frame: RTP header update failed!\n"); + exit (1); + } + return 0; +} + + +/////////////////////////////////////////////////////////////////////////// +// Applies another SOURCE IP address, +// - either a random one (tx.ip_src_rand==1) +// - or from a specified range (tx.ip_src_isrange==1) +// to a given IP-PTAG. +// +// Note: tx.ip_src MUST be already initialized with tx.ip_src_start. +// This is done by 'get_ip_range_src()' in tools.c. +// +// +// RETURNS '1' if tx.ip_src restarts +// +int update_IP_SA (libnet_t *l, libnet_ptag_t t) +{ + u_int8_t *x, *y; + int i=0; + + if (tx.ip_src_rand) + { + tx.ip_src_h = (u_int32_t) ( ((float) rand()/RAND_MAX)*0xE0000000); //this is 224.0.0.0 + i=1; + } + else if (tx.ip_src_isrange) + { + tx.ip_src_h++; + if (tx.ip_src_h > tx.ip_src_stop) // reached the end of the range => restart! + { + tx.ip_src_h = tx.ip_src_start; + i=1; + } + } + + // Now convert "tx.ip_src_h" into "tx.ip_src" which is in 'Network Byte Order': + x = (unsigned char*) &tx.ip_src_h; + y = (unsigned char*) &tx.ip_src; + + *y = *(x+3); + y++; + *y = *(x+2); + y++; + *y = *(x+1); + y++; + *y = *x; + + // TODO: Omit certain IP addresses: + // E.g. if (rand_ip == tx.ip_src) goto rand_again; // never use true interface IP + // TODO: Check other address exceptions ... + + t = libnet_build_ipv4 (tx.ip_len, + tx.ip_tos, + tx.ip_id, + tx.ip_frag, + tx.ip_ttl, + tx.ip_proto, + tx.ip_sum, + tx.ip_src, // possibly now random + tx.ip_dst, + (mode==IP) ? (tx.ip_payload_s) ? tx.ip_payload : NULL : NULL, // if e.g. mode=UDP ignore payload argument + (mode==IP) ? tx.ip_payload_s : 0, + l, + t); + + if (t == -1) + { + fprintf(stderr," mz/update_IP_SA: IP address manipulation failed!\n"); + exit (1); + } + + return i; +} + + + + +///////////////////////////////////////////////////////////////////////////////////////// +// Applies another DESTINATION IP address from a specified range (tx.ip_dst_isrange==1) +// to a given IP-PTAG. +// +// Note: tx.ip_dst MUST be already initialized with tx.ip_dst_start. +// tx.ip_dst_h 'mirrors' tx.ip_dst +// (i. e. tx.ip_dst_h is NOT in network byte order => easy to count) +// This is done by 'get_ip_range_dst()' in tools.c. +// +// RETURN VALUE: '1' if tx.ip_dst restarts +// +int update_IP_DA(libnet_t *l, libnet_ptag_t t) +{ + u_int8_t *x, *y; + int i=0; + + + if (tx.ip_dst_isrange) + { + tx.ip_dst_h++; + if (tx.ip_dst_h > tx.ip_dst_stop) // we reached the end of the range => restart! + { + tx.ip_dst_h = tx.ip_dst_start; + i=1; + } + } + + + // Now convert "tx.ip_dst_h" into "tx.ip_dst" which is in 'Network Byte Order': + + x = (unsigned char*) &tx.ip_dst_h; + y = (unsigned char*) &tx.ip_dst; + + *y = *(x+3); + y++; + *y = *(x+2); + y++; + *y = *(x+1); + y++; + *y = *x; + + + // TODO: Omit certain IP addresses: + // E.g. if (rand_ip == tx.ip_src) goto rand_again; // never use true interface IP + // TODO: Check other address exceptions ... + + t = libnet_build_ipv4 (tx.ip_len, + tx.ip_tos, + tx.ip_id, + tx.ip_frag, + tx.ip_ttl, + tx.ip_proto, + tx.ip_sum, + tx.ip_src, + tx.ip_dst, + (mode==IP) ? (tx.ip_payload_s) ? tx.ip_payload : NULL : NULL, // if e.g. mode=UDP ignore payload argument + (mode==IP) ? tx.ip_payload_s : 0, + l, + t); + + if (t == -1) + { + fprintf(stderr," mz/update_IP_DA: IP address manipulation failed!\n"); + exit (1); + } + + return i; +} + + + + +/////////////////////////////////////////////////////////////////////////////////////// +// +// Applies another DESTINATION PORT from a specified range to a given UDP- or TCP-PTAG. +// +// Note: tx.dp MUST be already initialized with tx.dp_start +// This is done by 'get_port_range()' in tools.c. +// +// RETURN VALUE: '1' if tx.dp restarts +// +int update_DPORT(libnet_t *l, libnet_ptag_t t) +{ + // u_int32_t DP; + int i=0; + + // DP = (u_int32_t) tx.dp; + // DP++; + tx.dp++; + + + // Exceeded range => restart: + if ((tx.dp > tx.dp_stop) || // we exceeded the end of the range + (tx.dp == 65535) ) // or exceeded the 16-bit range + { + tx.dp = tx.dp_start; + i=1; + } + + + if (mode==UDP) + { + t = libnet_build_udp(tx.sp, + tx.dp, + tx.udp_len, + tx.udp_sum, + (tx.udp_payload_s) ? tx.udp_payload : NULL, + tx.udp_payload_s, + l, + t); + + if (t == -1) + { + fprintf(stderr," mz/send_frame: UDP header manipulation failed!\n"); + exit (1); + } + } + else // TCP + { + t = libnet_build_tcp (tx.sp, + tx.dp, + tx.tcp_seq, + tx.tcp_ack, + tx.tcp_control, + tx.tcp_win, + tx.tcp_sum, + tx.tcp_urg, + tx.tcp_len, + (tx.tcp_payload_s) ? tx.tcp_payload : NULL, + tx.tcp_payload_s, + l, + t); + + if (t == -1) + { + fprintf(stderr, " mz/update_DPORT: Can't build TCP header: %s\n", libnet_geterror(l)); + exit (0); + } + } + + return i; +} + + +/////////////////////////////////////////////////////////////////////////////////// +// +// Applies another SOURCE PORT from a specified range to a given UDP- or TCP-PTAG. +// +// Note: tx.sp MUST be already initialized with tx.sp_start +// This is done by 'get_port_range()' in tools.c. +// +// RETURN VALUE: '1' if tx.sp restarts +// +int update_SPORT(libnet_t *l, libnet_ptag_t t) +{ + +// u_int32_t SP; + int i=0; + +// SP = (u_int32_t) tx.sp; +// SP++; + tx.sp++; + + + // Exceeded range => restart: + if ((tx.sp > tx.sp_stop) || // we exceeded the end of the range + (tx.sp == 65535) ) // or exceeded the 16-bit range + { + tx.sp = tx.sp_start; + i=1; + } + + if (mode==UDP) + { + t = libnet_build_udp(tx.sp, + tx.dp, + tx.udp_len, + tx.udp_sum, + (tx.udp_payload_s) ? tx.udp_payload : NULL, + tx.udp_payload_s, + l, + t); + + if (t == -1) + { + fprintf(stderr," mz/send_frame: UDP header manipulation failed!\n"); + exit (1); + } + } + else // TCP + { + t = libnet_build_tcp (tx.sp, + tx.dp, + tx.tcp_seq, + tx.tcp_ack, + tx.tcp_control, + tx.tcp_win, + tx.tcp_sum, + tx.tcp_urg, + tx.tcp_len, + (tx.tcp_payload_s) ? tx.tcp_payload : NULL, + tx.tcp_payload_s, + l, + t); + + if (t == -1) + { + fprintf(stderr, " mz/update_DPORT: Can't build TCP header: %s\n", libnet_geterror(l)); + exit (0); + } + } + + return i; +} + + + +/////////////////////////////////////////////////////////////////////// +// +// Applies another TCP SQNR from a specified range to a given TCP-PTAG +// +// RETURN VALUE: '1' if tx.txp_seq restarts +// +int update_TCP_SQNR(libnet_t *l, libnet_ptag_t t) +{ + + u_int32_t diff; + int i=0; + + tx.tcp_seq += tx.tcp_seq_delta; + diff = tx.tcp_seq_stop - tx.tcp_seq_start; + + if (diff < tx.tcp_seq_stop) // start < stop + { + if (tx.tcp_seq > tx.tcp_seq_stop) + { + tx.tcp_seq = tx.tcp_seq_start; + i=1; + } + } + else // stop < start + { + if ( (tx.tcp_seqtx.tcp_seq_stop) ) + { + tx.tcp_seq = tx.tcp_seq_start; + i=1; + } + + } + + t = libnet_build_tcp (tx.sp, + tx.dp, + tx.tcp_seq, + tx.tcp_ack, + tx.tcp_control, + tx.tcp_win, + tx.tcp_sum, + tx.tcp_urg, + tx.tcp_len, + (tx.tcp_payload_s) ? tx.tcp_payload : NULL, + tx.tcp_payload_s, + l, + t); + + if (t == -1) + { + fprintf(stderr, " mz/update_TCP_SQNR: Can't build TCP header: %s\n", libnet_geterror(l)); + exit (0); + } + + return i; +} + + +//////////////////////////////////////////////////////////////////////// +// +// + +int print_frame_details() +{ + unsigned char *dum1, *dum2; + char pld[65535]; + char sa[32], da[32]; + + if (!tx.packet_mode) + { + bs2str(tx.eth_dst, da, 6); + bs2str(tx.eth_src, sa, 6); + fprintf(stderr, " Eth: DA = %s, SA = %s\n",da,sa); + } + + + if (tx.dot1Q) + { + fprintf(stderr, " 802.1Q VLAN-TAG = %s\n", tx.dot1Q_txt); + } + + if (tx.mpls) + { + fprintf(stderr," MPLS labels (label:exp:bos:ttl): %s\n",tx.mpls_verbose_string); + + } + + + dum1 = (unsigned char*) &tx.ip_src_h; + dum2 = (unsigned char*) &tx.ip_dst_h; + (mode==IP) ? (void) bs2str(tx.ip_payload, pld, tx.ip_payload_s) : strcpy(pld, "[see next layer]"); + fprintf(stderr," IP: ver=4, len=%u, tos=%u, id=%u, frag=%u, ttl=%u, proto=%u, sum=%u, " + "SA=%u.%u.%u.%u, DA=%u.%u.%u.%u,\n" + " payload=%s\n", tx.ip_len, tx.ip_tos, + tx.ip_id, tx.ip_frag, tx.ip_ttl, tx.ip_proto, tx.ip_sum, + *(dum1+3),*(dum1+2),*(dum1+1),*(dum1), *(dum2+3),*(dum2+2),*(dum2+1),*(dum2+0), pld); + + if ((mode==UDP)||(mode==DNS)||(mode==RTP)) + { + bs2str(tx.udp_payload, pld, tx.udp_payload_s); + fprintf(stderr, " UDP: sp=%u, dp=%u, len=%u, sum=%u, \n" + " payload=%s\n", tx.sp, tx.dp, tx.udp_len, tx.udp_sum, pld); + } + if (mode==TCP) // TODO: Improve message details (flags, ...) + { + bs2str(tx.tcp_payload, pld, tx.tcp_payload_s); + fprintf(stderr, " TCP: sp=%u, dp=%u, S=%u, A=%u, flags=%x, win=%u, len=%u, sum=%u, \n" + " payload=%s\n", + tx.sp, tx.dp, tx.tcp_seq, tx.tcp_ack, tx.tcp_control, tx.tcp_win, tx.tcp_len, tx.tcp_sum, pld); + } + + // send_icmp must prepare the verbose string because there are many + // different types of ICMP packets... + if (mode==ICMP) + { + fprintf(stderr, " %s\n", tx.icmp_verbose_txt); + } + + // libnet_diag_dump_pblock(l); + fprintf(stderr,"\n"); + + if (simulate) + { + fprintf(stderr, "*** NOTE: Simulation only! Nothing has been sent! ***\n"); + exit(0); + } + + + return 0; +} + diff --git a/src/mops.c b/src/mops.c new file mode 100644 index 0000000..f56deff --- /dev/null +++ b/src/mops.c @@ -0,0 +1,769 @@ +/* + * Mausezahn - A fast versatile traffic generator + * Copyright (C) 2008-2010 Herbert Haas + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 as published by the + * Free Software Foundation. + * + * 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, see http://www.gnu.org/licenses/gpl-2.0.html + * +*/ + + + + +// -- TOC: -- +// +// struct mops * mops_init () +// struct mops * mops_alloc_packet (struct mops *cur) +// struct mops * mops_delete_packet (struct mops *cur) +// int mops_reset_packet (struct mops *cur) +// +// int mops_dump_all (struct mops* list) +// struct mops * mops_search_name (struct mops* list, char* key) +// struct mops * mops_search_id (struct mops* list, int key) +// void mops_delete_all (struct mops* list) +// void mops_cleanup (struct mops* list) +// +// int mops_set_defaults (struct mops *mp) +// int mops_print_frame (struct mops *mp, char *str) +// +// int mops_get_new_pkt_id (struct mops *mp) +// int mops_clear_layers (struct mops *mp) + +// int mops_get_device_index (char *devname) +// int mops_use_device (struct mops * mp, int i) + +// int mops_get_proto_info (struct mops * mp, char *layers, char *proto) + +#include "mz.h" +#include "mops.h" + + + + +// Creates first element, aka "head" element +// This element can also be used! See mops_alloc_packet! +// +struct mops * mops_init() +{ + // these defaults can be changed by the user: + min_frame_s = MIN_MOPS_FRAME_SIZE; // important global; depends on used packet tx subsystem such as libnet + max_frame_s = MAX_MOPS_FRAME_SIZE-MOPS_SIZE_MARGIN; + + // Create initial mops element: + struct mops *new_mops = (struct mops*) malloc(sizeof(struct mops)); + new_mops->next = new_mops; + new_mops->prev = new_mops; + new_mops->state = MOPS_STATE_NULL; + new_mops->id = 0; // importante! + mops_set_defaults (new_mops); + strncpy(new_mops->packet_name, "-------", 8); + + return new_mops; +} + + + + + +// Returns pointer to new mops element: +// 1) either insert a new mops element in list +// 2) or returns same pointer again if current mops element is empty +// Note that new element N is always PREPENDED to cur: +// ... = N-2 = N-1 = N = cur = 1 = 2 = ... +// +// +// RETURN VALUE: + Pointer to new mops +// - NULL upon failure +struct mops * mops_alloc_packet(struct mops *cur) +{ + int j; + struct mops *new_mops; + int new_pkt_id, pkt_id_name; + char pname[MAX_MOPS_PACKET_NAME_LEN]; + + if (cur->state == MOPS_STATE_NULL) { // allows to use first packet in list + new_mops = cur; // current mops was unused => no need to insert a new mops! + } + else { // create new mops element + new_mops = (struct mops *) malloc(sizeof(struct mops)); + if (new_mops==NULL) { + fprintf(stderr, "MZ alert: cannot create new mops entry - memory full?\n"); + return NULL; // memory full? + } + } + + new_mops->state = MOPS_STATE_INIT; + + // Assign unique packet id + new_pkt_id = mops_get_new_pkt_id (cur); + if (new_pkt_id==-1) return NULL; + new_mops->id = new_pkt_id; + + // Assign unique packet name + pkt_id_name = new_pkt_id; + do { + sprintf(pname, "PKT%04d", pkt_id_name); + pkt_id_name++; + } while (mops_search_name (mp_head, pname)); // check if this name is really unique + + strncpy(new_mops->packet_name, pname, MAX_MOPS_PACKET_NAME_LEN); + + // append to doubly linked list + new_mops->prev = cur->prev; + new_mops->next = cur; + cur->prev = new_mops; + new_mops->prev->next = new_mops; + + mops_set_defaults (new_mops); // set header parametes (addresses etc) + + // Reset protocol descriptor + new_mops->p_desc = NULL; + new_mops->p_desc_type = MOPS_NO_PDESC; + + // clear counter values + new_mops->used_counters=0; + for (j=0; jcounter[j].use = 0; + new_mops->counter[j].offset = 0; + new_mops->counter[j].random = 0; + } + + return new_mops; +} + + + +// Delete particular packet (remove it from list). +// +// If mp_head is deleted, makes previous element mp_head. +// Note that the global mp_head must exist but within MOPS this +// is always the case. +// +// Returns pointer to previous element in the list +// or NULL if packet is active +struct mops * mops_delete_packet(struct mops *cur) +{ + struct mops *last; + + if (mops_is_active(cur)) { + mops_destroy_thread(cur); + } + + mops_ext_del_pdesc (cur); // delete p_desc (if available) + + // remove automops data if available + if (cur->amp != NULL) { + free(cur->amp); + cur->amp=NULL; + } + if (cur->amp_pdu != NULL) { + free (cur->amp_pdu); + cur->amp_pdu=NULL; + } + + last = cur->prev; + cur->next->prev = cur->prev; + cur->prev->next = cur->next; + if (cur==mp_head) { + mp_head = last; + } + if (cur!=NULL) { + free (cur); + cur=NULL; + } + return last; +} + + + +// Erase all data of a mops entry and even chooses a new standard name +// DOES NOT delete the entry from the list +// +int mops_reset_packet(struct mops *cur) +{ + int i=0; + char pname[16]; + + // stop thread if necessary + if (mops_is_active(cur)) { + mops_destroy_thread(cur); + } + + // remove pdesc if available + mops_ext_del_pdesc (cur); + cur->state = MOPS_STATE_NULL; + + // remove automops data if available + if (cur->amp != NULL) { + free(cur->amp); + cur->amp=NULL; + } + if (cur->amp_pdu != NULL) { + free (cur->amp_pdu); + cur->amp_pdu=NULL; + } + // find another name + do { + sprintf(pname, "PKT%04d", i); + i++; + } while (mops_search_name (mp_head, pname)); // check if this name is really unique + strncpy(cur->packet_name, pname, MAX_MOPS_PACKET_NAME_LEN); + + // Place everything else in this function: + mops_set_defaults (cur); + + return 0; +} + + + + +// Runs through all packets and dumps some statistics into 'str' +// Returns 1 if only the uninitialized head is available +// +int mops_dump_all(struct mops* list, char *str) +{ + struct mops *head = list; + struct mops *cur = list; + + char output[100]; + int anzmops=0, active=0, config=0, raw=0, ival=0; + + do { + if (cur->state == MOPS_STATE_ACTIVE) { + active++; + } else if (cur->state == MOPS_STATE_CONFIG) { + config++; + } else if (cur->interval_used==2) { + ival++; + } + if (cur->use_ETHER == 0) raw++; + + anzmops++; + cur = cur->next; + } while (head != cur); + + snprintf(output, 99, "%i Mopse(s) (interval: %i, active: %i, config: %i, raw: %i)", + anzmops, ival, active, config, raw); + + strncpy(str, output, 99); + + if ((!active) && (!config)) return 1; + + return 0; +} + + + + + +// Search for key = name and return pointer to that mops +// Return NULL if not found +struct mops * mops_search_name (struct mops* list, char *key) +{ + struct mops *head = list; + struct mops *cur = list; + do { + if ( (strncasecmp(key, + cur->packet_name, + MAX_MOPS_PACKET_NAME_LEN) == 0)) { + return cur; // FOUND! + } + cur = cur->next; + } + while (head != cur); + return NULL; // NOT FOUND! +} + + + +// Search for key = id and return pointer to that mops +// Return NULL if not found +struct mops * mops_search_id (struct mops* list, u_int32_t key) +{ + struct mops *head = list; + struct mops *cur = list; + do { + if ( cur->id == key ) { + return cur; // FOUND! + } + cur = cur->next; + } + while (head != cur); + return NULL; // NOT FOUND! +} + + + + +// Deletes all elements except the specified element which us usually +// the head element. Also ACTIVE elements will be removed and the +// corresponding threads will be stopped. +// +// Thus the list can grow again later via mops_alloc_packet +// +void mops_delete_all(struct mops* list) +{ + struct mops *head = list; + struct mops *cur = list->next; + struct mops *tmp; + + // Delete all but head element: + while (head != cur) + { + tmp = cur->next; + mops_ext_del_pdesc (cur); // delete p_desc (if available) + mops_destroy_thread(cur); + + // remove automops data if available + if (cur->amp != NULL) { + free(cur->amp); + cur->amp=NULL; + } + if (cur->amp_pdu != NULL) { + free (cur->amp_pdu); + cur->amp_pdu=NULL; + } + cur->amp_pdu_s=0; + + if (cur!=NULL) { + free(cur); + cur=NULL; + } + cur = tmp; + } + + head->next = head; + head->prev = head; + + head->state = MOPS_STATE_NULL; +} + + + +// Same as mops_delete_all but also destroys the head element: +void mops_cleanup(struct mops* list) +{ + mops_delete_all(list); + mops_ext_del_pdesc (list); // delete p_desc (if available) + mops_destroy_thread(list); + if (list!=NULL) { + free(list); + list=NULL; + } +} + + + + +// Set default MOPS and protocol header parameters +// Currently most parameters are taken from the legacy tx-structure +// +// NOTE: Does NOT and should NOT change the packet_name !!! +// Because user might be confused if it is changed to something +// unexpected such as 'PKT0341'. +// +// TODO: find out MAC of default GW +int mops_set_defaults (struct mops *mp) +{ + // Initialize frame arrays with zero bytes + memset(mp->frame, 0x00, MAX_MOPS_FRAME_SIZE); + memset(mp->msg, 0x00, MAX_MOPS_MSG_SIZE); + + // Basics -- MOPS Management Parameters + pthread_mutex_init (& mp->mops_mutex, NULL); +// mp->mops_thread = 0; // TODO +// mp->interval_thread = 0; // TODO + mp->verbose = 1; // normal verbosity + mp->use_ETHER = 0; + mp->use_SNAP = 0; + mp->use_dot1Q = 0; + mp->use_MPLS = 0; + mp->use_IP = 0; + mp->use_UDP = 0; + mp->use_TCP = 0; + mp->frame_s = 0; + mp->msg_s = 0; + mp->description[0]='\0'; + mp->auto_delivery_off = 0; + mp->mz_system = 0; + strncpy (mp->device, tx.device, 16); + mp->count = 0; + mp->cntx = 0; + + mp->ndelay.tv_sec = 0; + mp->ndelay.tv_nsec = 100000000L; // 100 ms default delay + + mp->interval_used = 0; + mp->interval.tv_sec = 0; + mp->interval.tv_nsec = 0; + + mp->delay_sigma.tv_sec = 0; + mp->delay_sigma.tv_nsec = 0; + + mp->MSG_use_RAW_FILE=0; + mp->MSG_use_HEX_FILE=0; + mp->MSG_use_ASC_FILE=0; + mp->fp=NULL; + mp->chunk_s = MAX_MOPS_MSG_CHUNK_SIZE; + + // TODO: check if amp and amp_header is free()'d in any case!!! + mp->amp = NULL; + mp->amp_pdu = NULL; + mp->amp_pdu_s = 0; + + // Ethernet defaults: + memcpy((void *) &mp->eth_dst, (void *) &tx.eth_dst, 6); + memcpy((void *) &mp->eth_src, (void *) &tx.eth_src, 6); + mp->eth_type = 0x800; + mp->eth_src_israndom = 0; + + mp->dot1Q_isrange = 0; + mp->mpls_isrange = 0; + + // IP defaults: + // abuse our hton: here we actually convert from net to host order: + mops_hton4 ((u_int32_t*) &tx.ip_dst, (u_int8_t*) &mp->ip_dst); + mops_hton4 ((u_int32_t*) &tx.ip_src, (u_int8_t*) &mp->ip_src); + // Note that the IP address of the "default interface" is assigned to that mops. + // If the mops is bind to another interface then use the associated interface. + // Implement this in cli_packet and function cmd_packet_bind + // + mp->ip_version = 4; + mp->ip_IHL = 0; + mp->ip_len = 20; + mp->ip_tos = 0; + mp->ip_flags_RS=0; // 0|1 ... Reserved flag "must be zero" + mp->ip_flags_DF=0; // 0|1 ... Don't Fragment + mp->ip_flags_MF=0; // 0|1 ... More Fragments + mp->ip_frag_offset=0; + mp->ip_fragsize=0; // fragmentation OFF + mp->ip_frag_overlap=0; // no overlapping fragments + mp->ip_ttl = 255; + mp->ip_proto = 17; // UDP + mp->ip_src_israndom = 0; + mp->ip_src_isrange = 0; + mp->ip_dst_isrange = 0; + mp->ip_option_used = 0; + mp->ip_IHL_false = 0; + mp->ip_len_false = 0; + mp->ip_sum_false = 0; + mp->ip_option_used = 0; + mp->ip_option_s = 0; + // L4 defaults (port numbers) + mp->sp=0; + mp->sp_start=0; + mp->sp_stop=0; + mp->sp_isrand=0; + mp->sp_isrange=0; + + mp->dp=0; + mp->dp_start=0; + mp->dp_stop=0; + mp->dp_isrand=0; + mp->dp_isrange=0; + + // UDP defaults + // + mp->udp_len_false = 0; + mp->udp_sum_false = 0; + mp->udp_sum = 0xffff; // this default means "transmitter didn't compute checksum" + + // TCP defaults + // + mp->tcp_seq = 0xcafebabe; + mp->tcp_seq_delta = 0; // no range + mp->tcp_seq_start = 0; + mp->tcp_seq_stop = 0xffffffff; + mp->tcp_ack = 0; + mp->tcp_ack_delta = 0; // no range + mp->tcp_ack_start = 0; + mp->tcp_ack_stop = 0xffffffff; + mp->tcp_win = 100; + mp->tcp_sum_false = 0; + mp->tcp_offset_false = 0; + mp->tcp_offset = 0; + mp->tcp_sum = 0xffff; // this default means "transmitter didn't compute checksum" + mp->tcp_option_used = 0; + mp->tcp_option_s =0; + mp->tcp_ctrl_CWR =0; + mp->tcp_ctrl_ECE =0; + mp->tcp_ctrl_URG =0; + mp->tcp_ctrl_ACK =0; + mp->tcp_ctrl_PSH =0; + mp->tcp_ctrl_RST =0; + mp->tcp_ctrl_SYN =1; // assume that we begin with a TCP SYN + mp->tcp_ctrl_FIN =0; + mp->tcp_urg =0; + mp->tcp_ack =0; + mp->tcp_res =0; + return 0; +} + + + + + + + +int mops_print_frame (struct mops *mp, char *str) +{ + int i=0, fs; + char octet[8], lnr[8], hex[MAX_MOPS_FRAME_SIZE*3]; + + hex[0]=0x00; + + if (! (fs = mp->frame_s) ) return -1; // frame length zero (no frame?) + + if (fs>1) + { + sprintf(lnr,"%4i ",i+1); + strcat(hex, lnr); + + for (i=0; i0) && (!(i%8))) + { + strcat(hex, " "); // insert space after each 8 bytes + hex[strlen(hex)-2]=' '; + } + + if ((i>0) && (!(i%MAX_CLI_LINE_BYTES))) + { + sprintf(lnr,"\n%4i ",i+1); + strcat(hex, lnr); + } + + sprintf(octet, "%02x:", mp->frame[i]); + strcat(hex, octet); + } + } + + hex[strlen(hex)-1]=' '; + strcpy(str, hex); + + return 0; +} + + + + + + + + +// Find and returns a new unique packet id +// If none can be found, returns -1. +// +int mops_get_new_pkt_id (struct mops *list) +{ + struct mops *head = list; + struct mops *cur = list; + int i, min=0xffffffff, max=0; + + do { + if (cur->id < min) min = cur->id; // determine current min id + if (cur->id > max) max = cur->id; // determine current max id + cur = cur->next; + } + while (head != cur); + + if (min>0) + i= min-1; + else + i = max+1; + + // just for paranoia: check again if unique! + do { + if (cur->id == i) { + return -1; // + } + cur = cur->next; + } + while (head != cur); + + return i; +} + + +// Simply sets specified 'layer switches' in mops struct +// (use_ETHER, use_IP, ...) to zero. +// +// RETURN VALUE: tells which layers had been configured before clearing. +// +// The presence of the layers is indicated via binary coding: +// +// MOPS_ALL 127 // clear all +// MOPS_ETH 1 +// MOPS_SNAP 2 // either LLC, LLC+SNAP +// MOPS_dot1Q 4 +// MOPS_MPLS 8 +// MOPS_IP 16 +// MOPS_UDP 32 +// MOPS_TCP 64 +// +int mops_clear_layers (struct mops *mp, int l) +{ + int ret=0; + + if (l & MOPS_ETH) { + if (mp->use_ETHER) ret+=1; + mp->use_ETHER = 0; + } + + if (l & MOPS_SNAP) { + if (mp->use_SNAP) ret+=2; + mp->use_SNAP = 0; + } + + if (l & MOPS_dot1Q) { + if (mp->use_dot1Q) ret+=4; + mp->use_dot1Q = 0; + } + + if (l & MOPS_MPLS) { + if (mp->use_MPLS) ret+=8; + mp->use_MPLS = 0; + } + + if (l & MOPS_IP) { + if (mp->use_IP) ret+=16; + mp->use_IP = 0; + } + + if (l & MOPS_UDP) { + if (mp->use_UDP) ret+=32; + mp->use_UDP = 0; + } + + if (l & MOPS_TCP) { + if (mp->use_TCP) ret+=64; + mp->use_TCP = 0; + } + + return ret; +} + + +// Get global device index for a given device name. +// +// RETURN VALUE: +// Either the desired device index or -1 if not found. +// +// EXAMPLE: +// i = mops_get_device_index("eth0") +// +int mops_get_device_index(char *devname) +{ + int i; + + for (i=0; ieth_src, (void *) &device_list[i].mac_mops[0], 6); + memcpy((void *) &mp->ip_src, (void *) &device_list[i].ip_mops[0], 4); + + return 0; +} + + +// Creates two strings as used by the 'show packet' command, +// 1) one identifying all used layers of a packet, +// 2) the other which higher layer protocol is used +// +// caller must define: +// char layers[16], proto[16]; +// +// RETURNS 0 upon success, 1 upon failure. +// +int mops_get_proto_info(struct mops *mp, char *layers, char *proto) +{ + char ds[16], pr[16]; + + if (mp==NULL) return 1; + + ds[0]='\0'; + pr[0]='\0'; + + if (mp->use_ETHER) strcat(ds,"E"); else strcat(ds,"-"); + if (mp->use_SNAP) strcat(ds,"S"); else strcat(ds,"-"); + if (mp->use_dot1Q) strcat(ds,"Q"); else strcat(ds,"-"); + if (mp->use_MPLS) strcat(ds,"M"); else strcat(ds,"-"); + if (mp->use_IP) { + if (mp->auto_delivery_off) + strcat(ds,"i"); + else + strcat(ds,"I"); + } else strcat(ds,"-"); + + if (mp->use_UDP) + strcat(ds,"U"); + else if + (mp->use_TCP) strcat(ds,"T"); + else strcat(ds,"-"); + + switch (mp->p_desc_type) { + case MOPS_ARP: + strncpy(pr, "ARP", 8); + break; + case MOPS_BPDU: + strncpy(pr, "BPDU", 8); + break; + case MOPS_CDP: + strncpy(pr, "CDP", 8); + break; + case MOPS_DNS: + strncpy(pr, "DNS", 8); + break; + case MOPS_ICMP: + strncpy(pr, "ICMP", 8); + break; + case MOPS_IGMP: + strncpy(pr, "IGMP", 8); + break; + case MOPS_LLDP: + strncpy(pr, "LLDP", 8); + break; + case MOPS_RTP: + strncpy(pr, "RTP", 8); + break; + case MOPS_SYSLOG: + strncpy(pr, "SYSLOG", 8); + break; + default: + break; + } + + strncpy(layers, ds, 16); + strncpy(proto, pr, 16); + return 0; +} + + diff --git a/src/mops.h b/src/mops.h new file mode 100644 index 0000000..ee9e720 --- /dev/null +++ b/src/mops.h @@ -0,0 +1,1023 @@ +/* + * Mausezahn - A fast versatile traffic generator + * Copyright (C) 2008-2010 Herbert Haas + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 as published by the + * Free Software Foundation. + * + * 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, see http://www.gnu.org/licenses/gpl-2.0.html + * +*/ + + +#ifndef __MOPS__ +#define __MOPS__ + + +#define MOPS_VERSION "0.3" +#define MOPS_CODENAME "Cyanistes caeruleus (DE+150)" +#define AUTOMOPS_ENABLED 0 // Automops subsystem (currently in development) +#define MAX_MOPS_FRAME_SIZE 8192 // total max frame size (=all headers plus payload) +#define MIN_MOPS_FRAME_SIZE 15 // total min frame size +#define MOPS_SIZE_MARGIN 50 // User limit: MAX_MOPS_FRAME_SIZE - MOPS_SIZE_MARGIN +#define MAX_MOPS_MSG_SIZE 7500 // payload limit +#define MAX_MOPS_MSG_CHUNK_SIZE 1000 // Chunks size when read data from a file for the payload +#define MAX_MOPS_COUNTERS_PER_PACKET 10 // number of user-defined counters per packet +#define MAX_MOPS_PACKET_NAME_LEN 32 // Each packet must have an unique name +#define MAX_MOPS_DESCRIPTION_LEN 80 // Max length of packet description string +#define MAX_MOPS_DOT1Q_TAGS 64 // Max number of 802.1Q tags within a frame (too many, practically ;-)) +#define MAX_MOPS_MPLS_TAGS 64 // Max number of MPLS tags within a frame (again too many, practically) +#define XN_MAX_STACK 7 // max nesting depth + +#define AUTOMOPS_MAX_FILE_SIZE 200000 // Max file size in bytes for AMP protocol definitions +#define AUTOMOPS_MAX_NAME_LEN 32 // used for all names (valname, field name, protocol name) +#define AUTOMOPS_MAX_SHORTDESC_LEN 64 + +#define XML_MAX_TAG_LEN 16 +#define XML_STRLEN 64 // required length of user string to hold tag + // but also alternatively an error message + + +#define MAX_LLDP_OPT_TLVS 500 // How many bytes are reserved for optional TLVs within an LLDP message? + +//#define MAX_MOPS_PACKETS 1000 // number of packet slots *** DEPRECATED *** +#define MAX_CLI_LINE_BYTES 32 // How many bytes 'mops_print_frame' should print before line break + +// Basic layers; see mops_clear_layers() +// Also used by automops (see layers_on, layers_off) +#define MOPS_ALL 127 +#define MOPS_ETH 1 +#define MOPS_SNAP 2 // either LLC, LLC+SNAP +#define MOPS_dot1Q 4 +#define MOPS_MPLS 8 +#define MOPS_IP 16 +#define MOPS_UDP 32 +#define MOPS_TCP 64 + +// The following definitions are needed as values for (int) p_desc_type +// which identifies the exact type of (void *) p_desc. +#define MOPS_NO_PDESC 100 +#define MOPS_ARP 101 +#define MOPS_BPDU 102 +#define MOPS_CDP 103 +#define MOPS_DNS 104 +#define MOPS_ICMP 105 +#define MOPS_LLDP 106 +#define MOPS_RTP 107 +#define MOPS_SYSLOG 108 +#define MOPS_IGMP 109 + +// packet states (variable 'state') +// NOTE: every state >2 (i. e. 3, 4, ...) is an active state, i. e. packet should +// be blocked from configurations etc. +#define MOPS_STATE_NULL 0 // transition state, only initially +#define MOPS_STATE_INIT 1 +#define MOPS_STATE_CONFIG 2 // normal state (when configured) +#define MOPS_STATE_ACTIVE 3 // has associated sending thread +#define MOPS_STATE_SEQACT 4 // packet is member of an active sequence + +// Return values of mops_pdesc utility functions (see mops_ext.c) +#define MOPS_PDESC_SUCCESS 0 // Value assigned properly | string present +#define MOPS_PDESC_FAILURE 1 // Unspecified problem | string not present +#define MOPS_PDESC_LOW 2 // Value smaller than lower bound - but will set +#define MOPS_PDESC_HIGH 3 // Value larger than upper bound - but will set +#define MOPS_PDESC_OVERFLOW 4 // Value exceeded possible range +#define MOPS_PDESC_NO_MAC 5 // Invalid MAC address +#define MOPS_PDESC_NO_IP 6 // Invalid IP address + +// These definitions are (should be) only used in mops_ext.c +#define MOPS_EXT_ARP struct mops_ext_arp * +#define MOPS_EXT_BPDU struct mops_ext_bpdu * +#define MOPS_EXT_CDP struct mops_ext_cdp * +#define MOPS_EXT_DNS struct mops_ext_dns * +#define MOPS_EXT_ICMP struct mops_ext_icmp * +#define MOPS_EXT_LLDP struct mops_ext_lldp * +#define MOPS_EXT_RTP struct mops_ext_rtp * +#define MOPS_EXT_SYSLOG struct mops_ext_syslog * +#define MOPS_EXT_IGMP struct mops_ext_igmp * + +// Very specific definitions here: +#define MOPS_RTP_EXT_MZID 0xcaca // first 16 bit of the Mausezahn RTP extension header +#define DSP_SOURCE 100 // any number >0 indicating /dev/dsp to be used as RTP payload +#define MOPS_RTP_MAX_PAYLOAD_SIZE 200 + +#include + + +// These are initialized with the definitions MIN_MOPS_FRAME_SIZE and +// MAX_MOPS_FRAME_SIZE above but can be overridden by the user (without +// extending these limits) +unsigned int min_frame_s; +unsigned int max_frame_s; + +struct mops_counter +{ + int use; // 1 = counter active + int offset; // points to counter location in *msg* + int random; // 1=random, 0=use start/stop/step + u_int32_t start; // HOST BYTE ORDER + u_int32_t stop; // HOST BYTE ORDER + u_int32_t step; // HOST BYTE ORDER + u_int32_t cur; // current value (HOST BYTE ORDER) + int bytes; // number of bytes used (1|2|4) - selects hton2 or hton4 + // and enables proper wraparounds (mod 256, mod 65536, ...) +}; + + +enum amperr { + ampSuccess, + ampInvalidIndex, + ampInvalidName, + ampDuplicateName, + ampDescTooLong, + ampInvalidType, + ampInvalidLayer, + ampTCPandUDP, + ampUnknownKeyword, + ampSingleWordRequired, + ampRangeError, + ampPayloadLen, + ampPayloadType, + ampUnknownTag +}; + +enum fieldtypes { + Byte8, Byte16, Byte32, Flag_in_Byte, MultiBytes, MultiBytesHex, + TLV // TODO: different/standard TLV formats (Cisco CDP, LLCP, ...) +}; + + +struct fields { + struct fields *next; + char name[AUTOMOPS_MAX_NAME_LEN+1]; // Official name of field -- CASE INSENSITIVE + char shortdesc[AUTOMOPS_MAX_SHORTDESC_LEN+1]; // One-line description + char * longdesc; // Long (multiline) description (helptext) + enum fieldtypes type; // Field type corresponds to length + int constant; // 1: only default value allowed, not changeable + + int i; // unique internal field entry index (strongly monotonic increasing!) + // Note: first entry starts with 0. + + int index; // protocol field index; Note: First field has index 1. + // successive fields have same index in two cases: + // 1) several flags within same byte + // 2) several different valname/val pairs for same field index. In this + // case the successive field-entries must only contain the valname + // and a corresponding value. + + // may contain a reserved value *name*, usually used with multiple + // successive fields with same field index N. + char valname[AUTOMOPS_MAX_NAME_LEN+1]; + + u_int32_t + tlv_type, + tlv_len, + val, // default initial value + min, // range min value + max; // range max value + + int leftshift; // when type=Flag_in_Byte + + u_int8_t *str; // default initial characters or hex values (when type=MultiByte or TLV) + int str_s; // length of str +}; + + +// Each automops object identifies another dynamically specified protocol. +// +// Usage and structure: +// +// 1) Doubly linked list to store new (dynamically defined) protocols. +// Protocol definitions are typically loaded from a file and converted +// to an automops entry via parse_protocol() defined in parse_xml.c +// +// 2) When the user chooses one of these protocols to be used for a mops +// then best is to copy the whole automops to the current mops; this +// way the protocol's field values can be easily modified and +// automops_update() can be directly applied to that automops entity. +// +// If you cannot understand anything you are maybe already mausezahn'ed ;-) +// +struct automops { + struct automops *next; + struct automops *prev; + + char name[AUTOMOPS_MAX_NAME_LEN+1]; // Protocol name + char desc[AUTOMOPS_MAX_SHORTDESC_LEN+1]; // One-line description + + // Specify required and allowed layers using the definitions above + // for example MOPS_ETH, MOPS_SNAP, MOPS_dot1Q, MOPS_MPLS, + // MOPS_IP, MOPS_UDP, and MOPS_TCP + int + layers_on, // which layers are REQUIRED + layers_off; // which layers MUST be DISABLED because of conflicts + // Not mentioned layers are arbitrary (e. g. MOPS_dot1Q) + // Protocol-specific addresses + // Usually only destination address/port is specific but there are some + // exceptions (e. g. DHCP uses well known sp/dp pair). + // Value zero means ignore; otherwise copy to mops. + u_int16_t etype; // EtherType + u_int8_t proto; // IP protocol number + u_int8_t sa[6], da[6]; // source/destination MAC address + u_int32_t SA, DA; // source/destination IPv4 address + int sp, dp; // Well-known port numbers + + + int payload_type; // 0=none, 1=ascii, 2=hex, 3=any + char *payload; // default payload data (if above is true) + int payload_s; + + struct fields *field; // points to single linked list describing each field + // or NULL + + /// ---- internal data ----- + int defined_externally; // 0=built-in, 1=file, -1=undefined + int used; // number of mopses using this automops; + // = -1 when allocated + // = 0 when got valid data + // = >0 when used by some mopses +}; + + +struct automops * amp_head; + + +struct mops +{ + struct mops *next; + struct mops *prev; + + // *** The Header *** + // Management issues for TX + int state; // see above + int id; // UNIQUE Identifier (NOTE: MUST ALLOW -1) + int mz_system; // identifies user and system packets (such as ARP) + int verbose; // Be more or less verbose when processing that MOPS + char packet_name[MAX_MOPS_PACKET_NAME_LEN]; // Each packet must have unique name + char description[MAX_MOPS_DESCRIPTION_LEN]; // An optional short packet description + + pthread_t mops_thread; // associated transmission thread + pthread_t interval_thread; + + pthread_mutex_t mops_mutex; // mutex to savely access mops data + + char device[16]; // every packet could be sent through a different device + // NOTE that we do NOT store the index of device_list[] because after + // a re-discovery of the network interfaces the same index could map + // to a different physical network device. Instead the device's name + // does not change (however, might be not available, but then we report + // an error message and the user can assign another interface) + // + // See function mops_get_device_index() + + unsigned long count; // Desired number of packets to be sent. 0 means infinite. + unsigned long cntx; // This value actually counts sent packets. + // NOTE: Count _down_ for finite count, count _up_ for infinite count. + + struct timespec ndelay; // Inter-packet delay; contains two members: + // tv_sec and tv_nsec (0 to 999999999) + + struct timespec interval; // An optional global interval + int interval_used; // 0=none, 1=configured, 2=active (i. e. interval_thread is valid) + + struct timespec delay_sigma; // Standard deviation + + int delay_pd; // Which propability distribution (density) + // MOPS_DELAY_GAUSS + // MOPS_DELAY_EXP will result in a Poisson process with lambda=delay + + + int auto_delivery_off; // 0 means, the destination MAC address will be chosen automatically (for IP packets) + // depending on the IP destination address ('direct or indirect delivery', i. e. based + // on ARP). + // + // 1 means, the user-provided destination MAC address will be used. + + // ****************** + + // Data section + + int + use_ETHER, // if unset (=0) then complete raw frame given in frame[] + use_SNAP, // NOTE: use_SNAP=1 may indicate either 802.3+LLC alone or 802.3+LLC+SNAP + use_dot1Q, + use_MPLS, + use_IP, + use_UDP, + use_TCP; + + int // pointers to important positions + begin_IP, // marks byte position of IP header within frame + begin_UDP, // marks byte position of UDP header within frame + begin_TCP, // marks byte position of TCP header within frame + begin_MSG; // marks byte position of first message byte (=payload) within frame + + int // **** get payload (message) from a file **** + MSG_use_RAW_FILE, // 1 means update function should copy next chunk from file + MSG_use_HEX_FILE, // same but assumes file content such as "aa:bb:cc:f3:1e:..." + MSG_use_ASC_FILE; // same but interpretes file content as ASCII characters + // NOTE: if one of these are set to 1 then a filepointer is open !!! + + // A protocol descriptor (p_desc) is only used for some statically + // defined protocols. Originally intended for more complicated protocols + // such as DNS. + void * p_desc; // optionally points to protocol descriptor (e. g. for DNS, CDP, etc) + int p_desc_type; // identifies the exact type of p_desc + + + + // AutoMOPS provides a dynamic method to define new protocols. Here we need a pointer + // to the protocol definition for convenience and the complete protocol header field + // which is created by automops_update() + // + // Note: The used 'amp' should be memcpy'd for this particular mops + // because then we can store current PDU values here and the + // user can modify it later arbitrarily. + // + // Use automops_clone_automops() in automops.c for this. + // + struct automops *amp; // points to protocol definition + u_int8_t *amp_pdu; // contains the complete PDU as bytestring + int amp_pdu_s; + + + // Resulting frame: + u_int8_t frame[MAX_MOPS_FRAME_SIZE]; // will hold the complete frame + u_int32_t frame_s; // indicates the total frame size + + + // Ethernet parameters: + u_int8_t eth_dst[6]; + u_int8_t eth_src[6]; + int eth_src_israndom; // if set to 1 then the source address is to be randomized + u_int16_t eth_type; + u_int16_t eth_type_backup; // if original type must be restored (e. g. when removing MPLS labels) + + // 802.3 parameters: LLC/SNAP + u_int16_t eth_len; + u_int8_t eth_snap[16]; // AA-AA-03-- + int eth_snap_s; // usually 8 bytes + + + // 802.1Q VLAN Tag !!! NOTE: outer tag has lower index number (same byte-order as in frame[]) !!! + u_int8_t dot1Q[MAX_MOPS_DOT1Q_TAGS*4]; // All successive 802.1Q/P headers, 4 bytes per header: 0x8100, pri, cfi, id + int dot1Q_s; // how many bytes from above are really used + int dot1Q_isrange; // if 1, only the outer tag loops through the range. + int dot1Q_start; + int dot1Q_stop; + + + // MPLS label stack + u_int8_t mpls[MAX_MOPS_MPLS_TAGS*4]; // All successive labels + int mpls_s; // how many bytes from above are really used + int mpls_isrange; // if 1, only the outer tag loops through the range. + int mpls_start; + int mpls_stop; + + // IP parameters -- NOTE: Everything here is in HOST BYTE ORDER !!! + + u_int32_t ip_src; // By default interface address + u_int32_t ip_src_start; // start of range (HOST byte order => easy to count) + u_int32_t ip_src_stop; // stop of range (HOST byte order => easy to count) + int ip_src_isrange; // if set to 1 then the start/stop values above are valid. + int ip_src_israndom; // if set to 1 then the source address is to be randomized + u_int32_t ip_dst; // (HOST byte order) + u_int32_t ip_dst_start; // start of range (NOT network byte order => easy to count) + u_int32_t ip_dst_stop; // stop of range (NOT network byte order => easy to count) + int ip_dst_isrange; // if set to 1 then the start/stop values above are valid. + u_int16_t + ip_len, + ip_id, + ip_frag_offset, // 13 bit Offset: allowed values: 0..8191 + ip_sum; // TODO: provide variable 'ip_sum_false' to create false checksum for various tests + int ip_IHL_false; // Default=0, set to 1 if user configured own (typically false) header length + int ip_len_false; // Default=0, set to 1 if user configured own (typically false) total length + int ip_sum_false; // Default=0, set to 1 if user configured own (typcially false) checksum + u_int8_t + ip_version, + ip_IHL, // header length (4 bits = 0..15) + ip_tos, + ip_flags_RS, // 0|1 ... Reserved flag "must be zero" + ip_flags_DF, // 0|1 ... Don't Fragment + ip_flags_MF, // 0|1 ... More Fragments + ip_fragsize, // if >0 it activates auto-fragmentation + ip_frag_overlap, // if >0 then all fragments overlap. Must be multiple of 8 but smaller than fragsize. + ip_ttl, + ip_proto; + u_int8_t + ip_option[1024]; // Any IP Option used? + int ip_option_used; // >0 if yes. The exact number also indicates which option(s) used - see mops_ip.c + u_int32_t + ip_option_s; + + + // General L4 parameters: + u_int16_t + sp, dp, + sp_start, sp_stop, + dp_start, dp_stop; + int + sp_isrand, // if set to 1 then use random port number for each sent packet + dp_isrand, // if set to 1 then use random port number for each sent packet + sp_isrange, // if set to 1 then start/stop values above are valid + dp_isrange; // if set to 1 then start/stop values above are valid + + // UDP parameters + u_int16_t + udp_len, // includes header size (8 bytes) + udp_sum; + int udp_sum_false; // Default=0, set to 1 if user configured own (typcially false) checksum + int udp_len_false; // Default=0, set to 1 if user configured own (typcially false) length + + // TCP parameters (RFC 793) + // + // 0 1 2 3 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | Source Port | Destination Port | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | Sequence Number | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | Acknowledgment Number | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | Data | |U|A|P|R|S|F| | + // | Offset| Reserved |R|C|S|S|Y|I| Window | + // | | |G|K|H|T|N|N| | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | Checksum | Urgent Pointer | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | Options | Padding | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | data | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // + u_int32_t + tcp_seq, + tcp_seq_start, + tcp_seq_stop, + tcp_seq_delta, // Also used instead of an 'isrange' variable + tcp_ack, + tcp_ack_start, + tcp_ack_stop, + tcp_ack_delta; // Also used instead of an 'isrange' variable + u_int8_t + tcp_offset, // Header length in multiples of 32 bit (4 bit value, 0..15) + tcp_res, // reserved (4 bits) + tcp_ctrl_CWR, // 0|1 - Congestion Window Reduced [RFC-3168] + tcp_ctrl_ECE, // 0|1 - ECN-Echo [RFC-3168] + tcp_ctrl_URG, // 0|1 + tcp_ctrl_ACK, // 0|1 + tcp_ctrl_PSH, // 0|1 + tcp_ctrl_RST, // 0|1 + tcp_ctrl_SYN, // 0|1 + tcp_ctrl_FIN; // 0|1 + u_int16_t + tcp_win, + tcp_sum, + tcp_urg, + tcp_len; // Only needed for the checksum calculation and is not transmitted (host order!) + + int + tcp_sum_false, // Default=0, set to 1 if user configured own (typcially false) checksum + tcp_offset_false; // Default=0, set to 1 if user configured own (typcially false) offset + u_int8_t + tcp_option[1024]; + u_int32_t + tcp_option_s; + int tcp_option_used; // >0 if yes. The exact number also indicates which option(s) used - see mops_tcp.c + + + // Message: + u_int8_t msg[MAX_MOPS_MSG_SIZE]; + u_int32_t msg_s; + FILE *fp; // points to file if MSG_use_RAW_FILE or MSG_use_HEX_FILE or MSG_use_ASC_FILE is set to 1 + u_int32_t chunk_s; // max chunk size to be copied from file + + + // User-defined counters: + struct mops_counter counter[MAX_MOPS_COUNTERS_PER_PACKET]; + int used_counters; // number of currently defined counters + +}; + + + +struct mops_ext_arp +{ + u_int16_t hw_type; + u_int16_t pr_type; + u_int8_t hw_size; + u_int8_t pr_size; + u_int16_t opcode; + u_int8_t sender_mac[6]; + u_int8_t sender_ip[4]; + u_int8_t target_mac[6]; + u_int8_t target_ip[4]; + u_int16_t trailer; +}; + + + +struct mops_ext_bpdu // TODO +{ + u_int16_t id; + u_int8_t version; // 0=802.1D, 2=RSTP(802.1w) + u_int8_t bpdu_type; // 0=conf, 1=topology change (actually in big endian!), 2=RSTP/MSTP + u_int8_t flags; // X... .... = TCN ACK + // .X.. .... = Agreement + // ..X. .... = Forwarding + // ...X .... = Learning + // .... XX.. = Port Role (e. g. 11=Desgn) + // .... ..X. = Proposal + // .... ...X = TCN + u_int8_t root_id[8]; // Root BID + u_int32_t root_pc; // Root Path Cost + u_int8_t bridge_id[8]; // Own BID + u_int16_t port_id; // Port Identifier + u_int16_t message_age; // All timers are multiples of 1/256 sec. Thus times range from 0 to 256 seconds. + u_int16_t max_age; + u_int16_t hello_time; + u_int16_t f_delay; + u_int8_t trailer[8]; // either all-zero or 34:00:02:VLAN(16bit):00:00 when PVST+ + + int rstp; // 1 = RSTP + int pvst; // 1=PVST+ , 0 = 802.1D + int mstp; // 1 = Multiple Instance STP + +}; + +struct mops_ext_lldp { + int non_conform; // if 1 then the order of TLVs is arbitrary + int chassis_id_subtype; + int chassis_id_len; + u_int8_t *chassis_id; + int port_id_subtype; + int port_id_len; + u_int8_t *port_id; + int TTL; + int optional_tlvs_s; + u_int8_t *optional_tlvs; + +}; + +enum igmp_type {IGMP_GENERAL_QUERY, + IGMP_GSPEC_QUERY, + IGMP_V2_REPORT, + IGMP_V1_REPORT, + IGMP_LEAVE}; + +struct igmp_sa_struct { // For single linked list to hold unicast addresses for IGMPv3 query + u_int32_t sa; + struct igmp_sa_struct *next; +}; + +struct igmp_aux_struct { // For single linked list to hold auxilary data for IGMPv3 report + u_int32_t aux_data; + struct igmp_aux_struct *next; +}; + + +struct igmp_group_struct { // For single linked list to hold IGMPv3 group records + u_int8_t record_type; + u_int8_t aux_data_len; + u_int16_t nr_sources; + u_int32_t mcast_addr; + struct igmp_sa_struct *sa_list; + struct igmp_aux_struct *aux_list; + struct igmp_group_struct *next; +}; + + + +struct mops_ext_igmp { + int version; // internal, not in header + u_int8_t type; + u_int8_t max_resp_code; // equally: 'max response time' for IGMPv2 + u_int16_t sum; + int sum_false; // if '1' then sum contains user-provided checksum; if '0' then autocompute! + u_int32_t group_addr; + u_int8_t + resv4, // resv4 + S + QRV => one byte in IGMPv3 query + S, // S = Suppress Router-Side Processing + QRV; // QRV = Querier's Robustness Variable + u_int8_t resv8; // needed in IGMPv3 response AND IGMPv1 query+response + u_int16_t resv16; // needed in IGMPv3 response + u_int8_t QQIC; // Querier's Query Interval Code + u_int16_t nr_entries; // either number of sources (=query) or group records (=response) + struct igmp_sa_struct *sa_list; +}; + + +struct mops_ext_cdp // TODO +{ + u_int8_t id; + u_int16_t hw_type; +}; + +struct mops_ext_dns // TODO: complete +{ + // Main 16-bit fields + u_int16_t id; + u_int16_t num_queries; + u_int16_t num_answers; + u_int16_t num_author; + u_int16_t num_add; + u_int16_t type; + + // Flags (1 bit, except where noted) + u_int8_t qr; + u_int8_t opcode; // 4 bits + u_int8_t aa; + u_int8_t tc; + u_int8_t rd; + u_int8_t ra; + u_int8_t z; // 3 bits + u_int8_t rcode; // 4 bits + +}; + + +struct mops_ext_icmp // TODO +{ + u_int8_t id; + u_int16_t hw_type; +}; + +struct mops_ext_rtp +{ + // Vars to hold flag values: + u_int8_t v, + p, + x, // only sets the flag; if you really want an extension header also set "x_type" (see below) + cc, // csrc_count visible in header (has no further meaning, thus support for "wrong" headers) + cc_real, // real csrc_count (only used internally to create CSRC list) + m, + pt; // selects inter-packet delay and payload_s; + + u_int16_t sqnr; // initial sqnr + u_int32_t tst; // initial timestamp + u_int32_t ssrc; // !!! also used to identify measurement streams !!! + u_int32_t csrc[16]; // NOTE: only up to 15 CSRC's are allowed according RFC 3550 + + // additionally: + int tst_inc; // The increment of the tst (depends on codec) + u_int8_t payload[MOPS_RTP_MAX_PAYLOAD_SIZE]; // + int payload_s; // is the same as tst_inc when codec is G.711 but different with other codecs! + int source; // Optionally draw data from file or /dev/dsp or such [TODO] + int rtp_header_len; // will be set by mops_update_rtp() + // one optional header extension: + int x_type; // IMPORTANT: which extension header to use: 0 = none, 42 = Mausezahn, 1 = Aero + u_int8_t extension[64]; // a user configurable extension header [CURRENTLY UNUSED] +}; + + + +struct mops_ext_syslog //TODO +{ + u_int16_t hw_type; + u_int16_t pr_type; +}; + + +///////////////////////////////////////////////////////////////// + +struct mops *mp_head; // This global will point to the head of the mops list + +///////////////////////////////////////////////////////////////// +// MOPS Prototypes: + +inline void mops_hton2 (u_int16_t *host16, u_int8_t *net16); +inline void mops_hton4 (u_int32_t *host32, u_int8_t *net32); + +int mops_get_proto_info (struct mops *mp, char *layers, char *proto); + +// Inserts value in 'flag' (up to 7 bits are useful) into the target +// with an optional left-shift. For example if flag contains a 4-bit value +// and should be placed within the target in bit positions 3-6 like: +// +// 7 6 5 4 3 2 1 0 +// +--+--+--+--+--+--+--+--+ +// | | FLAGS | | | | +// +--+--+--+--+--+--+--+--+ +// +// then simply call: +// +// (void) mops_flags ( &target, &flag, 3 ); +// +// Note: +// 1) shift=0 means no shift +// 2) Because of speed we do not check if the arguments are reasonable +// +inline void mops_flags (u_int8_t *target, u_int8_t *flag, int shift); + +u_int16_t mops_sum16 (u_int16_t len, u_int8_t buff[]); + +struct mops * mops_init (); +struct mops * mops_alloc_packet (struct mops *cur); +struct mops * mops_delete_packet (struct mops *cur); +int mops_reset_packet(struct mops *cur); + +int mops_dump_all (struct mops* list, char* str); +struct mops * mops_search_name (struct mops* list, char *key); +struct mops * mops_search_id (struct mops* list, u_int32_t key); + +void mops_delete_all (struct mops* list); +void mops_cleanup (struct mops* list); + +// State functions +int mops_state (struct mops *mp); +int mops_is_active (struct mops *mp); +void mops_set_conf (struct mops *mp); +void mops_set_active (struct mops *mp); +void mops_set_seqact (struct mops *mp); +int mops_is_seqact (struct mops *mp); +int mops_is_any_active (struct mops *mp); + +// For debugging purposes +int mops_print_frame (struct mops *mp, char *str); + +// sets UDP or TCP checksum within mp->frame +// TODO: copying the whole segment is ugly and slow; +// make it more efficient and realize it in-place. +// +int mops_get_transport_sum (struct mops *mp); + +// returns new counter index for given packet +// or -1 if all counters used already +int mops_get_counter (struct mops *mp); + +// This is the very basic MOPS update function. It simply updates the whole +// MOPS frame specified by pointer mp. If you only want to update specific +// details then please see the other related specialized functions which are +// faster. +int mops_update (struct mops *mp); + +int mops_set_defaults (struct mops *mp); + +// Get global device index for a given device name. +int mops_get_device_index(char *devname); + +// Assign device-specific addresses to packet. +int mops_use_device(struct mops * clipkt, int i); + +// Find and returns a new unique packet id +// If none can be found, returns -1. +int mops_get_new_pkt_id (struct mops *mp); + +// Simply sets specified 'layer switches' in struct mops to zero +int mops_clear_layers (struct mops *mp, int l); + +// Transmission functions +int mops_tx_simple (struct mops *mp); +void *mops_tx_thread_native (void *arg); +void *mops_interval_thread (void *arg); +void *mops_sequence_thread (void *arg); + + +int mops_destroy_thread (struct mops *mp); + +// Utility functions for packet headers (aka *** METHODS *** for the object-oriented nerds) +int mops_dot1Q_remove (struct mops *mp, int k); +int mops_dot1Q_nocfi (struct mops *mp, int k); +int mops_dot1Q_cfi (struct mops *mp, int k); +int mops_dot1Q (struct mops *mp, int i, int m, u_int16_t v, u_int16_t c); + +int mops_mpls_remove (struct mops *mp, int j); +int mops_mpls_bos (struct mops *mp, int k); +int mops_mpls_nobos (struct mops *mp, int k); +int mops_mpls(struct mops *mp, int i, int m, u_int32_t Label, u_int8_t Exp, u_int8_t TTL); + +int mops_ip_get_dst_mac(struct device_struct *dev, u_int8_t *ip, u_int8_t *mac); +int mops_ip_dscp(struct mops *mp, char *argv); +int mops_ip_tos (struct mops* mp, int ipp, int tos, int mbz); +int mops_ip_option_ra (struct mops* mp, int value); +int mops_ip_option_remove_all (struct mops* mp); + +u_int32_t mops_tcp_complexity_sqnr (struct mops * mp); +u_int32_t mops_tcp_complexity_acknr (struct mops * mp); + +// Prints current flag settings in the provided string 'str'. +int mops_tcp_flags2str (struct mops* mp, char *str); + +int mops_tcp_add_option (struct mops* mp, + int mss, + int sack, + int scale, + u_int32_t tsval, + u_int32_t tsecr); + + +////////////////////////////////////////////////////////////////////////////// +// +// ****** The following are important to easily create new packet types ****** +// +////////////////////////////////////////////////////////////////////////////// + +// Adds single byte to msg +int mops_msg_add_byte (struct mops *mp, u_int8_t data); + +// Adds bit field in *previous* msg-byte using optional left-shift +int mops_msg_add_field (struct mops *mp, u_int8_t data, int shift); + +// Adds two bytes in network byte order to msg +int mops_msg_add_2bytes (struct mops *mp, u_int16_t data); + +// Adds four bytes in network byte order to msg +int mops_msg_add_4bytes (struct mops *mp, u_int32_t data); + +// Adds string of bytes with lenght len +int mops_msg_add_string (struct mops *mp, u_int8_t *str, int len); + +// Add counter to message +int mops_msg_add_counter (struct mops *mp, + int random, // 1=random, 0=use start/stop/step + u_int32_t start, // HOST BYTE ORDER + u_int32_t stop, // HOST BYTE ORDER + u_int32_t step, // HOST BYTE ORDER + int bytes // number of bytes used (1|2|4) - selects hton2 or hton4 + ); + +// Returns 0 if identical, 1 if different +int compare_ip (u_int8_t *ip1, u_int8_t *ip2); + +// Returns 0 if identical, 1 if different +int compare_mac (u_int8_t *mac1, u_int8_t *mac2); + +// Converts a 'struct timespec' value into a human readable string +int timespec2str(struct timespec *t, char *str); + +// ------------------------------------------------------------------------------- + +// Add protocol descriptor of type ptype +// +// Smart behaviour: If a p_desc has been already assigned, this function +// clears and frees everything before assigning another p_desc structure. +// +int mops_ext_add_pdesc (struct mops *mp, int ptype); + +// Create msg based on p_desc data. +// After that call mops_update and the frame is complete. +int mops_ext_update (struct mops *mp); + +// Delete any protocol descriptor +int mops_ext_del_pdesc (struct mops *mp); + +// Initialization functions for p_desc +int mops_init_pdesc_arp(struct mops *mp); +int mops_init_pdesc_bpdu(struct mops *mp); +int mops_init_pdesc_cdp(struct mops *mp); +int mops_init_pdesc_dns(struct mops *mp); +int mops_init_pdesc_icmp(struct mops *mp); +int mops_init_pdesc_igmp(struct mops *mp); +int mops_init_pdesc_lldp(struct mops *mp); +int mops_init_pdesc_syslog(struct mops *mp); +int mops_init_pdesc_rtp(struct mops *mp); + +int mops_create_igmpv2 (struct mops *mp, + int override, // normally zero, but if '1' the user want to override defaults + int igmp_type, // IGMP_GENERAL_QUERY, IGMP_GSPEC_QUERY, IGMP_V2_REPORT, IGMP_V1_REPORT, IGMP_LEAVE + int mrt, // max response time + int sum, //-1 means auto-compute, other values means 'use this user-defined value' + u_int32_t group_addr); + + +// Update functions for p_desc => msg +int mops_update_arp(struct mops * mp); +int mops_update_bpdu(struct mops * mp); +int mops_update_igmp (struct mops * mp); +int mops_update_lldp (struct mops * mp); +int mops_update_rtp (struct mops * mp); +int mops_update_rtp_dynamics (struct mops * mp); + +// Utility functions for p_desc +int mops_pdesc_mstrings (char *dst, char* argv[], int argc, int max); +int mops_pdesc_1byte (u_int8_t *dst, char* usr, int spec, int min, int max); +int mops_pdesc_2byte (u_int16_t *dst, char* usr, int spec, int min, int max); +int mops_pdesc_4byte (u_int32_t *dst, char* usr, int spec, unsigned long int min, unsigned long int max); +int mops_pdesc_mac (u_int8_t *dst, char* usr); +int mops_pdesc_ip (u_int8_t *dst, char* usr); + +// Other p_desc related functions +int mops_create_bpdu_bid(struct mops * mp, int pri, int esi, char *mac, int bid_or_rid); +int mops_create_bpdu_trailer (struct mops * mp, u_int16_t vlan); +int mops_lldp_tlv (u_int8_t *tlv, int type, int len, u_int8_t *value); +int mops_lldp_tlv_chassis (u_int8_t *tlv, int subtype, int len, u_int8_t *cid); +int mops_lldp_tlv_port (u_int8_t *tlv, int subtype, int len, u_int8_t *pid); +int mops_lldp_tlv_TTL (u_int8_t *tlv, int ttl); +int mops_lldp_tlv_end (u_int8_t *tlv); +int mops_lldp_opt_tlv_bad (struct mops *mp, int type, int badlen, int len, u_int8_t *value); +int mops_lldp_opt_tlv_org (struct mops *mp, int oui, int subtype, int len, u_int8_t *inf); +int mops_lldp_opt_tlv_chassis (struct mops *mp, int subtype, int len, u_int8_t *cid); +int mops_lldp_opt_tlv_port (struct mops *mp, int subtype, int len, u_int8_t *pid); +int mops_lldp_opt_tlv_TTL (struct mops *mp, int ttl); +int mops_lldp_opt_tlv_vlan (struct mops *mp, int vlan); +int mops_lldp_opt_tlv (struct mops *mp, int type, int len, u_int8_t *value); +int mops_lldp_opt_tlv_end (struct mops *mp) ; + + +/////////////////////////// Services ///////////////////////////// + +// ARP Service: Resolves MAC address of given IP address and interface +int service_arp(char *dev, u_int8_t *ip, u_int8_t *mac); + +int mops_rx_arp (); +void *rx_arp (void *arg); +void got_arp_packet (u_char *args, const struct pcap_pkthdr *header, const u_char *packet); + + +//////////////////// directmops prototypes: /////////////////////////// +int mops_direct(char* dev, int mops_type, char* argstring); + + +//////////////////// automops prototypes: ////////////////////////////////// + + +struct automops * automops_init(); +struct automops * automops_alloc_protocol(); +struct automops * automops_delete_protocol(); +struct automops * automops_search_protocol(); +int automops_dump_all (struct automops* list); +void automops_set_defaults(struct automops * cur); +struct fields * automops_add_field (struct automops *amp); +void automops_field_set_defaults(struct fields *f); +int automops_delete_fields (struct automops *amp); +int mops_str2layers(char *d); +int amp_add_pentry (struct automops *amp, int xntag, char *d); +int amp_add_fentry (struct automops *amp, struct fields *f, int xntag, char *d); +int amp_checkindex(struct automops *amp, int i); +int amp_str2type(char *d); +int amp_type2str(int t, char *s); +struct fields * amp_getfield_byname(struct automops *amp, char *d); +struct automops * amp_getamp_byname(struct automops *head, char *d); +// Creates an independent automops element for mops +// (it will be not part of any linked list so, next=prev=NULL) +struct automops * automops_clone_automops(struct automops * amp); +int amperr2str (int e, char *s); + +// Create automops PDU within *mp based on data in *amp +// +int automops_update (struct mops *mp, struct automops *amp); +void automops_cleanup (struct automops *list); + +char * mapfile (char *fn); + +////////////////////////// XML support ////////////////////////////// +// +// + + +// Simple stack needed to check proper XML nesting. +// The corresponding methods are defined at the bottom. +struct xnstack { + int data[XN_MAX_STACK]; + int cursize; +}; + +enum xml_tags { // mention all allowed tags here! + xml_protocol, + xml_field, + xml_name, + xml_desc, + xml_requires, + xml_conflicts, + xml_payloadtype, + xml_payload, + xml_payloadhex, + xml_index, + xml_longdesc, + xml_type, + xml_constant, + xml_value, + xml_valname, + xml_min, + xml_max, + xml_tlvt, + xml_tlvl, + xml_lshift +}; + + +int xml_check_parent(int t, int p); +int xml_tag2int (char *t); + +int parse_protocol (char *p); +int xml_getnext_tag (char *p, char *t); +int xml_canonic (char *p); +int xml_get_data (char *p, char *t); +int xml_readin (struct automops *amp, char *p); + +void xnstack_init(struct xnstack *s); +int xnstack_get_top(struct xnstack *s); +int xnstack_push(struct xnstack *s, int d); +int xnstack_pop(struct xnstack *s); +int xnstack_size(struct xnstack *s); + +#endif + diff --git a/src/mops_checksums.c b/src/mops_checksums.c new file mode 100644 index 0000000..be20f21 --- /dev/null +++ b/src/mops_checksums.c @@ -0,0 +1,128 @@ +/* + * Mausezahn - A fast versatile traffic generator + * Copyright (C) 2008-2010 Herbert Haas + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 as published by the + * Free Software Foundation. + * + * 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, see http://www.gnu.org/licenses/gpl-2.0.html + * +*/ + +#include "mz.h" +#include "mops.h" + + + + +// -- TOC: -- +// +// u_int16_t mops_sum16 (u_int16_t len, u_int8_t buff[]) +// int mops_get_transport_sum (struct mops *mp) + + +////////////////////////////////////////////////////////////////////////////////// +// +// See also: +// +// RFC1071 - Computing the Internet checksum +// +////////////////////////////////////////////////////////////////////////////////// + + + +// Generic 16-bit checksum code as required for IP and other headers. +// The checksum is calculated over buff[] which is of length len. +// +// RETURN VALUE: The checksum! (Validated - correct!!!) +// +// Example: t16 = mops_sum16 (20, &mp->frame[fp]); +// +u_int16_t mops_sum16 (u_int16_t len, u_int8_t buff[]) +{ + + u_int16_t word16; + u_int32_t sum=0; + u_int16_t i; + + // make 16 bit words out of every two adjacent 8 bit words in the packet and add them up + for (i=0; i>16) + sum = (sum & 0xFFFF)+(sum >> 16); + + // one's complement the result + sum = ~sum; + + return ((u_int16_t) sum); +} + + + + + +// sets UDP or TCP checksum within mp[]->frame +// TODO: copying the whole segment is ugly and slow; +// make it more efficient and realize it in-place. +// +int mops_get_transport_sum(struct mops *mp) +{ + u_int8_t buf[MAX_PAYLOAD_SIZE]; + u_int16_t len; + int udp_used; + + u_int16_t sum; + + udp_used = mp->use_UDP; // 0 or 1, 0 means TCP + + // IP Pseudoheader (12 Bytes) + mops_hton4(&mp->ip_src, &buf[0]); + mops_hton4(&mp->ip_dst, &buf[4]); + buf[9]=0x00; + + + // Copy segment + if (udp_used) + { + buf[10]=0x11; // proto UDP (17 dec) + len = mp->udp_len; + mops_hton2(&len, &buf[11]); + memcpy(&buf[13], &mp->frame[mp->begin_UDP], len); + // reset checksum to zero + buf[19] = 0x00; + buf[20] = 0x00; + sum = mops_sum16(len+12, buf); + // insert checksum in UDP header (in frame) + mops_hton2 (&sum, &mp->frame[(mp->begin_UDP)+7]); + + } + else + { + buf[10]=0x06; // proto TCP + len = mp->ip_len - mp->ip_IHL; + mops_hton2((u_int16_t*)&len, &buf[11]); + memcpy(&buf[13], &mp->frame[mp->begin_TCP], len); + // reset checksum to zero + buf[29] = 0x00; + buf[30] = 0x00; + sum = mops_sum16(len+12, buf); + // insert checksum in TCP header (in frame) + mops_hton2 (&sum, &mp->frame[(mp->begin_TCP)+17]); + } + + + return 0; +} + diff --git a/src/mops_dot1Q.c b/src/mops_dot1Q.c new file mode 100644 index 0000000..1a22439 --- /dev/null +++ b/src/mops_dot1Q.c @@ -0,0 +1,131 @@ +/* + * Mausezahn - A fast versatile traffic generator + * Copyright (C) 2008-2010 Herbert Haas + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 as published by the + * Free Software Foundation. + * + * 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, see http://www.gnu.org/licenses/gpl-2.0.html + * +*/ + +#include "mz.h" +#include "mops.h" + + +// Remove 802.1Q tags from packet mp +// +// k indicates which tag to be removed (1..n) +// k=0 means: remove all tags! +// +// RETURN VALUE: 1 upon failure, 0 upon success +int mops_dot1Q_remove (struct mops *mp, int k) +{ + int a,b,n; + + if (k==0) { + mp->dot1Q_s=0; + mp->use_dot1Q=0; + return 0; + } + + n = mp->dot1Q_s/4; // n = total number of tags + if (k>n) return 1; + + if (k==1) { // only delete the single tag + mp->dot1Q_s=0; + mp->use_dot1Q=0; + return 0; + } + + // we have more than one tag: + // + if (k==n) { // remove last tag (of several) + mp->dot1Q_s -=4; + return 0; + } + + // remove some non-ending tag: 0, 1, 2, 3 + a = (k-1)*4; // target + b = k*4; // source (what should be copied) + memcpy(&mp->dot1Q[a], &mp->dot1Q[b], (n-k)*4); + mp->dot1Q_s -=4; + + return 0; +} + + +// Unset CFI in tag k where k=1..n +int mops_dot1Q_nocfi (struct mops *mp, int k) +{ + int n; + + n = mp->dot1Q_s/4; // n = total number of tags + if (k>n) return 1; + + mp->dot1Q[((k-1)*4)+2] &=0xef; // unset CFI (0xef = 1110 1111) + return 0; +} + + +// Set CFI in tag k where k=1..n +int mops_dot1Q_cfi (struct mops *mp, int k) +{ + int n; + + n = mp->dot1Q_s/4; // n = total number of tags + if (k>n) return 1; + + mp->dot1Q[((k-1)*4)+2] |=0x10; // set CFI (0x10 = 0001 0000) + return 0; +} + + +// Assign 802.1Q tag with +// v ... VLAN +// c ... CoS +// i ... tag position (starting from zero!) +// +// m ... modification: 1 = dot1Q_s is not changed +// +// NOTE: +// When called from for-loop to add all tags the total size dot1Q_s +// is updated continuously, therefore use m=1. +// +// But when changing a particular tag within an existing 802.1Q stack +// the total number of tags does not change, therefore use m=0. +// +// RETURN VALUE: 0 upon success, 1 upon failure +// +int mops_dot1Q (struct mops *mp, int i, int m, u_int16_t v, u_int16_t c) +{ + u_int8_t *ptr, c8; + + if (i>=MAX_MOPS_DOT1Q_TAGS) return 1; // max number of tags, see definitions in mops.h + if ((v>4095)||(c>7)) return 1; // greater values do not make sense + + // Format: 0x8100 CoS-CFI-VLAN + // where c=CoS, v=VLAN + c8 = (u_int8_t) c; + mp->dot1Q[4*i+0]= 0x81; + mp->dot1Q[4*i+1]= 0x00; + ptr = (u_int8_t*) &v; + mp->dot1Q[4*i+3]=*ptr; + mp->dot1Q[4*i+2]=*(ptr+1); + mp->dot1Q[4*i+2]^= (c8 << 5); + + if (m) { + mp->dot1Q_s=4*(1+i); // NOTE: dot1Q_s = current tag position + 1 + if (mp->dot1Q_s) mp->use_dot1Q = 1; + } + + return 0; +} + diff --git a/src/mops_ext.c b/src/mops_ext.c new file mode 100644 index 0000000..0a32a05 --- /dev/null +++ b/src/mops_ext.c @@ -0,0 +1,466 @@ +/* + * Mausezahn - A fast versatile traffic generator + * Copyright (C) 2008-2010 Herbert Haas + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 as published by the + * Free Software Foundation. + * + * 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, see http://www.gnu.org/licenses/gpl-2.0.html + * +*/ + + + +#include "mz.h" +#include "mops.h" + + + +// Add protocol descriptor of type ptype +// +// Smart behaviour: +// +// - If the desired p_desc has been assigned already, we leave everything +// as it is and return to the calling function (return 0). +// +// - If a p_desc of another type has been already assigned, this function +// clears and frees everything before assigning another p_desc structure. +// +int mops_ext_add_pdesc (struct mops *mp, int ptype) +{ + + // 1. check if desired p_desc is already assigned + if ( (mp->p_desc != NULL) && (mp->p_desc_type == ptype) ) { + return 0; + } + + // 2. remove older p_desc + if (mp->p_desc_type != MOPS_NO_PDESC) { + if (mops_ext_del_pdesc (mp)) return 1; + } + + // 3. allocate and assign a p_desp + switch (ptype) { + case MOPS_ARP: + mp->p_desc = ( MOPS_EXT_ARP ) malloc ( sizeof (struct mops_ext_arp ) ); + mp->p_desc_type = MOPS_ARP; + mops_init_pdesc_arp(mp); + break; + case MOPS_BPDU: + mp->p_desc = ( MOPS_EXT_BPDU ) malloc ( sizeof (struct mops_ext_bpdu ) ); + mp->p_desc_type = MOPS_BPDU; + mops_init_pdesc_bpdu(mp); + break; + case MOPS_CDP: + mp->p_desc = ( MOPS_EXT_CDP ) malloc ( sizeof (struct mops_ext_cdp ) ); + mp->p_desc_type = MOPS_CDP; + mops_init_pdesc_cdp(mp); + break; + case MOPS_DNS: + mp->p_desc = ( MOPS_EXT_DNS ) malloc ( sizeof (struct mops_ext_dns ) ); + mp->p_desc_type = MOPS_DNS; + mops_init_pdesc_dns(mp); + break; + case MOPS_ICMP: + mp->p_desc = ( MOPS_EXT_ICMP ) malloc ( sizeof (struct mops_ext_icmp ) ); + mp->p_desc_type = MOPS_ICMP; + mops_init_pdesc_icmp(mp); + break; + case MOPS_IGMP: + mp->p_desc = ( MOPS_EXT_IGMP ) malloc ( sizeof (struct mops_ext_igmp ) ); + mp->p_desc_type = MOPS_IGMP; + mops_init_pdesc_igmp(mp); + break; + case MOPS_RTP: + mp->p_desc = ( MOPS_EXT_RTP ) malloc ( sizeof (struct mops_ext_rtp ) ); + mp->p_desc_type = MOPS_RTP; + mops_init_pdesc_rtp(mp); + break; + case MOPS_LLDP: + mp->p_desc = ( MOPS_EXT_LLDP ) malloc ( sizeof (struct mops_ext_lldp ) ); + ((struct mops_ext_lldp *)mp->p_desc)->chassis_id = NULL; + ((struct mops_ext_lldp *)mp->p_desc)->port_id = NULL; + ((struct mops_ext_lldp *)mp->p_desc)->optional_tlvs = NULL; + mp->p_desc_type = MOPS_LLDP; + mops_init_pdesc_lldp(mp); + break; + case MOPS_SYSLOG: + mp->p_desc = ( MOPS_EXT_SYSLOG ) malloc ( sizeof (struct mops_ext_syslog ) ); + mp->p_desc_type = MOPS_SYSLOG; + mops_init_pdesc_syslog(mp); + break; + default: + return 1; // unknown protocol + } + + if (mp->p_desc == NULL) { + fprintf (stderr, "mz/mops: could not allocate memory for mops element!\n"); + mp->p_desc_type = MOPS_NO_PDESC; + return 1; + } + + return 0; +} + + +// Delete any protocol descriptor +// 1) Free memory +// 2) Reset p_desc and p_desc_type +// +int mops_ext_del_pdesc (struct mops *mp) +{ + + mp->p_desc_type = MOPS_NO_PDESC; + if (mp->p_desc==NULL) return 1; // already NULL pointer, nothing to free() + + switch (mp->p_desc_type) { + case MOPS_ARP: + free ( (MOPS_EXT_ARP) mp->p_desc ); + break; + case MOPS_BPDU: + free ( (MOPS_EXT_BPDU) mp->p_desc ); + break; + case MOPS_CDP: + free ( (MOPS_EXT_CDP) mp->p_desc ); + break; + case MOPS_DNS: + free ( (MOPS_EXT_DNS) mp->p_desc ); + break; + case MOPS_ICMP: + free ( (MOPS_EXT_ICMP) mp->p_desc ); + break; + case MOPS_IGMP: + free ( (MOPS_EXT_IGMP) mp->p_desc ); + break; + case MOPS_RTP: + free ( (MOPS_EXT_RTP) mp->p_desc ); + break; + case MOPS_LLDP: + if ( ((struct mops_ext_lldp *) mp->p_desc)->chassis_id != NULL) + free ( ((struct mops_ext_lldp *) mp->p_desc)->chassis_id); + if ( ((struct mops_ext_lldp *) mp->p_desc)->port_id != NULL) + free ( ((struct mops_ext_lldp *) mp->p_desc)->port_id); + if ( ((struct mops_ext_lldp *) mp->p_desc)->optional_tlvs != NULL) + free ( ((struct mops_ext_lldp *) mp->p_desc)->optional_tlvs); + free ( (MOPS_EXT_LLDP) mp->p_desc ); + break; + case MOPS_SYSLOG: + free ( (MOPS_EXT_SYSLOG) mp->p_desc ); + break; + case MOPS_NO_PDESC: // already cleared? + break; + + /* nothing */ + } + + mp->p_desc = NULL; + return 0; +} + + +// Create msg based on p_desc data. +// After that call mops_update and the frame is complete. +int mops_ext_update (struct mops *mp) +{ + + switch (mp->p_desc_type) { + case MOPS_ARP: + mops_update_arp(mp); + break; + case MOPS_BPDU: + mops_update_bpdu(mp); + break; + case MOPS_CDP: + break; + case MOPS_DNS: + break; + case MOPS_ICMP: + break; + case MOPS_IGMP: + mops_update_igmp(mp); + break; + case MOPS_RTP: + mops_update_rtp(mp); + break; + case MOPS_LLDP: + mops_update_lldp(mp); + break; + case MOPS_SYSLOG: + break; + case MOPS_NO_PDESC: + return 0; // OK! + break; + default: + return 1; // Unknown value!? + } + + return 0; +} + + +//////// General parameter update functions - modify a single parameter of p_desc structure +// +// 'Standardized' return values: +// +// MOPS_PDESC_LOW Value smaller than lower bound - but will set +// MOPS_PDESC_HIGH Value larger than upper bound - but will set +// +// MOPS_PDESC_OVERFLOW Value exceeded possible range +// +// MOPS_PDESC_NO_MAC Invalid MAC address +// MOPS_PDESC_NO_IP Invalid IP address +// +// MOPS_PDESC_FAILURE Unspecified problem +// MOPS_PDESC_SUCCESS = 0 Value assigned properly +// +// 'Standardized' format: +// +// mops_pdesc_function ( *PDESC_VAR , USER_STRING , LIMITS ) + + + + +// Assign one or more strings to a single string +// Practical example: Concatenate multiple tokens from the CLI +// Will never copy more than 'max' bytes to 'dst' +// +// EXAMPLE: +// +// mops_pdesc_mstrings (clipkt->description, argv, argc, 20); +// +int mops_pdesc_mstrings (char *dst, char* argv[], int argc, int max) +{ + int i; + char tmp[10000]; // should be sufficient for all purposes here + + dst[0]=0x00; + tmp[0]=0x00; + + for (i=0; i(10000-strlen(tmp))) // The '1+' counts for the additional space + return MOPS_PDESC_OVERFLOW; + else + { + strncat(tmp, argv[i], 80); // Enforcing a maximum word length + strcat(tmp, " "); // We get only the tokens, not the spaces inbetween + } + } + + strncpy(dst, tmp, max); + if (strlen(tmp)>max) return MOPS_PDESC_OVERFLOW; + + return MOPS_PDESC_SUCCESS; +} + + + + + +// Assign decimal or hexadecimal u_int8_t value, depending on spec +// spec can be 0=dec or 1=hex +int mops_pdesc_1byte (u_int8_t *dst, char* usr, int spec, int min, int max) +{ + u_int32_t i; + int retval = MOPS_PDESC_SUCCESS; + + if ((max>255)||(min>255)) return MOPS_PDESC_FAILURE; + + if (spec==0) + { + i = (u_int32_t) str2int (usr); + } + else + { + i = (u_int32_t) xstr2int (usr); + } + + if (i>255) return MOPS_PDESC_OVERFLOW; + if (imax) + retval = MOPS_PDESC_HIGH; + + *dst = (u_int8_t) i; + + return retval; +} + + + +// Assign decimal or hexadecimal u_int16_t value, depending on spec +// spec can be 0=dec or 1=hex +int mops_pdesc_2byte (u_int16_t *dst, char* usr, int spec, int min, int max) +{ + u_int32_t i; + int retval = MOPS_PDESC_SUCCESS; + + if ((max>0xffff)||(min>0xffff)) return MOPS_PDESC_FAILURE; + + if (spec==0) + { + i = (u_int32_t) str2int (usr); + } + else + { + i = (u_int32_t) xstr2int (usr); + } + + if (i>0xffff) return MOPS_PDESC_OVERFLOW; + if (imax) + retval = MOPS_PDESC_HIGH; + + *dst = (u_int16_t) i; + + return retval; +} + + +// Assign decimal or hexadecimal u_int32_t value, depending on spec +// spec can be 0=dec or 1=hex +int mops_pdesc_4byte (u_int32_t *dst, char* usr, int spec, unsigned long int min, unsigned long int max) +{ + unsigned long int i; + int retval = MOPS_PDESC_SUCCESS; + + if ((max>0xffffffff)||(min>0xffffffff)) return MOPS_PDESC_FAILURE; + + if (spec==0) + { + i = str2int (usr); + } + else + { + i = xstr2int (usr); + } + + if (i>0xffffffff) return MOPS_PDESC_OVERFLOW; + if (imax) + retval = MOPS_PDESC_HIGH; + + *dst = (u_int32_t) i; + + return retval; +} + + + +// Maps MAC address given in 'usr' (e. g. 00:11:22:aa:bb:cc) into 'dst' +// which is an u_int8_t array. +// +// Returns MOPS_PDESC_FAILURE (=1) upon invalid MAC address +// +int mops_pdesc_mac (u_int8_t *dst, char* usr) +{ + u_int8_t tmp[6]; + + // temporarily backup current value + memcpy ((void*) tmp, (void*) dst, 6); + + if (str2hex_mac (usr, dst)) + { + // restore original value + memcpy ((void*) dst, (void*) tmp, 6); + return MOPS_PDESC_FAILURE; + }; + + return MOPS_PDESC_SUCCESS; +} + + +// Maps an IP address string into an byte-array u_int8_t ip[4] +// Note: the destination is NOT an u_int32_t !!! +int mops_pdesc_ip (u_int8_t *dst, char* usr) +{ + u_int8_t tmp[4]; + int i, len, j=0; + + // Check if format is correct IPv4: + len = strlen(usr); + for (i=0; ip_desc == NULL) return 1; // p_desc not properly assigned + + + return 0; +} + + +int mops_init_pdesc_dns(struct mops *mp) +{ + if (mp->p_desc == NULL) return 1; // p_desc not properly assigned + + + return 0; +} + + +int mops_init_pdesc_icmp(struct mops *mp) +{ + if (mp->p_desc == NULL) return 1; // p_desc not properly assigned + + + return 0; +} + + + +int mops_init_pdesc_syslog(struct mops *mp) +{ + if (mp->p_desc == NULL) return 1; // p_desc not properly assigned + + return 0; +} + + + + + + diff --git a/src/mops_ext_arp.c b/src/mops_ext_arp.c new file mode 100644 index 0000000..79c33a4 --- /dev/null +++ b/src/mops_ext_arp.c @@ -0,0 +1,239 @@ +/* + * Mausezahn - A fast versatile traffic generator + * Copyright (C) 2008-2010 Herbert Haas + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 as published by the + * Free Software Foundation. + * + * 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, see http://www.gnu.org/licenses/gpl-2.0.html + * +*/ + + + +#include "mz.h" +#include "mops.h" + + +// Initialization function - specify defaults here! +// +int mops_init_pdesc_arp(struct mops *mp) +{ + + struct mops_ext_arp * pd; + + char tmac[6] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + + if (mp->p_desc == NULL) return 1; // p_desc not properly assigned + + pd = mp->p_desc; + + pd->hw_type = 0x0001; + pd->pr_type = 0x800; + pd->hw_size = 6; + pd->pr_size = 4; + pd->opcode = 0x0001; // request + memcpy ((void*) pd->sender_mac, (void*) tx.eth_src, 6); + memcpy ((void*) pd->target_mac, (void*) tmac, 6); + memcpy ((void*) pd->sender_ip, (void*) &tx.ip_src, 4); + memcpy ((void*) pd->target_ip, (void*) &tx.ip_src, 4); + + pd->trailer = 18; // default is 18 byte trailer to get a 60 byte packet (instead of only 42) + + return 0; +} + + + + + + + + +///////////////////////////////////////////////////////////////////////////////// +/////////////////////////////// Update functions //////////////////////////////// +// +// **** Here is a summary of mops tool functions: **** +// +// Adds single byte to msg +// int mops_msg_add_byte (struct mops *mp, u_int8_t data); +// +// Adds bit field in *previous* msg-byte using optional left-shift +// int mops_msg_add_field (struct mops *mp, u_int8_t data, int shift); +// +// Adds two bytes in network byte order to msg +// int mops_msg_add_2bytes (struct mops *mp, u_int16_t data); +// +// Adds four bytes in network byte order to msg +// int mops_msg_add_4bytes (struct mops *mp, u_int32_t data); +// +// Adds string of bytes with lenght len +// int mops_msg_add_string (struct mops *mp, u_int8_t *str, int len); +// +// Add counter to message +// int mops_msg_add_counter (struct mops *mp, +// int random, // 1=random, 0=use start/stop/step +// u_int32_t start, // HOST BYTE ORDER +// u_int32_t stop, // HOST BYTE ORDER +// u_int32_t step, // HOST BYTE ORDER +// int bytes // number of bytes used (1|2|4) - selects hton2 or hton4 +// ); +// +// + + +int mops_update_arp(struct mops * mp) +{ + + struct mops_ext_arp * pd; + int i; + + pd = mp->p_desc; + if (pd==NULL) return 1; // no valid pointer to a p_desc + + mp->msg_s = 0; // important! Otherwise the msg would get longer and longer after each call! + + mops_msg_add_2bytes (mp, pd->hw_type); + mops_msg_add_2bytes (mp, pd->pr_type); + mops_msg_add_byte (mp, pd->hw_size); + mops_msg_add_byte (mp, pd->pr_size); + mops_msg_add_2bytes (mp, pd->opcode); + mops_msg_add_string (mp, pd->sender_mac, 6); + mops_msg_add_string (mp, pd->sender_ip, 4); + mops_msg_add_string (mp, pd->target_mac, 6); + mops_msg_add_string (mp, pd->target_ip, 4); + + // Avoid buffer problems: + if (pd->trailer>2000) + { + pd->trailer=2000; + } + + for (i=0; itrailer; i++) + { + mops_msg_add_byte (mp, 0x00); + } + + return 0; +} + + + +// ARP Service: Resolves MAC address of given IP address and interface +// The result is stored in the last argument 'mac'. +// +// EXAMPLE: +// +// u_int8_t mymac[6]; +// int ip[4]={192,186,0,1}; +// +// service_arp("eth0", ip, mymac); +// /* now mymac should contain the MAC address */ +// +// RETURN VALUE: 0 upon success +// 1 upon error +// +int service_arp(char *dev, u_int8_t *ip, u_int8_t *mac) +{ + int i, devind=0, dev_found=0; + struct mops * mp; + struct mops_ext_arp * pd; + char tmac[6] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + struct arp_table_struct *cur; + + // MOPS framework already available? + if (mp_head==NULL) return 1; + + // Get list index for that device: + for (i=0; ipacket_name, "sysARP_service", 15); + mp->mz_system=1; // indicates MZ private packet + if (mops_ext_add_pdesc (mp, MOPS_ARP)) { + return 1; // error + } + } + } + + // Configure ARP request: + mops_clear_layers(mp, MOPS_ALL); + mops_init_pdesc_arp(mp); + + mp->verbose = 0; + mp->use_ETHER = 1; + mp->count = 1; + mp->eth_type = 0x806; + mz_strncpy(mp->device, dev, 16); + + pd = mp->p_desc; + memcpy ((void*) pd->sender_mac, (void*) device_list[devind].mac_mops, 6); + memcpy ((void*) pd->target_mac, (void*) tmac, 6); + memcpy ((void*) pd->sender_ip, (void*) device_list[devind].ip_mops, 4); + pd->target_ip[0]=ip[0]; + pd->target_ip[1]=ip[1]; + pd->target_ip[2]=ip[2]; + pd->target_ip[3]=ip[3]; + + mops_update_arp(mp); + mops_set_conf(mp); + + // Send ARP request + + if (mops_tx_simple (mp)) { + fprintf(stderr, " Warning: sysARP_service failed!\n"); + return 1; + } + + usleep(100000); // wait 100 ms + // Now hopefully we got an ARP response; + // look up in ARP cache + + cur=device_list[devind].arp_table; + while(cur!=NULL) { + if ((cur->sip[0]==ip[0]) && + (cur->sip[1]==ip[1]) && + (cur->sip[2]==ip[2]) && + (cur->sip[3]==ip[3])) { // entry found! + for (i=0; i<6; i++) { + mac[i] = cur->smac[i]; + } + } + cur=cur->next; + } + + return 0; +} diff --git a/src/mops_ext_bpdu.c b/src/mops_ext_bpdu.c new file mode 100644 index 0000000..8cb180d --- /dev/null +++ b/src/mops_ext_bpdu.c @@ -0,0 +1,242 @@ +/* + * Mausezahn - A fast versatile traffic generator + * Copyright (C) 2008-2010 Herbert Haas + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 as published by the + * Free Software Foundation. + * + * 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, see http://www.gnu.org/licenses/gpl-2.0.html + * +*/ + + + +#include "mz.h" +#include "mops.h" + + +// Initialization function - specify defaults here! +// +int mops_init_pdesc_bpdu(struct mops *mp) +{ + struct mops_ext_bpdu * pd; + int i; + + + if (mp->p_desc == NULL) return 1; // p_desc not properly assigned + pd = mp->p_desc; + + // 1. - Initialize Ethernet header + str2hex("01:80:C2:00:00:00",mp->eth_dst, 6); + + // 2. - Initialize BPDU fields + pd->id = 0; + pd->version = 0; // 0=802.1D, 2=RSTP(802.1w) + pd->bpdu_type = 0x80; // 0=conf, 0x80=topology change, 2=RSTP/MSTP + pd->flags = 0; // X... .... = TCN ACK + // .X.. .... = Agreement + // ..X. .... = Forwarding + // ...X .... = Learning + // .... XX.. = Port Role (e. g. 11=Desgn) + // .... ..X. = Proposal + // .... ...X = TCN + + i = mops_get_device_index(tx.device); + if (i!=-1) { // found + memcpy((void*) &pd->root_id[2], (void*) device_list[i].mac_mops, 6); + memcpy((void*) &pd->bridge_id[2], (void*) device_list[i].mac_mops, 6); + } else { + str2hex("00:00:00:00:00:00", &pd->root_id[2], 6); + str2hex("00:00:00:00:00:00", &pd->bridge_id[2], 6); + } + + pd->root_id[0] = 0x00; + pd->root_id[1] = 0x00; + + pd->bridge_id[0] = 0x00; + pd->bridge_id[1] = 0x00; + + pd->root_pc = 0; // Root Path Cost + pd->port_id = 0; // Port Identifier + pd->message_age = 0; // All timers are multiples of 1/256 sec. Thus times range from 0 to 256 seconds. + pd->max_age = 5120; // 20 seconds + pd->hello_time = 512; + pd->f_delay = 3840; + + str2hex("00:00:00:00:00:00:00:00", pd->trailer, 8); + // either all-zero or 00:00:00:00:02:VLAN(16bit) when PVST+ + pd->rstp = 0; // 1 = RSTP + pd->pvst = 0; // 1=PVST+ , 0 = 802.1D + pd->mstp = 0; // 1 = Multiple Instance STP + + return 0; +} + + +///////////////////////////////////////////////////////////////////////////////// +/////////////////////////////// Update functions //////////////////////////////// +// +// **** Here is a summary of mops tool functions: **** +// +// Adds single byte to msg +// int mops_msg_add_byte (struct mops *mp, u_int8_t data); +// +// Adds bit field in *previous* msg-byte using optional left-shift +// int mops_msg_add_field (struct mops *mp, u_int8_t data, int shift); +// +// Adds two bytes in network byte order to msg +// int mops_msg_add_2bytes (struct mops *mp, u_int16_t data); +// +// Adds four bytes in network byte order to msg +// int mops_msg_add_4bytes (struct mops *mp, u_int32_t data); +// +// Adds string of bytes with lenght len +// int mops_msg_add_string (struct mops *mp, u_int8_t *str, int len); + +int mops_update_bpdu(struct mops * mp) +{ + + struct mops_ext_bpdu * pd; + + pd = mp->p_desc; + if (pd==NULL) return 1; // no valid pointer to a p_desc + mp->msg_s = 0; // important! Otherwise the msg would get longer and longer after each call! + + + // NOTE: the length field does not include the trailer! + if (pd->pvst) + { + str2hex("01:00:0C:CC:CC:CD", mp->eth_dst, 6); + mp->eth_len=50; + str2hex("aa:aa:03:00:00:0c:01:0b",mp->eth_snap, 8); + mp->eth_snap_s = 8; + } + else + { + str2hex("01:80:C2:00:00:00",mp->eth_dst, 6); + mp->eth_len=38; + str2hex("42:42:03",mp->eth_snap, 3); + mp->eth_snap_s = 3; + } + + mops_msg_add_2bytes (mp, pd->id); + mops_msg_add_byte (mp, pd->version); + mops_msg_add_byte (mp, pd->bpdu_type); + + if (pd->bpdu_type & 0x80) // if TCN then don't add more fields + { + if (pd->pvst) mp->eth_len=12; else mp->eth_len=7; + } + else + { + mops_msg_add_byte (mp, pd->flags); + mops_msg_add_string (mp, pd->root_id, 8); + mops_msg_add_4bytes (mp, pd->root_pc); + mops_msg_add_string (mp, pd->bridge_id, 8); + mops_msg_add_2bytes (mp, pd->port_id); + mops_msg_add_2bytes (mp, pd->message_age); + mops_msg_add_2bytes (mp, pd->max_age); + mops_msg_add_2bytes (mp, pd->hello_time); + mops_msg_add_2bytes (mp, pd->f_delay); + } + + // we always add the trailer + mops_msg_add_string (mp, pd->trailer, 8); + + return 0; +} + + + +// Create RID or BID based on priority, ext-sys-id, and MAC address. +// The last parameter selects BID (0) or RID (1) +// +// pri .... 0-15 +// esi .... 0-4095 +// mac .... XX:XX:XX:XX:XX:XX or interface name +// +// NOTE: Invalid parameters will result in default values +// +// RETURN VALUE: Only informational; identifies which parameter +// was errourness, using the following values: +// +// 0 ... all parameters valid +// 1 ... priority exceeded range +// 2 ... ext-sys-id exceeded range +// 3 ... invalid MAC address or invalid interface +// 4 ... other + +int mops_create_bpdu_bid(struct mops * mp, int pri, int esi, char *mac, int bid_or_rid) +{ + int i; + struct mops_ext_bpdu * pd = mp->p_desc; + u_int8_t rid[8]; + u_int16_t p16; + + if ((pri<0)||(pri>15)) return 1; + if ((esi<0)||(esi>4095)) return 2; + + if (mac!=NULL) { + // first check if an interface is specified: + i = mops_get_device_index(mac); + if (i!=-1) { // found + memcpy((void*) &rid[2], (void*) device_list[i].mac_mops, 6); + } + else { // MAC address given? + if (mops_pdesc_mac(&rid[2], mac)) { + return 3; + } + } + } else { // mac==NULL + // use MAC of default interface! + i = mops_get_device_index(tx.device); + if (i!=-1) { // found + memcpy((void*) &rid[2], (void*) device_list[i].mac_mops, 6); + } + else { + str2hex("00:00:00:00:00:00", &rid[2], 6); + return 4; + } + } + + // now prepend pri, esi + + p16 = pri; + p16 <<= 12; + p16 |= esi; + + mops_hton2 (&p16, &rid[0]); + if (bid_or_rid) + memcpy((void*) pd->root_id, (void*) rid, 8); + else + memcpy((void*) pd->bridge_id, (void*) rid, 8); + return 0; +} + + +int mops_create_bpdu_trailer (struct mops * mp, u_int16_t vlan) +{ + struct mops_ext_bpdu * pd = mp->p_desc; + + // PVST+ requires a trailer with either all-zero + // or 00:00:00:00:02:VLAN(16bit) + + // trailer already initialized with zeroes + pd->trailer[0]=0x00; + pd->trailer[1]=0x00; + pd->trailer[2]=0x00; + pd->trailer[3]=0x00; + pd->trailer[4]=0x02; + pd->trailer[5]=0x00; + pd->trailer[6]=0x00; + mops_hton2 (&vlan, &pd->trailer[5]); + + return 0; +} diff --git a/src/mops_ext_igmp.c b/src/mops_ext_igmp.c new file mode 100644 index 0000000..3bd83fa --- /dev/null +++ b/src/mops_ext_igmp.c @@ -0,0 +1,270 @@ +/* + * Mausezahn - A fast versatile traffic generator + * Copyright (C) 2008-2010 Herbert Haas + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 as published by the + * Free Software Foundation. + * + * 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, see http://www.gnu.org/licenses/gpl-2.0.html + * +*/ + +#include "mz.h" +#include "mops.h" +#include "cli.h" + + + +// Initialization function - specify defaults here! +// +int mops_init_pdesc_igmp(struct mops *mp) +{ + struct mops_ext_igmp * pd; + + if (mp->p_desc == NULL) return 1; // p_desc not properly assigned + pd = mp->p_desc; + + pd->version = 2; + pd->type = IGMP_V2_REPORT; + pd->max_resp_code = 0; + pd->sum_false = 0; + pd->group_addr = 0; // TODO: consider initialization with well-known mcast address? + pd->sa_list = NULL; + + return 0; +} + + + + +// IGMPv2 query and report (see RFC 2236) +// +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Type | Max Resp Time | Checksum | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Group Address | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// +// +// IGMPv1 query and report (see RFC 1112) +// +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// |Version| Type | Unused | Checksum | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Group Address | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// +// Therefore IGMPv1 only uses IGMP_GENERAL_QUERY or IGMP_V1_REPORT and mrt=0. +// +int mops_create_igmpv2 (struct mops *mp, + int override, // normally zero, but if '1' the user want to override defaults + int igmp_type, // IGMP_GENERAL_QUERY, IGMP_GSPEC_QUERY, IGMP_V2_REPORT, IGMP_V1_REPORT, IGMP_LEAVE + int mrt, // max response time (unused == 0 for IGMPv1) + int sum, //-1 means auto-compute, other values means 'use this user-defined value' + u_int32_t group_addr) +{ + struct mops_ext_igmp * pd; + + // --- sanity check params --- + // Do we have a valid pointer? + if (mp->p_desc == NULL) return 1; // p_desc not properly assigned + pd = mp->p_desc; + if (mrt>255) return 1; + if (sum>65535) return 1; + // --------------------------- + + // +++ Set values in pdesc ++++++++++++++++++++++++ + pd->version = 2; + + switch (igmp_type) { + case IGMP_GENERAL_QUERY: + pd->type = 0x11; + pd->group_addr = 0; + pd->max_resp_code = mrt; + break; + case IGMP_GSPEC_QUERY: + pd->type = 0x11; + pd->group_addr = group_addr; + pd->max_resp_code = mrt; + break; + case IGMP_V2_REPORT: + pd->type = 0x16; + pd->group_addr = group_addr; + if (override) pd->max_resp_code = mrt; else pd->max_resp_code = 0; + break; + case IGMP_V1_REPORT: + pd->type = 0x12; + pd->group_addr = group_addr; + if (override) pd->max_resp_code = mrt; else pd->max_resp_code = 0; + break; + case IGMP_LEAVE: + pd->type = 0x17; + pd->group_addr = group_addr; + if (override) pd->max_resp_code = mrt; else pd->max_resp_code = 0; + break; + default: + return 1; // unknown type + } + + if (sum==-1) { + pd->sum_false = 0; + } else { + pd->sum_false = 1; + pd->sum = sum; // mops_update_igmp() will process this! + } + + // ++++++++++++++++++++++++++++++++++++++++++++++++ + + return 0; +} + + + + + + + + + +int mops_update_igmp (struct mops * mp) +{ + struct mops_ext_igmp * pd; + + pd = mp->p_desc; + if (pd==NULL) return 1; // no valid pointer to a p_desc + mp->msg_s = 0; // important! Otherwise the msg would get longer and longer after each call! + u_int16_t sum; + + switch (pd->version) { + + case 1: + break; + + case 2: + // IGMPv2 query and report (see RFC 2236) + // + // 0 1 2 3 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | Type | Max Resp Time | Checksum | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | Group Address | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // + mops_msg_add_byte (mp, pd->type); + mops_msg_add_byte (mp, pd->max_resp_code); + if (pd->sum_false) + mops_msg_add_2bytes (mp, pd->sum); // used defined (typically wrong) checksum + else // must be set to zero before checksum computation + mops_msg_add_2bytes (mp, 0x0000); + mops_msg_add_4bytes (mp, pd->group_addr); + if (pd->sum_false==0) { + sum = mops_sum16 (mp->msg_s, mp->msg); + mops_hton2(&sum, &mp->msg[2]); + } + break; + + case 3: + break; + + + default: + return 1; + } + + + + return 0; +} + + + + + + + + +// IGMP messages are encapsulated in IPv4 datagrams, with an IP protocol +// number of 2. Every IGMP message described in this document is sent +// with an IP Time-to-Live of 1, IP Precedence of Internetwork Control +// (e.g., Type of Service 0xc0), and carries an IP Router Alert option +// [RFC-2113] in its IP header. + + + +// +// +// IGMPv3 report message (see RFC 3376) +// +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Type = 0x22 | Reserved | Checksum | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Reserved | Number of Group Records (M) | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | | +// . . +// . Group Record [1] . +// . . +// | | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | | +// . . +// . Group Record [2] . +// . . +// | | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | . | +// . . . +// | . | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | | +// . . +// . Group Record [M] . +// . . +// | | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// +// +// +// +// IGMPv3 query message (see RFC 3376) +// +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Type = 0x11 | Max Resp Code | Checksum | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Group Address | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Resv |S| QRV | QQIC | Number of Sources (N) | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Source Address [1] | +// +- -+ +// | Source Address [2] | +// +- . -+ +// . . . +// . . . +// +- -+ +// | Source Address [N] | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// +// +// + +// +// +// +// diff --git a/src/mops_ext_lldp.c b/src/mops_ext_lldp.c new file mode 100644 index 0000000..d11fab0 --- /dev/null +++ b/src/mops_ext_lldp.c @@ -0,0 +1,430 @@ +/* + * Mausezahn - A fast versatile traffic generator + * Copyright (C) 2008-2010 Herbert Haas + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 as published by the + * Free Software Foundation. + * + * 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, see http://www.gnu.org/licenses/gpl-2.0.html + * +*/ + + +#include "mz.h" +#include "mops.h" +#include "cli.h" + + + +// Initialization function - specify defaults here! +// +int mops_init_pdesc_lldp(struct mops *mp) +{ + struct mops_ext_lldp * pd; + int i=0; + + if (mp->p_desc == NULL) return 1; // p_desc not properly assigned + pd = mp->p_desc; + + mp->eth_type = 0x88cc; + str2hex("01:80:c2:00:00:0e", mp->eth_dst, 6); + mp->ndelay.tv_sec = 30; + mp->ndelay.tv_nsec = 0; + + // get interface index for that packet + i = mops_get_device_index(mp->device); + + pd->non_conform = 0; + pd->chassis_id_subtype = 4; // MAC address + if (pd->chassis_id==NULL) pd->chassis_id = malloc(255); + if (pd->chassis_id==NULL) return 1; + memcpy((void*) pd->chassis_id, (void*) device_list[i].mac_mops, 6); + pd->chassis_id_len = 6; + pd->port_id_subtype = 5; // interface name + pd->port_id_len = strnlen(mp->device, 15); + if (pd->port_id==NULL) pd->port_id = malloc(255); + if (pd->port_id==NULL) return 1; + memcpy((void*) pd->port_id, (void*) mp->device, pd->port_id_len); + pd->TTL = 120; + pd->optional_tlvs_s = 0; + if (pd->optional_tlvs==NULL) pd->optional_tlvs = malloc(MAX_LLDP_OPT_TLVS); + if (pd->optional_tlvs == NULL) return 1; + return 0; +} + + + +int mops_update_lldp (struct mops * mp) +{ + struct mops_ext_lldp * pd; + + pd = mp->p_desc; + if (pd==NULL) return 1; // no valid pointer to a p_desc + mp->msg_s = 0; // important! Otherwise the msg would get longer and longer after each call! + + switch (pd->non_conform) { + + case 0: // Derive mandatory TLVs from struct entries and insert optional_tlvs + mp->msg_s += mops_lldp_tlv_chassis(mp->msg, + pd->chassis_id_subtype, + pd->chassis_id_len, + pd->chassis_id); + mp->msg_s += mops_lldp_tlv_port(&mp->msg[mp->msg_s], + pd->port_id_subtype, + pd->port_id_len, + pd->port_id); + mp->msg_s += mops_lldp_tlv_TTL(&mp->msg[mp->msg_s], + pd->TTL); + if (pd->optional_tlvs_s) { + memcpy((void*) &mp->msg[mp->msg_s], + (void*) pd->optional_tlvs, + pd->optional_tlvs_s); + mp->msg_s += pd->optional_tlvs_s; + } + mp->msg_s += mops_lldp_tlv_end(&mp->msg[mp->msg_s]); + break; + + case 1: // User defined ALL TLVs (i. e. ignore struct entries) + if (pd->optional_tlvs_s) { + memcpy((void*) &mp->msg[mp->msg_s], + (void*) pd->optional_tlvs, + pd->optional_tlvs_s); + mp->msg_s += pd->optional_tlvs_s; + } + mp->msg_s += mops_lldp_tlv_end(&mp->msg[mp->msg_s]); + break; + default: + return 1; + } + return 0; +} + + +/////////////////////////////////////////////////////////////////////////////// +// // +// Below are utility functions to creade the LLDPU. From these, the // +// following can be used for the optional part: // +// // +// // +// // +// // + +/* + +int mops_lldp_opt_tlv (struct mops *mp, int type, int len, u_int8_t *value) +int mops_lldp_opt_tlv_chassis (struct mops *mp, int subtype, int len, u_int8_t *cid) +int mops_lldp_opt_tlv_port (struct mops *mp, int subtype, int len, u_int8_t *pid) +int mops_lldp_opt_tlv_TTL (struct mops *mp, int ttl) +int mops_lldp_opt_tlv_vlan (struct mops *mp, int vlan) +int mops_lldp_opt_tlv_end (struct mops *mp) +int mops_lldp_opt_tlv_bad (struct mops *mp, int type, int badlen, int len, u_int8_t *value) +int mops_lldp_opt_tlv_org (struct mops *mp, int oui, int subtype, int len, u_int8_t *inf) + +*/ + + +// // +// // +// // +// // +// // +// // +// // +// // +/////////////////////////////////////////////////////////////////////////////// + + + + + +// Creates a LLDP TLV for a given type number and value string. The result will +// be written into 'tlv'. +// +// NOTE: len must be given and indicates the length of value. +// +// RETURN VALUE: - Total number of bytes of this tlv +// - 0 upon error +// +int mops_lldp_tlv (u_int8_t *tlv, int type, int len, u_int8_t *value) +{ + u_int16_t tl=0, tln=0; + + if ((type>127) || (len>511)) return 0; + + tl = type << 9; + tl |= len; + + tln = htons(tl); + memcpy((void*) tlv, (void*) &tln, 2); + memcpy((void*) &tlv[2], (void*) value, len); + + return len+2; +} + + +// Same as above but **adds the TLVs to the 'pd->optional_tlvs' string.** +// It also checks if MAX_LLDP_OPT_TLVS is exceeded. +// +// NOTE: first argument is a pointer to that mops! +// +// RETURN VALUE: - 0 upon error (no more space) +// - Total number of bytes written +// +int mops_lldp_opt_tlv (struct mops * mp, int type, int len, u_int8_t *value) +{ + struct mops_ext_lldp * pd; + u_int8_t tmp[MAX_LLDP_OPT_TLVS]; // this *must* be sufficient in length + int l; + + if (mp->p_desc == NULL) return 1; // p_desc not properly assigned + pd = mp->p_desc; + + l = mops_lldp_tlv (tmp, type, len, value); + + if ((MAX_LLDP_OPT_TLVS - pd->optional_tlvs_s)< (l+1)) return -1; // not enough space + memcpy((void*) (pd->optional_tlvs + pd->optional_tlvs_s), (void*) tmp, l); + pd->optional_tlvs_s += l; + return l; +} + + +/////////////////////////////////////////////////////////////////////////////// +// // +// // +// // +/////////////////////////////////////////////////////////////////////////////// + + + +// Creates a Chassis ID TLV -- the first mandatory TLV. +// The result will be written into 'tlv'. +// +// RETURN VALUE: - Total number of bytes within tlv +// - 0 upon error +// +int mops_lldp_tlv_chassis (u_int8_t *tlv, int subtype, int len, u_int8_t *cid) +{ + u_int8_t tmp[256]; + + if ((len>255) || (subtype>255)) return 0; + + tmp[0] = (u_int8_t) subtype; + memcpy((void*) (tmp+1), (void*) cid, len); + return mops_lldp_tlv(tlv, 1, len+1, tmp); + +} + +// Same but for optional tlv string +int mops_lldp_opt_tlv_chassis (struct mops *mp, int subtype, int len, u_int8_t *cid) +{ + u_int8_t tmp[256]; + + if ((len>255) || (subtype>255)) return 0; + tmp[0] = (u_int8_t) subtype; + memcpy((void*) (tmp+1), (void*) cid, len); + return mops_lldp_opt_tlv(mp, 1, len+1, tmp); +} + + + +/////////////////////////////////////////////////////////////////////////////// +// // +// // +// // +/////////////////////////////////////////////////////////////////////////////// + + + +// Creates a Port ID TLV -- the second mandatory TLV. +// The result will be written into 'tlv'. +// +// RETURN VALUE: - Total number of bytes within tlv +// - 0 upon error +// +int mops_lldp_tlv_port (u_int8_t *tlv, int subtype, int len, u_int8_t *pid) +{ + u_int8_t tmp[256]; + + if ((len>255) || (subtype>255)) return 0; + + tmp[0] = (u_int8_t) subtype; + memcpy((void*) (tmp+1), (void*) pid, len); + return mops_lldp_tlv(tlv, 2, len+1, tmp); +} + +// Same but for optional tlv string +int mops_lldp_opt_tlv_port (struct mops *mp, int subtype, int len, u_int8_t *pid) +{ + u_int8_t tmp[256]; + + if ((len>255) || (subtype>255)) return 0; + tmp[0] = (u_int8_t) subtype; + memcpy((void*) (tmp+1), (void*) pid, len); + return mops_lldp_opt_tlv(mp, 2, len+1, tmp); +} + + +/////////////////////////////////////////////////////////////////////////////// +// // +// // +// // +/////////////////////////////////////////////////////////////////////////////// + + +// Creates a TTL TLV -- the third mandatory TLV. +// The result will be written into 'tlv'. +// +// RETURN VALUE: - Total number of bytes within tlv +// - 0 upon error +// +int mops_lldp_tlv_TTL (u_int8_t *tlv, int ttl) +{ + u_int16_t ttlh=0, ttln=0; + + if (ttl>0xffff) return 0; + + ttlh = (u_int16_t) ttl; + ttln = htons(ttlh); + + return mops_lldp_tlv(tlv, 3, 2, (u_int8_t*) &ttln); +} + + +// Same but for optional tlv string +int mops_lldp_opt_tlv_TTL (struct mops *mp, int ttl) +{ + u_int16_t ttlh=0, ttln=0; + + if (ttl>0xffff) return 0; + + ttlh = (u_int16_t) ttl; + ttln = htons(ttlh); + + return mops_lldp_opt_tlv(mp, 3, 2, (u_int8_t*) &ttln); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// // +// // +/////////////////////////////////////////////////////////////////////////////// + + +// Creates an End of LLDPDU TLV -- the last mandatory TLV. +// The result will be written into 'tlv'. +// +// RETURN VALUE: - Total number of bytes within tlv +// - 0 upon error +// +int mops_lldp_tlv_end (u_int8_t *tlv) +{ + tlv[0] = 0x00; + tlv[1] = 0x00; + return 2; +} + +// Same but for optional tlv string +int mops_lldp_opt_tlv_end (struct mops *mp) +{ + struct mops_ext_lldp * pd; + + if (mp->p_desc == NULL) return 1; // p_desc not properly assigned + pd = mp->p_desc; + + if ((MAX_LLDP_OPT_TLVS - pd->optional_tlvs_s) > 2) { + pd->optional_tlvs[pd->optional_tlvs_s++] = 0x00; + pd->optional_tlvs[pd->optional_tlvs_s++] = 0x00; + return 2; + } else + return 0; +} + + +/////////////////////////////////////////////////////////////////////////////// +// // +// // +// // +/////////////////////////////////////////////////////////////////////////////// + + +// Creates a 'bad' LLDP TLV for a given type number and value string. +// The result will be appended into 'pd->optional_tlvs' +// +// NOTE: 'len' must be given and indicates the TRUE length of value. +// 'badlen' can be any number and is used as official length within the TLV +// +// RETURN VALUE: - Total number of bytes within tlv +// - 0 upon error +// +int mops_lldp_opt_tlv_bad (struct mops *mp, + int type, + int badlen, + int len, + u_int8_t *value) +{ + u_int16_t tl=0, tln=0; + u_int8_t tlv[512]; + struct mops_ext_lldp * pd = mp->p_desc; + + if ((type>127) || (len>511) || (badlen>511)) return 0; + if ((MAX_LLDP_OPT_TLVS - pd->optional_tlvs_s) < (len+3)) return 0; + + tl = type << 9; + tl |= badlen; + + tln = htons(tl); + memcpy((void*) tlv, (void*) &tln, 2); + memcpy((void*) &tlv[2], (void*) value, len); + // this detour has historical reasons ;-) + memcpy((void*) (pd->optional_tlvs + pd->optional_tlvs_s), (void*) tlv, len+2); + pd->optional_tlvs += len+2; + + return len+2; +} + + + +// Creates a Organisational-specific TLV -- the second mandatory TLV. +// The result will be appended into 'pd->optional_tlvs' +// +// RETURN VALUE: - Total number of bytes within tlv +// - 0 upon error +// +int mops_lldp_opt_tlv_org (struct mops *mp, + int oui, + int subtype, + int len, + u_int8_t *inf) +{ + u_int8_t tmp[512]; + u_int8_t *x; + u_int32_t oui_n = (u_int32_t) oui; + + if ((len>507) || (subtype>255) || (oui_n>0xffffff)) return 0; + + x = (u_int8_t *) &oui_n; + tmp[0] = *(x+2); + tmp[1] = *(x+1); + tmp[2] = *x; + tmp[3] = (u_int8_t) subtype; + memcpy((void*) (tmp+4), (void*) inf, len); + return mops_lldp_opt_tlv(mp, 127, len+4, tmp); +} + + +int mops_lldp_opt_tlv_vlan (struct mops *mp, + int vlan) +{ + u_int16_t vid; + if (vlan>0xffff) return 0; // yes, we also allow VLAN IDs > 4095 + vid = htons(vlan); + return mops_lldp_opt_tlv_org (mp, 0x80c2, 1, 2, (u_int8_t*) &vid); +} + diff --git a/src/mops_ext_rtp.c b/src/mops_ext_rtp.c new file mode 100644 index 0000000..2bc3813 --- /dev/null +++ b/src/mops_ext_rtp.c @@ -0,0 +1,243 @@ +/* + * Mausezahn - A fast versatile traffic generator + * Copyright (C) 2008-2010 Herbert Haas + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 as published by the + * Free Software Foundation. + * + * 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, see http://www.gnu.org/licenses/gpl-2.0.html + * +*/ + +#include "mz.h" +#include "mops.h" +#include "cli.h" + + + +// Initialization function - specify defaults here! +// +int mops_init_pdesc_rtp(struct mops *mp) +{ + struct mops_ext_rtp * pd; + + if (mp->p_desc == NULL) return 1; // p_desc not properly assigned + pd = mp->p_desc; + + // set RTP defaults + pd->v = 2; + pd->p = 0; + pd->x = 0; + pd->cc = 0; + pd->m = 0; + + pd->pt = 8; // 0=PCMU, 8=PCMA + pd->sqnr = 0; + pd->tst = 0; + pd->tst_inc = 160; + pd->ssrc = mz_rand32(); // Default Mausezahn stream would be 0xCAFEBABE + pd->source = 0; // don't use /dev/dsp (but user may configure source = DSP_SOURCE) + pd->cc_real = 0; + + pd->x_type = 0; // no extension by default + + // General packet parameters + mp->dp = 30000; + mp->sp = 30002; + mp->ndelay.tv_sec = 0; + mp->ndelay.tv_nsec = 20000000; + + memset(&pd->payload, 0x00, MOPS_RTP_MAX_PAYLOAD_SIZE); + + return 0; +} + + +/* + * Standard RTP header according RFC 3550 + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * |V=2|P|X| CC |M| PT | sequence number | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | timestamp | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | synchronization source (SSRC) identifier | + * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ + * | contributing source (CSRC) identifiers | + * | .... | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * !!! NOTE !!! -- This function should be used only to prepare the RTP + * header once. It does not update dynamic fields. To update dynamic fields + * each time a subsequent RTP packet is sent, use the function + * mops_update_rtp_dynamics(). + * + */ +int mops_update_rtp (struct mops * mp) +{ + struct mops_ext_rtp * pd; + int i,j; + + pd = mp->p_desc; + if (pd==NULL) return 1; // no valid pointer to a p_desc + mp->msg_s = 0; // !! IMPORTANT !! Otherwise the msg would get longer and longer after each call! + + // 1st byte + mops_msg_add_byte (mp, pd->cc); + mops_msg_add_field (mp, pd->v, 6); + mops_msg_add_field (mp, pd->p, 5); + mops_msg_add_field (mp, pd->x, 4); + + // 2nd byte + mops_msg_add_byte (mp, pd->pt); + mops_msg_add_field (mp, pd->m, 7); + + // remaining + mops_msg_add_2bytes (mp, pd->sqnr); + mops_msg_add_4bytes (mp, pd->tst); + mops_msg_add_4bytes (mp, pd->ssrc); + + // Add CSRC list? + if ((j=pd->cc_real)) { + if (j>16) { j=16; pd->cc_real=16; } // silent self healing if desired :-) + for (i=0; icsrc[i]); + } + pd->rtp_header_len = 12 + j*4; + + /* + * Add Extension header? + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | defined by profile | length | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | header extension | + * | .... | + */ + + switch (pd->x_type) { + case 0: // none + break; + case 1: // set aero, 8 bytes in total -- TODO -- + break; + case 42: // Mausezahn extension header: + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | MOPS_RTP_EXT_MZID | length=4 | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | TX-timestamp sec | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | TX-timestamp nsec | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | Estimated Peer TX-timestamp sec | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | Estimated Peer TX-timestamp nsec | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + mops_msg_add_2bytes (mp, MOPS_RTP_EXT_MZID); + mops_msg_add_2bytes (mp, 2); + mops_msg_add_4bytes (mp, 0); // only placeholders, must be updated each packet + mops_msg_add_4bytes (mp, 0); // only placeholders, must be updated each packet + mops_msg_add_4bytes (mp, 0); // only placeholders, must be updated each packet + mops_msg_add_4bytes (mp, 0); // only placeholders, must be updated each packet + + pd->rtp_header_len += 20; + break; + default: + return 1; + break; // paranoid? + } + + // Now add the payload + switch (pd->pt) { + case 0: + case 8: + mp->msg_s = 160 + pd->rtp_header_len; // simply set total RTP PDU length (the RTP payload is still undefined) + mp->ndelay.tv_sec = 0; + mp->ndelay.tv_nsec = 20000000; + break; + default: + break; + } + + return 0; +} + + + +// This function directly updates the dynamic RTP fields +// within the mops frame (=be quick here). +// +// This function is typically called from within the transmission loops, +// see e. g. mops_tx_thread_native() +// +// This includes: +// +// - RTP SQNR +// - RTP Timestamp +// - Mausezahn extension header if any +// - The RTP payload +// +int mops_update_rtp_dynamics (struct mops * mp) +{ + struct mops_ext_rtp * pd; + struct timespec ct; + int j, i = mp->begin_MSG; + + pd = mp->p_desc; if (pd==NULL) return 1; + + + // The following variables must be incremented AFTER assignment to frame, + // so the initial values are also used! + // + mops_hton2 (&pd->sqnr, &mp->frame[i+2]); + pd->sqnr++; + + mops_hton4 (&pd->tst, &mp->frame[i+4]); + pd->tst += pd->tst_inc; + + + // Extension header: + // Timestamp must be updated BEFORE assignment to frame + // + switch (pd->x_type) { + case 42: // Mausezahn extension header: Update timestamps + j = i + pd->rtp_header_len; // points to first byte of timestamp of MZ extension header + clock_gettime(CLOCK_MONOTONIC, &ct); + mops_hton4 ((u_int32_t*) &ct.tv_sec, &mp->frame[j-16]); + mops_hton4 ((u_int32_t*) &ct.tv_nsec, &mp->frame[j-12]); +//[TODO] **** estimated peer timestamp **** PSEUDOCODE FOLLOWING: +// if (peer_exists) { +// get_peer_timestamp_estimation(&est); +// mops_hton4 ((u_int32_t*) &est.sec, &mp->frame[j-8]); +// mops_hton4 ((u_int32_t*) &est.nsec, &mp->frame[j-4]); +// } + break; + default: + return 0; + break; + } + + // The pd->payload contains either zeroes or realtime voice data + // The pd->payload is initialized with zeroes and IFF a reading thread + // exists, it may copy voice segments (e. g. from /dev/dsp) to + // pd->payload. + // NOTE that there is NO NEED to protect pd->payload with mutexes, because + // only if the reading thread is finished it (itself!) will call THIS function. + if (pd->source == DSP_SOURCE) { + memcpy((void*) &mp->frame[j], (void*) pd->payload, pd->payload_s); + } + + return 0; +} + diff --git a/src/mops_ip.c b/src/mops_ip.c new file mode 100644 index 0000000..46102d7 --- /dev/null +++ b/src/mops_ip.c @@ -0,0 +1,447 @@ +/* + * Mausezahn - A fast versatile traffic generator + * Copyright (C) 2008-2010 Herbert Haas + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 as published by the + * Free Software Foundation. + * + * 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, see http://www.gnu.org/licenses/gpl-2.0.html + * +*/ + + +#include "mz.h" +#include "mops.h" +#include "cli.h" + + +// PURPOSE +// +// Determine destination MAC address to provide direct or indirect +// delivery of IP packets, depending on which is appropriate. +// +// Doing this, the caller must provide +// 1) A pointer to the interface (within the device_list) +// 2) The destination IP address +// 3) A pointer to the destination MAC address +// +// If a Class D (multicast) address is given, a proper IEEE multicast MAC +// address is derived. +// +// EXAMPLE +// +// u_int8_t ip[4], +// mac[6]; +// +// mops_hton4 (mp->ip_src, ip); +// +// mops_ip_get_dst_mac(&device_list[0], ip, mac); +// +// RETURN VALUES +// +// 0 upon success +// 1 upon error +// +int mops_ip_get_dst_mac(struct device_struct *dev, u_int8_t *ip, u_int8_t *mac) +{ + int i; + u_int8_t dst_net[4]; + + if ((dev==NULL)||(ip==NULL)||(mac==NULL)) return 1; + + // Multicast address? + if ((0xe0 & ip[0]) == 0xe0) { + mac[0] = 0x01; + mac[1] = 0x00; + mac[2] = 0x5e; + mac[3] = ip[1] & 127; + mac[4] = ip[2]; + mac[5] = ip[3]; + return 0; + } + + // Is destination network == local network? + for (i=0; i<4; i++) { + dst_net[i] = ip[i] & (u_int8_t) dev->mask[i]; + } + + if (compare_ip(dst_net, dev->net)==0) { + // dst is on local LAN => resolve MAC! + service_arp(dev->dev, ip, mac); + } else { // dst is on a remote network => use default gw! + for (i=0; i<6; i++) mac[i] = dev->mac_gw[i]; + } + + return 0; +} + + +/////////////////////////////////////////////////////////////////////////////////// +// +// PURPOSE +// +// Accept a DSCP specification as string argument +// and configure the IP-ToS field accordingly. +// +// EXAMPLE STRINGS +// +// AF32 .... specify AF codepoint with class 3 and drop probability 2 +// EF .... specify Expedited Forwarding +// CS7 .... specify Code Selector 7 +// 101110 .... specify the DSCP in binary +// 56 .... specify the DSCP in decimal +// +// RETURN VALUES +// +// -1 general bad argument format +// 0 upon success +// 1 Invalid AF format (Format: AFxy, e. g. af31 or AF23) +// 2 Invalid CS format +// 3 Invalid decimal DSCP value +// +int mops_ip_dscp (struct mops* mp, char *argv) +{ + int i; + char cs[4], ps[4], str[16]; + u_int8_t c=0, p=0, dscp=0; + + if (strlen(argv)==0) return -1; + strncpy(str,argv,15); + + if (strncasecmp(str, "af", 2)==0) // e.g. 'AF32' or 'af41' + { + if (strlen(str)!=4) return 1; // ERROR: Invalid AF codepoint + i=sscanf(str, "%*[afAF]%c%c", cs, ps); + cs[1]=0x00; ps[1]=0x00; + c=(u_int8_t) str2int(cs); + p=(u_int8_t) str2int(ps); + if ((c<1)||(c>4)||(p<1)||(p>3)) return 1; + // Now create correct ToS-byte representation: This is simple, since if a=3 and b=1 + // we have in binary already a=0000 0011 and b=0000 0001 and with bit-shifting we + // get the desired dscp=011 01 000 (the least signfificant three bits are always 0). + c <<=5; + p <<=3; + dscp = c | p; + } + else if (strncasecmp(str, "cs", 2)==0) // e.g. 'CS7' or 'cs4' + { + if (strlen(str)!=2) return 2; // ERROR: Invalid Code Selector + i=sscanf(str, "%*[afAF]%c", cs); + cs[1]=0x00; + c=(u_int8_t) str2int(cs); + if (c>7) return 2; + c <<=5; + dscp = c; + } + else if (mz_strcmp(str, "ef", 2)==0) // e.g. 'ef' or 'EF' + { + dscp = 0xb8; // = DSCP 46 = 101110 00 or 1011 1000 + } + else if (mz_strisbinary(str)==6) // binary, e. g. 101110 + { + for (i=0; i<6; i++) if (str[i]=='1') dscp |= ( 0x01 << (5-i) ); + dscp <<= 2; + } + else if (strlen(str)==2) // decimal DSCP value + { + if ( !(isdigit(str[0])) || !(isdigit(str[1]))) return 3; + dscp = (u_int8_t) str2int(str); + if (dscp>63) return 3; + dscp <<= 2; + } + else return -1; + + // TEST: printf("dscp=%02x\n",dscp); + mp->ip_tos = dscp; + + return 0; +} + + + + + + + + +// +// IP TOS-FIELD FORMAT +// +// MSB LSB +// 0 1 2 3 4 5 6 7 +// +-----+-----+-----+-----+-----+-----+-----+-----+ Note that the bit numbering is usually from right +// | | Del Trp Rel Cst | | to left, but here is the original pic of the RFC +// | PRECEDENCE | TOS | MBZ | 1349. Also here, the MSB is left (strangely bit 0) +// | | | | and the LSB is right (strangely bit 7). +// +-----+-----+-----+-----+-----+-----+-----+-----+ +// +// ARGUMENTS +// if unused +// ipp ... IP Precedence (0..7) or -1 +// tos ... Type of Service (0..15) or -1 +// mbz ... if 1 sets MBZ or 0 +int mops_ip_tos (struct mops* mp, int ipp, int tos, int mbz) +{ + u_int8_t TOS=0; + + if (ipp!=-1) + { + if (ipp>7) return 1; // Invalid IPP value + TOS |= (ipp << 5); + } + + if (tos!=-1) + { + if (tos>15) return 2; // Invalid ToS value + TOS |= (tos << 1); + } + + if (mbz==1) // not used if mbz is either 0 or -1 + { + TOS |= 0x01; // set + } + + mp->ip_tos = TOS; + + return 0; +} + + + +// +// +// =================== ONLY IP OPTION HANDLING FUNCTION BELOW ================ +// +/////////////////////////////////////////////////////////////////////////////// + + + +/////////////////////////////////////////////////////////////////////////////// +// +// There are two cases for the format of an option: +// +// Case 1: A single octet of option-type. +// Case 2: An option-type octet, an option-length octet, and the +// actual option-data octets. +// +// The option-length octet counts the WHOLE number of bytes of the option +// +// The option-type consists of: +// +// +--------+--------+--------+--------+--------+--------+--------+--------+ +// | copied | option class | number (identifies option) | +// | flag | | | +// +--------+-----------------+--------------------------------------------+ +// +// +// The following Internet options are defined in RFC 791: +// +// CLASS NUMBER LENGTH DESCRIPTION +// ----- ------ ------ ----------- +// 0 0 - End of Option list. This option occupies only +// 1 octet; it has no length octet. +// 0 1 - No Operation. This option occupies only 1 +// octet; it has no length octet. +// 0 2 11 Security. Used to carry Security, +// Compartmentation, User Group (TCC), and +// Handling Restriction Codes compatible with DOD +// requirements. +// 0 3 var. Loose Source Routing. Used to route the +// internet datagram based on information +// supplied by the source. +// 0 9 var. Strict Source Routing. Used to route the +// internet datagram based on information +// supplied by the source. +// 0 7 var. Record Route. Used to trace the route an +// internet datagram takes. +// 0 8 4 Stream ID. Used to carry the stream +// identifier. +// 2 4 var. Internet Timestamp. +// +// +// Possible options and associated number in mp->ip_option_used +// +// 1 - Security and handling restrictions (for military applications) +// 2 - Record route +// 4 - Timestamp +// 8 - Loose source routing +// 16 - Strict source routing +// +// + +// *** See RFCs 791, 1071, 1108 *** + +// Remove all options +int mops_ip_option_remove_all (struct mops* mp) +{ + mp->ip_option_used = 0; + mp->ip_option_s = 0; + return 0; +} + + +// Add no-option +int mops_ip_option_nop (struct mops* mp) +{ + + return 0; +} + +// Add end of option list +int mops_ip_option_eol (struct mops* mp) +{ + + return 0; +} + + + +// Add loose source route option +int mops_ip_option_lsr (struct mops* mp) +{ + + return 0; +} + +// Add strict source route option +int mops_ip_option_ssr (struct mops* mp) +{ + + return 0; +} + +// Add record route option +int mops_ip_option_rr (struct mops* mp) +{ + + return 0; +} + +// Add time stamp option +int mops_ip_option_ts (struct mops* mp) +{ + + return 0; +} + + + +// Add security option. +// +// This option provides a way for hosts to send security, compartmentation, +// handling restrictions, and TCC (closed user group) parameters. The format +// for this option is as follows: +// +// +--------+--------+---//---+---//---+---//---+---//---+ +// |10000010|00001011|SSS SSS|CCC CCC|HHH HHH| TCC | +// +--------+--------+---//---+---//---+---//---+---//---+ +// Type=130 Length=11 +// +// Security (S field): 16 bits +// +// Specifies one of 16 levels of security (eight of which are +// reserved for future use). +// +// 00000000 00000000 - Unclassified +// 11110001 00110101 - Confidential +// 01111000 10011010 - EFTO +// 10111100 01001101 - MMMM +// 01011110 00100110 - PROG +// 10101111 00010011 - Restricted +// 11010111 10001000 - Secret +// 01101011 11000101 - Top Secret +// 00110101 11100010 - (Reserved for future use) +// 10011010 11110001 - (Reserved for future use) +// 01001101 01111000 - (Reserved for future use) +// 00100100 10111101 - (Reserved for future use) +// 00010011 01011110 - (Reserved for future use) +// 10001001 10101111 - (Reserved for future use) +// 11000100 11010110 - (Reserved for future use) +// 11100010 01101011 - (Reserved for future use) +// +// +// Compartments (C field): 16 bits +// +// An all zero value is used when the information transmitted is not +// compartmented. Other values for the compartments field may be obtained +// from the Defense Intelligence Agency. +// +// Handling Restrictions (H field): 16 bits +// +// The values for the control and release markings are alphanumeric digraphs +// and are defined in the Defense Intelligence Agency Manual DIAM 65-19, +// "Standard Security Markings". +// +// Transmission Control Code (TCC field): 24 bits +// +// Provides a means to segregate traffic and define controlled communities +// of interest among subscribers. The TCC values are trigraphs, and are available +// from HQ DCA Code 530. +// +// Must be copied on fragmentation. This option appears at most +// once in a datagram. + +int mops_ip_option_sec (struct mops* mp) +{ + + return 0; +} + + +// Add the IP Router Alert Option - a method to efficiently signal +// transit routers to more closely examine the contents of an IP packet. +// See RFC 2113, and FYI also 3175 (RSVP Aggregation), and RFC 5350 +// (new IANA-defined Router Alert Options (RAO)). +// +// The Router Alert option has the following format: +// +// +--------+--------+--------+--------+ +// |10010100|00000100| 2 octet value | +// +--------+--------+--------+--------+ +// +// Type: +// Copied flag: 1 (all fragments must carry the option) +// Option class: 0 (control) +// Option number: 20 (decimal) +// +// Length: 4 +// +// Value: A two octet code with the following values: +// 0 - Router shall examine packet +// 1-65535 - Reserved +// +// RETURN VALUE: 0 upon success +// 1 upon failure +// +int mops_ip_option_ra (struct mops* mp, int value) +{ + int ptr; + u_int16_t val; + + if ((mp==NULL) || (value>0xffff)) return 1; + + val = (u_int16_t) value; + + ptr = mp->ip_option_s; // add option at the end of existing option list (if any) + mp->ip_option_used=20; + + // create option header + mp->ip_option[ptr] = 0x94; + + ptr++; + mp->ip_option[ptr] = 0x04; + + ptr++; + mops_hton2 (&val, &mp->ip_option[ptr]); + ptr+=2; + mp->ip_option_s=4; + + return 0; +} diff --git a/src/mops_mpls.c b/src/mops_mpls.c new file mode 100644 index 0000000..5a03a0a --- /dev/null +++ b/src/mops_mpls.c @@ -0,0 +1,149 @@ +/* + * Mausezahn - A fast versatile traffic generator + * Copyright (C) 2008-2010 Herbert Haas + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 as published by the + * Free Software Foundation. + * + * 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, see http://www.gnu.org/licenses/gpl-2.0.html + * +*/ + + +#include "mz.h" +#include "mops.h" + +// Assigns MPLS tag at position i (starting at zero!) with values: +// +// m ... total number of tags (important to set BoS in last tag) +// Label ... label value +// Exp ... EXP field (typically CoS) +// TTL ... Time To Live +// +// NOTE: Two usage possibilities! +// +// 1.) When called from for-loop to add all tags the total size mpls_s +// is updated continuously and the BoS is set in the last tag. +// Therefore set m = total number of tags! +// +// 2.) But when changing a particular tag within an existing MPLS stack +// the total number of tags does not change, therefore use m=0. +// +// RETURN VALUE: 0 upon success, 1 upon failure +// +int mops_mpls(struct mops *mp, int i, int m, u_int32_t Label, u_int8_t Exp, u_int8_t TTL) +{ + u_int8_t *ptr; + + if ((m) && (i>=m)) return 1; // label index greater than number of labels! + if (Label > 1048575) return 1; + if (Exp > 7) return 1; + + // Create binary tag: Label(20) EXP(3) BoS(1) TTL(8) + Label <<= 4; + ptr = (u_int8_t *) &Label; + mp->mpls[4*i+0] = *(ptr+2); + mp->mpls[4*i+1] = *(ptr+1); + mp->mpls[4*i+2] = *(ptr+0); + Exp <<= 1; + mp->mpls[4*i+2] |= Exp; + mp->mpls[4*i+3] = TTL; + + if ((m) && (i==(m-1))) // reached last tag! + { + mp->mpls[4*i+2] |= 0x01; // set BoS in last tag + mp->mpls_s =4*m; + mp->use_MPLS = 1; + if ( (mp->eth_type != 0x8847) && (mp->eth_type != 0x8848) ) + { + mp->eth_type_backup = mp->eth_type; + } + mp->eth_type = 0x8847; + } + return 0; +} + + +// Remove MPLS tags from packet mp +// +// j indicates which tag to be removed (1..n) +// j=0 means: remove all tags! +// +// RETURN VALUE: 1 upon failure, 0 upon success +int mops_mpls_remove (struct mops *mp, int j) +{ + int a, b, k; + + + if (j==0) // remove all tags + { + if (mp->use_MPLS) + { + mp->mpls_s=0; + mp->use_MPLS=0; + mp->eth_type = mp->eth_type_backup; // restore original ethertype + return 0; + } + else + return 1; + } + + k = mp->mpls_s/4; + if (j>k) return 1; // The packet only consists of k tag(s) + + if (k==1) // only delete the single tag + { + mp->mpls_s=0; + mp->use_MPLS=0; + mp->eth_type = mp->eth_type_backup; // restore original ethertype + return 0; + } + + // if we got here we have more than one tag: + + if (j==k) // remove last tag (of several) + { + mp->mpls_s -=4; + return 0; + } + + // remove some non-ending tag: 0, 1, 2, 3 + a = (j-1)*4; // target + b = j*4; // source (what should be copied) + memcpy(&mp->mpls[a], &mp->mpls[b], (k-j)*4); + mp->mpls_s -=4; + return 0; +} + + +// Set BOS in tag k where k=1..n +int mops_mpls_bos (struct mops *mp, int k) +{ + int n; + + n = mp->mpls_s/4; // n = total number of tags + if (k>n) return 1; + + mp->mpls[(k-1)*4+2] |= 0x01; + return 0; +} + + +// Unset BOS in tag k where k=1..n +int mops_mpls_nobos (struct mops *mp, int k) +{ + int n; + + n = mp->mpls_s/4; // n = total number of tags + if (k>n) return 1; + + mp->mpls[(k-1)*4+2] &= 0xfe; + return 0; +} diff --git a/src/mops_sequence.c b/src/mops_sequence.c new file mode 100644 index 0000000..32e7895 --- /dev/null +++ b/src/mops_sequence.c @@ -0,0 +1,303 @@ +/* + * Mausezahn - A fast versatile traffic generator + * Copyright (C) 2010 Herbert Haas + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 as published by the + * Free Software Foundation. + * + * 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, see http://www.gnu.org/licenses/gpl-2.0.html + * +*/ + + +#include "mz.h" +#include "cli.h" +#include "mops.h" +#include "llist.h" + + +///////////////////// TOC ///////////////////// +// +// int mops_delete_sequence (char *name) +// struct mz_ll * mops_create_sequence (char *name) +// int mops_dump_sequence (char* str) +// int mops_add_packet_to_sequence (struct mz_ll *seq, struct mops *mp) +// int mops_add_delay_to_sequence (struct mz_ll *seq, struct timespec *t) +// int mops_delete_packet_from_pseq (struct mz_ll *seq, int index) +// int stop_sequence (char *name) +// int stop_all_sequences () + + +// delete one sequence element (from the global packet_sequence list) +// which must be specified by its name +// +int mops_delete_sequence (char *name) +{ + struct mz_ll *v; + + v = mz_ll_search_name (packet_sequences, name); + if (v==NULL) return 1; // name not found + + if (v->state) return 2; // sequence is currently active! + + if (mz_ll_delete_element (v)) + return -1; // cannot delete head element! + return 0; +} + + + +struct mz_ll * mops_create_sequence (char *name) +{ + struct mz_ll *cur; + struct pseq *seq; + int i; + + cur = mz_ll_create_new_element(packet_sequences); + if (cur==NULL) return NULL; + strncpy(cur->name, name, MZ_LL_NAME_LEN); + // add data + cur->data = (struct pseq*) malloc (sizeof(struct pseq)); + // initialize data + seq = (struct pseq*) cur->data; + seq->count = 0; + for (i=0; ipacket[i] = NULL; // pointer to the packets + seq->gap[i].tv_sec = 0; + seq->gap[i].tv_nsec = 0; + } + return cur; +} + + + +// PURPOSE: dumps all sequence objects line-by-line +// +// ARGUMENTS: Caller must provide a pointer to a string of size MZ_LL_NAME_LEN+(MAX_PACKET_SEQUENCE_LEN*6) +// (recommendation: 512 bytes !) +// +// RETURN VALUE: 0 if list is finished, 1 otherwise +// +// EXAMPLE: char str[512]; +// while (mops_dump_sequence(str) +// printf("%s\n", str); +// +int mops_dump_sequence (char* str) +{ + static int init=0; + static struct mz_ll *cur; + struct pseq *seq; + struct mops *pkt; + + char tmp[256], t[16]; + int i, c; + + tmp[0]='\0'; + + if (init==-1) { // last turn said stop now! + init=0; + return 0; + } + + if (init==0) { + cur=packet_sequences->next; + if (cur==NULL) { + str[0]='\0'; + return 0; + } + init=1; + } + + seq = (struct pseq*) cur->data; + if (seq==NULL) { + init=-1; + sprintf(str, "(no sequences found)"); + return 1; + } + + c = seq->count; // amount of currently stored packets in this sequence object + + // create string with all packet IDs: + for (i=0; ipacket[i]; + if (pkt == NULL) break; + snprintf(t, 15, "%i", pkt->id); + if (strnlen(tmp,256)>249) break; + strncat(tmp, t, 6); + if (iname, tmp); + + cur=cur->next; + if (cur==packet_sequences) init=-1; // stop next turn! + return 1; +} + + +// finds next free slot in sequence seq and adds packet mp +// +// RETURN VALUE: 0 upon success +// -1 failure: array full +// -2 failure: cannot add packets with infinite count +// +int mops_add_packet_to_sequence (struct mz_ll *seq, struct mops *mp) +{ + struct pseq *cur; + int i; + + if (seq==NULL) return 1; + + // don't add packets with count=0 + if (mp->count==0) return -2; + + cur = (struct pseq*) seq->data; + if (cur->count >= MAX_PACKET_SEQUENCE_LEN) return -1; // packet array full! + for (i=0; ipacket[i]==NULL) { // found empty slot + cur->packet[i]=mp; + cur->count++; + return 0; + } + } + return 1; // never reach here +} + + +// adds the given delay 't' to the last packet in the sequence's pseq +// +// NOTE: return index number of pseq where delay had been added +// or upon failure: -1 if there is no packet yet defined +// -2 if array is full +int mops_add_delay_to_sequence (struct mz_ll *seq, struct timespec *t) +{ + struct pseq *cur; + int i; + + if (seq==NULL) return 1; + + cur = (struct pseq*) seq->data; + i = cur->count; + if (i>= MAX_PACKET_SEQUENCE_LEN) return -2; // packet array full! + + cur->gap[i-1].tv_sec = t->tv_sec; + cur->gap[i-1].tv_nsec = t->tv_nsec; + + return i-1; // note: is -1 if there is no packet yet (count=0) +} + + +// Deletes packet and associated delay from a pseq for given index +// If index == -1 then the last packet/delay is removed +// +// NOTE: index range is {1..count} +// +// RETURN VALUES: 0 upon success +// 1 upon failure +// 2 upon failure, index too big +// +int mops_delete_packet_from_pseq (struct mz_ll *seq, int index) +{ + struct pseq *cur; + int i; + + if (seq==NULL) return 1; + cur = (struct pseq*) seq->data; + if (cur->count==0) return 1; // list is empty, nothing to delete + if (index>cur->count) return 2; + if ((index==0) || (index<-1)) return 1; // total invalid index values + if (index==-1) { // remove last element + cur->packet[cur->count-1]=NULL; + cur->gap[cur->count-1].tv_sec=0; + cur->gap[cur->count-1].tv_nsec=0; + } else { + for (i=index-1; i<(cur->count-1); i++) { + cur->packet[i] = cur->packet[i+1]; + cur->gap[i].tv_sec = cur->gap[i+1].tv_sec; + cur->gap[i].tv_nsec=cur->gap[i+1].tv_nsec; + } + } + cur->count--; + return 0; +} + + +int mops_delete_all_packets_from_pseq (struct mz_ll *seq) +{ + struct pseq *cur; + int i; + + if (seq==NULL) return 1; + cur = (struct pseq*) seq->data; + if (cur->count==0) return 1; // list is empty, nothing to delete + // DELETE ALL: + cur->count = 0; + for (i=0; ipacket[i] = NULL; // pointer to the packets + cur->gap[i].tv_sec = 0; + cur->gap[i].tv_nsec = 0; + } + return 0; +} + + + +// Stops an active sequence and sets all involved packets from state SEQACT to CONFIG. +// +// RETURN VALUE: 0 upon success +// 1 if sequence does not exist +// 2 if sequence is not actice +int stop_sequence (char *name) +{ + struct mz_ll *v; + struct pseq *cur; + int i; + + v = mz_ll_search_name (packet_sequences, name); + if (v==NULL) return 1; // name not found + if (!v->state) return 2; // sequence is not currently active! + + // now stop thread: + pthread_cancel(v->sequence_thread); + + // reset packet states: + cur = (struct pseq*) v->data; + for (i=0; icount; i++) + cur->packet[i]->state=MOPS_STATE_CONFIG; + + // reset sequence state: + v->state = 0; + return 0; +} + + +// runs through 'packet_sequences' and cancels all active sequences +// (i. e. stops threads and sets states appropriately) +// +// Comment: It might seem a bit inefficient to call 'stop_sequence' for the +// detailed work, but this is the more safer way and it is fast enough. +// +// RETURN VALUE: Number of stopped sequences. +// +int stop_all_sequences () +{ + struct mz_ll *cur=packet_sequences->next; + int i=0; + + while (cur!=packet_sequences) { + if (cur!=packet_sequences) { // just for safety + if (stop_sequence(cur->name)==0) i++; + } + cur=cur->next; + } + + return i; +} + diff --git a/src/mops_tcp.c b/src/mops_tcp.c new file mode 100644 index 0000000..4d788bd --- /dev/null +++ b/src/mops_tcp.c @@ -0,0 +1,154 @@ +/* + * Mausezahn - A fast versatile traffic generator + * Copyright (C) 2008-2010 Herbert Haas + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 as published by the + * Free Software Foundation. + * + * 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, see http://www.gnu.org/licenses/gpl-2.0.html + * +*/ + + +#include "mz.h" +#include "mops.h" +#include "cli.h" + +// Calculates the number of TCP transmissions based on SQNR range +u_int32_t mops_tcp_complexity_sqnr (struct mops * mp) +{ + u_int32_t a,b,t,result; + + a = mp->tcp_seq_start; + b = mp->tcp_seq_stop; + t = mp->tcp_seq_delta; + + if (!t) return 1; // delta set to zero means no range + + if (atcp_ack_start; + b = mp->tcp_ack_stop; + t = mp->tcp_ack_delta; + + if (!t) return 1; // delta set to zero means no range + + if (atcp_ctrl_CWR) ? "CRW" : "---", + (mp->tcp_ctrl_ECE) ? "ECE" : "---", + (mp->tcp_ctrl_URG) ? "URG" : "---", + (mp->tcp_ctrl_ACK) ? "ACK" : "---", + (mp->tcp_ctrl_PSH) ? "PSH" : "---", + (mp->tcp_ctrl_RST) ? "RST" : "---", + (mp->tcp_ctrl_SYN) ? "SYN" : "---", + (mp->tcp_ctrl_FIN) ? "FIN" : "---"); + + return 0; +} + +// Add TCP options +// +// TODO: currently all params are ignored and a default option combination is added. +// +int mops_tcp_add_option (struct mops* mp, + int mss, + int sack, + int scale, + u_int32_t tsval, + u_int32_t tsecr) +{ + + u_int8_t tcp_default_options[] = { + 0x02, 0x04, 0x05, 0xac, // MSS + 0x04, 0x02, // SACK permitted + 0x08, 0x0a, 0x19, 0x35, 0x90, 0xc3, 0x00, 0x00, 0x00, 0x00, // Timestamps + 0x01, // NOP + 0x03, 0x03, 0x05 // Window Scale 5 + }; + + + /* Kind: 8 + Length: 10 bytes + + +-------+-------+---------------------+---------------------+ + |Kind=8 | 10 | TS Value (TSval) |TS Echo Reply (TSecr)| + +-------+-------+---------------------+---------------------+ + 1 1 4 4 + * + * The Timestamps option carries two four-byte timestamp fields. The + * Timestamp Value field (TSval) contains the current value of the + * timestamp clock of the TCP sending the option. + * + * The Timestamp Echo Reply field (TSecr) is only valid if the ACK bit + * is set in the TCP header; if it is valid, it echos a times- tamp + * value that was sent by the remote TCP in the TSval field of a + * Timestamps option. When TSecr is not valid, its value must be zero. + * The TSecr value will generally be from the most recent Timestamp + * option that was received; however, there are exceptions that are + * explained below. + * + * A TCP may send the Timestamps option (TSopt) in an initial + * segment (i.e., segment containing a SYN bit and no ACK bit), and + * may send a TSopt in other segments only if it re- ceived a TSopt in + * the initial segment for the connection. + * + */ + + memcpy((void*) mp->tcp_option, (void*) tcp_default_options, 20); + mp->tcp_option_s = 20; + mp->tcp_option_used = 1; + + return 0; +} + diff --git a/src/mops_threads.c b/src/mops_threads.c new file mode 100644 index 0000000..69893db --- /dev/null +++ b/src/mops_threads.c @@ -0,0 +1,639 @@ +/* + * Mausezahn - A fast versatile traffic generator + * Copyright (C) 2008-2010 Herbert Haas + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 as published by the + * Free Software Foundation. + * + * 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, see http://www.gnu.org/licenses/gpl-2.0.html + * +*/ + +#include "mz.h" +#include "mops.h" +#include "cli.h" +#include "llist.h" + + + +void mops_set_active (struct mops *mp) +{ + pthread_mutex_lock (& (mp->mops_mutex) ); + mp->state = MOPS_STATE_ACTIVE; + pthread_mutex_unlock (& (mp->mops_mutex) ); +} + +void mops_set_seqact (struct mops *mp) +{ + pthread_mutex_lock (& (mp->mops_mutex) ); + mp->state = MOPS_STATE_SEQACT; + pthread_mutex_unlock (& (mp->mops_mutex) ); +} + + +void mops_set_conf (struct mops *mp) +{ + pthread_mutex_lock (& (mp->mops_mutex) ); + mp->state = MOPS_STATE_CONFIG; + pthread_mutex_unlock (& (mp->mops_mutex) ); +} + + +int mops_is_active (struct mops *mp) +{ + int i=0; + pthread_mutex_lock (& (mp->mops_mutex) ); + if (mp->state == MOPS_STATE_ACTIVE) i=1; + pthread_mutex_unlock (& (mp->mops_mutex) ); + return i; +} + +// Returns 1 if the packet is in any running state +// such as MOPS_STATE_ACTIVE or MOPS_STATE_SEQACT +int mops_is_any_active (struct mops *mp) +{ + int i=0; + pthread_mutex_lock (& (mp->mops_mutex) ); + if (mp->state > MOPS_STATE_CONFIG) i=1; + pthread_mutex_unlock (& (mp->mops_mutex) ); + return i; +} + + +int mops_is_seqact (struct mops *mp) +{ + int i=0; + pthread_mutex_lock (& (mp->mops_mutex) ); + if (mp->state == MOPS_STATE_SEQACT) i=1; + pthread_mutex_unlock (& (mp->mops_mutex) ); + return i; +} + + + +// return mops state (0=MOPS_STATE_NULL, 1=MOPS_STATE_INIT, 2=MOPS_STATE_CONFIG, 3=MOPS_STATE_ACTIVE, 4=MOPS_STATE_SEQACT) +int mops_state (struct mops *mp) +{ + int i=0; + pthread_mutex_lock (& (mp->mops_mutex) ); + i = mp->state; + pthread_mutex_unlock (& (mp->mops_mutex) ); + return i; +} + + +int mops_tx_simple (struct mops *mp) +{ + + if (mops_is_active(mp)) { + return 3; + } + + if (mp->interval_used) { + if ( pthread_create( &(mp->interval_thread), NULL, mops_interval_thread, mp) ) { + mp->interval_used=1; // 1 means interval only configured + return 1; // Error creating thread + } + } else // normal packet train + if ( pthread_create( &(mp->mops_thread), NULL, mops_tx_thread_native, mp) ) { + return 1; // Error creating thread + } + + return 0; +} + + +// Starts a packet sequence. +// +// RETURN VALUES: 0 upon success +// 1 failure: packet not in CONFIG state +// 2 failure: packet has infinite count +int mops_tx_sequence (struct mz_ll *seq) +{ + struct pseq *cur; + int i; + + // verify 1) that all packets are in config state + // 2) and have finite count: + cur = (struct pseq*) seq->data; + for (i=0; icount; i++) { + if (cur->packet[i]->state!=MOPS_STATE_CONFIG) return 1; + if (cur->packet[i]->count==0) return 2; + } + + // Set all packets in this sequence into state SEQACT: + for (i=0; icount; i++) + mops_set_seqact (cur->packet[i]); + + if ( pthread_create( &(seq->sequence_thread), NULL, mops_sequence_thread, seq) ) { + return 3; // Error creating thread + } + seq->state=1; + return 0; +} + + +// This is the sequence sending thread +void *mops_sequence_thread (void *arg) +{ + struct mz_ll *seq = (struct mz_ll*) arg; + struct pseq *cur; + int i; + + cur = (struct pseq*) seq->data; + + // Send one packet after each other, possibly with gaps inbetween: + for (i=0; icount; i++) { + mops_tx_thread_native (cur->packet[i]); + // if gap exists... + if ((cur->gap[i].tv_sec) || (cur->gap[i].tv_nsec)) { + nanosleep(&cur->gap[i], NULL); //...apply it. + } + } + + // Finally: + // 1) reset all packets into config state + for (i=0; icount; i++) + cur->packet[i]->state=MOPS_STATE_CONFIG; + // 2) join to main + pthread_exit(NULL); + // 3) set sequence state to inactive (=0) + seq->state=0; + + return NULL; +} + +// This is the interval management thread which starts +// packet transmission threads by itself. +// +// Note how this works: After the while statement below we have actually +// two threads, mops_tx_thread_native (sending the packet) and mops_interval_thread which +// starts mops_tx_thread_native every mp->interval. If mp->interval is smaller than +// mp->delay (and mp->count > 1) then multiple transmission threads will be active at the +// same time which is usually not what the user wants. We do not catch this case here +// but the user interface should do that (it is done in 'cmd_packet_interval'). +// +void *mops_interval_thread (void *arg) +{ + struct mops *mp = (struct mops*) arg; + + mp->interval_used=2; // 2 means active interval + while (1) { + if ( pthread_create( &(mp->mops_thread), NULL, mops_tx_thread_native, mp) ) { + mp->interval_used=1; + pthread_exit(NULL); + } + nanosleep(&mp->interval, NULL); + } + + pthread_exit(NULL); // hmm...does this make sense? + return NULL; +} + + +// General MOPS sending thread using packet sockets. +// +void *mops_tx_thread_native (void *arg) +{ + struct mops *mp = (struct mops*) arg; + struct mops_ext_rtp * pd; + int ps, i, n=0; + u_int8_t DA[4]; + // Local vars are faster -------------------------- + struct timespec tv; + register int infinity, devind; + int ip_src_isrange = mp->use_IP & mp->ip_src_isrange; + int ip_dst_isrange = mp->use_IP & mp->ip_dst_isrange; + int sp_isrange = (mp->use_UDP | mp->use_TCP) & mp->sp_isrange; + int dp_isrange = (mp->use_UDP | mp->use_TCP) & mp->dp_isrange; + int ip_src_israndom = mp->use_IP & mp->ip_src_israndom; + int sp_isrand = (mp->use_UDP | mp->use_TCP) & mp->sp_isrand; + int dp_isrand = (mp->use_UDP | mp->use_TCP) & mp->dp_isrand; + + + u_int32_t + ip_src_start = mp->ip_src_start, + ip_src_stop = mp->ip_src_stop, + ip_dst_start = mp->ip_dst_start, + ip_dst_stop = mp->ip_dst_stop, + tcp_seq_delta = mp->tcp_seq_delta, + tcp_seq_range = 0, + tcp_ack_delta = mp->tcp_ack_delta, + tcp_ack_range = 0, + tcp_ack_count = 0, + tcp_seq_count = 0; + + int + sp_start = mp->sp_start, + dp_start = mp->dp_start, + sp_stop = mp->sp_stop, + dp_stop = mp->dp_stop; + + int + rtp_mode = 0; // RTP not used + + int + fragsize = 0, + frag_overlap = 0, + fragptr = 0, + offset = 0, + offset_delta = 0, + begin_ip_payload = 0, + ip_payload_s = 0, + original_msg_s = 0, + whats_used = 0; // store use_UDP or use_TCP here to clean up packet parameters finally + char + original_msg[MAX_MOPS_MSG_SIZE+1], // temporary buffer when fragmentation is needed + ip_payload[MAX_MOPS_MSG_SIZE+1]; // temporary buffer when fragmentation is needed + + + // ------------------------------------------------- + + + ///////////////////////////// + // NOTE: If packet is part of a sequence, then this function is already part of a sequence thread + // and all packets are already in state SEQACT. Otherwise we set the packet in state ACTIVE. + if (!mops_is_seqact(mp)) + mops_set_active (mp); + ///////////////////////////// + + + // infinite or not? Count up or down? + if (mp->count == 0) { + infinity = 1; + mp->cntx = 0; + } + else { + infinity = 0; + mp->cntx = mp->count; // count down + } + + // Which delay? + tv.tv_sec = mp->ndelay.tv_sec; + tv.tv_nsec = mp->ndelay.tv_nsec; + + // Which interface? + for (i=0; idevice, 15)==0) break; + } + devind=i; + + // Packet socket already existing and valid? + ps = device_list[devind].ps; // the packet socket + if (ps<0) goto FIN; + + // Automatic direct or indirect delivery for IP packets? + if ((mp->use_IP) && (mp->auto_delivery_off == 0)) { + if (mp->ip_dst_isrange) + mops_hton4(&mp->ip_dst_start, DA); + else + mops_hton4(&mp->ip_dst, DA); + + mops_ip_get_dst_mac(&device_list[devind], DA, mp->eth_dst); + } + + + // Impossible settings + if (((ip_src_isrange) && (ip_src_israndom)) || + ((sp_isrand) && (sp_isrange)) || + ((dp_isrand) && (dp_isrange))) { + fprintf(stderr, "[ERROR] (mops_tx_thread_native) -- conflicting requirements: both range and random!\n"); + goto FIN; + } + + // Initialize start values when ranges have been defined + if (ip_src_isrange) mp->ip_src = mp->ip_src_start; + if (ip_dst_isrange) mp->ip_dst = mp->ip_dst_start; + if (sp_isrange) mp->sp = mp->sp_start; + if (dp_isrange) mp->dp = mp->dp_start; + if (tcp_seq_delta) { + tcp_seq_range = mops_tcp_complexity_sqnr(mp); + mp->tcp_seq = mp->tcp_seq_start; + tcp_seq_count = tcp_seq_range; + } + if (tcp_ack_delta) { + tcp_ack_range = mops_tcp_complexity_acknr(mp); + mp->tcp_ack = mp->tcp_ack_start; + tcp_ack_count = tcp_ack_range; + } + + // RTP special message treatment + if (mp->p_desc_type == MOPS_RTP) { + pd = mp->p_desc; + if (pd==NULL) return NULL; + if (pd->source == DSP_SOURCE) + rtp_mode = 2; // dsp payload + else + rtp_mode = 1; // zero payload + + mops_update_rtp (mp); // initialize RTP packet here + } + + // TODO: VLAN, MPLS - ranges + + // + // ---------------------- The holy transmission loop ---------------- // + // + + // Update whole packet (once before loop!) + mops_ext_update (mp); + mops_update(mp); + + + // Check if IP fragmentation is desired. + // If yes, set local 'fragsize' and 'begin_ip_payload' pointer. + if (mp->ip_fragsize) { + if (mp->use_IP) { + fragsize = mp->ip_fragsize; + frag_overlap = mp->ip_frag_overlap; + offset = mp->ip_frag_offset; + offset_delta = (fragsize-frag_overlap)/8; + if (mp->use_UDP) { + begin_ip_payload = mp->begin_UDP; + whats_used = 1; + } else if (mp->use_TCP) { + begin_ip_payload = mp->begin_TCP; + whats_used = 2; + } else { + begin_ip_payload = mp->begin_MSG; + whats_used = 0; + } + ip_payload_s = mp->frame_s - begin_ip_payload; + memcpy((void*) original_msg, (void*) mp->msg, mp->msg_s); + original_msg_s = mp->msg_s; + memcpy((void*) ip_payload, (void*) &mp->frame[begin_ip_payload], ip_payload_s); + } + } + + + goto START; // looks like a dirty hack but reduces a few cpu cycles each loop + + do { + INLOOP: + nanosleep(&tv, NULL); // don't apply this before first and after last packet. + START: + + // +++++++++++++++++++++++++++++++++++ + + + // ------ IP fragmentation required? ------------------------------------------------------ + // + // Basic idea: At this point we assume that all updates have been already applied + // so mp->frame contains a valid packet. But now we do the following: + // + // 1. Determine first byte after end of IP header (IP options may be used) [done above] + // 2. Store the 'IP payload' in the temporary buffer 'ip_payload' [done above] + // 3. Create a new IP payload but take only the first fragsize bytes out of 'ip_payload' + // 4. This new IP payload is copied into mp->msg + // 5. Set the IP parameters: MF=1, offset=0 + // 6. Call mops_update() and send the packet + // 7. offset = offset + fragsize/8 + // 8. Increment the IP identification number + // 9. Repeat this until the last fragment is reached. For the last fragment + // set the flag MF=0. + // 10. Restore the original IP parameters (use_UDP or use_TCP) + if (fragsize) { + mp->use_UDP=0; + mp->use_TCP=0; + fragptr=0; // NOTE: by intention we do not set mp->ip_frag_offset to 0 here !!! The user knows what she does! + mp->ip_flags_MF=1; + mp->ip_id++; // automatically wraps around correctly (u_int16_t) + // send all fragments except the last one: + while(fragptr+fragsize < ip_payload_s) { + memcpy((void*) mp->msg, (void*) ip_payload+fragptr, fragsize); + mp->msg_s = fragsize; + mops_update(mp); + n = write(ps, mp->frame, mp->frame_s); + if (n!=mp->frame_s) { + fprintf(stderr, "ERROR: Could not send IP fragment through interface %s\n", mp->device); + // LOG error msg + goto FIN; + } + fragptr+=fragsize; + mp->ip_frag_offset += offset_delta; + } + // send last fragment: + mp->ip_flags_MF=0; + memcpy((void*) mp->msg, (void*) ip_payload+fragptr, ip_payload_s-fragptr); + mp->msg_s = ip_payload_s-fragptr; + mops_update(mp); + n = write(ps, mp->frame, mp->frame_s); + if (n!=mp->frame_s) { + fprintf(stderr, "ERROR: Could not send IP fragment through interface %s\n", mp->device); + // LOG error msg + goto FIN; + } + + // -- restore original mops parameters -- + switch (whats_used) { + case 1: mp->use_UDP = 1; break; + case 2: mp->use_TCP = 1; break; + } + memcpy((void*) mp->msg, (void*) original_msg, original_msg_s); + mp->msg_s = original_msg_s; + mp->ip_frag_offset=offset; + goto NEXT; + } + + // -- send unfragmented packets here: -- + n = write(ps, mp->frame, mp->frame_s); + if (n!=mp->frame_s) { + fprintf(stderr, "ERROR: Could not send packet through interface %s\n", mp->device); + // LOG error msg + goto FIN; + } + + NEXT: + + /* [ RTP TODO: ] Use another thread reading from /dev/dsp and signalling us to continue! + * It should work like this: (pseudocode below) + * + * if (rtp_mode == DSP_SOURCE) { + * pthread_cond_wait ( &mycond, &mymutex ); // wait until pthread condition is signaled + * // now, frame should contain 160 bytes from /dev/dsp + * goto INLOOP; + * } + * + * The reading thread will do something like this: (again fuzzy code only) + * + * loop: + * read(fd, pd->rtp_payload, 160); // this takes 20 msec anyway + * mops_update_rtp_dynamics (mp); // also updates dynamic header fields + * pthread_cond_broadcast (&mycond); // wake up TX thread + * goto loop; + * + * See also + * http://www.oreilly.de/catalog/multilinux/excerpt/ch14-05.htm + * + * NOTE that we must not reach nanosleep below because the 20 msec delay is + * done implicitely by reading 160 bytes from /dev/dsp + */ + + switch (rtp_mode) { + case 1: // dummy payload => segmentation delay is controlled by nanosleep below! + mops_update_rtp_dynamics (mp); + break; + case 2: // await data from /dev/dsp => segmentation delay is controlled by a reading thread! + /* pthread_cond_wait ( &mycond, &mymutex ); // wait until pthread condition is signaled + * // now, frame should contain 160 bytes from /dev/dsp + * goto INLOOP; + */ + break; + default: + // no RTP, continue as usual + break; + } + + + // +++++++++++++++++++++++++++++++++++ + // + // *** begin of modifiers -- order is important! *** *************** // + // + if (tcp_seq_delta) { + if (--tcp_seq_count) { + mp->tcp_seq += tcp_seq_delta; + mops_update(mp); + goto INLOOP; + } else { + tcp_seq_count = tcp_seq_range; + mp->tcp_seq = mp->tcp_seq_start; + mops_update(mp); + } + } + + if (tcp_ack_delta) { + if (--tcp_ack_count) { + mp->tcp_ack += tcp_ack_delta; + mops_update(mp); + goto INLOOP; + } else { + tcp_ack_count = tcp_ack_range; + mp->tcp_ack = mp->tcp_ack_start; + mops_update(mp); + } + } + + if (ip_src_isrange) { + if (++mp->ip_src > ip_src_stop) { + mp->ip_src = ip_src_start; + mops_update(mp); + } + else { + mops_update(mp); + goto INLOOP; + } + } + + if (ip_src_israndom) { + mp->ip_src = 0x01000001 + (u_int32_t) ( ((float) rand()/RAND_MAX)*0xE0000000); //latter is 224.0.0.0 + } + + if (ip_dst_isrange) { + if (++mp->ip_dst > ip_dst_stop) { + mp->ip_dst = ip_dst_start; + if (mp->auto_delivery_off == 0) { + mops_hton4(&mp->ip_dst, DA); + mp->eth_dst[0] = 0x01; + mp->eth_dst[1] = 0x00; + mp->eth_dst[2] = 0x5e; + mp->eth_dst[3] = DA[1] & 127; + mp->eth_dst[4] = DA[2]; + mp->eth_dst[5] = DA[3]; + } + mops_update(mp); + } + else { + if (mp->auto_delivery_off == 0) { + mops_hton4(&mp->ip_dst, DA); + mp->eth_dst[0] = 0x01; + mp->eth_dst[1] = 0x00; + mp->eth_dst[2] = 0x5e; + mp->eth_dst[3] = DA[1] & 127; + mp->eth_dst[4] = DA[2]; + mp->eth_dst[5] = DA[3]; + } + mops_update(mp); + goto INLOOP; + } + } + + if (dp_isrange) { + if (++mp->dp > dp_stop) { + mp->dp = dp_start; + mops_update(mp); + } + else { + mops_update(mp); + goto INLOOP; + } + } + + if (dp_isrand) { + mp->dp = (u_int16_t) ( ((float) rand()/RAND_MAX)*0xffff); + } + + + if (sp_isrange) { + if (++mp->sp > sp_stop) { + mp->sp = sp_start; + mops_update(mp); + } + else { + mops_update(mp); + goto INLOOP; + } + } + + if (sp_isrand) { + mp->sp = (u_int16_t) ( ((float) rand()/RAND_MAX)*0xffff); + } + + + // *** end of modifiers ******************************************** // + if (infinity) { + mp->cntx++; // count up + goto INLOOP; + } + } while (--mp->cntx); + + FIN: + if (!mops_is_seqact(mp)) { + // only [change state and close thread] if packet is NOT part of a sequence. + // If the packet is part of a sequence then THIS function is already part of + // a sequence thread and it will be closed in 'mops_sequence_thread'. + mops_set_conf (mp); + pthread_exit(NULL); + } + return NULL; +} + + + + + +int mops_destroy_thread (struct mops *mp) +{ + int r=1; + + if (mp->interval_used==2) { + pthread_cancel(mp->interval_thread); + mp->interval_used=1; + r=0; + } + + if (mops_is_active(mp)) { + pthread_cancel(mp->mops_thread); + pthread_mutex_destroy(& mp->mops_mutex); + mops_set_conf(mp); + r=0; + } + + return r; +} diff --git a/src/mops_tools.c b/src/mops_tools.c new file mode 100644 index 0000000..022f45b --- /dev/null +++ b/src/mops_tools.c @@ -0,0 +1,259 @@ +/* + * Mausezahn - A fast versatile traffic generator + * Copyright (C) 2008,2009 Herbert Haas + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 as published by the + * Free Software Foundation. + * + * 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, see http://www.gnu.org/licenses/gpl-2.0.html + * +*/ + + +#include "mz.h" +#include "mops.h" + +// Inserts value in 'flag' (up to 7 bits are useful) into the target +// with an optional left-shift. For example if flag contains a 4-bit value +// and should be placed within the target in bit positions 3-6 like: +// +// 7 6 5 4 3 2 1 0 +// +--+--+--+--+--+--+--+--+ +// | | FLAGS | | | | +// +--+--+--+--+--+--+--+--+ +// +// then simply call: +// +// (void) mops_flags ( &target, &flag, 3 ); +// +// Note that shift=0 means no shift. +inline void mops_flags (u_int8_t *target, u_int8_t *flag, int shift) +{ + *target |= (*flag << shift); +} + + + +inline void mops_hton2 (u_int16_t *host16, u_int8_t *net16) +{ + char *x; + + x = (char*) host16; + + *(net16++) = *(x+1); + *net16 = *x; +} + + +inline void mops_hton4 (u_int32_t *host32, u_int8_t *net32) +{ + char *x; + + x = (char*) host32; + + *(net32++) = *(x+3); + *(net32++) = *(x+2); + *(net32++) = *(x+1); + *(net32) = *x; +} + + + + +// returns new counter index for given packet +// or -1 if all counters used already +int mops_get_counter (struct mops *mp) +{ + int i=0; + + while (mp->counter[i].offset) + { + i++; + if (i==MAX_MOPS_COUNTERS_PER_PACKET) // exceeded range + return -1; + } + return i; +} + + +// Adds single byte to msg +int mops_msg_add_byte (struct mops *mp, u_int8_t data) +{ + mp->msg[mp->msg_s++] = data; + return 0; +} + + +// Adds bit field in *previous* msg-byte using optional left-shift +int mops_msg_add_field (struct mops *mp, u_int8_t data, int shift) +{ + mp->msg[mp->msg_s -1] |= (data << shift); + return 0; +} + + +// Adds two bytes in network byte order to msg +int mops_msg_add_2bytes (struct mops *mp, u_int16_t data) +{ + char *x; + x = (char*) &data; + mp->msg[mp->msg_s++] = *(x+1); + mp->msg[mp->msg_s++] = *(x); + return 0; +} + + +// Adds four bytes in network byte order to msg +int mops_msg_add_4bytes (struct mops *mp, u_int32_t data) +{ + char *x; + x = (char*) &data; + mp->msg[mp->msg_s++] = *(x+3); + mp->msg[mp->msg_s++] = *(x+2); + mp->msg[mp->msg_s++] = *(x+1); + mp->msg[mp->msg_s++] = *(x); + return 0; +} + +// Adds string of bytes with lenght len +int mops_msg_add_string (struct mops *mp, u_int8_t *str, int len) +{ + memcpy((void *) &mp->msg[mp->msg_s], (void *) str, len); + mp->msg_s += len; + + return 0; +} + + + +// Add counter to message +int mops_msg_add_counter (struct mops *mp, + int random, // 1=random, 0=use start/stop/step + u_int32_t start, // HOST BYTE ORDER + u_int32_t stop, // HOST BYTE ORDER + u_int32_t step, // HOST BYTE ORDER + int bytes // number of bytes used (1|2|4) - selects hton2 or hton4 + ) +{ + + int i; + + // check if unsupported byte count + if ( (bytes!=1) && + (bytes!=2) && + (bytes!=4) ) + return 1; + + // get new counter + i = mops_get_counter(mp); + if (i==-1) return 1; + + // configure counter values + mp->counter[i].offset = mp->msg_s; + mp->counter[i].random = random; + mp->counter[i].start = start; + mp->counter[i].stop = stop; + mp->counter[i].step = step; + mp->counter[i].bytes = bytes; + mp->counter[i].cur = start; + mp->counter[i].use = 1; + + + // configure first pointer value + switch (bytes) + { + case 1: + mops_msg_add_byte(mp, (u_int8_t) start); + break; + case 2: + mops_msg_add_2bytes(mp, (u_int16_t) start); + break; + case 4: + mops_msg_add_4bytes(mp, start); + break; + default: // never be reached + return 1; + } + + return 0; +} + + + +// Compares two IP addresses byte by byte +// returns 0 if identical, 1 if different +// +// Note that this works independent of endianess +// as long as both addresses have same endianess. +// +int compare_ip (u_int8_t *ip1, u_int8_t *ip2) +{ + if (*ip1 != *ip2) return 1; + if (*(ip1+1) != *(ip2+1)) return 1; + if (*(ip1+2) != *(ip2+2)) return 1; + if (*(ip1+3) != *(ip2+3)) return 1; + + return 0; +} + + +// Compares two MAC addresses byte by byte +// returns 0 if identical, 1 if different +int compare_mac (u_int8_t *mac1, u_int8_t *mac2) +{ + if (*mac1 != *mac2) return 1; + if (*(mac1+1) != *(mac2+1)) return 1; + if (*(mac1+2) != *(mac2+2)) return 1; + if (*(mac1+3) != *(mac2+3)) return 1; + if (*(mac1+4) != *(mac2+4)) return 1; + if (*(mac1+5) != *(mac2+5)) return 1; + + return 0; +} + + +// Converts a 'struct timespec' value into a human readable string +// This stringt is written into 'str' which must be at least a 32 byte +// array. +int timespec2str(struct timespec *t, char *str) +{ + unsigned int d=0, h, m, s; + + // zero delay + if ((t->tv_sec==0) && (t->tv_nsec==0)) { + sprintf(str, "(none)"); + return 0; + } + + h = t->tv_sec/3600; + m = (t->tv_sec - h*3600)/60; + s = t->tv_sec - h*3600 - m*60; + + if (h>24) { + d = h/24; + h = h - d*24; + sprintf(str, "%u days %02u:%02u:%02u", d, h, m, s); + return 0; + } + + if (h|m) + sprintf(str, "%02u:%02u:%02u", h, m, s); // ignore nanoseconds if delay is in order of hours + else if (s) + sprintf(str, "%u%s sec", s, (t->tv_nsec>1000000) ? "+" : ""); + else if (t->tv_nsec>1000000) + sprintf(str, "%u msec", (unsigned int) t->tv_nsec/1000000); + else if (t->tv_nsec>1000) + sprintf(str, "%u usec", (unsigned int) t->tv_nsec/1000); + else + sprintf(str, "%lu nsec", t->tv_nsec); + + return 0; +} + diff --git a/src/mops_update.c b/src/mops_update.c new file mode 100644 index 0000000..4fa1b3a --- /dev/null +++ b/src/mops_update.c @@ -0,0 +1,422 @@ +/* + * Mausezahn - A fast versatile traffic generator + * Copyright (C) 2008-2010 Herbert Haas + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 as published by the + * Free Software Foundation. + * + * 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, see http://www.gnu.org/licenses/gpl-2.0.html + * +*/ + + + + +// -- TOC: -- +// int mops_update (stuct mops *mp) + + +#include "mz.h" +#include "mops.h" + + + +// This is the very basic MOPS update function. It simply updates the whole +// MOPS frame specified by the pointer mp. If you only want to update specific +// details then please see the other related specialized functions which are +// more effcient. +// +int mops_update (struct mops *mp) +{ + int + i, // the standard loop variable; outside a loop fully undetermined! + t, // temp + fp=0; // frame pointer; always points to NEXT byte + + char *x; + u_int8_t t8=0; // temp 8 bits + u_int16_t t16; // temp 16 bits + + u_int8_t ip_pseudo_header[12]; + + + // set MAC addresses? + if (mp->use_ETHER) + { + for (i=0; i<6; i++) + { + mp->frame[i] = mp->eth_dst[i]; + mp->frame[i+6] = mp->eth_src[i]; + } + fp = 12; // next byte + } + + + + // VLAN tags? + if (mp->use_dot1Q) + { + t = mp->dot1Q_s; + for (i=0; iframe[fp++] = mp->dot1Q[i]; + } + } + + + + // Standard Ethernet or SNAP? (SNAP includes 802.3, see comments in mops.h) + if (mp->use_SNAP) // note that if use_SNAP is set, then the 'else if' below is ignored! + { + // 802.3 length + x = (char*) &mp->eth_len; + mp->frame[fp++] = *(x+1); + mp->frame[fp++] = *x; + // SNAP + t = mp->eth_snap_s; + for (i=0; iframe[fp++] = mp->eth_snap[i]; + } + } + else if (mp->use_ETHER) // add TYPE field (note the ELSE IF here!) + { + // EtherType + x = (char*) &mp->eth_type; + mp->frame[fp++] = *(x+1); + mp->frame[fp++] = *x; + } + // alternatively the user specified whole raw frame + // + // + // + // MPLS? + if (mp->use_MPLS) + { + t = mp->mpls_s; + for (i=0; iframe[fp++] = mp->mpls[i]; + } + } + + + + + // IP? + if (mp->use_IP) + { + mp->begin_IP = fp; // marks byte position of IP header within frame + + // ----- 1st row: ----- + // + mp->frame[fp] = (mp->ip_version << 4); // version + mp->frame[fp++] |= mp->ip_IHL; // IHL (user value - corrected at end of function if required) + mp->frame[fp++] = mp->ip_tos; // ToS + mops_hton2 ( &mp->ip_len, &mp->frame[fp] ); // Total Length (user value - corrected at end of function if required) + fp+=2; + + // ----- 2nd row: ----- + // + mops_hton2 ( &mp->ip_id, &mp->frame[fp] ); // Fragment Identification + fp+=2; + + mops_hton2 ( &mp->ip_frag_offset, &mp->frame[fp] ); // Fragment Identification + // set flags: + if (mp->ip_flags_MF) mp->frame[fp] |= 0x20; else mp->frame[fp] &= 0xDF; // More Frag + if (mp->ip_flags_DF) mp->frame[fp] |= 0x40; else mp->frame[fp] &= 0xBF; // Don't Frag + if (mp->ip_flags_RS) mp->frame[fp] |= 0x80; else mp->frame[fp] &= 0x7F; // reserved + fp+=2; + + // ----- 3rd row: ----- + + mp->frame[fp++] = mp->ip_ttl; // TTL + mp->frame[fp++] = mp->ip_proto; // Protocol + mops_hton2 ( &mp->ip_sum, &mp->frame[fp] ); // Checksum (user value - corrected at end of function if required) + fp+=2; + + // ----- 4th and 5th row: ----- + // + mops_hton4 ( &mp->ip_src, &mp->frame[fp] ); // SA + fp+=4; + mops_hton4 ( &mp->ip_dst, &mp->frame[fp] ); // DA + fp+=4; + + // ----- options ----- + // + if (mp->ip_option_used) + { + t = mp->ip_option_s; + for (i=0; iframe[fp++] = mp->ip_option[i]; + } + } + } + + + + + // UDP? + if (mp->use_UDP) + { + mp->begin_UDP = fp; // marks byte position of UDP header within frame + + mops_hton2 ( &mp->sp, &mp->frame[fp] ); // Source Port + fp+=2; + mops_hton2 ( &mp->dp, &mp->frame[fp] ); // Destination Port + fp+=2; + mops_hton2 ( &mp->udp_len, &mp->frame[fp] ); // Length (user value - corrected at end of function if required) + fp+=2; + mops_hton2 ( &mp->udp_sum, &mp->frame[fp] ); // CheckSum (user value - corrected at end of function if required) + fp+=2; + } + + + + // TCP? + if (mp->use_TCP) + { + mp->begin_TCP = fp; // marks byte position of TCP header within frame + + // ----- 1st row: ----- + // + mops_hton2 ( &mp->sp, &mp->frame[fp] ); // Source Port + fp+=2; + mops_hton2 ( &mp->dp, &mp->frame[fp] ); // Destination Port + fp+=2; + + // ----- 2nd and 3rd row: ----- + // + mops_hton4 ( &mp->tcp_seq, &mp->frame[fp] ); // SQNR + fp+=4; + mops_hton4 ( &mp->tcp_ack, &mp->frame[fp] ); // ACKNR + fp+=4; + + // ----- 4th row: ----- + // +// t16 = (mp->tcp_offset<<12) + (mp->tcp_res<<8); // Data Offset (HLEN) and 4 reserved bits + t16 = mp->tcp_res<<8; // Data Offset (HLEN) and 4 reserved bits + // (user value - corrected at end of function if required) + // + if (mp->tcp_ctrl_CWR) t16 |= 0x0080; else t16 &= 0xff7f; // URG Flag + if (mp->tcp_ctrl_ECE) t16 |= 0x0040; else t16 &= 0xffbf; // URG Flag + if (mp->tcp_ctrl_URG) t16 |= 0x0020; else t16 &= 0xffdf; // URG Flag + if (mp->tcp_ctrl_ACK) t16 |= 0x0010; else t16 &= 0xffef; // ACK Flag + if (mp->tcp_ctrl_PSH) t16 |= 0x0008; else t16 &= 0xfff7; // PSH Flag + if (mp->tcp_ctrl_RST) t16 |= 0x0004; else t16 &= 0xfffb; // RST Flag + if (mp->tcp_ctrl_SYN) t16 |= 0x0002; else t16 &= 0xfffd; // SYN Flag + if (mp->tcp_ctrl_FIN) t16 |= 0x0001; else t16 &= 0xfffe; // FIN Flag + + mops_hton2 ( &t16, &mp->frame[fp] ); // copy HLEN, reserved bits, and flags to frame + fp+=2; + + + mops_hton2 ( &mp->tcp_win, &mp->frame[fp] ); // Window + fp+=2; + + // ----- 5th row: ----- + // + mops_hton2 ( &mp->tcp_sum, &mp->frame[fp] ); // Checksum + fp+=2; + + mops_hton2 ( &mp->tcp_urg, &mp->frame[fp] ); // Urgent pointer + fp+=2; + + + // ----- options: ----- + // + if (mp->tcp_option_used) { + t=mp->tcp_option_s; + for (i=0; iframe[fp++] = mp->tcp_option[i]; + } + } + } + + // Eventually the payload: + if ((t = mp->msg_s)) + { + mp->begin_MSG = fp; + for (i=0; iframe[fp++] = mp->msg[i]; + } + } + + mp->frame_s = fp; // finally set the total frame length + + + ////////////////////////////////////////////////////////////// + // Protect TX subsystem from too short or long packets // + // TODO: Consider to support mops-specific limits + // (which are itself limited by these global limits) + if (fp < min_frame_s) + mp->frame_s = min_frame_s; + else + if (fp > max_frame_s) + mp->frame_s = max_frame_s; + // // + ////////////////////////////////////////////////////////////// + + + + + //////////////////////////////////////////////////////////////////////////////// + // + // Now update "derivable" fields if required: + // + // IP: ip_IHL, ip_len, ip_sum + // UDP: udp_len, udp_sum + // TCP: tcp_offset, tcp_sum + // + // + if (mp->use_IP) + { + fp = mp->begin_IP; // marks byte position of IP header within frame + + /// HLEN + if (!mp->ip_IHL_false) { // user has NOT set an own header length + t8 = 5; + if (mp->ip_option_used) { // add option length if option exists + t8 += mp->ip_option_s/4; + } + t8 &= 0x0f; // set most significant 4 bits to zero because reserved for IP version + mp->frame[fp] |= t8; + } + + /// LEN + if (!mp->ip_len_false) { // user has NOT set an own total length + t16 = mp->frame_s-fp; + mops_hton2 ( &t16, &mp->frame[fp+2] ); // Calculated total Length + } + + /// SUM + if (!mp->ip_sum_false) { // user has NOT set an own header checksum + mp->frame[fp+10]=0x00; + mp->frame[fp+11]=0x00; + t16 = mops_sum16 (t8*4, &mp->frame[fp]); + mops_hton2 ( &t16, &mp->frame[fp+10] ); // Checksum (user value - corrected at end of function if required) + } + } + + + if (mp->use_UDP) + { + fp = mp->begin_UDP; // marks byte position of UDP header within frame + + /// LEN + if (!mp->udp_len_false) { // user has NOT set an own total length + t16 = mp->frame_s-fp; + mops_hton2 ( &t16, &mp->frame[fp+4] ); // Calculated total Length + } + + /// SUM + // + // The pseudo header conceptually prefixed to the UDP header contains the + // source address, the destination address, the protocol, and the UDP + // length. [RFC 768] + // + // 0 7 8 15 16 23 24 31 + // +--------+--------+--------+--------+ + // | source address | + // +--------+--------+--------+--------+ + // | destination address | + // +--------+--------+--------+--------+ + // | zero |protocol| UDP length | + // +--------+--------+--------+--------+ + // + // + if (!mp->udp_sum_false) // user has NOT set an own checksum + { + // Create IP pseudoheader + memcpy(&ip_pseudo_header[0], &mp->frame[mp->begin_IP+12], 4); // copy SA to pseudoheader + memcpy(&ip_pseudo_header[4], &mp->frame[mp->begin_IP+16], 4); // copy DA to pseudoheader + ip_pseudo_header[8]=0x00; + ip_pseudo_header[9]=mp->ip_proto; + memcpy(&ip_pseudo_header[10], &mp->frame[fp+4], 2); // copy UDP length to pseudoheader + + mp->frame[fp+6]=0x00; // set checksum to 0x0000 + mp->frame[fp+7]=0x00; + + t = 12+mp->frame_s-fp; // udp datagram length (including 12 byte pseudoheader) + + // Pad one extra byte if length is odd, and append the + // pseudoheader at the end of mp->frame (only for checksum computation) + if (t%2) + { + t++; + mp->frame[mp->frame_s]=0x00; + memcpy(&mp->frame[mp->frame_s+1], ip_pseudo_header, 12); + } + else + memcpy(&mp->frame[mp->frame_s], ip_pseudo_header, 12); + + t16 = mops_sum16 (t, &mp->frame[fp]); + mops_hton2 ( &t16, &mp->frame[fp+6] ); + } + } + + + + + if (mp->use_TCP) + { + fp = mp->begin_TCP; // marks byte position of TCP header within frame + + /// OFFSET (=HLEN) + if (!mp->tcp_offset_false) // user has NOT set an own header length + { + t8 = 5; + if (mp->tcp_option_used) {// add option length if option exists + t8 += mp->tcp_option_s/4; + } + t8 <<=4; + mp->frame[fp+12] |= t8; + } + + // The TCP checksum is calculated similarily as the UDP checksum (see above). + // (The TCP length is needed instead of the UDP length of course, although + // the TCP length is not part of the header) + // + if (!mp->tcp_sum_false) { + // Create IP pseudoheader + memcpy(&ip_pseudo_header[0], &mp->frame[mp->begin_IP+12], 4); // copy SA to pseudoheader + memcpy(&ip_pseudo_header[4], &mp->frame[mp->begin_IP+16], 4); // copy DA to pseudoheader + ip_pseudo_header[8]=0x00; + ip_pseudo_header[9]=mp->ip_proto; + mp->tcp_len = mp->frame_s-fp; // TCP segment length + t16 = htons (mp->tcp_len); + memcpy(&ip_pseudo_header[10], &t16, 2); // copy TCP length to pseudoheader + + mp->frame[fp+16]=0x00; // set checksum to 0x0000 + mp->frame[fp+17]=0x00; + + t = mp->tcp_len+12; // TCP segment length plus pseudoheader length + + // Pad one extra byte if length is odd, and append the + // pseudoheader at the end of mp->frame (only for checksum computation) + if (t%2) { + t++; + mp->frame[mp->frame_s]=0x00; + memcpy(&mp->frame[mp->frame_s+1], ip_pseudo_header, 12); + } + else + memcpy(&mp->frame[mp->frame_s], ip_pseudo_header, 12); + + t16 = mops_sum16 (t, &mp->frame[fp]); + mops_hton2 ( &t16, &mp->frame[fp+16] ); + } + } + + + return 0; +} + + diff --git a/src/mopsrx_arp.c b/src/mopsrx_arp.c new file mode 100644 index 0000000..52351cb --- /dev/null +++ b/src/mopsrx_arp.c @@ -0,0 +1,301 @@ +/* + * Mausezahn - A fast versatile traffic generator + * Copyright (C) 2008-2010 Herbert Haas + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 as published by the + * Free Software Foundation. + * + * 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, see http://www.gnu.org/licenses/gpl-2.0.html + * +*/ + +#include "mz.h" +#include "mops.h" +#include "cli.h" + +// Starts an ARP RX thread for *every* device in the device_list. +// (Except for the loopback interface) +// +// RETURN VALUE: 0 upon success, +// 1 upon error. +// +int mops_rx_arp () +{ + int i; + + for (i=0; idev, + 100, // max num of bytes to read + 1, // 1 if promiscuous mode + PCAP_READ_TIMEOUT_MSEC, // read timeout 'until error' (-1 = indefinitely) + errbuf); + + if (p_arp == NULL) { + fprintf(stderr," rx_arp: [ERROR] %s\n",errbuf); + return NULL; // TODO: Should return pointer to error message or something similar + } + + dev->p_arp = p_arp; // also assign pointer to a global which is needed for clean_up + + if ( pcap_compile(p_arp, + &filter, // the compiled version of the filter + filter_str, // text version of filter + 0, // 1 = optimize + 0) // netmask + == -1) { + fprintf(stderr," rx_arp: [ERROR] Error calling pcap_compile\n"); + return NULL; + } + + if ( pcap_setfilter(p_arp, &filter) == -1) { + fprintf(stderr," rx_arp: [ERROR] Error setting pcap filter\n"); + pcap_perror(p_arp, " rx_arp: "); + return NULL; + } + + if (pcap_setdirection(p_arp, PCAP_D_IN) == -1) { + pcap_perror(p_arp, " rx_arp: "); + return NULL; + } + + again: + pcap_loop (p_arp, + 1, // number of packets to wait + got_arp_packet, // name of callback function + (u_char*) dev); // optional additional arguments for callback function + goto again; + + pthread_exit(NULL); // destroy thread + return NULL; +} + + +void got_arp_packet (u_char *args, + const struct pcap_pkthdr *header, // statistics about the packet (see 'struct pcap_pkthdr') + const u_char *packet) // the bytestring sniffed +{ + const struct struct_ethernet *ethernet; + const struct struct_arp *arp; + int size_ethernet = sizeof(struct struct_ethernet); + struct device_struct *dev = (struct device_struct*) args; + + u_int8_t + da[6], // eth da + sa[6], // eth sa + smac[6], // source hw address + sip[4], // source protocol address + tmac[6], // target hw address + tip[4]; // target protocol address + u_int16_t op; // operation + u_int32_t sec, nsec; + u_int8_t *x; + + // These are the most important lines here: + ethernet = (struct struct_ethernet*)(packet); + arp = (struct struct_arp*)(packet+size_ethernet); + sec = (u_int32_t) header->ts.tv_sec; + nsec = (u_int32_t) ((header->ts.tv_usec) * 1000); + + op = arp->arp_op; // note that we don't have network byte order anymore! + // tmact is: + // 100 instead of 00:01 (request) + // 200 instead of 00:02 (response) + + memcpy((void*) da, (void*) ethernet->eth_da, 6); + memcpy((void*) sa, (void*) ethernet->eth_sa, 6); + memcpy((void*) smac, (void*) arp->arp_smac, 6); + memcpy((void*) sip, (void*) arp->arp_sip, 4); + memcpy((void*) tmac, (void*) arp->arp_tmac, 6); + memcpy((void*) tip, (void*) arp->arp_tip, 4); + + // Only handle the packet if it is really an ARP response! + ////AND if it is not sent by THIS host! (not possible, we only scan inbound!) + x = (u_int8_t*) & op; + if (*(x+1) == 0x02) { + // ARP RESPONSE: Update ARP table + arptable_add(dev, sa, da, smac, sip, sec, nsec); + } else if (*(x+1) == 0x01) { + // ARP REQUEST: Detect poisoning attacks + arpwatch(dev, sa, da, smac, sip, tmac, tip, sec, nsec); + } + + + + + // ARP binding consists of: sip (IP) - smac (MAC) + // + // User alert, 2 possibilities: + // + // 1. Learned new binding: does smac belong to sip? + // + // 2. Alert: Mismatch of stored versus announced sip-to-smac binding + // + // In both cases user action: [Learn] [Ignore] [Attack] [Amok Attack] + // Countermeasures: Mausezahn him! + // + // ALSO correct ARP tables of other hosts, especially on the default gateway + // that is, send arp replies with true binding + // + // Finally: Create logging message + +} + + + +// Add new entry in device-specific ARP table +// but first check if already existing or change. +// +// RETURN VALUE: 0 upon success +// 1 upon error +// +int arptable_add(struct device_struct *dev, + u_int8_t *sa, + u_int8_t *da, + u_int8_t *smac, + u_int8_t *sip, + u_int32_t sec, + u_int32_t nsec) +{ + struct arp_table_struct *prev=NULL, *cur = dev->arp_table; + int i=0, alert=0; + + // If SA and SMAC are different this might be a MITM !!! + if (compare_mac(smac, sa)) alert=1; + + // Check if IP (sip) is already existing in arp table: + while (cur!=NULL) { + if (compare_ip(sip, cur->sip)==0) { // IP found! + timestamp_hms(cur->when); + if (da[0]==0xff) cur->bc_resp++; + else cur->uni_resp++; + if (compare_mac(smac, cur->smac)==0) { + // entry identical ! + cur->sec=sec; + cur->nsec=nsec; + return 0; + } else { + // entry with other MAC address found ! + if (cur->locked==0) { + cur->changed++; + memcpy((void*) cur->smac_prev, (void*) cur->smac, 6); + memcpy((void*) cur->smac, (void*) smac, 6); + cur->sec_prev=cur->sec; + cur->nsec_prev=cur->nsec; + cur->sec=sec; + cur->nsec=nsec; + if (alert) cur->flags|=0x02; + } + return 0; + } + } + prev = cur; + cur = cur->next; + i++; + } + + // If we get here, then there was no entry for that IP yet! + // Create new arp_table entry: + cur = (struct arp_table_struct *) malloc(sizeof(struct arp_table_struct)); + if (cur==NULL) return 1; + + // Append element: + if (dev->arp_table==NULL) dev->arp_table = cur; + else prev->next = cur; + + memcpy((void*) cur->sa, (void*) sa, 6); + memcpy((void*) cur->smac, (void*) smac, 6); + cur->smac_prev[0]=0x00; + cur->smac_prev[1]=0x00; + cur->smac_prev[2]=0x00; + cur->smac_prev[3]=0x00; + cur->smac_prev[4]=0x00; + cur->smac_prev[5]=0x00; + memcpy((void*) cur->sip, (void*) sip, 4); + if (da[0]==0xff) { + cur->bc_resp=1; + cur->uni_resp=0; + } else { + cur->bc_resp=0; + cur->uni_resp=1; + } + cur->changed=1; + cur->locked=0; + cur->dynamic=1; + cur->flags=0; + cur->sec=sec; + cur->nsec=nsec; + cur->sec_prev=0; + cur->nsec_prev=0; + cur->index=i+1; // I assume users prefer to count from 1. + timestamp_hms(cur->when); + if (alert) cur->flags|=0x02; + cur->next=NULL; + return 0; +} + + + +// Validate ARP requests +int arpwatch(struct device_struct *dev, + u_int8_t *sa, + u_int8_t *da, + u_int8_t *smac, + u_int8_t *sip, + u_int8_t *tmac, + u_int8_t *tip, + u_int32_t sec, + u_int32_t nsec) +{ + // Unicast requests are considered as anomaly + + if ((da[0]&0x01)==0) { // broadcast bit NOT set? + fprintf(stderr, "NOTE: Non-broadcast ARP request from %02x:%02x:%02x:%02x:%02x:%02x\n", + sa[0], sa[1], sa[2], sa[3], sa[4], sa[5]); + } + + return 0; +} + diff --git a/src/mz.c b/src/mz.c new file mode 100644 index 0000000..92665d9 --- /dev/null +++ b/src/mz.c @@ -0,0 +1,341 @@ +/* + * Mausezahn - A fast versatile traffic generator + * Copyright (C) 2008-2010 Herbert Haas + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 as published by the + * Free Software Foundation. + * + * 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, see http://www.gnu.org/licenses/gpl-2.0.html + * +*/ + + + +#include "mz.h" +#include "cli.h" +#include "mops.h" +#include "llist.h" + + +// Catch SIGINT and clean up, close everything... +void clean_up(int sig) +{ + int i; + struct arp_table_struct *cur, *next; + + if (!quiet) fprintf(stderr, "\nMausezahn cleans up...\n"); + + if (fp!=NULL) { + if (verbose) fprintf(stderr, " close files (1) ...\n"); + (void) fflush(fp); + (void) fclose(fp); + } + + if (fp2!=NULL) { + if (verbose) fprintf(stderr, " close files (2) ...\n"); + (void) fflush(fp2); + (void) fclose(fp2); + } + + // interactive mode? + if (mz_port) { + if (verbose) fprintf(stderr, " clear mops list...\n"); + mops_cleanup (mp_head); + if (verbose) fprintf(stderr, " clear automops list...\n"); + automops_cleanup (amp_head); + if (verbose) fprintf(stderr, " clear packet sequences...\n"); + mz_ll_delete_list (packet_sequences); + } + + for (i=0; inext; + if (cur!=NULL) free(cur); + cur=next; + } + } + + // close packet sockets + if (device_list[i].ps>=0) { + close(device_list[i].ps); + } + + } + + + + if (verbose) fprintf(stderr, "finished.\n"); + exit(sig); +} + + + +void usage() +{ + (void) fprintf (stderr,"\n" + MAUSEZAHN_VERSION + "\n" + "|\n" + "| USAGE: mz [options] [interface] keyword | arg_string | hex_string\n" + "|\n" + "| Short option description (see doc or manpage for more information):\n" + "| -h Prints this information.\n" + "| -c Send the packet count times (default: 1, infinite: 0).\n" + "| -d Apply delay between transmissions. The delay value can be\n" + "| specified in usec (default, no additional unit needed), or in\n" + "| msec (e. g. 100m or 100msec), or in seconds (e. g. 100s or 100sec).\n" + "| -r Multiplies the specified delay with a random value.\n" + "| -p Pad the raw frame to specified length (using random bytes).\n" + "| -a Use specified source mac address, no matter what has\n" + "| been specified with other arguments. Keywords see below.\n" + "| Default is own interface MAC.\n" + "| -b Same with destination mac address.\n" + "| Keywords are: \n" + "| rand use a random MAC address\n" + "| bc use a broadcast MAC address\n" + "| own use own interface MAC address (default for source MAC)\n" + "| stp use IEEE 802.1d STP multicast address\n" + "| cisco use Cisco multicast address as used for CDP, VTP, or PVST+\n" + "| -A Use specified source IP address (default is own interface IP).\n" + "| -B Send packet to specified destination IP or domain name.\n" + "| -P Use the specified ASCII payload.\n" + "| -f Read the ASCII payload from a file.\n" + "| -F Read the hexadecimal payload from a file.\n" + "| -Q <[CoS:]vlan> Specify 802.1Q VLAN tag and optional Class of Service. You can\n" + "| specify multiple 802.1Q VLAN tags (QinQ...) by separating them\n" + "| via a comma or a period (e. g. '5:10,20,2:30').\n" + "| -t Specify packet type for autobuild (you don't need to care for\n" + "| encapsulations in lower layers. Most packet types allow/require\n" + "| additional packet-specific arguments in an arg_string.\n" + "| Currently supported types: arp, bpdu, cdp, ip, icmp, udp, tcp,\n" + "| dns, rtp, syslog, lldp.\n" + "| For context-help use 'help' as arg_string!\n" + "| -T Specify packet type for server mode. Currently only rtp is supported.\n" + "| Enter -T help or -T rtp help for further information.\n" + "| -M Insert a MPLS label. Enter '-M help' for a syntax description.\n" + "| -v|V Verbose and more verbose mode\n" + "| -q Quiet mode, i. e. even omit 'important standard short messages'.\n" + "| -S Simulation mode: DOES NOT put anything on the wire. This is\n" + "| typically combined with one of the verbose modes (v or V).\n" + "\n" + ); + exit(0); +} + + + + + +int main(int argc, char *argv[]) +{ + + + // These handles are only used when creating L3 and above packets. + libnet_t *l; // the context + libnet_ptag_t t2=0, t3=0, t4=0; // handles to layers + + double cpu_time_used; + + // Check if we have root priviliges + if ( (getuid()!=0) && (geteuid()!=0) ) + { + fprintf(stderr, " Mausezahn requires root privileges.\n Exit.\n"); + return 1; + } + + + // Reset all globals + (void) reset(0); + + // Get all CLI options (sets globals, see mz.h) + if ( getopts(argc, argv) ) + { + (void) fprintf(stderr, " Invalid command line parameters!\n"); + usage(); + } + + // Check whether hires timers are supported or not: + (void) check_timer(); + + + + // ********************************************************************* + // First prefer data in a mausezahn description file! + // ********************************************************************* + + + + // >>> TODO: + // Note that argument 'device' is also used here! + // Support libpcap + // Must end in state machine! + + + + // ********************************************************************* + // If no MDF given, then send packet according CLI specifications + // ********************************************************************* + + + (void) signal(SIGINT, clean_up); // to close all file pointers etc upon SIGINT + + switch (mode) + { + case BYTE_STREAM: + send_eth(); + break; + + case ARP: + (void) send_arp(); + break; + + case BPDU: + (void) send_bpdu(); + break; + + case CDP: + (void) send_cdp(); + break; + + case IP: // From now on a new much more modular method is used: + l = get_link_context(); + t3 = create_ip_packet(l); // t3 can be used for later header changes + if (!quiet) complexity(); + if (tx.packet_mode==0) // Ethernet manipulation features does NOT use ARP to determine eth_dst + t2 = create_eth_frame(l, t3, t4); // t2 can be used for later header changes + else + send_frame (l, t3, t4); // NOTE: send_frame also destroys context finaly + break; + + case ICMP: + tx.ip_proto = 1; + l = get_link_context(); + t4 = create_icmp_packet(l); // t4 can be used for later header changes + t3 = create_ip_packet(l); // t3 can be used for later header changes + if (!quiet) complexity(); + if (tx.packet_mode==0) // Ethernet manipulation features does NOT use ARP to determine eth_dst + t2 = create_eth_frame(l, t3, t4); // t2 can be used for later header changes + else + send_frame (l, t3, t4); // NOTE: send_frame also destroys context finaly + break; + + case UDP: + tx.ip_proto = 17; + l = get_link_context(); + t4 = create_udp_packet(l); // t4 can be used for later header changes + t3 = create_ip_packet(l); // t3 can be used for later header changes + if (!quiet) complexity(); + if (tx.packet_mode==0) // Ethernet manipulation features does NOT use ARP to determine eth_dst + t2 = create_eth_frame(l, t3, t4); // t2 can be used for later header changes + else + send_frame (l, t3, t4); // NOTE: send_frame also destroys context finaly + break; + + case TCP: + tx.ip_proto = 6; + l = get_link_context(); + t4 = create_tcp_packet(l); // t4 can be used for later header changes + t3 = create_ip_packet(l); // t3 can be used for later header changes + if (!quiet) complexity(); + if (tx.packet_mode==0) // Ethernet manipulation features does NOT use ARP to determine eth_dst + t2 = create_eth_frame(l, t3, t4); // t2 can be used for later header changes + else + send_frame (l, t3, t4); // NOTE: send_frame also destroys context finaly + break; + + case DNS: + tx.ip_proto = 17; + l = get_link_context(); + (void) create_dns_packet(); + t4 = create_udp_packet(l); // t4 can be used for later header changes + t3 = create_ip_packet(l); // t3 can be used for later header changes + if (!quiet) complexity(); + if (tx.packet_mode==0) // Ethernet manipulation features does NOT use ARP to determine eth_dst + t2 = create_eth_frame(l, t3, t4); // t2 can be used for later header changes + else + send_frame (l, t3, t4); // NOTE: send_frame also destroys context finaly + break; + + case RTP: + tx.ip_proto = 17; + l = get_link_context(); + if (!quiet) fprintf(stderr, " mz: RTP mode! (count=%u, delay=%u usec)\n\n", tx.count, tx.delay); + (void) create_rtp_packet(); + t4 = create_udp_packet(l); // t4 can be used for later header changes + t3 = create_ip_packet(l); // t3 can be used for later header changes + if (!quiet) complexity(); + if (tx.packet_mode==0) // Ethernet manipulation features does NOT use ARP to determine eth_dst + t2 = create_eth_frame(l, t3, t4); // t2 can be used for later header changes + else + send_frame (l, t3, t4); // NOTE: send_frame also destroys context finaly + break; + + case RX_RTP: // Receive RTP packets + rcv_rtp_init(); + rcv_rtp(); + break; + + case SYSLOG: + tx.ip_proto = 17; + l = get_link_context(); + (void) create_syslog_packet(); + t4 = create_udp_packet(l); // t4 can be used for later header changes + t3 = create_ip_packet(l); // t3 can be used for later header changes + if (!quiet) complexity(); + + if (tx.packet_mode==0) // Ethernet manipulation features does NOT use ARP to determine eth_dst + t2 = create_eth_frame(l, t3, t4); // t2 can be used for later header changes + else + send_frame (l, t3, t4); // NOTE: send_frame also destroys context finaly + break; + + case LLDP: // start with a new concept here + //l = get_link_context(); + //(void) create_lldp_packet(); + // // // printf("SIZE=%lu\n",sizeof(struct tx_struct)); + fprintf(stderr, "LLDP is currently only supported via the interactive mode\n"); + exit(1); + break; + + + default: + (void) fprintf(stderr," mz/main: unknown mode! Stop.\n"); + return (1); + } + + if (!quiet) + { + mz_stop = clock(); + cpu_time_used = ((double) (mz_stop - mz_start)) / CLOCKS_PER_SEC; + if (cpu_time_used > 0) + { + total_d /= cpu_time_used; + fprintf(stderr, "%.2f seconds (%.Lf packets per second)\n",cpu_time_used,total_d); + } + else + { + fprintf(stderr, "\n"); + } + } + + return(0); +} diff --git a/src/mz.h b/src/mz.h new file mode 100644 index 0000000..083cffb --- /dev/null +++ b/src/mz.h @@ -0,0 +1,895 @@ +/* + * Mausezahn - A fast versatile traffic generator + * Copyright (C) 2008-2010 Herbert Haas + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 as published by the + * Free Software Foundation. + * + * 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, see http://www.gnu.org/licenses/gpl-2.0.html + * +*/ + + + +#ifndef __MAUSEZAHN__ +#define __MAUSEZAHN__ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//#include + + +//////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// +#define MAUSEZAHN_VERSION "Mausezahn 0.40 - (C) 2007-2010 by Herbert Haas - http://www.perihel.at/sec/mz/" +#define MAUSEZAHN_VERSION_SHORT "0.40" +// +// +//////////////////////////////////////////////////////////////////////////////////////////////////////////// + + +// "Dies ist ein schrecklicher Ort." + +#define MZ_DEFAULT_CONFIG_PATH "/etc/mausezahn/" // see also mz_default_config_path below +#define MZ_DEFAULT_LOG_PATH "/var/log/mausezahn/" // see also mz_default_log_path below + +#define SLEEP usleep // The sleep function to use. Consider 'nanosleep' in future. +#define DEFAULT_DELAY 0 +#define PCAP_READ_TIMEOUT_MSEC 1 // The read timeout for pcap_open_live() +#define MZ_MAX_DEVICES 10 // Max number of network devices supported +#define MAX_PAYLOAD_SIZE 3*8192 +#define MAX_DNS_NAME 256 +#define MAX_8021Q_TAGS 16 +#define TIME_COUNT_MAX 10000 // the size of the timestamp arrays timeRX and timeTX upon creation +#define TIME_COUNT 100 // the default used-size of the timestamp arrays timeRX and timeTX +#define MAX_DATA_BLOCKS 1000 // how many data blocks of size TIME_COUNT-1 should be written per file +#define MAXBYTES_TO_READ 1500 // how many bytes the pcap routine should read from net +#define RCV_RTP_MAX_BAR_WIDTH 500 // max line-width printed in BAR mode (see rcv_rtp.c) + +#define ETH_SRC 1 // These are only some symbols used by some functions. (Don't touch) +#define ETH_DST 2 // These are only some symbols used by some functions. +#define SRC_PORT 1 // These are only some symbols used by some functions. +#define DST_PORT 2 // These are only some symbols used by some functions. + +#define TEST fprintf(stderr, "HERE at line %i in file %s\n", __LINE__,__FILE__ ); fflush(stderr); + + +// ----- PCAP-specific definitions: --------------------- +#define IPADDRSIZE 46 + + +int MZ_SIZE_LONG_INT; + +char mz_default_config_path[256]; +char mz_default_log_path[256]; + + +struct arp_table_struct { + int index; // an entry index (1, 2, ...) for easier user access + u_int8_t sa[6]; // sent by this MAC SA + u_int8_t smac[6]; // announced MAC + u_int8_t smac_prev[6]; // previously announced MAC + u_int8_t sip[4]; // announced IP + unsigned long int uni_rq; // count unidirectional ARP requests for this IP + unsigned long int bc_resp; // count broadcast ARP responses for this IP + unsigned long int uni_resp; // count normal (unidir) ARP responses for this IP + unsigned long int changed; // count how often the MAC address has changed! + int locked; // 1=this entry cannot be overidden anymore + int dynamic; // 1=learned dynamically, 0=configured by user + int flags; // anomaly information (length anomaly: bit 0, sa!=smac: bit 1 , ...) + int gw; // 1=Default GW + char when[10]; // human readable timestamp (e. g. "11:42:53") + u_int32_t sec, nsec; // timestamp of last ARP response + u_int32_t sec_prev, nsec_prev; // timestamp of previous ARP response + //-----------------// + struct arp_table_struct *next; +}; + +// Device list +struct device_struct +{ + char dev[16]; // Device name + int index; // Device index (assigned by OS) + int phy; // 1 if physical, 0 if not (e. g. loopback) + int mtu; + int cli; // if set to 1 then the CLI connection must terminate here + int mgmt_only; // if set to 1 then no data traffic is allowed through that interface + // ---- MAC addresses ---- + u_int8_t mac[6]; // Real MAC address + u_int8_t mac_mops[6]; // MAC address to be used + // ---- IP related ----- + char ip_str[IPADDRSIZE+1]; // Real IP address as string in dotted decimal notation + u_int8_t ip[4]; // Real IP address + u_int8_t net[4]; // Real network + u_int8_t mask[4]; // Real mask + u_int8_t ip_mops[4]; // IP address to be used + // ---- Default Gateway per interface: + u_int8_t mac_gw[6]; // MAC address of default gateway + u_int8_t ip_gw[4]; // IP address of default gateway + // ---- various device-specific handles ---- + pthread_t arprx_thread; + pcap_t *p_arp; // pcap handle + struct arp_table_struct *arp_table; // dedicated ARP table + int ps; // packet socket +} device_list[MZ_MAX_DEVICES]; + +int device_list_entries; + + +#pragma pack(1) +struct struct_ethernet +{ + u_int8_t eth_da[6]; + u_int8_t eth_sa[6]; + u_int16_t eth_type; +}; + +struct struct_arp +{ + u_int16_t arp_hrd; // hardware address format + u_int16_t arp_pro; // protocol address format + u_int8_t arp_hln; // hardware address length + u_int8_t arp_pln; // protocol address length + u_int16_t arp_op; // ARP operation type + u_int8_t arp_smac[6]; // sender's hardware address + u_int8_t arp_sip[4]; // sender's protocol address + u_int8_t arp_tmac[6]; // target hardware address + u_int8_t arp_tip[4]; // target protocol address +}; + + + +//#pragma pack(1) +struct struct_ip +{ + u_int8_t + hlen :4, + ver :4; + u_int8_t + tos; + u_int16_t + len; + + u_int16_t + id, + offset; // flags and fragment offset field + + u_int8_t + ttl, + proto; + u_int16_t + sum; + + u_int8_t src[4]; + u_int8_t dst[4]; +}; + +//#pragma pack(1) +struct struct_udp { + u_int16_t + sp, + dp, + len, + sum; +}; + +//#pragma pack(1) +struct struct_rtp { + u_int8_t + byte1, + ptype; + u_int16_t + sqnr; + u_int32_t + timestamp, // official timestamp, created by codecs + ssrc; + // csrc, // only used by mixers + u_int16_t + ext_id, + ext_len; + u_int32_t + time_sec, + time_nsec, + time_sec2, + time_nsec2; +}; + +// ---------End of PCAP-specific definitions--------------- + + + + +// ************************************ +// +// Global variables +// +// ************************************ + +enum operating_modes +{ + BYTE_STREAM, + ARP, + BPDU, + IP, + ICMP, + UDP, + TCP, + DNS, + CDP, + RTP, + RX_RTP, + SYSLOG, + LLDP +} mode; + + +int quiet; // don't even print 'important standard short messages' +int verbose; // report character +int simulate; // if 1 then don't really send frames + +char path[256]; +char filename[256]; +FILE *fp, *fp2; // global multipurpose file pointer + +long double total_d; +clock_t mz_start, mz_stop; + +enum rtp_display_mode { + BAR, NCURSES, TEXT +} rtp_dm; + + +int mz_rand; +int bwidth; + +struct mz_timestamp { + u_int32_t sec; + u_int32_t nsec; +}; + +struct mz_timestamp + tv, + timeTX[TIME_COUNT_MAX], + timeRX[TIME_COUNT_MAX]; + +int32_t + time0, + jitter_rfc, + jitter[TIME_COUNT_MAX]; + +int + rtp_log, + time0_flag, // If set then time0 has valid data + sqnr0_flag; + +u_int8_t + mz_ssrc[4]; // holds RTP stream identifier for rcv_rtp() + +u_int16_t + sqnr_cur, + sqnr_last, + sqnr_next; + +u_int32_t + drop, // packet drop count + dis, // packet disorder count + gind, // a global index to run through deltaRX, deltaTX, and jitter + gind_max, // the amount of entries used in the (ugly oversized) arrays; per default set to TIME_COUNT + gtotal; // counts number of file write cycles (see "got_rtp_packet()") + + +char rtp_filter_str[64]; + +struct tx_struct +{ + // Management issues for TX + char device[16]; // every packet could be sent through a different device + int packet_mode; // 0 means use LIBNET_LINK_ADV, 1 means LIBNET_RAW4 + unsigned int count; // 0 means infinite, 1 is default + unsigned int delay; // Delay in microseconds, 0 means no delay (default) + char arg_string[MAX_PAYLOAD_SIZE]; // Argument-string when -t is used + + // Ethernet and 802.3 parameters + int eth_params_already_set; // if set to 1 then send_eth should only send the frame + u_int8_t eth_mac_own[6]; // Contains own interface MAC if needed by some modules + char eth_dst_txt[32]; // Text version of eth_dst (or keyword such as 'rand') + u_int8_t eth_dst[6]; + int eth_dst_rand; // 1 if random + char eth_src_txt[32]; // Text version of eth_src (or keyword such as 'rand') + u_int8_t eth_src[6]; + int eth_src_rand; // 1 if random + u_int16_t eth_type; + u_int16_t eth_len; + u_int8_t eth_payload[MAX_PAYLOAD_SIZE]; + u_int32_t eth_payload_s; + unsigned int padding; + + // CDP parameters + u_int8_t + cdp_version, + cdp_ttl, + cdp_payload[MAX_PAYLOAD_SIZE], + cdp_tlv_id[2048]; // The ID is the only required TLV + u_int16_t + cdp_sum; + u_int32_t + cdp_tlv_id_len, + cdp_payload_s; + + // 802.1Q VLAN Tag + int dot1Q; // 1 if specified + char dot1Q_txt[32]; // contains 802.1p(CoS) and VLAN-ID ("5:130" or only VLAN "130") + u_int8_t dot1Q_CoS; + u_int16_t dot1Q_vlan; + u_int8_t dot1Q_header[256]; // Contains the complete 802.1Q/P headers (but NOT the Ethernet header!) + u_int8_t dot1Q_header_s; + int dot1Q_at_least_two_headers; // If '1' then we have at least QinQ (or more VLAN tags) + + // ASCII PAYLOAD + int ascii; // 1 if specified + u_int8_t ascii_payload[MAX_PAYLOAD_SIZE]; + + // HEX PAYLOAD + u_int8_t hex_payload[MAX_PAYLOAD_SIZE]; + u_int32_t hex_payload_s; // >0 if hex payload is specified + + // MPLS Parameters + char mpls_txt[128]; // contains MPLS parameters (label, exp, S, TTL) + char mpls_verbose_string[1024]; // contains all labels for print_frame_details() + int mpls; // 1 if specified + u_int32_t mpls_label; + u_int8_t mpls_exp; + u_int8_t mpls_bos; + u_int8_t mpls_ttl; + + // IP parameters + u_int32_t ip_src; // has always network byte order(!) + char ip_src_txt[256]; + int ip_src_rand; // if set to 1 then SA should be random + u_int32_t ip_src_h; // mirror of ip_src (NOT network byte order => easy to count) + u_int32_t ip_src_start; // start of range (NOT network byte order => easy to count) + u_int32_t ip_src_stop; // stop of range (NOT network byte order => easy to count) + int ip_src_isrange; // if set to 1 then the start/stop values above are valid. + u_int32_t ip_dst; // has always network byte order(!) + char ip_dst_txt[256]; + u_int32_t ip_dst_h; // mirror of ip_dst (NOT network byte order => easy to count) + u_int32_t ip_dst_start; // start of range (NOT network byte order => easy to count) + u_int32_t ip_dst_stop; // stop of range (NOT network byte order => easy to count) + int ip_dst_isrange; // if set to 1 then the start/stop values above are valid. + u_int16_t + ip_len, + ip_id, + ip_frag, // Flags and Offset !!! + ip_sum; + u_int8_t + ip_tos, + ip_ttl, + ip_proto; + u_int8_t + ip_option[1024], + ip_payload[MAX_PAYLOAD_SIZE]; + u_int32_t + ip_option_s, + ip_payload_s; + + // ICMP + char + icmp_verbose_txt[256]; // used for verbose messages in send.c + u_int8_t + icmp_type, + icmp_code; + u_int16_t icmp_ident; // ATTENTION: libnet.h already #defines 'icmp_id', 'icmp_sum', and 'icmp_num' + u_int16_t icmp_chksum; // therefore I needed a renaming here -- be careful in future... + u_int16_t icmp_sqnr; // + u_int32_t + icmp_gateway, + icmp_payload_s; + u_int8_t + icmp_payload[MAX_PAYLOAD_SIZE]; + + // General L4 parameters: + u_int16_t + sp, dp, + sp_start, sp_stop, + dp_start, dp_stop; + int + sp_isrange, // if set to 1 then start/stop values above are valid + dp_isrange; // if set to 1 then start/stop values above are valid + + // UDP parameters + u_int16_t + udp_len, // includes header size (8 bytes) + udp_sum; + u_int8_t + udp_payload[MAX_PAYLOAD_SIZE]; + u_int32_t + udp_payload_s; + + // TCP parameters + u_int32_t + tcp_seq, + tcp_seq_start, + tcp_seq_stop, // is always set! Usually seq_start = seq_stop (=no range) + tcp_seq_delta, // Also used instead of an 'isrange' variable + tcp_ack; + u_int8_t + tcp_control; + u_int16_t + tcp_win, + tcp_sum, + tcp_urg, + tcp_len; // only needed by libnet and must include header size + u_int8_t + tcp_payload[MAX_PAYLOAD_SIZE]; + u_int32_t + tcp_payload_s; + + // RTP parameters + u_int32_t + rtp_sqnr, + rtp_stmp; + +} tx; // NOTE: tx elements are considered as default values for MOPS + + + + + +u_int8_t gbuf[MAX_PAYLOAD_SIZE]; // This is only a generic global buffer to handover data more easily +u_int32_t gbuf_s; // + + +// ************************************ +// +// Prototypes: General Tools +// +// ************************************ + +void clean_up(int sig); +int reset(); +void usage(); +int getopts(int argc, char *argv[]); +int getarg(char *str, char *arg_name, char *arg_value); +unsigned long int str2int(char *str); // converts "65535" to 65535 +unsigned long long int str2lint(char *str); // same but allows 64-bit integers +unsigned long int xstr2int(char *str); // converts "ffff" to 65535 +unsigned long long int xstr2lint(char *str); // same but allows 64-bit integers +int mz_strisbinary(char *str); +int mz_strisnum(char *str); +int mz_strishex(char *str); +int str2bin8 (char *str); +long int str2bin16 (char *str); +int char2bits (char c, char *str); +int mz_strcmp(char* usr, char* str, int min); +int mz_tok(char * str, char * delim, int anz, ...); +int delay_parse (struct timespec *t, char *a, char *b); + +// ************************************ +// +// Prototypes: Layer1 +// +// ************************************ + +int send_eth(); +libnet_ptag_t create_eth_frame (libnet_t *l, libnet_ptag_t t3, libnet_ptag_t t4); + +// ************************************ +// +// Prototypes: Layer 2 +// +// ************************************ + +int send_arp (); +int send_bpdu (); +int send_cdp (); + +// ************************************ +// +// Prototypes: Layer 3 +// +// ************************************ + + +libnet_t* get_link_context(); +libnet_ptag_t create_ip_packet (libnet_t *l); +int send_frame (libnet_t *l, libnet_ptag_t t3, libnet_ptag_t t4); + + + +// ************************************ +// +// Prototypes: Layer 4 +// +// ************************************ +libnet_ptag_t create_udp_packet (libnet_t *l); +libnet_ptag_t create_icmp_packet (libnet_t *l); +libnet_ptag_t create_tcp_packet (libnet_t *l); + + +// ************************************ +// +// Prototypes: Layer 7 +// +// ************************************ +int create_dns_packet (); +int create_rtp_packet(); +int create_syslog_packet(); + +// ************************************ +// +// Prototypes: Helper functions for +// byte manipulation, +// address conversion, +// etc +// +// ************************************ + +// Converts MAC address specified in str into u_int8_t array +// Usage: str2hex_mac ( "00:01:02:aa:ff:ee", src_addr ) +int str2hex_mac (char* str, u_int8_t *addr); + +// Converts ascii hex values (string) into integer array, similarly as above but for any size. +// Example: "1a 00:00-2f" => {26, 0, 0, 47} +// Note: apply any improvements here and prefer this function in future! +// Return value: Number of converted elements (=length of array) +int str2hex (char* str, u_int8_t *hp, int n); + +// Converts ascii numbers (string) into integer array +// Every byte can be specified as integers {0..255} +// For example "192.16.1.1" will be converted to {C0, 10, 01, 01} +int num2hex(char* str, u_int8_t *hp); + +// Convert array of integers into string of hex. Useful for verification messages. +// Example: {0,1,10} => "00-01-0A" +// Usage: bs2str ( src_mac, src_mac_txt, 6 ) +int bs2str (u_int8_t *bs, char* str, int len); + +// Extract contiguous sequence of bytes from an array. First element has index 1 !!! +// Usage: getbytes (bs, da, 1, 6); +int getbytes(u_int8_t *source, u_int8_t *target, int from, int to); + +// For any IP address given in 'dotted decimal' returns an unsigned 32-bit integer. +// Example: "192.168.0.1" => 3232235521 +// Note: Result is in LITTLE ENDIAN but usually with IP you need BIG ENDIAN, see next. +u_int32_t str2ip32 (char* str); + +// For any IP address given in 'dotted decimal' into an unsigned 32-bit integer +// This version does the same as str2ip32() but in BIG ENDIAN. +// Note: With netlib you need this one, not the previous function. +u_int32_t str2ip32_rev (char* str); + +// Converts a 2-byte value (e. g. a EtherType field) +// into a nice string using hex notation. +// Useful for verification messages. +// Example: type2str (tx.eth_type, msg) may result in msg="08:00" +// Return value: how many hex digits have been found. +int type2str(u_int16_t type, char *str); + + +// Parses string 'arg' for an IP range and finds start and stop IP addresses. +// Return value: 0 upon success, 1 upon failure. +// +// NOTE: The results are written in the following variables: +// +// (u_int32_t) tx.ip_dst_start ... contains start value +// (u_int32_t) tx.ip_dst_stop ... contains stop value +// int tx.ip_dst_isrange ... set to 1 if above values valid +// +// The other function does the same for the source address! +// +// Possible range specifications: +// +// 1) 192.168.0.0-192.168.0.12 +// 2) 10.2.11.0-10.55.13.2 +// 3) 172.18.96.0/19 +// +// That is: +// +// FIRST detect a range by scanning for the "-" OR "/" chars +// THEN determine start and stop value and store them as normal unsigned integers +// +int get_ip_range_dst (char *arg); +int get_ip_range_src (char *arg); + +// Sets a random SA for a given IP packet. +// Return value: 0 upon success, 1 upon failure +// +int set_rand_SA (libnet_t *l, libnet_ptag_t t3); + +// Scans tx.eth_dst_txt or tx.eth_src_txt and sets the corresponding +// MAC addresses (tx.eth_dst or tx.eth_src) accordingly. +// Argument: What string should be checked, ETH_SRC or ETH_DST. +// Return value: +// 0 when a MAC address has been set or +// 1 upon failure. +// Currently eth_src|dst_txt can be: +// 'rand', 'own', 'bc'|'bcast', 'stp', 'pvst', +// or a real mac address. +// +int check_eth_mac_txt(int src_or_dst); + +// Scans argument for a port number or range +// and sets the corresponding values in the +// tx struct. +// +// Arguments: sp_or_dp is either SRC_PORT or DST_PORT +// Return value: 0 on success, 1 upon failure +// +int get_port_range (int sp_or_dp, char *arg); + +// Return a 4-byte unsigned int random number +u_int32_t mz_rand32 (); + +// Scans argument for TCP flags and sets +// tx.tcp_control accordingly. +// +// Valid keywords are: fin, syn, rst, psh, ack, urg, ecn, cwr +// Valid delimiters are: | or + or - +// Return value: 0 on success, 1 upon failure +// +int get_tcp_flags (char* flags); + +// Scans string 'params' for MPLS parameters +// and sets tx.mpls_* accordingly. +// +// CLI Syntax Examples: +// +// -M help .... shows syntax +// +// -M 800 .... label=800 +// -M 800:S .... label=800 and BOS flag set +// -M 800:S:64 .... label=800, BOS, TTL=64 +// -M 800:64:S .... same +// -M 64:77 .... label=64, TTL=77 +// -M 64:800 .... INVALID +// -M 800:64 .... label=800, TTL=64 +// -M 800:3:S:64 .... additionall the experimental bits are set (all fields required!) +// +// Note: S = BOS(1), s = NOT-BOS(0) +// +// Valid delimiters: :-.,+ +// Return value: 0 on success, 1 upon failure +int get_mpls_params(char *params); + +// Parses str for occurence of character or sequence ch. +// Returns number of occurences +int exists(char* str, char* ch); + + +// Applies another random Ethernet source address to a given Ethernet-PTAG. +// (The calling function should check 'tx.eth_src_rand' whether the SA +// should be randomized.) +int update_Eth_SA(libnet_t *l, libnet_ptag_t t); + + +// Update timestamp and sequence number in the RTP header. +// The actual RTP message is stored in tx.udp_payload. +int update_RTP(libnet_t *l, libnet_ptag_t t); + + +// Applies another SOURCE IP address, +// - either a random one (tx.ip_src_rand==1) +// - or from a specified range (tx.ip_src_isrange==1) +// to a given IP-PTAG. +// +// Note: tx.ip_src MUST be already initialized with tx.ip_src_start. +// This is done by 'get_ip_range_src()' in tools.c. +// +// RETURNS '1' if tx.ip_src restarts +int update_IP_SA (libnet_t *l, libnet_ptag_t t); + + +// Applies another DESTINATION IP address from a specified range (tx.ip_dst_isrange==1) +// to a given IP-PTAG. +// +// Note: tx.ip_dst MUST be already initialized with tx.ip_dst_start. +// This is done by 'get_ip_range_dst()' in tools.c. +// +// RETURN VALUE: '1' if tx.ip_dst restarts +int update_IP_DA(libnet_t *l, libnet_ptag_t t); + + +// Applies another DESTINATION PORT from a specified range to a given UDP- or TCP-PTAG. +// +// Note: tx.dp MUST be already initialized with tx.dp_start +// This is done by 'get_port_range()' in tools.c. +// +// RETURN VALUE: '1' if tx.dp restarts +int update_DPORT(libnet_t *l, libnet_ptag_t t); + + +// Applies another SOURCE PORT from a specified range to a given UDP- or TCP-PTAG. +// +// Note: tx.sp MUST be already initialized with tx.sp_start +// This is done by 'get_port_range()' in tools.c. +// +// RETURN VALUE: '1' if tx.sp restarts +int update_SPORT(libnet_t *l, libnet_ptag_t t); + + +// Applies another TCP SQNR from a specified range to a given TCP-PTAG +// +// RETURN VALUE: '1' if tx.txp_seq restarts +// +int update_TCP_SQNR(libnet_t *l, libnet_ptag_t t); + + +// +// +int print_frame_details(); + + +// Calculates the number of frames to be sent. +// Should be used as standard output except the +// 'quiet' option (-q) has been specified. +int complexity(); + + +// Purpose: Calculate time deltas of two timestamps stored in struct timeval. +// Subtract the "struct timeval" values X and Y, storing the result in RESULT. +// Return 1 if the difference is negative, otherwise 0. +int timestamp_subtract (struct mz_timestamp *x, + struct mz_timestamp *y, + struct mz_timestamp *result); + +void timestamp_add (struct mz_timestamp *x, + struct mz_timestamp *y, + struct mz_timestamp *result); + +// Returns a human readable timestamp in the string result. +// Optionally a prefix can be specified, for example if the +// timestamp is part of a filename. +// +// Example: +// char myTimeStamp[128]; +// +// timestamp_human(myTimeStamp, NULL); +// +// => "20080718_155521" +// +// /* or with prefix */ +// +// timestamp_human(myTimeStamp, "MZ_RTP_jitter_"); +// +// => MZ_RTP_jitter_20080718_155521 +// +int timestamp_human(char* result, const char* prefix); + +// Returns a human readable timestamp in the string result. +// Optionally a prefix can be specified, for example if the +// timestamp is part of a filename. +// +// Example: +// char myTimeStamp[8]; +// +// timestamp_hms (myTimeStamp); +// +// => "15:55:21" +int timestamp_hms(char* result); + +// Initialize the rcv_rtp process: Read user parameters and initialize globals +int rcv_rtp_init(); + +// Defines the pcap handler and the callback function +int rcv_rtp(); + +// Print current RFC-Jitter on screen +void print_jitterbar (long int j, unsigned int d); + +// Compares two 4-byte variables byte by byte +// returns 0 if identical, 1 if different +int compare4B (u_int8_t *ip1, u_int8_t *ip2); + +// PURPOSE: Find usable network devices +// +// NOTE: +// +// 1. Ignores devices without IP address +// 2. Ignores loopback (etc) +// +// RETURN VALUES: +// +// 0 if usable device found (device_list[] and tx.device set) +// 1 if no usable device found +// +int lookupdev(); + + +// For a given device name, find out the following parameters: +// +// - MTU +// - Network +// - Mask +// - Default GW (IP) +// +int get_dev_params (char *name); + +// Handler function to do something when RTP messages are received +void got_rtp_packet(u_char *args, + const struct pcap_pkthdr *header, // statistics about the packet (see 'struct pcap_pkthdr') + const u_char *packet); // the bytestring sniffed + + +// Check if current system supports the nanosecond timer functions. +// Additionally, measure the precision. +// This function should be called upon program start. +// +int check_timer(); + +// This is the replacement for gettimeofday() which would result in 'jumps' if +// the system clock is adjusted (e. g. via a NTP process) and finally the jitter +// measurement would include wrong datapoints. +// +// Furthermore the function below utilizes the newer hi-res nanosecond timers. +inline void getcurtime (struct mz_timestamp *t); + +// Only print out the help text for the 02.1Q option +void print_dot1Q_help(void); + +// Determines ip and mac address of specified interface 'ifname' +// Caller must provide an unsigned char ip[4], mac[6] +// +int get_if_addr (char *ifname, unsigned char *ip, unsigned char *mac); + +// Takes filename and prepends valid configuration/logging directory +// NOTE: filename is overwritten and must be big enough to hold full path! +int getfullpath_cfg (char *filename); +int getfullpath_log (char *filename); + +// A safer replacement for strncpy which ensures \0-termination +char * mz_strncpy(char *dest, const char *src, size_t n); + +// Helper function to count the number of arguments +// in the Mausezahn argument string (comma separated args) +// RETURN VALUE: Number of arguments +int number_of_args (char *str); + +int arptable_add(struct device_struct *dev, + u_int8_t *sa, + u_int8_t *da, + u_int8_t *smac, + u_int8_t *sip, + u_int32_t sec, + u_int32_t nsec); + +// Validate ARP requests +int arpwatch(struct device_struct *dev, + u_int8_t *sa, + u_int8_t *da, + u_int8_t *smac, + u_int8_t *sip, + u_int8_t *tmac, + u_int8_t *tip, + u_int32_t sec, + u_int32_t nsec); + + +#endif diff --git a/src/parse_xml.c b/src/parse_xml.c new file mode 100644 index 0000000..2189b83 --- /dev/null +++ b/src/parse_xml.c @@ -0,0 +1,568 @@ +/* + * Mausezahn - A fast versatile traffic generator + * Copyright (C) 2008-2010 Herbert Haas + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 as published by the + * Free Software Foundation. + * + * 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, see http://www.gnu.org/licenses/gpl-2.0.html + * +*/ + + +#include "mz.h" +#include "mops.h" +#include "cli.h" + + + +// Returns integer number for given tag string +// For example xml_tag2int("field") => xml_field == 1 +// +// Returns -1 when tag not known +int xml_tag2int (char *t) +{ + if (!strncasecmp(t, "protocol", XML_MAX_TAG_LEN)) + return xml_protocol; + + if (!strncasecmp(t, "field", XML_MAX_TAG_LEN)) + return xml_field; + + if (!strncasecmp(t, "name", XML_MAX_TAG_LEN)) + return xml_name; + + if (!strncasecmp(t, "desc", XML_MAX_TAG_LEN)) + return xml_desc; + + if (!strncasecmp(t, "requires", XML_MAX_TAG_LEN)) + return xml_requires; + + if (!strncasecmp(t, "conflicts", XML_MAX_TAG_LEN)) + return xml_conflicts; + + if (!strncasecmp(t, "payloadtype", XML_MAX_TAG_LEN)) + return xml_payloadtype; + + if (!strncasecmp(t, "payload", XML_MAX_TAG_LEN)) + return xml_payload; + + if (!strncasecmp(t, "payloadhex", XML_MAX_TAG_LEN)) + return xml_payloadhex; + + if (!strncasecmp(t, "index", XML_MAX_TAG_LEN)) + return xml_index; + + if (!strncasecmp(t, "longdesc", XML_MAX_TAG_LEN)) + return xml_longdesc; + + if (!strncasecmp(t, "type", XML_MAX_TAG_LEN)) + return xml_type; + + if (!strncasecmp(t, "constant", XML_MAX_TAG_LEN)) + return xml_constant; + + if (!strncasecmp(t, "value", XML_MAX_TAG_LEN)) + return xml_value; + + if (!strncasecmp(t, "valname", XML_MAX_TAG_LEN)) + return xml_valname; + + if (!strncasecmp(t, "min", XML_MAX_TAG_LEN)) + return xml_min; + + if (!strncasecmp(t, "max", XML_MAX_TAG_LEN)) + return xml_max; + + if (!strncasecmp(t, "tlvt", XML_MAX_TAG_LEN)) + return xml_tlvt; + + if (!strncasecmp(t, "tlvl", XML_MAX_TAG_LEN)) + return xml_tlvl; + + if (!strncasecmp(t, "lshift", XML_MAX_TAG_LEN)) + return xml_lshift; + + return -1; +} + + +// For a given pair of tag t and parent p check +// if t is really an allowed child of p. +// RETURN VALUE: 0 if correct, -1 otherwise +// +int xml_check_parent(int t, int p) +{ + // For given tag t specify allowed parent p + switch (t) { + + // no parent allowed + case xml_protocol: + if (p==-1) return 0; + break; + + // has protocol as parent + case xml_field: + case xml_requires: + case xml_conflicts: + case xml_payloadtype: + case xml_payload: + case xml_payloadhex: + if (p==xml_protocol) return 0; + break; + + // has field OR protocol as parent + case xml_name: + case xml_desc: + if ((p==xml_protocol)||(p==xml_field)) return 0; + break; + + // has field as parent + case xml_longdesc: + case xml_type: + case xml_constant: + case xml_valname: + case xml_value: + case xml_min: + case xml_max: + case xml_index: + case xml_lshift: + case xml_tlvt: + case xml_tlvl: + if (p==xml_field) return 0; + + } + return -1; +} + + +// Parse a single protocol definition. +// The string 'p' must start with '' and end with +// +// RETURN VALUE: 0 upon success, >0 otherwise. +// +int parse_protocol (char *p) +{ + int i; + char p_clone[AUTOMOPS_MAX_FILE_SIZE+1]; + struct automops *new_amp; + + // Make a local copy of the protocol definition + strncpy(p_clone, p, AUTOMOPS_MAX_FILE_SIZE); + p_clone[AUTOMOPS_MAX_FILE_SIZE]='\0'; + + // Check if XML form is correct. + // I thought that this check should be done separately (and not during + // the xml_readin() function) for safety reasons. If read-in plus + // validation would be combined, we would have more to clean-up at in + // case the XML data is corrupt. + i = xml_canonic (p_clone); + + // If incorrect, tell where error is: + if ((!quiet) && (i)) { + p_clone[i+1]='\0'; + fprintf(stderr, "(EE) Mausezahn automops xml parse error:\n" + "========================================\n" + "%s <>\n", p_clone); + fprintf(stderr, "(EE) Error occured at character number %i\n", i); + fprintf(stderr," --- (printed all valid data until error position) ---\n"); + } + + if (verbose) { + fprintf(stderr, "...XML verification finished.\n"); + } + + // XML is correct, now create automops entry + + if (i==0) { + strncpy(p_clone, p, AUTOMOPS_MAX_FILE_SIZE); + p_clone[AUTOMOPS_MAX_FILE_SIZE]='\0'; + new_amp = automops_alloc_protocol(amp_head); + i = xml_readin(new_amp, p_clone); + + if ((!quiet) && (i)) { + if (verbose) { + p_clone[i+1]='\0'; + fprintf(stderr, "(EE) Invalid XML data at position %i: %s <>\n", + i, p_clone); + fprintf(stderr," --- (printed all valid data until error position) ---\n"); + } + automops_delete_protocol(new_amp); + } + } + return i; +} + + + +// Scans p until the next tag is found and stores +// tag name in t which must be a string of size +// XML_STRLEN (at least). +// +// Returns +// >0 if opening tag is found +// 0 if no tag is found or other problem +// <0 if closing tag is found +// +// If any tag is found the absolut return value +// indicates the position AFTER the tag, e. g. +// ...... or ...... +// ^here ^here +// +// Upon problem, the errorness char number is +// stored as string within t along with the +// error reason as string. +// +int xml_getnext_tag (char *p, char *t) +{ + int i=0,j=0,k=0, + sign=1, + len; + + // are there non-white characters left? + len = strnlen(p, AUTOMOPS_MAX_FILE_SIZE); + for (i=0; i(len-3)) { + snprintf(t, XML_STRLEN, "%4i - no end", i); + return 0; // no tag found (smallest tag is '') + } + + j=++i; + + // closing tag? + if (p[i]=='/') { + i++; + j++; + sign=-1; + } + + // find closing bracket + // and get tag name + do { + if (p[i]=='>') { + k=i; // =found + break; + } + i++; + if (i==len) { + snprintf(t, XML_STRLEN, "%4i - no end?", i); + return 0; + } + } while (i<(j+XML_MAX_TAG_LEN+1)); + + // closing '>' really found? + if (!k) { + sprintf(t, "%4i - closing bracket missing", i); + return 0; + } + + // now the tag name is from p[j]..p[k-1] + + memcpy((void*) t, (void*) &p[j], k-j); + t[k-j]='\0'; + + return sign*(k+1); +} + + +// Copies data between opening and closing XML tags +// into 't' and returns the length of the data in bytes +// or zero if nothing found +// or -1 if protocol or data length is too long +// Note: Assumes that *p points to first byte after opening tag! +int xml_get_data (char *p, char *t) +{ + int i=0, len; + + // basic length checks + len = strnlen(p, AUTOMOPS_MAX_FILE_SIZE); + if (len==0) return 0; + + if (len>AUTOMOPS_MAX_FILE_SIZE) { + snprintf(t, XML_STRLEN, "invalid length (%u)",len); + return -1; + } + + // find closing tag + // i. e. next opening ('<') bracket + do { + if (p[i]=='<') break; + i++; + } while (i1500) return -1; // TODO: consider more reasonable limit + + // copy data + memcpy((void*) t, (void*) &p[0], i); + t[i]='\0'; + return i; +} + + + +// Make some simple checks whether XML data correct +// Currently only checks if +// - every opening tag has an ending tag (only via verbose now) +// - tags are properly nested (only 1st order tests now) +// +// RETURN VALUE: 0 upon success +// or position of mistake +// +int xml_canonic (char *p) +{ + int i=0, l, dlen=0, plen, xntag=-1; + char t[XML_STRLEN]; + char d[1500]; + + struct xnstack stack, *s; + + s=&stack; + xnstack_init(s); + + if (verbose==2) { + fprintf(stderr, "Parsing {%s}\n\n", p); + } + + plen = strnlen(p, AUTOMOPS_MAX_FILE_SIZE); + + do { + l = xml_getnext_tag (p, t); // Now t contains next tag name and l tells whether open or closing + if (l==0) { + if (t[0]==0x00) // no more tag found + return 0; + else { // general failure + fprintf(stderr, "%s\n", t); + return i; + } + + } + i += abs(l); + if (verbose==2) { + fprintf(stderr, "%4i %4i stack=%i %s%s>\n",i,l,xnstack_size(s), + (l>0) ? "<" : "=plen) { // break condition (regular, not an error!) + i=plen-1; + } + p+=abs(l); // now p points to first byte after tag + + if (xml_tag2int(t)<0) { + fprintf(stderr, "mz/xml_canonic: UNKNOWN TAG at position %i\n", i); + return i; + } + + // Closing tag found: does it match last opening tag? + if (l<0) { + if (xml_tag2int(t)!=xnstack_pop(s)) { + if (verbose) { + fprintf(stderr, "mz/xml_canonic: Incoherent nesting at position %i\n", i); + } + return i; + } + } + + // Opening tag found: store it in last_tag! + if (l>0) { + xntag=xml_tag2int(t); + // Check if this tag has proper parent + if (xml_check_parent(xntag, xnstack_get_top(s))) { + fprintf(stderr, "mz/xml_canonic: Wrong parent tag\n"); + return i; + } + if (xnstack_push(s, xntag)==-1) { + if (verbose) { + fprintf(stderr, "mz/xml_canonic: max nesting depth exceeded\n"); + } + return i; + } + // also print data: + dlen = xml_get_data (p, d); + if (dlen==-1) { + if (verbose) { + fprintf(stderr, "mz/xml_canonic: %s\n", d); + } + return i; + } + if ((dlen>0) && (verbose==2)) { + fprintf(stderr, " %s\n", d); // the data + } + + } + + if (i==plen-1) return 0; + } while (l!=0); + + if (xnstack_size(s)!=0) { + fprintf(stderr,"mz/xml_canonic: number of opening and closing tags does not match!\n"); + return i; + } + + return 0; +} + + + +// Copy data elements of *p into struct *amp +// ============================================================= +// NOTE: THE XML STRUCTURE MUST BE CORRECT !!! +// NO XML CHECKS ARE DONE TO KEEP THIS FUNCTION SMALL !!! +// THEREFORE ALWAYS RUN xml_canonic() FIRST !!! +// ============================================================= +// +// However, this function checks if the *data* is valid. +// +// RETURN VALUE: 0 upon success, +// otherwise character position of wrong data +// +int xml_readin (struct automops *amp, char *p) +{ + int i=0, l, dlen=0, plen, xntag=-1, parent=-1, err=0; + char t[XML_STRLEN]; + char d[1500], errmsg[64]; + + struct xnstack stack, *s; + struct fields *f=NULL; + + s=&stack; + xnstack_init(s); + + plen = strnlen(p, AUTOMOPS_MAX_FILE_SIZE); + + do { + l = xml_getnext_tag (p, t); // Now t contains next tag name and l tells whether open or closing + if (l==0) { + if (t[0]==0x00) return 0; + else + return i; + } + i += abs(l); + if (i>=plen) { // break condition (regular, not an error!) + i=plen-1; + } + p+=abs(l); // now p points to first byte after tag + + + // Closing tag found: does it match last opening tag? + if (l<0) xnstack_pop(s); + + // Opening tag found: store it in last_tag! + if (l>0) { + xntag=xml_tag2int(t); + parent=xnstack_get_top(s); // get parent tag; + xnstack_push(s, xntag); + dlen = xml_get_data (p, d); + + if (xntag==xml_field) { // Create new field + f=automops_add_field(amp); + } else + // Now copy the data 'd' into (the header & fields of) 'amp' + if (dlen>0) { + if (parent==xml_protocol) { + err = amp_add_pentry(amp, xntag, d); + } else + if (parent==xml_field) { + err = amp_add_fentry(amp, f, xntag, d); + } + if (err) { + if (!quiet) { + amperr2str(err, errmsg); + fprintf(stderr, "WARNING: Automops found '%s' at XML position %i\n", errmsg, i); + } + return i; + } + } + } + if (i==(plen-1)) return 0; + + } while (l!=0); + return 0; +} + + + + + + + + + + + + + + + + +/////////////////////////////////////////////////////////////////////////////// +// // +////////////// ONLY XML NESTING STACK FUNCTIONS BELOW THIS LINE /////////////// +// +// + +void xnstack_init(struct xnstack *s) +{ + s->cursize=0; +} + +// Returns top data element or -1 if stack empty +// Does NOT remove data elements! +int xnstack_get_top(struct xnstack *s) +{ + if (s->cursize==0) return -1; + return s->data[s->cursize-1]; +} + +// Push data onto stack +// Returns -1 if max stack depth exceeded +int xnstack_push(struct xnstack *s, int d) +{ + if (s->cursizedata[s->cursize++]=d; + else + return -1; + return 0; +} + + +// Returns top data element and ALSO REMOVES it from stack +// Returns -1 if stack is empty +int xnstack_pop(struct xnstack *s) +{ + int d; + d=xnstack_get_top(s); + if (d>=0) s->cursize--; + return d; +} + +int xnstack_size(struct xnstack *s) +{ + return s->cursize; +} + diff --git a/src/rcv_rtp.c b/src/rcv_rtp.c new file mode 100644 index 0000000..336a6e0 --- /dev/null +++ b/src/rcv_rtp.c @@ -0,0 +1,769 @@ +/* + * Mausezahn - A fast versatile traffic generator + * Copyright (C) 2008-2010 Herbert Haas + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 as published by the + * Free Software Foundation. + * + * 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, see http://www.gnu.org/licenses/gpl-2.0.html + * +*/ + + +/////////////////////////////////////////////////// +// +// Table of contents: +// +// rcv_rtp_init() +// rcv_rtp() +// compare4B() +// got_rtp_packet() +// print_jitterbar() +// + +/////////////////////////////////////////////////// +// +// Documentation about RTP traffic analysis +// +// See http://wiki.wireshark.org/RTP_statistics +// +// + +#include "mz.h" +#include "mops.h" + +// Initialize the rcv_rtp process: Read user parameters and initialize globals +int rcv_rtp_init() +{ + char argval[MAX_PAYLOAD_SIZE]; + char dummy[512]; + int len; + u_int32_t port = 30000; // 4-byte variable to catch errors, see below + + int ssrc_s = 0; + + // Help text + + if (getarg(tx.arg_string,"help", NULL)==1) { + fprintf(stderr,"\n" + MAUSEZAHN_VERSION + "\n" + "| RTP reception for jitter measurements.\n" + "|\n" + "| Parameters:\n" + "|\n" + "| bar ...... Display modes: By default 'bar' is used and shows the RFC 3550 jitter as\n" + "| ASCII-based waterfall diagram.\n" + "| txt ...... The 'txt' mode prints all measurement values numerically upon each\n" + "| measurement interval.\n" +// "| curse ...... Shows all values and a diagram within an resizesable ncurses window.\n" + "|\n" + "| ssrc ....... Listen to the stream with the specified SSRC. You must specify this\n" + "| when there are concurrent streams, e. g. one in each direction.\n" + "|\n" + "| log ....... Write moving average also in a datafile (not only on terminal).\n" + "| logg ....... Like log but additionally write detailed real-time statistics in a data file\n" + "| path = ....... Path to directory where datafiles can be stored (default: local directory).\n" + "| num = <10-%d> ...... number of packets to be received for averaging (default: %d).\n" + "| port = <0-65535> ....... Change if RTP packets are sent to a different port than 30000 (default).\n" + "|\n" + "| Note:\n" + "|\n" + "| Mausezahn can log actual realtime measurement data in data files (in the specified path or\n" + "| current directory) but always prints the moving average on the command line (this can be disabled\n" + "| using the 'quiet' option (-q)).\n" + "|\n" + "| The realtime data file(s) consist of two columns:\n" + "|\n" + "| 1. relative timestamp in usec\n" + "| 2. 'true' jitter in usec\n" + "|\n" + "| where the 'true' jitter is calculated using the (relative) timestamps inside the received\n" + "| packets t(i) and the (relative) timestamps T(i) observed locally when packets are received using\n" + "| the formula:\n" + "|\n" + "| jitter(i) = [T(i) - T(i-1)] - [t(i) - t(i-1)] + jitter(i-1) .\n" + "|\n" + "| This method has two advantages: (i) we do not need to synchronize the clocks of sender and\n" + "| receiver, and (ii) the TX-side jitter (mainly caused by the kernel-scheduler) is subtracted\n" + "| so that we primarily measure the jitter caused by the network.\n" + "| \n" + "| The data files consist of seven columns:\n" + "| \n" + "| 1. relative timestamp in seconds\n" + "| 2. minimum jitter\n" + "| 3. average jitter\n" + "| 4. minimum jitter\n" + "| 5. estimated jitter variance according RFC-3550\n" + "| 6. packet drop count (total)\n" + "| 7. packet disorder count (total)\n" + "| \n" + "| All measurement values are done in usec and refer to the current set of samples (see parameter 'num').\n" + "| Note that an RFC-conform jitter (smoothed mean deviation) is calculated and collected in column five.\n" + "| The drop value refers to the current measurement window, while the total drop and disorder values are\n" + "| calculated using some weird estimation functions; the goal was to provide a 'time-less' estimation\n" + "| while being able to automatically resynchronize to a re-started RTP measurement stream.\n" + "| \n" + "| EXAMPLE USAGE:\n" + "|\n" + "| At the TX-station enter:\n" + "|\n" + "| # mz eth0 -t rtp -B 10.3.3.42 (optionally change rate via -d option, payload size via pld command)\n" + "|\n" + "| At the RX-station (10.3.3.42) enter:\n" + "|\n" + "| # mz eth0 -T rtp \"log, path=/tmp/mz/\"\n" + "|\n" + "\n", TIME_COUNT_MAX, TIME_COUNT); + exit(0); + } + + + // check argstring for arguments + + if (getarg(tx.arg_string,"bar", NULL)==1) { + rtp_dm = BAR; + } + + if (getarg(tx.arg_string,"txt", NULL)==1) { + rtp_dm = TEXT; + } + + if (getarg(tx.arg_string,"curses", NULL)==1) { + rtp_dm = BAR; //NCURSES; + fprintf(stderr, " XXX This Mausezahn version does not support ncurses windows.\n"); + } + + if (getarg(tx.arg_string,"width", argval)==1) { + if (rtp_dm != BAR) { + fprintf(stderr, " mz/rcv_rtp: The 'width' parameter requires the display mode 'bar'\n"); + return -1; + } + bwidth = (int) str2int(argval); // [TODO] bwidth is currently not used + if (bwidth>RCV_RTP_MAX_BAR_WIDTH) { + fprintf(stderr, "The width must not exceed %i\n", + RCV_RTP_MAX_BAR_WIDTH); + return -1; + } + } else bwidth=80; + + if (getarg(tx.arg_string,"ssrc", argval)==1) { + ssrc_s = str2hex(argval, mz_ssrc, 4); + if (ssrc_s<0) { + fprintf(stderr, " mz/rtp_rcv: invalid ssrc!\n"); + return -1; + } + } + + if (getarg(tx.arg_string,"log", NULL)==1) { + rtp_log = 1; + } + + if (getarg(tx.arg_string,"logg", NULL)==1) { + rtp_log = 2; + } + + + if (getarg(tx.arg_string,"path", argval)==1) { + len = strlen(argval); + if (len>128) { + fprintf(stderr, " mz/Error: path must not exceed 128 characters!\n"); + exit (-1); + } + if (argval[len-1]!='/') { + strncat(argval, "/",1); // ensure that all paths end with "/" + } + strncpy(path, argval, 128); + } + + + if (getarg(tx.arg_string,"num", argval)==1) { + gind_max = (u_int32_t) str2int(argval); + if (gind_max > TIME_COUNT_MAX) { + gind_max = TIME_COUNT_MAX; + fprintf(stderr, " mz/Warning: num range is 10..%d. Will reset to %d.\n", + TIME_COUNT_MAX, TIME_COUNT_MAX); + } + else if (gind_max < 10) { + gind_max = 10; + fprintf(stderr, " mz/Warning: num range is 10..%d. Will reset to 10.\n", + TIME_COUNT_MAX); + } + } + + + // initialize global filter string + strncpy (rtp_filter_str, "udp dst port 30000", 64); + + if (getarg(tx.arg_string,"port", argval)==1) { + port = (u_int32_t) str2int(argval); + if (port>65535) { + port = 30000; + fprintf(stderr, " mz: Too large port number! Reset to default port (30000).\n"); + } + + sprintf(rtp_filter_str, "udp dst port %u", (unsigned int) port); + } + + // + if (ssrc_s==0) str2hex("ca:fe:fe:ed", mz_ssrc, 4); + + // open file + // + if (rtp_log) { + // get a new filename + timestamp_human(filename, "rtp_avg_"); + strncpy(dummy, path, 128); + strncat(dummy, filename, 64); + if (verbose) fprintf(stderr, " mz: Will open %s\n", dummy); + + fp = fopen (dummy, "w+"); + + if (fp == NULL) { + perror("fopen"); + exit (-1); + } + + gtotal=0; // counts written data blocks + fprintf(fp, "# Average jitter measurements made by Mausezahn " MAUSEZAHN_VERSION_SHORT ".\n"); + fprintf(fp, "# Timestamp is in seconds, all other values in microseconds.\n"); + fprintf(fp, "# Column values (from left to right):\n"); + fprintf(fp, "# 1. Timestamp\n" + "# 2. min_jitter\n" + "# 3. avg_jitter\n" + "# 4. max_jitter\n" + "# 5. estimated jitter according RFC-3550\n" + "# 6. packet drop count (total)\n" + "# 7. packet disorder count (total)\n"); + + + ///////////// also detailed log required ///////////// + if (rtp_log==2) { + // get a new filename + timestamp_human(filename, "rtp_rt_"); + strncpy(dummy, path, 128); + strncat(dummy, filename, 64); + if (verbose) fprintf(stderr, " mz: Will open %s\n", dummy); + + fp2 = fopen (dummy, "w+"); + + if (fp2 == NULL) { + perror("fopen"); + exit (-1); + } + + fprintf(fp2, "# Jitter measurements by Mausezahn " MAUSEZAHN_VERSION_SHORT ".\n"); + fprintf(fp2, "# Timestamp (usec) , true jitter (nsec)\n"); + } + + } + + drop=0; + dis=0; + jitter_rfc=0; + + return 0; +} + + + + + + + +//////////////////////////////////////////////////////////////////////////////////////////// +// +// Defines the pcap handler and the callback function +int rcv_rtp() +{ + char errbuf[PCAP_ERRBUF_SIZE]; + + pcap_t *p; + + struct bpf_program filter; + + + + p = pcap_open_live (tx.device, + MAXBYTES_TO_READ, // max num of bytes to read + 0, // 1 if promiscuous mode + PCAP_READ_TIMEOUT_MSEC, // read timeout in msec + errbuf); + + if (p == NULL) + { + fprintf(stderr," mz/rcv_rtp: %s\n",errbuf); + exit(1); + } + + + if ( pcap_compile(p, + &filter, // the compiled version of the filter + rtp_filter_str, // text version of filter + 0, // 1 = optimize + 0) // netmask + == -1) + { + fprintf(stderr," mz/rcv_rtp: Error calling pcap_compile\n"); + exit(1); + } + + + + if ( pcap_setfilter(p, &filter) == -1) + { + fprintf(stderr," mz/rcv_rtp: Error setting filter\n"); + pcap_geterr(p); + exit(1); + } + + again: + + + pcap_loop (p, + 1, // number of packets to wait + got_rtp_packet, // name of callback function + NULL); // optional additional arguments for callback function + + + goto again; + + + // TODO: Currently we never reach this point! + fprintf(stderr, " mz: receiving of RTP finished.\n"); + pcap_close(p); + + return 0; +} + + + + +// Compares two 4-byte variables byte by byte +// returns 0 if identical, 1 if different +inline int compare4B (u_int8_t *ip1, u_int8_t *ip2) +{ + if (*ip1 != *ip2) return 1; + if (*(ip1+1) != *(ip2+1)) return 1; + if (*(ip1+2) != *(ip2+2)) return 1; + if (*(ip1+3) != *(ip2+3)) return 1; + + return 0; +} + + + + + +// Handler function to do something when RTP messages are received +void got_rtp_packet(u_char *args, + const struct pcap_pkthdr *header, // statistics about the packet (see 'struct pcap_pkthdr') + const u_char *packet) // the bytestring sniffed +{ + const struct struct_ethernet *ethernet; + const struct struct_ip *ip; + const struct struct_udp *udp; + const struct struct_rtp *rtp; + + int size_ethernet = sizeof(struct struct_ethernet); + int size_ip = sizeof(struct struct_ip); + int size_udp = sizeof(struct struct_udp); + // int size_rtp = sizeof(struct struct_rtp); + // + ethernet = (struct struct_ethernet*)(packet); + ip = (struct struct_ip*)(packet+size_ethernet); + udp = (struct struct_udp*)(packet+size_ethernet+size_ip); + rtp = (struct struct_rtp*)(packet+size_ethernet+size_ip+size_udp); + + struct mz_timestamp + deltaTX, + deltaRX; + + u_int32_t + i, + jitter_abs, + jitter_avg, + jitter_max, + jitter_min, + curtime=0; + + int32_t ltemp; + + u_int8_t *x,*y; + + char dummy[256]; + char ts_hms[10]; + unsigned char *dum; + static u_int32_t drop_last=0, drop_prev=0; + int s1, s2; + + // check if the RTP packet is really from a Mausezahn instance: + if (compare4B((u_int8_t*) &rtp->ssrc, mz_ssrc)==0) { + // we got a valid RTP packet from a Mausezahn instance + // Get current SQNR and store it in 'sqnr_cur' in host byte order + x = (u_int8_t*) &rtp->sqnr; + y = (u_int8_t*) &sqnr_cur; + + *y = *(x+1); + y++; + *y = *x; + + ///////////////////////////////////////////////////////////////////// + // Packet drop and disorder detection: + if (sqnr0_flag) { + if (sqnr_next==sqnr_cur) { // correct SQNR received + sqnr_next++; + sqnr_last++; + } else if (sqnr_last>sqnr_cur) { // disordered sequence + dis++; + if (drop) drop--; // don't get below 0 + else { // drop reached zero: resync (restarted RTP stream?) + sqnr_last = sqnr_cur; + sqnr_next = (++sqnr_last); + dis=0; + } + } else { // packet drop + drop += (sqnr_cur-sqnr_next); + sqnr_last = sqnr_cur; + sqnr_next = (++sqnr_last); + } + } else { + // initial synchronization with observed SQNR: + sqnr_last = sqnr_cur; + sqnr_next = (++sqnr_last); + sqnr0_flag++; + } + // + ///////////////////////////////////////////////////////////////////// + + + // Get RX timestamp from pcap header + timeRX[gind].sec = header->ts.tv_sec; + timeRX[gind].nsec = header->ts.tv_usec *1000; + + // Get TX timestamp from the packet + mops_hton4((u_int32_t*) &rtp->time_sec, (u_int8_t*) &timeTX[gind].sec); + mops_hton4((u_int32_t*) &rtp->time_nsec, (u_int8_t*) &timeTX[gind].nsec); + +// printf("%li %li\n", (long int) timeTX[gind].sec, (long int) timeTX[gind].nsec); + + gind++; + + //////////////////////////////////////////////////////////////// + if (gind == gind_max) { // array full, now calculate statistics + gind=0; + gtotal++; + + jitter_avg = 0; + jitter_min = 0xffffffff; + jitter_max = 0; + + + /////////////////////////////////////////////////////// + // calculate deltas and jitters + for (i=2; i>4); + // Add previous pseudojitter to get the true jitter + // (See Documentation!) + jitter[i] += jitter[i-1]; + // + //////////////////////////////////////////////// + + + + + //////////////////////////////////////////////// + // Determine avg, min, and max jitter within this time frame: + jitter_abs = labs(jitter[i]); + jitter_avg += jitter_abs; + if (jitter_abs < jitter_min) jitter_min = jitter_abs; + if (jitter_abs > jitter_max) jitter_max = jitter_abs; + // + //////////////////////////////// + + /// PRINT IN FILE_2: Detailed jitter data /// + if (rtp_log==2) { + // Calculate relative timestamp for column 1 of the datafile + curtime = timeRX[i].sec*1000000+timeRX[i].nsec/1000; + if (time0_flag) { + curtime = curtime - time0; + } else { // this is only done once during the Mausezahn process + time0 = curtime; + time0_flag=1; + curtime = curtime - time0; + } + fprintf(fp2, "%lu, %li\n", + (long unsigned int) curtime, + (long int) jitter[i]); + fflush(fp2); // save everything immediately + // (CHECK if fsync() is additionally needed) + } + } // end for (i=2; i=drop_prev) { // because the total drop count may decrease(!) if disordered packets appear lately + drop_last = drop - drop_prev; + drop_prev=drop; + } else drop_last=0; + + // PRINT ON CLI: statistics data + switch (rtp_dm) { + case TEXT: + dum = (unsigned char*) &ip->src; + fprintf(stdout, + "Got %u packets from host %u.%u.%u.%u: %lu lost (%lu absolute lost, %lu out of order)\n" + " Jitter_RFC (low pass filtered) = %li usec\n" + " Samples jitter (min/avg/max) = %lu/%lu/%lu usec\n", + gind_max, + *(dum),*(dum+1),*(dum+2),*(dum+3), + (long unsigned int) drop_last, + (long unsigned int) drop, + (long unsigned int) dis, + (long int) jitter_rfc/1000, + (long unsigned int) jitter_min/1000, + (long unsigned int) jitter_avg/1000, + (long unsigned int) jitter_max/1000); + break; + + case BAR: + print_jitterbar(jitter_rfc/1000, drop_last); + break; + + case NCURSES: // would be nice...? + break; + + default: + break; + } + + // Determine whether some packets got lost: + // + // + // + // + + + + /// PRINT IN FILE_1: statistics only /// + if (rtp_log) { + ts_hms[0]=0x00; + timestamp_hms (ts_hms); + fprintf(fp, + "%s, %lu, %lu, %lu, %li, %u, %u\n", + ts_hms, + (long unsigned int) jitter_min/1000, + (long unsigned int) jitter_avg/1000, + (long unsigned int) jitter_max/1000, + (long int) jitter_rfc/1000, + drop, + dis); + fflush(fp); + } + + + + // Open another file if current file reaches a limit + // + if ((rtp_log==2) && (gtotal>MAX_DATA_BLOCKS)) { // file big enough, + gtotal=0; + if (fclose(fp2) == EOF) { + perror("fclose"); + exit(1); + } + + if (verbose) + fprintf(stderr, " mz: %s written.\n",filename); + + timestamp_human(filename, "rtp_"); // get a new filename + strncpy(dummy, path, 128); + strncat(dummy, filename, 64); + + if (verbose) fprintf(stderr, " mz: Will open %s\n", dummy); + + if ( (fp2 = fopen (dummy, "w+")) == NULL) { + if (errno != EAGAIN) { + perror("fopen"); + exit (-1); + } + } + fprintf(fp2, "# Jitter measurements by Mausezahn " + MAUSEZAHN_VERSION_SHORT ".\n"); + fprintf(fp2, "# Timestamp (usec) , true jitter (nsec)\n"); + } + } // statistics end ********************************************************************* + } +} + + + + +void print_jitterbar (long int j, u_int32_t d) +{ + // Determine actual data window by considering two events: + // + // 1) window move (j exceeds lower or upper limit) + // 2) window rescale (window moves happen too often or the variance + // of successive data points is too small) + // + // The most critical value is the chosen resolution (window range), + // especially the _initial_ resolution. + + static long int range=0, min=0, max=0, minvar=0, j0=0, dj=0; + static int moved=0, varcount=0, barcount=0; + char str[128], bar[150], + str1[8], str2[8], str3[8], str4[8]; + int event=0, anz; + long int tmp; + + // Initialize vars (start with an opened window) + // Note that 'range' is actually half of the window + if (!range) { + range=j; + if (range<500) range=500; + max = j+range; + min = 0; + minvar=range/40; + event++; + } else { + dj = labs(j-j0); // no initialization: calculate jitter delta + } + + // Move window when borders crossed: + if ((jmax)) { + max = j + range; + min = max-2*range; + if (min<0) { + min=0; + range=(max-min)/2; + fprintf(stdout, "\nNOTE: +- Rescaled window to %4.2f msec\n", (double) range/500); + } + moved++; + event++; + fprintf(stdout,"\n"); +// printf("move event: min=%li max=%li\n", min, max); + } else { + if (moved) moved--; +// printf("normal event: min=%li max=%li\n", min, max); + } + + + // Increase range when window moved 5 times in a row + if (moved>2) { + range*=3; + if (range>10000000L) range=10000000L; + minvar=range/40; + if (minvar<1000) minvar=1000; + max=j+range; + min=j-range; + if (min<0) { + min=0; + range=(max-min)/2; + } + moved=0; + event++; +// printf("scale up event: min=%li max=%li\n", min, max); + fprintf(stdout, "\nNOTE: ++ Rescaled window to %4.2f msec\n", (double) range/500); + } + + + // Decrease range when jitter deltas are smaller than minvar + // 5 times in a row + if (dj5) { + range*=0.75; + if (range>j) range=j; + if (range<500) { + range=500; + } + minvar=range/40; + if (minvar<1000) minvar=1000; + max=j+range; + min=j-range; + if (min<0) { + min=0; + range=(max-min)/2; + } + fprintf(stdout, "\nNOTE: -- Rescaled window to %4.2f msec\n", (double) range/500); + varcount=0; + event++; +// printf("scale down event: min=%li max=%li\n", min, max); + } + + j0=j; + + barcount++; + if (barcount==24) { + event=1; + barcount=0; + } + + if (event) { + tmp=range*0.667; + sprintf(str1,"%4.2f", (double) min/1000); + sprintf(str2,"%4.2f", (double) (min+tmp)/1000); + sprintf(str3,"%4.2f", (double) (max-tmp)/1000); + sprintf(str4,"%4.2f", (double) max/1000); + + fprintf(stdout, + "%-6s %-6s %-6s %-6s\n" + "|-------------------------|-------------------------|-------------------------|\n", + str1, str2, str3, str4); + barcount=0; + } + + anz = 80*(j-min)/(2*range); + if (anz) { + memset((void*) str, '#', anz); + memset((void*) str+anz, ' ', 80-anz); + str[80]='\0'; + } + else { + memset((void*) str, ' ', 80); + str[0]='#'; + str[80]='\0'; + } + if (d) + sprintf(bar, "%s%4.2f msec !%lu dropped!", str, (double) j/1000, (unsigned long int) d); + else + sprintf(bar, "%s%4.2f msec", str, (double) j/1000); + + fprintf(stdout,"%s\n", bar); +} + diff --git a/src/rtp.c b/src/rtp.c new file mode 100644 index 0000000..6ce4458 --- /dev/null +++ b/src/rtp.c @@ -0,0 +1,217 @@ +/* + * Mausezahn - A fast versatile traffic generator + * Copyright (C) 2008 Herbert Haas + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 as published by the + * Free Software Foundation. + * + * 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, see http://www.gnu.org/licenses/gpl-2.0.html + * +*/ + + + +#include "mz.h" +#include "cli.h" +#include "mops.h" + +#define MZ_RTP_HELP \ + "| RTP type: Send Real Time Protocol packets.\n" \ + "|\n" \ + "| This mode is solely intended to conduct delay, drop, and jitter measurements in\n" \ + "| Voice (Video) over IP networks. You will typically initiate another Mausezahn\n" \ + "| instance on the destination host, which will perform the measurements or even\n" \ + "| 'bounce back' the packets for Round Trip Time (RTT) measurements.\n" \ + "|\n" \ + "| When the delay parameter is not specified, the default (inter-packet) delay is\n" \ + "| set to 20 msec. You must specify the destination host using the -B option.\n" \ + "| The default destination port is (UDP) 30000 but can be overridden (dp parameter).\n" \ + "| You do not need to specify the count option (-c), because 'infinite' (0) is assumed.\n" \ + "|\n" \ + "| You can specify these additional GENERAL options:\n" \ + "|\n" \ + "| -c ..... use this packet count value instead of infinity.\n" \ + "| -d ..... use this delay value instead of the defaul. Per default\n" \ + "| the units are microseconds but you can also use msec or sec\n" \ + "|\n" \ + "| You can specify these additional UDP/RTP-specific arguments:\n" \ + "|\n" \ + "| dp = <1-65535> ..... use this UDP destination port instead of 30,000.\n" \ + "| sp = <1-65535> ..... use this UDP source port instead of random.\n" \ + "| ssrc = XX:XX:XX:XX ... use this hex sequence as stream identifier\n" \ + "| (=SSRC, required for multiple concurrent measurements)\n" \ + "| codec ..... simulate G.711 codec (other will follow).\n" \ + "| pld = <1..1000> ....... create specified payload size (default=160 bytes, which results\n" \ + "| in a total datagram length of 180 bytes, considering the UDP and\n" \ + "| RTP header lengths (8 and 12 bytes, respectively).\n" \ + "|\n" \ + "| Additional help: enter 'mz -T rtp help'\n" \ + "|\n" + + + +int create_rtp_packet() +{ + u_int8_t byte1, byte2; + u_int16_t seqnr; + u_int8_t ssrc[4] = {0,0,0,0} ; + int ssrc_s = 0; + u_int8_t *ptr; + char argval[MAX_PAYLOAD_SIZE]; + unsigned int rtp_payload_size=160; + struct mz_timestamp ts; + + if ( (getarg(tx.arg_string,"help", NULL)==1) && (mode==RTP) ) { + if (mz_port) + { + cli_print(gcli, "%s", MZ_RTP_HELP); + return -1; + } + else + { + + fprintf(stderr,"\n" + MAUSEZAHN_VERSION + "\n%s", MZ_RTP_HELP); + exit(0); + } + } + + + if (getarg(tx.arg_string,"pld", argval)==1) { + rtp_payload_size = (unsigned int) str2int(argval); + } + + if (getarg(tx.arg_string,"codec", argval)==1) { + tx.delay = 20000; + } + + if (getarg(tx.arg_string,"ssrc", argval)==1) { + ssrc_s = str2hex(argval, ssrc, 4); + if (ssrc_s<0) { + fprintf(stderr, " mz/rtp: invalid ssrc!\n"); + return -1; + } + } + + // TODO: Optional arguments for RTP + + + // Create header: // + + // Byte 1 + // + // +--+--+--+--+--+--+--+--+ + // | ver | P| X| CSRC Count| + // +--+--+--+--+--+--+--+--+ + // + // Default: ver=2, Padding=0, Extension_Header=1, CSRC_Count=0 => 10 0 1 0000 = 0x90 + + byte1 = 0x90; + + // Byte 2 + // + // +--+--+--+--+--+--+--+--+ + // | M| Payload Type | + // +--+--+--+--+--+--+--+--+ + // + // Marker=0, Payload Type=0 (or 8 alternatively) + + byte2 = 0x00; + + // Bytes 3,4 + // + // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + // | Sequence Number | + // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + + seqnr = 0x0000; + + // Bytes 5,6,7,8 + // + // Timestamp /* done below */ + // + + + // Bytes 9,10,11,12 + // + // Synchronization Source Identifier + // + + if (ssrc_s==0) str2hex("ca:fe:fe:ed", ssrc, 4); + + // Bytes 13,14,15,16 + // + // CSRC - Contributing Source Identifiers (optional, only used by mixers) + // + // csrc = 0x00000000; + + // Bytes 17,18,19,20 + // + // Header Extension (optional) NOT USED HERE! + // + + // !!! Thus payload begins with index 16 in a C array !!! + + // ------------ Now combine all fields: ---------------- + tx.udp_payload[0] = byte1; + tx.udp_payload[1] = byte2; + + ptr = (u_int8_t*) &seqnr; + tx.udp_payload[2] = *(ptr+1); + tx.udp_payload[3] = *ptr; + + // TIMESTAMP: will be linearly increased, e.g. using 20msec G.711: 0, 160, 320, ... + tx.udp_payload[4] = 0x00; + tx.udp_payload[5] = 0x00; + tx.udp_payload[6] = 0x00; + tx.udp_payload[7] = 0x00; + + tx.udp_payload[8] = ssrc[0]; + tx.udp_payload[9] = ssrc[1]; + tx.udp_payload[10] = ssrc[2]; + tx.udp_payload[11] = ssrc[3]; + + /* + ptr = (u_int8_t*) &csrc; + tx.udp_payload[12] = *(ptr+3); + tx.udp_payload[13] = *(ptr+2); + tx.udp_payload[14] = *(ptr+1); + tx.udp_payload[15] = *ptr; + */ + + // Add the NEW Mausezahn extension header (see mops_ext_rtp.c) + tx.udp_payload[12] = 0xca; // identifier + tx.udp_payload[13] = 0xca; + tx.udp_payload[14] = 0x00; + tx.udp_payload[15] = 0x04; // length + getcurtime(&ts); // Now add TX timestamp: + mops_hton4 ((u_int32_t*) &ts.sec, &tx.udp_payload[16]); + mops_hton4 ((u_int32_t*) &ts.nsec, &tx.udp_payload[20]); + // NOTE: The remaining 8 bytes of this extension header are set to zero + // via the following code. + + memset(&tx.udp_payload[24], 0x00, (rtp_payload_size-12)); // payload (considering our 8 byte timestamp) + tx.udp_payload_s = 12 + rtp_payload_size; // the latter ist the payload size + + // ---------- now hand over to UDP ----------------- + + tx.dp = 30000; + tx.sp = 30000; + + tx.udp_len = 8 + tx.udp_payload_s; + + return 0; +} + + + + + diff --git a/src/send.c b/src/send.c new file mode 100644 index 0000000..5ad1a20 --- /dev/null +++ b/src/send.c @@ -0,0 +1,264 @@ +/* + * Mausezahn - A fast versatile traffic generator + * Copyright (C) 2008 Herbert Haas + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 as published by the + * Free Software Foundation. + * + * 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, see http://www.gnu.org/licenses/gpl-2.0.html + * +*/ + + + + + +// *************************************************************************** +// +// This sections contains: +// +// - complexity() ... calculates and reports how many frames will +// be generated. +// - send_frame() ... the general and mighty SENDING FUNCTION. +// +// *************************************************************************** + +#include "mz.h" +#include "cli.h" + + +// Calculates the number of frames to be sent. +// Should be used as standard output except the +// 'quiet' option (-q) has been specified. +int complexity() +{ + unsigned long int + nr_sqnr = 1, + nr_dp = 1, + nr_sp = 1, + nr_da = 1, + nr_sa = 1; + + u_int32_t + sn1, + sn2, + delta; + + long double ref; + + if (tx.count==0) goto infinity; + + total_d = 1.0; + + // How many sequence numbers? + if (tx.tcp_seq_delta) + { + sn1 = tx.tcp_seq_start; + sn2 = tx.tcp_seq_stop; + delta = tx.tcp_seq_delta; + + if (sn1ref) + { + fprintf(stderr, "You must be crazy...\n"); + } + else if (total_d>0xffffffff) + { + fprintf(stderr, "Do you REALLY know what you do?\n"); + } + else if (total_d>0xffffff) + { + fprintf(stderr, "Do you know what you do?\n"); + } + + if (mz_port) + { + cli_print(gcli, "Mausezahn will send %.Lf frames...\r", total_d); + } + else + { + fprintf(stderr, "Mausezahn will send %.Lf frames... ", total_d); + fflush(stderr); + if (verbose) fprintf(stderr,"\n"); + } + + + + mz_start = clock(); + + infinity: + + + if (tx.count==0) + { + if (mz_port) + { + cli_print(gcli, "Mausezahn will send frames infinitly...\n"); + } + else + { + fprintf(stderr, "Mausezahn will send frames infinitly...\n"); + } + } + + + return 0; +} + + + +/////////////////////////////////////////////////////////////////////// +// +// Send complete frame (layers 2, 3, 4) multiple times if required +// +// +int send_frame (libnet_t *l, libnet_ptag_t t3, libnet_ptag_t t4) +{ + int i=0, count; + + int // local vars are faster ;-) + tcp_seq_delta, + dp_isrange, + sp_isrange, + ip_dst_isrange, + ip_src_isrange, + rtp_mode=0; + + + count = tx.count; + tcp_seq_delta = tx.tcp_seq_delta; + dp_isrange = tx.dp_isrange; + sp_isrange = tx.sp_isrange; + ip_dst_isrange = tx.ip_dst_isrange; + ip_src_isrange = tx.ip_src_isrange | tx.ip_src_rand; + if (mode == RTP) rtp_mode = 1; + + if (count==0) goto AGAIN; + + for (i=0; i 7) + { + fprintf(stderr, " mz/create_eth_frame: CoS too high, adjusted to 7\n"); + CoS = 7; + } + + if (vlan > 4095) + { + fprintf(stderr, " mz/create_eth_frame: VLAN number too high, adjusted to 4095\n"); + vlan = 4095; + } + + // create 4 byte 802.1Q header: + + dot1Q[bytecnt+0]=0x81; + dot1Q[bytecnt+1]=0x00; + ptr = (u_int8_t*) &vlan; + dot1Q[bytecnt+3]=*ptr; + ptr++; + *ptr = *ptr ^ (CoS<<5); // add CoS + dot1Q[bytecnt+2]=*ptr; + //check: + //printf("%02x %02x %02x %02x\n",dot1Q[bytecnt+0],dot1Q[bytecnt+1],dot1Q[bytecnt+2],dot1Q[bytecnt+3]); + bytecnt+=4; // next tag (note that bytecnt will finally hold the number of used bytes!) + + } while ( (ptrsubstring = strtok_r(NULL, ",.", &saveptr)) !=NULL); //get all VLAN tags + + // now create the whole packet: + + dot1Q_eth_type = 0x8100; //these are also the first two bytes of dot1Q[] + bytecnt = bytecnt-2; + + for (i=0;i use normal 'l' context: + { + if (eth_src_rand) update_Eth_SA(l, t); + if (verbose) (void) print_frame_details(); + libnet_write(l); + } + + +// if (verbose) (void) print_frame_details(); + if (delay) SLEEP (delay); + + + if (tcp_seq_delta) + { + if (update_TCP_SQNR(l, t4)==0) // end of range not yet reached + { + goto AGAIN; + } + } + + if (dp_isrange) + { + if (update_DPORT(l, t4)==0) // end of range not yet reached + { + goto AGAIN; + } + } + + if (sp_isrange) + { + if (update_SPORT(l, t4)==0) // end of range not yet reached + { + goto AGAIN; + } + } + + if (ip_dst_isrange) + { + if (update_IP_DA(l, t3)==0) // end of range not yet reached + { + goto AGAIN; + } + } + + if (ip_src_isrange) + { + if (update_IP_SA(l, t3)==0) // end of range not yet reached + { + goto AGAIN; + } + } + + + if (rtp_mode) // update SQNR and Timestamps in RTP header and payload + { + update_RTP(l, t4); + } + + + if (!count) goto AGAIN; + } + + + libnet_destroy(l); + if (isdot1Q) + libnet_destroy(L); + + + return t; +} + + + +void print_dot1Q_help(void) +{ + + fprintf(stderr,"\n" + MAUSEZAHN_VERSION + "\n" + "| 802.1Q header Syntax: -Q tag[,tag[,tag[,...]]]\n" + "| where each tag may consist of a CoS value using the syntax:\n" + "|\n" + "| :\n" + "|\n" + "| Examples:\n" + "|\n" + "| # mz -Q 100\n" + "| # mz -Q 5:100\n" + "| # mz -Q 5:100,200\n" + "| # mz -Q 5:100,7:200\n" + "| # mz -Q 100,200,300,5:400\n" + "\n\n"); + + exit(0); +} + + diff --git a/src/syslog.c b/src/syslog.c new file mode 100644 index 0000000..c8fac9b --- /dev/null +++ b/src/syslog.c @@ -0,0 +1,248 @@ +/* + * Mausezahn - A fast versatile traffic generator + * Copyright (C) 2008,2009 Herbert Haas + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 as published by the + * Free Software Foundation. + * + * 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, see http://www.gnu.org/licenses/gpl-2.0.html + * +*/ + + +#include "mz.h" +#include "cli.h" + +#define MZ_SYSLOG_HELP \ + "| Syslog type: Send (traditional) Syslog packets via UDP.\n" \ + "|\n" \ + "| Parameters:\n" \ + "|\n" \ + "| severity, sev 0-7 .... Severity level from Emergency (0) to Debug (7)\n" \ + "| facility, fac 0-23 .... Facility number\n" \ + "|\n" \ + "| time hh:mm:ss .... Local time, 24-hour format\n" \ + "| month, mon Mmm .... Current month, 1-12\n" \ + "| day dd .... Current day, 0-31\n" \ + "|\n" \ + "| host max 314 bytes .... Name or IP Address of sending host\n" \ + "|\n" \ + "| Defaults:\n" \ + "|\n" \ + "| Per default the severity \"Warning\" (4), the facility \"Security\" (4), and the\n" \ + "| current time stamp is used. If no host is given, host is set to \"MZ\"\n" \ + "|\n" \ + "| You can define the Syslog message itself using the -P flag. For example:\n" \ + "|\n" \ + "| mz eth0 -t syslog sev=3 -P \"You have been mausezahned.\"\n" \ + "|\n" \ + "| By the way, mz (by intention) does not check if your timestamp is valid according\n" \ + "| calendar rules. It is generally recommended to follow the Darwin Era Calendar ;-)\n" \ + "|\n" + + + +// RFC 3164 states that a Syslog message consists of three parts: PRI, HEADER, and MSG. +// +// 1) PRI: contains facility(f) and severity(s), using the syntax "" where N = f * 8 + s +// +// 2) HEADER: contains a timestamp and a sender-ID (name or IP), for example "May 25 23:42:42 Mausezahnhost" +// Note that instead of leading zeroes a space must be used for the day e. g. "May 5". +// However leading zeroes are required for hour, minutes, seconds, e. g. "01:05:09" +// +// 3) MSG: consists of TAG and CONTENT field. The TAG identifies the program or process and +// must not exceed 32 characters. Typically the TAG and the CONTENT fields are delimited +// via either a "[", or a colon (:) or a space. The CONTENT field is a simple text. +// +// EXAMPLE from RFC 3164: +// +// <34>Oct 11 22:14:15 mymachine su: 'su root' failed for lonvick on /dev/pts/8 +// +// EXAMPLE from Cisco Router: +// +// *Mar 23 13:45:08.727: %ENVMON-3-FAN_FAILED: Fan 2 not rotating +// + + +int create_syslog_packet() +{ + unsigned int pri, sev, fac, day, curday, mon, curmon; + char lt[8], host[314]; + char *Months[12] = + { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; + + + time_t curtime; + struct tm curtime_broken; + char argval[MAX_PAYLOAD_SIZE]; + int ca=0, aa; + + aa=number_of_args(tx.arg_string); + + if ( (getarg(tx.arg_string,"help", NULL)==1) && (mode==SYSLOG) ) + { + ca++; // counts each argument + if (mz_port) + { + cli_print(gcli, "%s", MZ_SYSLOG_HELP); + return -1; + } + else + { + fprintf(stderr,"\n" + MAUSEZAHN_VERSION + "\n%s", MZ_SYSLOG_HELP); + + exit(0); + } + } + + + if ( (getarg(tx.arg_string,"severity", argval)==1) || + (getarg(tx.arg_string,"sev", argval)==1) ) + { + ca++; // counts each argument + sev = (unsigned int) str2int(argval); + } + else + { + sev = 4; + } + + if ( (getarg(tx.arg_string,"facility", argval)==1) || + (getarg(tx.arg_string,"fac", argval)==1) ) + { + ca++; // counts each argument + fac = (unsigned int) str2int(argval); + } + else + { + fac = 4; + } + + + time(&curtime); + localtime_r (&curtime, &curtime_broken); + + + + if (getarg(tx.arg_string,"time", argval)==1) + { + ca++; // counts each argument + strncpy(lt,argval,8); + // TODO: check if specified timestamp has valid format, e. g. 15:03:22 + } + else + { + timestamp_hms (lt); + } + + + + curmon = curtime_broken.tm_mon; // Note that Jan = 0, ..., Dec = 11 !!! + + if ( (getarg(tx.arg_string,"month", argval)==1) || + (getarg(tx.arg_string,"mon", argval)==1) ) + { + ca++; // counts each argument + mon = (unsigned int) str2int(argval); + if ( (mon<1) || (mon>12) ) + { + fprintf(stderr, " mz/syslog: Invalid month; will use current month (%i)!\n", curmon+1); + mon = curmon; + } + } + else + { + mon = curmon; + } + + curday = curtime_broken.tm_mday; + + if (getarg(tx.arg_string,"day", argval)==1) + { + ca++; // counts each argument + day = (unsigned int) str2int(argval); + if ( (day<1) || (day>31) ) + { + fprintf(stderr, " mz/syslog: Invalid day; will use current day(%i)!\n", curday); + day = curday; + } + } + else + { + day = curday; + } + + + if (getarg(tx.arg_string,"host", argval)==1) + { + ca++; // counts each argument + strncpy(host,argval,314); // 314 is just an arbitrary number ;-) + } + else + { + strcpy(host, "MZ42"); + } + + + // CHECK SURPLUS ARGUMENTS + if (aa!=ca) { + fprintf(stderr, "WARNING: %i unmatched arguments within argument string!\n", aa-ca); + } + + + // Now put everything together: + // + // Again the EXAMPLE from RFC 3164: + // + // <34>Oct 11 22:14:15 mymachine su: 'su root' failed for lonvick on /dev/pts/8 + // + + + pri = 8*fac+sev; + + sprintf((char*) tx.udp_payload, "<%d>%s %2i %s %s ", + pri, + Months[mon], + day, + lt, + host); + + if (tx.ascii) // ASCII PAYLOAD overrides hex payload + { + strncat((char *)tx.udp_payload, (char *)tx.ascii_payload, 2048); + tx.ascii=0; // avoid that 'create_udp_packet' overwrites this! + } + else + { + strcat((char *)tx.udp_payload, "%MZSYS-42-CRN: Main reactor exceeded critical temperature!"); + } + + + tx.udp_payload_s = strlen((char *)tx.udp_payload); + + tx.dp = 514; + tx.sp = 514; + + tx.udp_len = 8 + tx.udp_payload_s; + + if (verbose) + { + fprintf(stderr, "Syslog: %s\n", tx.udp_payload); + } + + + return 0; + +} + + + diff --git a/src/time.c b/src/time.c new file mode 100644 index 0000000..4225b9e --- /dev/null +++ b/src/time.c @@ -0,0 +1,211 @@ +/* + * Mausezahn - A fast versatile traffic generator + * Copyright (C) 2008-2010 Herbert Haas + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 as published by the + * Free Software Foundation. + * + * 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, see http://www.gnu.org/licenses/gpl-2.0.html + * +*/ + + +#include "mz.h" + + +// Check if current system supports the nanosecond timer functions. +// Additionally, measure the precision. +// This function should be called upon program start. +// +int check_timer() +{ + struct timespec res; + int r; + +// Check if the glibc is recent enough: +#ifdef _POSIX_C_SOURCE + + if (_POSIX_C_SOURCE >= 199309L) { + r = clock_getres(CLOCK_MONOTONIC, &res); + if (r!=0) perror(" mz/check_timer:"); + if (verbose) { + fprintf(stderr, " This system supports a high resolution clock.\n"); + fprintf(stderr, " The clock resolution is %li nanoseconds.\n", + res.tv_nsec); + } + } + else { + fprintf(stderr, + " WARNING: Your system does NOT support the newer high resolution clock\n" + " Please inform the author: herbert@perihel.at\n"); + exit(1); + } +#endif + return 0; +} + + + + +// This is the replacement for gettimeofday() which would result in 'jumps' if +// the system clock is adjusted (e. g. via a NTP process) and finally the jitter +// measurement would include wrong datapoints. +// +// Furthermore the function below utilizes the newer hi-res nanosecond timers. +inline void getcurtime (struct mz_timestamp *t) +{ + struct timespec ct; + clock_gettime(CLOCK_MONOTONIC, &ct); + t->sec = ct.tv_sec; + t->nsec = ct.tv_nsec; +} + + + + +////////////////////////////////////////////////////////////////////////////////////// +// Purpose: Calculate time deltas of two timestamps stored in struct timeval. +// +// Subtract the "struct timeval" values X and Y, storing the result in RESULT, +// i. e. X-Y=RESULT. +// +// RETURN VALUES: +// +// Sign: 1 = negative, 0 = positive +// Error: -1 due to a wrong timestamp (i. e. nsec > 999999999L) +// +inline int timestamp_subtract (struct mz_timestamp *x, struct mz_timestamp *y, struct mz_timestamp *result) +{ + int32_t ndiff; + int sign=0, carry=0; + + // Check for wrong timestamps + if ((x->nsec>999999999L) || (y->nsec>999999999L)) return -1; + + if (y->sec > x->sec) sign=1; + else if ((y->sec == x->sec) && (y->nsec > x->nsec)) sign=1; + + ndiff = x->nsec - y->nsec; + if ((ndiff>0) && (sign)) carry=1; + if ((ndiff<0) && (sign)) ndiff = y->nsec - x->nsec; + if ((ndiff<0) && (!sign)) { + ndiff = 1000000000L + ndiff; + carry=1; + } + + if (sign) + result->sec = y->sec - x->sec - carry; + else + result->sec = x->sec - y->sec - carry; + + result->nsec = ndiff; + return sign; +} + + +// Add two variables of type struct mz_timestamp: x+y=result. +// +inline void timestamp_add (struct mz_timestamp *x, struct mz_timestamp *y, struct mz_timestamp *result) +{ + int carry=0; + u_int32_t c; + + c = x->nsec + y->nsec; + if (c>999999999L) { + carry=1; + result->nsec =c-1000000000; + } else result->nsec =c; + + result->sec = x->sec + y->sec + carry; +} + + + +// Returns a human readable timestamp in the string result. +// Optionally a prefix can be specified, for example if the +// timestamp is part of a filename. +// +// Example: +// char myTimeStamp[128]; +// +// timestamp_human(myTimeStamp, NULL); +// +// => "20080718_155521" +// +// /* or with prefix */ +// +// timestamp_human(myTimeStamp, "MZ_RTP_jitter_"); +// +// => "MZ_RTP_jitter_20080718_155521" +// +int timestamp_human(char* result, const char* prefix) +{ + time_t curtime; + struct tm curtime_broken; + char curtime_str[32]; + + time(&curtime); + localtime_r (&curtime, &curtime_broken); + + sprintf(curtime_str, "%4i%02i%02i-%02i%02i%02i", + curtime_broken.tm_year+1900, + curtime_broken.tm_mon+1, + curtime_broken.tm_mday, + curtime_broken.tm_hour, + curtime_broken.tm_min, + curtime_broken.tm_sec); + + if (prefix==NULL) + { + strncpy(result, curtime_str, 32); + } + else + { + strncpy(result, prefix, 32); + strncat(result, curtime_str, 32); + } + + return 0; +} + + +// Creates a human readable timestamp in the string result. +// Optionally a prefix can be specified, for example if the +// timestamp is part of a filename. +// +// Example: +// char myTimeStamp[9]; +// +// timestamp_hms (myTimeStamp); +// +// => "15:55:21" +int timestamp_hms(char* result) +{ + time_t curtime; + struct tm curtime_broken; + char curtime_str[32]; + + time(&curtime); + localtime_r (&curtime, &curtime_broken); + + sprintf(curtime_str, "%02i:%02i:%02i", + curtime_broken.tm_hour, + curtime_broken.tm_min, + curtime_broken.tm_sec); + + strncpy(result, curtime_str, 9); + + return 0; +} + + + + + diff --git a/src/tools.c b/src/tools.c new file mode 100644 index 0000000..96f0bb7 --- /dev/null +++ b/src/tools.c @@ -0,0 +1,1344 @@ +/* + * Mausezahn - A fast versatile traffic generator + * Copyright (C) 2008-2010 Herbert Haas + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 as published by the + * Free Software Foundation. + * + * 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, see http://www.gnu.org/licenses/gpl-2.0.html + * +*/ + + + +//////////////////////////////////////////////////////////////////////////////////////////// +// +// Contains various tools to ease development of new modules: +// +// getarg ............. scans string for arguments and returns assigned value +// str2int ............. Converts a string into unsigned long int in a safe way +// str2lint ............ Same as str2int but returns an unsigned long long int +// xstr2int ............ Same as str2int but expects hex digits +// xstr2lint ........... Same as above but returns an unsigned long long int +// get_ip_range_dst .... Parses string for an IP range and sets start/stop addresses +// get_ip_range_src .... Same for source addresses +// check_eth_mac_txt ... Scans tx.eth_dst|src_txt and sets tx.eth_dst|src appropriately +// get_port_range ...... Parses string for a dst|src-port range and sets start/stop values +// get_tcp_flags ....... Parses string for TCP arguments and sets tx.tcp_control +// get_mpls_params ..... Parses string for MPLS parameters (label, exp, BOS, TTL) +// exists .............. Parses a string for a single character and returns "1" if found +// mz_strisbinary ...... Checks whether string consists only of 0 and 1, returns how many digits total +// str2bin8 ............ Converts a string containing max 8 binary digits into a number +// str2bin16 ........... Converts a string containing max 16 binary digits into a number +// char2bits ........... Converts a char into a string containing ones and zeros +// getfullpath_cfg ..... Creates a full filename with path to the desired config directory +// getfullpath_log ..... Creates a full filename with path to the desired logging directory +// mz_strncpy .......... A safer implementation of strncpy +// number_of_args ...... Returns number of arguments of the Mausezahn argument string +// mz_strisnum ......... Returns 1 if string only consists of decimal digits +// mz_strishex ......... Returns 1 if string only consists of hexadecimal digits +// mz_strcmp ........... Matches a string or a prefix of it with given min-length +// Example usage: User CLI input +// mz_tok .............. Decomposes a string into tokens and maps them to args +// Example usage: IPv6-addresses, user input for MPLS-tags +// delay_parse ......... Parses one or two strings for a delay specification and sets a struct timespec +// +//////////////////////////////////////////////////////////////////////////////////////////// + +#include "mz.h" + + + +// Scan 'str' for an argument 'arg_name' and returns its value in arg_value +// Return value: number of occurences of arg_name +// Note that if arg_name occurs multiple times, the last found value is returned. +// If last argument (arg_value) is set to NULL it will be ignored. +// Example: +// int i; +// char ip[64]; +// i = getarg ("request, da=10.1.1.2, SYN", "da", ip); +// ...will assign "10.1.1.2" to ip and the occurence i is set to 1. +int getarg(char *str, char *arg_name, char *arg_value) +{ + char tmp[MAX_PAYLOAD_SIZE]; + char *str1, *str2, *token, *subtoken; + char *saveptr1, *saveptr2; + int j, occurence=0; + + strncpy(tmp,str,MAX_PAYLOAD_SIZE); // only operate on local strings + + for (j = 1, str1 = tmp; ; j++, str1 = NULL) + { + + token = strtok_r(str1, ",", &saveptr1); + if (token == NULL) + break; + + str2 = token; + if ( (subtoken = strtok_r(str2, " =", &saveptr2))!=NULL) + { + if (strcasecmp(subtoken,arg_name)==0) + { + occurence+=1; + //printf("found %s\n",arg_name); + if ( (subtoken = strtok_r(NULL, " =", &saveptr2))!=NULL) + { + // argument has a value! + //printf("%s has value: [%s]\n",arg_name, subtoken); + if (arg_value!=NULL) + { + strcpy(arg_value,subtoken); + } + } + } + } + else + break; + } + return occurence; +} + + +// Convert str to (unsigned long) int +// Return value: the unsigned long int +unsigned long int str2int(char *str) +{ + unsigned long int i; + + errno=0; + + i = strtoul(str, (char **)NULL, 10); + + if ((errno == ERANGE && (i == ULONG_MAX)) + || (errno != 0 && i == 0)) + { + perror("strtoul"); + } + + return i; +} + + + +// Convert str to (unsigned long long) int +// Return value: the unsigned long long int +unsigned long long int str2lint(char *str) +{ + unsigned long long int i; + + errno=0; + + i = strtoull(str, (char **)NULL, 10); + + if ((errno == ERANGE && (i == ULLONG_MAX)) + || (errno != 0 && i == 0)) + { + perror("strtoull"); + } + + return i; +} + + +// Convert hex-str to (unsigned long) int +// Return value: the unsigned long int +unsigned long int xstr2int(char *str) +{ + unsigned long int i; + + errno=0; + + i = strtoul(str, (char **)NULL, 16); + + if ((errno == ERANGE && (i == ULONG_MAX)) + || (errno != 0 && i == 0)) + { + + perror("strtoul"); + } + + return i; +} + + +// Convert hex-str to (unsigned long long) int +// Return value: the unsigned long long int +unsigned long long int xstr2lint(char *str) +{ + unsigned long long int i; + + errno=0; + + i = strtoull(str, (char **)NULL, 16); + + if ((errno == ERANGE && (i == ULLONG_MAX)) + || (errno != 0 && i == 0)) + { + perror("strtoull"); + } + + return i; +} + + + + +// Parses string 'arg' for an IP range and finds start and stop IP addresses. +// Return value: 0 upon success, 1 upon failure. +// +// NOTE: The results are written in the following variables: +// +// (u_int32_t) tx.ip_dst_start ... contains start value +// (u_int32_t) tx.ip_dst_stop ... contains stop value +// (u_int32_t) tx.ip_dst ... initialized with start value +// int tx.ip_dst_isrange ... set to 1 if above values valid +// +// Possible range specifications: +// +// 1) 192.168.0.0-192.168.0.12 +// 2) 10.2.11.0-10.55.13.2 +// 3) 172.18.96.0/19 +// +// That is: +// +// FIRST detect a range by scanning for the "-" OR "/" chars +// THEN determine start and stop value and store them as normal unsigned integers +// +int get_ip_range_dst (char *arg) +{ + + int + i, len, + found_slash=0, found_dash=0; + + unsigned int q; + u_int32_t mask, invmask; + + char *start_str, *stop_str; + + len = strnlen(arg, 32); + + if ( (len>31) || (len<9) ) // 255.255.255.200-255.255.255.255 (31 chars) OR 1.0.0.0/8 (9 chars) + return 1; // ERROR: no range + + // Find "-" or "/" + for (i=0; i31) || (len<9) ) // 255.255.255.200-255.255.255.255 (31 chars) OR 1.0.0.0/8 (9 chars) + return 1; // ERROR: no range + + // Find "-" or "/" + for (i=0; i65535)) + { + fprintf(stderr," mz/get_port_range: Invalid port range!\n"); + exit (-1); + } + *start = tmp1; + + tmp2 = str2int (stop_str); + if ( (tmp2<0)||(tmp2>65535)) + { + fprintf(stderr," mz/get_port_range: Invalid port range!\n"); + exit (-1); + } + *stop = tmp2; + + if (tmp1>tmp2) // swap start/stop values! + { + *start = tmp2; + *stop = tmp1; + } + + *port = *start; + *isrange = 1; + + return 0; + } + else // single port number + { + tmp1 = str2int (arg); + if ( (tmp1<0)||(tmp1>65535)) tmp1=0; + *port = tmp1; + *isrange = 0; + return 0; + } + + return 1; // error +} + + + + +// Scans argument for TCP flags and sets +// tx.tcp_control accordingly. +// +// Valid keywords are: fin, syn, rst, psh, ack, urg, ecn, cwr +// Valid delimiters are: | or + or - +// Return value: 0 on success, 1 upon failure +// +int get_tcp_flags (char* flags) +{ + char *f; + + // From LSB to MSB: fin, syn, reset, push, ack, urg, ecn, cwr + // ecn...ECN-Echo, cwr...Congestion Window Reduced + + if (strnlen(flags,40)==0) return 1; // error + + + f = strtok (flags, "|+-"); + do + { + if (strncmp(f,"fin",3)==0) + { + tx.tcp_control = tx.tcp_control | 1; + } + else if (strncmp(f,"syn",3)==0) + { + tx.tcp_control = tx.tcp_control | 2; + } + else if (strncmp(f,"rst",3)==0) + { + tx.tcp_control = tx.tcp_control | 4; + } + else if (strncmp(f,"psh",3)==0) + { + tx.tcp_control = tx.tcp_control | 8; + } + else if (strncmp(f,"ack",3)==0) + { + tx.tcp_control = tx.tcp_control | 16; + } + else if (strncmp(f,"urg",3)==0) + { + tx.tcp_control = tx.tcp_control | 32; + } + else if (strncmp(f,"ecn",3)==0) + { + tx.tcp_control = tx.tcp_control | 64; + } + else if (strncmp(f,"cwr",3)==0) + { + tx.tcp_control = tx.tcp_control | 128; + } + + } while ( (f=strtok(NULL, "|+-")) != NULL); + + return 0; +} + + + +// Scans string 'params' for MPLS parameters +// and sets tx.mpls_* accordingly. +// +// CLI Syntax Examples: +// +// -M help .... shows syntax +// +// -M 800 .... label=800 +// -M 800:S .... label=800 and BOS flag set +// -M 800:S:64 .... label=800, BOS, TTL=64 +// -M 800:64:S .... same +// -M 64:77 .... label=64, TTL=77 +// -M 64:800 .... INVALID +// -M 800:64 .... label=800, TTL=64 +// -M 800:3:S:64 .... additionally the experimental bits are set (all fields required!) +// +// Note: S = BOS(1), s = NOT-BOS(0) +// +// Valid delimiters: :-.,+ +// Return value: 0 on success, 1 upon failure +int get_mpls_params(char *p) +{ + + char *f1, *f2, *f3, *f4; + char params[256]; + + tx.mpls_exp = 0; + tx.mpls_ttl = 255; + + strncpy(params, p, 256); + + if (strncmp(params,"help",4)==0) + { + fprintf(stderr,"\n" + MAUSEZAHN_VERSION + "\n" + "| MPLS header Syntax: -M label[,label[,label[,...]]]\n" + "| where each header may consist of the following parameters:\n" + "|\n" + "| label ... the MPLS label (mandatory, 0..1048575)\n" + "| exp ..... experimental/CoS (default: 0, allowed values: 0..7)\n" + "| TTL ..... Time To Live (default: 255)\n" + "| BOS ..... marks bottom-of-stack; per default the last (most inner) header\n" + "| will have BOS=1. If desired you can set this flag for any header\n" + "| inbetween but this will lead to an invalid packet. Simply use\n" + "| 'S' to set BOS=1, or 's' to set BOS=0. Note that this flag MUST be\n" + "| the LAST argument.\n" + "|\n" + "| Examples:\n" + "|\n" + "| -M 800 .... label=800\n" + "| -M 800:6 .... label=800 and CoS=6\n" + "| -M 800:6:55 .... label=800, CoS=6, TTL=55\n" + "| -M 800:S .... label=800 and BOS=1\n" + "| -M 800:6:s .... label=800, CoS=6, and BOS=0\n" + "|\n" + "| multiple headers:\n" + "|\n" + "| -m 30,20:7,800:5:128 ... outer label=800 with CoS=5 and TTL=128,\n" + "| middle label=20 with CoS=7,\n" + "| inner label=30 (this one is closest to L3).\n" + "|\n" + "| Valid delimiters inside a header: : - . +\n" + "|\n" + "\n"); + exit (0); + } + else + { + + if ( (f1 = strtok (params, ":-.+")) == NULL ) + { + return 1; // error! + } + + tx.mpls_label = (u_int32_t) str2int (f1); + if (tx.mpls_label>1048575) + { + tx.mpls_label = 1048575; // 2^20 + fprintf(stderr," Warning: MPLS label too big! Reduced to maximum allowed value.\n"); + } + } + + + if ( (f2 = strtok (NULL, ":-.+")) != NULL ) // 2nd param set + { + if (strncmp(f2,"S",1)==0) + { + tx.mpls_bos = 1; + return 0; + } + else if (strncmp(f2,"s",1)==0) + { + tx.mpls_bos = 0; + return 0; + } + else + { + tx.mpls_exp = (u_int8_t) str2int (f2); + if (tx.mpls_exp > 7) + { + tx.mpls_exp = 7; + fprintf(stderr," Warning: MPLS CoS too big! Reduced to maximum allowed value.\n"); + } + } + + + if ( (f3 = strtok (NULL, ":-.+")) != NULL ) // 3rd param set + { + if (strncmp(f3,"S",1)==0) + { + tx.mpls_bos = 1; + return 0; + } + else if (strncmp(f3,"s",1)==0) + { + tx.mpls_bos = 0; + return 0; + } + else + { + if ((u_int16_t) str2int (f3)>255) + { + fprintf(stderr," Warning: MPLS TTL too big! Reduced to maximum allowed value.\n"); + tx.mpls_ttl = 255; + } + else + { + tx.mpls_ttl = (u_int8_t) str2int (f3); + } + } + + if ( (f4 = strtok (NULL, ":-.+")) != NULL ) // 4th param set + { + + if (strncmp(f3,"S",1)==0) + { + tx.mpls_bos = 1; + } + else if (strncmp(f3,"s",1)==0) + { + tx.mpls_bos = 0; + } + + } + + } + } + + + return 0; +} + + +// Parses str for occurence of character or sequence ch. +// Returns number of occurences +int exists(char* str, char* ch) +{ + int i,match; + + size_t len_str, len_ch; + + len_str = strlen(str); + len_ch = strlen(ch); + match=0; + + for (i=0; i8)) return -1; + + for (i=0; i16)) return -1; + + for (i=0; i "1 0 0 0 0 0 0 1" +// +int char2bits (char c, char *str) +{ + int i,j=1; + char tmp[]="0 0 0 0 0 0 0 0"; + + for (i=0; i<8; i++) + { + if (c&j) tmp[14-i*2]='1'; + j=j*2; + } + + strncpy(str, tmp, 15); + return 0; +} + + +// Takes filename and prepends valid configuration directory +// +// 1) prefer configurable mz_default_config_path[] +// 2) otherwise use MZ_DEFAULT_CONFIG_PATH +// +// NOTE: 'filename' finally holds the full path +// and must therefore be big enough +// +// +// RETURN VALUE: +// 0 upon success +// 1 upon failure +// +int getfullpath_cfg (char *filename) +{ + int lenf, lenp; + char tmp[32]; + + lenf = strnlen(filename, 32); + + // filename not given? + if ((lenf==0) || (lenf==32)) return 1; + + strncpy(tmp, filename, 32); + + // Prefer user-defined path if provided: + lenp = strnlen(mz_default_config_path,255); + + if (lenp) { + if (strncmp(mz_default_config_path+lenp-1, "/",1)) + strncat(mz_default_config_path, "/",1); + snprintf(filename, 255, "%s%s",mz_default_config_path,tmp); + } + else { + lenp = strlen(MZ_DEFAULT_CONFIG_PATH); + snprintf(filename, 255, "%s%s",MZ_DEFAULT_CONFIG_PATH,tmp); + } + + if ((lenf+lenp)>255) return 1; + + return 0; +} + + + +// Takes filename and prepends valid logging directory +// +// 1) prefer configurable mz_default_log_path[] +// 2) otherwise use MZ_DEFAULT_LOG_PATH +// +// NOTE: filename is overwritten and must be big enough to hold full path! +// +int getfullpath_log (char *filename) +{ + int lenf, lenp; + char tmp[32]; + + lenf = strnlen(filename, 32); + + // filename not given? + if ((lenf==0) || (lenf==32)) return 1; + + strncpy(tmp, filename, 32); + + // Prefer user-defined path if provided: + lenp = strnlen(mz_default_log_path,255); + if (lenp) { + if (strncmp(mz_default_log_path+lenp-1, "/",1)) + strncat(mz_default_log_path, "/",1); + snprintf(filename, 255, "%s%s",mz_default_log_path,tmp); + } + else { + lenp = strlen(MZ_DEFAULT_LOG_PATH); + snprintf(filename, 255, "%s%s",MZ_DEFAULT_LOG_PATH,tmp); + } + + if ((lenf+lenp)>255) return 1; + + return 0; +} + +// Behaves much like strncpy but additionally ensures +// that dest is always \0-terminated. +// +// USAGE NOTE: If you know exactly the length n of your string, +// then you must provide n+1 to support the termination character. +// +// EXAMPLE: src="Hello", n=strlen(src)=5, and mz_strncpy(dest, src, n) +// would result in dest={H,e,l,l,\0}. +// Therefore the correct usage is: +// mz_strncpy(dest, src, strlen(src)+1); +// ============= +// +// RETURN VALUE: pointer to dest +char * mz_strncpy(char *dest, const char *src, size_t n) +{ + char *tmp; + tmp = strncpy(dest, src, n); + dest[n-1]='\0'; + return tmp; +} + + + + +// Helper function to count the number of arguments +// in the Mausezahn argument string (comma separated args) +// +// RETURN VALUE: Number of arguments +// +// TODO: Improve it. Use strtok. +// +int number_of_args (char *str) +{ + int len=0, i=0, commas=1; + if ((len=strnlen(str,MAX_PAYLOAD_SIZE))<2) return 0; // no valid argument + for (i=0; imax)) return 1; + + // now check how many bytes really match + for (i=0; i t1="Am", t2="Dam", t3="Des", t4=NULL +// +// NOTE: +// +// 1. If the delimiter symbol occurs twice without gap, it is interpreted +// as 'fill-up' command. To avoid ambiguities this may only occur once. +// See the IPv6 address format shortcuts as similar example. +// +// 2. If there are less tokens than allowed, the arguments are filled up +// in order, while the remaining are casted to NULL: +// +// 3. str must be smaller than 4096 bytes! +// +// 4. To mitigate buffer overflow problems, the maximum token size is +// currently limited to 64 bytes. Therefore it is recommended to +// allocate 64 bytes for each argument. +// +// RETURN VALUE: Number of returned tokens or -1 upon error + +int mz_tok(char * str, char * delim, int anz, ...) +{ + + va_list ap; + int i=0, n=0, len, llen, rlen, ltok=0, rtok=0; + char *d, *l, *r, *token, *saveptr, *arg; + char str2[4096], delim2[4]="", delim3[4]="";; + + if (strlen(delim)!=1) return -1; // delim must contain a single character! + strncpy(str2, str, 4095); // protect the original str from strtok => operate on a copy only + len = strlen(str2); + + // Check if some tokens are omitted (::) + strncpy(delim2, delim, 1); strncat(delim2, delim, 1); // create the double-delim + strncpy(delim3, delim2, 2); strncat(delim3, delim, 1); // create the double-delim + if (strstr(str2, delim3)!=NULL) return -1; // Error: ':::' occured! + + if ( (d=strstr(str2, delim2))!=NULL ) { // delim2 ('::') found + // Check 3 cases: "::Sat:Sun", "Mon::Sat:Sun", and "Mon:Tue::" + if (strlen(d)>2) { // '::' is not at the end of str2 + r=d+2; // r points to beginning of right string + if (strstr(r, delim2)!=NULL) return -1; // Error: '::' occurs more than once! + rtok++; // there is at least one token in the right string + rlen = strlen(r); + for(i=0;ianz) return -1; // More tokens than arguments ('::' here leads to ambiguous mapping) + } + else + ltok=len+1; // makes subsequent algorithm to ignore exception handling + + + + rtok=anz-rtok; + va_start(ap, anz); + + token = strtok_r(str2, delim, &saveptr); + if (token==NULL) { va_end(ap); return n; } + + for(i=0; i assign NULL to the remaining arguments! + ((i>=ltok) && (i assume msec + } + } else { // caller specified two arguments + if (mz_strcmp(b,"nsec", 1)==0) + nfactor=1; + else if (mz_strcmp(b,"usec", 1)==0) + nfactor=1000; + else if (mz_strcmp(b,"msec", 1)==0) + nfactor=1000000; + else if (mz_strcmp(b,"sec", 1)==0) { + sfactor=1; + nfactor=0; + } + else if (mz_strcmp(b,"min", 1)==0) { + sfactor=60; + nfactor=0; + } + else if (mz_strcmp(b,"hour", 1)==0) { + sfactor=3600; + nfactor=0; + } + else return 1; // Invalid unit + } + + // Get user-defined actual value: + delay = strtoull(a, (char **)NULL, 10); + if ((errno==ERANGE) || (delay>999999999L)) { // see man 2 nanosleep + return 2; // Value too large! Supported range is from 0 to 999999999 + } + + sdelay = delay * sfactor; + ndelay = delay * nfactor; + + if (ndelay>999999999L) { + sdelay = ndelay/1000000000L; + ndelay = ndelay - (sdelay*1000000000L); + } + + t->tv_sec = sdelay; + t->tv_nsec = ndelay; + return 0; +} + diff --git a/src/tx_switch.c b/src/tx_switch.c new file mode 100644 index 0000000..b1a0d55 --- /dev/null +++ b/src/tx_switch.c @@ -0,0 +1,178 @@ +/* + * Mausezahn - A fast versatile traffic generator + * Copyright (C) 2008-2010 Herbert Haas + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 as published by the + * Free Software Foundation. + * + * 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, see http://www.gnu.org/licenses/gpl-2.0.html + * +*/ + + +#include "mz.h" +#include "cli.h" + + +int tx_switch(struct cli_def *cli) +{ + + // These handles are only used when creating L3 and above packets. + libnet_t *l; // the context + libnet_ptag_t t2=0, t3=0, t4=0; // handles to layers + + double cpu_time_used; + + switch (mode) + { + case BYTE_STREAM: + send_eth(); + break; + + case ARP: + if (send_arp()==-1) return 0; + break; + + case BPDU: + if (send_bpdu()==-1) return 0; + break; + + case CDP: + if (send_cdp()==-1) return 0; + break; + + case IP: // From now on a new much more modular method is used: + l = get_link_context(); + t3 = create_ip_packet(l); // t3 can be used for later header changes + if (!quiet) complexity(); + if (tx.packet_mode==0) // Ethernet manipulation features does NOT use ARP to determine eth_dst + t2 = create_eth_frame(l, t3, t4); // t2 can be used for later header changes + else + send_frame (l, t3, t4); // NOTE: send_frame also destroys context finaly + break; + + case ICMP: + tx.ip_proto = 1; + l = get_link_context(); + t4 = create_icmp_packet(l); // t4 can be used for later header changes + if (t4==-1) return 0; + t3 = create_ip_packet(l); // t3 can be used for later header changes + if (!quiet) complexity(); + if (tx.packet_mode==0) // Ethernet manipulation features does NOT use ARP to determine eth_dst + t2 = create_eth_frame(l, t3, t4); // t2 can be used for later header changes + else + send_frame (l, t3, t4); // NOTE: send_frame also destroys context finaly + break; + + case UDP: + tx.ip_proto = 17; + l = get_link_context(); + t4 = create_udp_packet(l); // t4 can be used for later header changes + if (t4==-1) return 0; + t3 = create_ip_packet(l); // t3 can be used for later header changes + if (!quiet) complexity(); + if (tx.packet_mode==0) // Ethernet manipulation features does NOT use ARP to determine eth_dst + t2 = create_eth_frame(l, t3, t4); // t2 can be used for later header changes + else + send_frame (l, t3, t4); // NOTE: send_frame also destroys context finaly + break; + + case TCP: + tx.ip_proto = 6; + l = get_link_context(); + t4 = create_tcp_packet(l); // t4 can be used for later header changes + if (t4==-1) return 0; + t3 = create_ip_packet(l); // t3 can be used for later header changes + if (!quiet) complexity(); + if (tx.packet_mode==0) // Ethernet manipulation features does NOT use ARP to determine eth_dst + t2 = create_eth_frame(l, t3, t4); // t2 can be used for later header changes + else + send_frame (l, t3, t4); // NOTE: send_frame also destroys context finaly + break; + + case DNS: + tx.ip_proto = 17; + l = get_link_context(); + if (create_dns_packet()==-1) return 0; + t4 = create_udp_packet(l); // t4 can be used for later header changes + t3 = create_ip_packet(l); // t3 can be used for later header changes + if (!quiet) complexity(); + if (tx.packet_mode==0) // Ethernet manipulation features does NOT use ARP to determine eth_dst + t2 = create_eth_frame(l, t3, t4); // t2 can be used for later header changes + else + send_frame (l, t3, t4); // NOTE: send_frame also destroys context finaly + break; + + case RTP: + tx.ip_proto = 17; + l = get_link_context(); + if (create_rtp_packet()==-1) return 0; + cli_print(cli, "RTP mode! (count=%u, delay=%u usec)\n", tx.count, tx.delay); + t4 = create_udp_packet(l); // t4 can be used for later header changes + t3 = create_ip_packet(l); // t3 can be used for later header changes + if (!quiet) complexity(); + if (tx.packet_mode==0) // Ethernet manipulation features does NOT use ARP to determine eth_dst + t2 = create_eth_frame(l, t3, t4); // t2 can be used for later header changes + else + send_frame (l, t3, t4); // NOTE: send_frame also destroys context finaly + break; + + case RX_RTP: // Receive RTP packets + rcv_rtp_init(); + rcv_rtp(); + break; + + case SYSLOG: + tx.ip_proto = 17; + l = get_link_context(); + if (create_syslog_packet()==-1) return 0; + t4 = create_udp_packet(l); // t4 can be used for later header changes + t3 = create_ip_packet(l); // t3 can be used for later header changes + if (!quiet) complexity(); + + if (tx.packet_mode==0) // Ethernet manipulation features does NOT use ARP to determine eth_dst + t2 = create_eth_frame(l, t3, t4); // t2 can be used for later header changes + else + send_frame (l, t3, t4); // NOTE: send_frame also destroys context finaly + break; + + case LLDP: // start with a new concept here + //l = get_link_context(); + //(void) create_lldp_packet(); + // // // printf("SIZE=%lu\n",sizeof(struct tx_struct)); + break; + + + default: + cli_print(cli,"Unknown mode!\n"); + return (1); + } + + + // ***** Re-init packet functions: ***** + tx.ip_payload_s = 0; + tx.udp_len = 0; + tx.tcp_payload_s = 0; + tx.icmp_payload_s = 0; + tx.cdp_sum = 0; + mode = 0; + // ************************************** + + + mz_stop = clock(); + cpu_time_used = ((double) (mz_stop - mz_start)) / CLOCKS_PER_SEC; + if (cpu_time_used > 0) + { + total_d /= cpu_time_used; + cli_print(cli, "%.2f seconds (%.Lf packets per second)\n",cpu_time_used,total_d); + } + + return 0; +}