bittwist-linux-3.8/0000755000175000017500000000000014451442047012563 5ustar opsopsbittwist-linux-3.8/COPYING0000644000175000017500000004325414451442047013626 0ustar opsops 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. bittwist-linux-3.8/README.md0000644000175000017500000001522514451442047014047 0ustar opsops# Bit-Twist: Libpcap-based Ethernet packet generator SPDX-License-Identifier: GPL-2.0-or-later Supported systems: Linux, BSD, macOS, and Windows. Bit-Twist is a simple yet powerful libpcap-based Ethernet packet generator and packet editor. It is designed to complement tcpdump, which by itself has done a great job at capturing network traffic. > With Bit-Twist, you can now regenerate your captured traffic onto a live > network! Packets are generated from tcpdump trace file (.pcap file). > Bit-Twist also comes with a comprehensive trace file editor to allow you to > change the contents of a trace file. > Bit-Twist is designed for exceptional speed! Utilizing a standard laptop with > Linux system, you can edit **10 million packets in less than 5 seconds**, and > send the packets onto a live network, achieving throughput levels that match > the maximum line rate of your NIC. Packet generator is useful in simulating networking traffic or scenario, testing firewall, IDS, and IPS, and troubleshooting various network problems. ## Features These are just a few significant features that makes Bit-Twist unique and stands out as one of the best Ethernet packet generator and packet editor package made available to the open source community. - Highly portable: Bit-Twist runs on Linux, BSD, macOS, and Windows. - Send multiple trace files indefinitely with set interval, packets per second, or line rate between 1 Mbps to 10 Gbps using built-in token bucket algorithm. - Comprehensive trace file editor to edit most fields in Ethernet, ARP, IPv4, IPv6, ICMPv4, ICMPv6, TCP, and UDP headers. Templates are also included to generate packets with these headers without needing an existing trace file. - Automatic header checksum correction (with option to disable). - Send packets with custom QoS bits to test classification and queuing features of switches and routers. - Send packets with uniformly distributed random numbers for port numbers, TCP sequence numbers, etc. - Append custom payload (e.g. copy of hex stream from Wireshark) to existing packets after a specific header, handy for testing new protocols. - Send packets with truncated or expanded length in bytes, facilitating incremental throughput testing. - Highly scriptable: With proper manipulation, you can turn Bit-Twist into a versatile packet generator and packet editor tooling to meet your network testing requirements. For the complete feature list, see Bit-Twist man pages: - [bittwist.1](https://bittwist.sourceforge.io/doc/bittwist.1.html) - pcap based ethernet packet generator - [bittwiste.1](https://bittwist.sourceforge.io/doc/bittwiste.1.html) - pcap capture file editor ## Examples Please visit https://bittwist.sourceforge.io/doc.html for examples on how to use Bit-Twist. ## Installation Follow the instructions for your operating system below to install, run, or build Bit-Twist on your machine. Unless specified otherwise: - executables (bittwist, bittwiste) are installed in /usr/local/bin - manual pages (bittwist.1, bittwiste.1) are installed in /usr/local/share/man/man1 For more general information, please visit https://bittwist.sourceforge.io ## For Windows systems This distribution is tested to work on Microsoft Windows 10. ### Installation - Download Npcap installer from https://npcap.com/dist/npcap-1.75.exe - Run the Npcap installer to install Npcap on your system. Select "Install Npcap in WinPcap API-compatible Mode" option during the installation. - Extract bittwist-windows-3.8.zip into C:\Users\_YOUR_USERNAME_\Downloads - In Command Prompt: ``` > cd C:\Users\_YOUR_USERNAME_\Downloads\bittwist-windows-3.8\src > bittwist -h (usage for packet generator) > bittwiste -h (usage for packet editor) > bittwist -d (to view available network cards you can send packets on) ``` - You may readily use Bit-Twist from the src directory as above. - If you wish to install Bit-Twist system-wide, copy the files from src directory into C:\WINDOWS\system32: - bittwist.exe - bittwist.exe - cygwin1.dll (From https://www.cygwin.com) - Manual pages are available in doc/ ### Recompilation This distribution is compiled against Npcap 1.75 with Npcap SDK 1.13 in Cygwin environment on Microsoft Windows 10. If you wish to rebuild Bit-Twist from source files, you will need Cygwin environment: - Download Cygwin installer from https://www.cygwin.com/setup-x86_64.exe - Run the Cygwin installer to install Cygwin environment on your system. - Be sure to select at least the following packages in the "Cygwin Setup - Select Packages" window: - Devel > gcc-core - Devel > make - Devel > binutils 2.38-1 (newer version may not work yet) - Devel > mingw64-x86_64-binutils 2.38-1 (newer version may not work yet) - Click on the "Cygwin64 Terminal" icon in your desktop to launch a new terminal under Cygwin environment. ``` $ cd /cygdrive/c/Users/_YOUR_USERNAME_/Downloads/bittwist-windows-3.8 $ make $ make install $ bittwist -h (usage for packet generator) $ bittwiste -h (usage for packet editor) $ bittwist -d (to view available network cards you can send packets on) ``` ## For Linux systems This distribution is tested to work on CentOS Stream 9. ### Required dependencies - Libpcap is required (available for download from https://www.tcpdump.org/). This distribution is compiled against libpcap 1.10.4. Sample installation of libpcap 1.10.4 on CentOS Stream 9: ``` $ sudo yum install make gcc flex bison $ wget https://www.tcpdump.org/release/libpcap-1.10.4.tar.gz $ tar -xzf libpcap-1.10.4.tar.gz $ cd libpcap-1.10.4 $ ./configure && make && sudo make install $ sudo ldconfig ``` ### Installation ``` $ tar -xzf bittwist-linux-3.8.tar.gz $ cd bittwist-linux-3.8 $ make $ sudo make install ``` ## For macOS systems This distribution is tested to work on macOS Ventura 13.3.1 ### Required dependencies - Libpcap is required (available for download from https://www.tcpdump.org/). This distribution is compiled against libpcap 1.10.4. However, any existing libpcap on your system may work normally with Bit-Twist. - Xcode command line developer tools; you will be prompted to install this automatically on your first attempt to run make below. ### Installation ``` $ tar -xzf bittwist-macos-3.8.tar.gz $ cd bittwist-macos-3.8 $ make $ sudo make install ``` ## For BSD systems This distribution is tested to work on FreeBSD 13.2-RELEASE. ### Required dependencies - Libpcap is required (available for download from https://www.tcpdump.org/). This distribution is compiled against libpcap 1.10.4. However, any existing libpcap on your system may work normally with Bit-Twist. ### Installation ``` $ tar -xzf bittwist-bsd-3.8.tar.gz $ cd bittwist-bsd-3.8 $ make $ sudo make install ``` bittwist-linux-3.8/CHANGES0000644000175000017500000002163314451442047013563 0ustar opsops06/07/2023: Version 3.8 - bumped from gcc -O2 to -O3 optimization flag: https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html#index-O3 bittwist (generator) - added RFC 2544 benchmark data generator and benchmark page - improved actual inter-packet gap replay by using token bucket algorithm for short IPG and self-regulated timer for long IPG 27/06/2023: Version 3.7 bittwist (generator) - 30% speed up when sending large pcap file bittwiste (editor) - added -G option to set fixed or random inter-packet gap in microseconds - added support for CIDR notation when modifying IPv4/IPv6 address - added support for random MAC address 17/06/2023: Version 3.6 - added github repo for source code browsing: https://github.com/ayeowch/bittwist bittwist (generator) - fixed linerate throttling - updated -p option to send packets at set packets per second (pps) - added -t option to set inter-packet gap (this was previously -p option) bittwiste (editor) - added option to edit IPv6 next header - added random number support for id, ttl, proto, next header, hop limit 16/06/2023: Version 3.5 bittwiste (editor) - added option to set uniformly distributed random number (mersenne twister) for tcp/udp source/destination port and tcp sequence/acknowledgment number - added -N flag to duplicate packets from trace file, e.g. -N 100000 to repeat packets from trace file for 100,000 times - added ip6tcp and ip6udp templates 13/06/2023: Version 3.4 bittwiste (editor) - updated -I flag to allow loading trace file from built-in templates 12/06/2023: Version 3.3 bittwiste (editor) - options to edit IPv4/IPv6 packets to allow testing of classification/queuing features of switches/routers, e.g. -T ip -e 3 to indicate congestion to the end hosts -T ip6 -c 16 to classify packet for operation and management of the network -T ip6 -f 0xfffff to set custom flow label 10/06/2023: Version 3.2 - added support for trace files with nanosecond resolution - updated manual pages bittwist (generator) - removed speed (-m) flag; use -p/-r to shape throughput - updated linerate (-r) flag to use token bucket algorithm bittwiste (editor) - added support for IPv6 packets - improved cksum() - expanded test cases 30/05/2023: Version 3.1 - use in-memory trace files when sending packets in loop - fixed integer overflow in stats output - added static code analysis in Makefile (make check) - reformatted source files for C17 standard - added initial python testing framework for Linux systems 24/05/2023: Version 3.0 - Linux distribution: rebuilt with libpcap 1.10.4 - BSD distribution: rebuilt with libpcap 1.10.4 - macOS distribution: rebuilt with libpcap 1.10.4 - Windows distribution: rebuilt with Npcap 1.75 with Npcap SDK 1.13 - updated homepage url: https://bittwist.sourceforge.io 21/04/2012: Version 2.0 - switched to "struct pcap_sf_pkthdr" to support 64-bit platforms - rebuilt with libpcap 1.2.1 - added Mac OS X distribution (author's primary platform) - BSD distribution no longer maintained - ended Bit-Twist Web Edition BETA - revised copyright information and manpages bittwistb (bridge) - no longer maintained on all platforms 12/12/2009: Version 1.1 - rebuilt with libpcap 1.0.0 - revised copyright information and manpages - removed Eclipse dependency; requires only Cygwin environment for Windows development - release of bittwist(w), Bit-Twist Web Edition BETA, http://twitter.com/bittwistw 26/10/2007: Version 1.0 - revised AUTHORS, INSTALL, copyright information and manpages - unified source files for all supported platforms - moved Windows development to Eclipse IDE for C/C++ Developers + Cygwin environment bittwist (generator) - increased max. packet throughput from 1Gbps to 10Gbps (option -r) bittwiste (editor) - corrected usage of strptime() - use comma (,) instead of slash (/) as delimiter for all replace options - added a feature request; replace a specific MAC address with a new MAC address for eth, arp - added a feature request; replace a specific IP address with a new IP address for arp 23/10/2006: Version 0.80 bittwist (generator) - improved performance; use single buffer for all packet data, calculate linerate_interval only when necessary bittwiste (editor) - corrected several malloc related bugs - added a feature request; replace the linktype stored in the pcap file header, e.g. to replace raw IP linktype to Ethernet linktype, bittwiste -I rawip.pcap -O rawip.1.pcap -M 1 - added a feature request; support for writing packets within a specific timeframe only, e.g. bittwist -I trace.pcap -O trace.1.pcap -S 22/10/2006,21:47:35-24/10/2006,13:16:05 17/09/2006: Version 0.74 bittwiste (editor) - minor bug correction - added a feature request; delete a specific byte offset from a packet, bittwiste -I trace.pcap -O trace.1.pcap -D 1-14 - added a feature request; replace a specific IP address with a new IP address, e.g. to replace source IP address 192.168.0.1 with 192.168.1.1, bittwiste -I ip.pcap -O ip.1.pcap -T ip -s 192.168.0.1:192.168.1.1 28/07/2006: Version 0.73 - minor correction in Makefile - merged 180506-0.72-def.h-patch-1 (patch for OpenBSD) into BSD distribution bittwiste (editor) - minor bug correction - added a feature request; replace a specific port number with a new port number, e.g. to replace destination port 1066 with 1067, bittwiste -I tcp.pcap -O tcp.1.pcap -T tcp -d 1066:1067 misc. - Bit-Twist 0.72 for Windows has been released prior to this update 29/04/2006: Version 0.72 - removed definition of in_addr_t in def.h for BSD distribution bittwistb (bridge) - minor bug correction 28/04/2006: Version 0.71 bittwist (generator) - removed maths.h which was previously required for round(), ROUND() is now defined in def.h - minor bug correction bittwistb (bridge) - fixed gethwaddr(), previous version does not work under Linux 11/04/2006: Version 0.7 bittwistb (bridge) - removed packet duplication check (based on resource hog MD5 hashing); this was initially required due to a faulty logic which causes the bridge to check and forward packets that are already departing from an interface - added explicit check for broadcast and multicast packets - added hash entry timeout check bittwiste (editor) - option -R range can take a single value, e.g. -R 10 instead of previous requirement of -R 10-10 to save only the 10th packet - added support for checksum correction for payload which covers ICMP, TCP, or UDP header and data - in def.h, #define IP_FO_MAX, 8191 changed to 7770 misc. - created manpages for bittwist, bittwistb, bittwiste - public release: http://bittwist.sourceforge.net 04/04/2006: Version 0.61 bittwistb (bridge) - minor correction: when "forward to all", we are not suppose to forward a packet back into the same port where we received it 03/04/2006: Version 0.6 bittwistb (bridge) - added pcap based program to bridge 2 or more LAN segments bittwiste (editor) - added proper handling for fragmented packets - skip checksum calculation for fragmented packets - option -R range is working now 17/03/2006: Version 0.5 bittwist (generator) - removed option -e timeout presented in Version 0.4 since its usage is limited and we can do better by using the OS ping program - added option -c count to send packets up to count bittwiste (editor) - full rewrite - added options to edit most fields in Ethernet, ARP, IP, ICMP, TCP and UDP header - added option -X payload to support addition of payload from standard input - added option -L layer to support writing up to specific layer only - added option -R range to support writing of specific packets only 10/03/2006: Version 0.4 - redundant definitions in bittwist.h and bittwiste.h moved to def.h - change of datatype, e.g. unsigned char is now u_char bittwist (generator) - added option -e timeout to allow sending packet as ping packet and check for host response (currently limited to ICMP echo only, expect support for TCP in later version) bittwiste (editor) - added option -c to disable/enable checksum correction 25/02/2006: Version 0.3 bittwist (generator) - added option -r linerate to limit packet throughput at the specified line rate (1 -> 1000Mbps) - explicit close for pcap descriptor and file pointer removed from cleanup() - more details in info() bittwiste (editor) - options to change source/destination MAC, source/destination IP, TTL, source/destination port, enable/disable checksum correction for IP, ICMP, TCP and UDP packets 21/02/2006: Version 0.2 - removes libnet dependency - requires libpcap 0.9.4 (and above) - options: -d, -vv, -i interface, -s len, -l loop, -m speed, -p sleep, -h - -v for timestamp and -vv to include hex data - user specified interval in seconds through -p option - interval multiplier through -m option - packet length: send captured length / on wire length / user specified length (14 to 1514 bytes) through option -s - SIGINT signal handling 25/01/2006: Version 0.1 - skeleton program, uses libpcap 0.9.4 and libnet 1.1.3-RC-01 - options: -D, -V, -i interface, -l loop - tested only on FreeBSD 6.0-STABLE bittwist-linux-3.8/Makefile0000644000175000017500000000524714451442047014233 0ustar opsops# SPDX-License-Identifier: GPL-2.0-or-later # # Makefile for Bit-Twist project # Copyright (C) 2006 - 2023 Addy Yeow # # 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. SHELL = /bin/sh prefix = /usr/local exec_prefix = ${prefix} bindir = ${exec_prefix}/bin mandir = ${prefix}/share/man/man1 # Bit-Twist 3.1 and earlier was using /usr instead of /usr/local. # These old paths are defined below to allow `sudo make uninstall` to also # remove any installation of Bit-Twist 3.1 or earlier. old_prefix = /usr old_exec_prefix = ${old_prefix} old_bindir = ${old_exec_prefix}/bin old_mandir = ${old_prefix}/share/man/man1 CC ?= gcc DEBUG = -g CFLAGS ?= -std=gnu17 CFLAGS += -O3 CFLAGS += ${DEBUG} -Wall SRC = src DOC = doc CPPCHECK ?= cppcheck CLANG_FORMAT ?= clang-format INSTALL = /usr/bin/install -c INSTALL_PROGRAM = ${INSTALL} INSTALL_DATA = ${INSTALL} -m 644 all: bittwist bittwiste bittwist: $(CC) $(CFLAGS) $(SRC)/bittwist.c $(SRC)/token_bucket.c -o $(SRC)/bittwist -I/usr/local/include -L/usr/local/lib -lpcap bittwiste: $(CC) $(CFLAGS) $(SRC)/bittwiste.c $(SRC)/tinymt/tinymt64.c $(SRC)/template_pcap.c -o $(SRC)/bittwiste -I $(SRC)/tinymt -I/usr/local/include -L/usr/local/lib -lpcap clean: rm -f $(SRC)/bittwist $(SRC)/bittwiste check: $(CPPCHECK) --enable=warning $(SRC) format: $(CLANG_FORMAT) -i src/def.h src/token_bucket.h src/token_bucket.c src/template_pcap.h src/template_pcap.c src/bittwist.h src/bittwist.c src/bittwiste.h src/bittwiste.c install: mkdir -p $(bindir) chmod 755 $(bindir) $(INSTALL_PROGRAM) $(SRC)/bittwist $(SRC)/bittwiste $(bindir) mkdir -p $(mandir) chmod 755 $(mandir) $(INSTALL_DATA) $(DOC)/bittwist.1 $(DOC)/bittwiste.1 $(mandir) uninstall: @rm -vf $(wildcard $(bindir)/bittwist) @rm -vf $(wildcard $(bindir)/bittwiste) @rm -vf $(wildcard $(mandir)/bittwist.1) @rm -vf $(wildcard $(mandir)/bittwiste.1) @rm -vf $(wildcard $(old_bindir)/bittwist) @rm -vf $(wildcard $(old_bindir)/bittwiste) @rm -vf $(wildcard $(old_mandir)/bittwist.1) @rm -vf $(wildcard $(old_mandir)/bittwiste.1) bittwist-linux-3.8/tests/0000755000175000017500000000000014451424732013726 5ustar opsopsbittwist-linux-3.8/tests/requirements.txt0000644000175000017500000000102314442751077017213 0ustar opsopsasttokens==2.2.1 backcall==0.2.0 black==23.3.0 click==8.1.3 decorator==5.1.1 executing==1.2.0 flake8==6.0.0 iniconfig==2.0.0 ipdb==0.13.13 ipython==8.13.2 jedi==0.18.2 matplotlib-inline==0.1.6 mccabe==0.7.0 mypy-extensions==1.0.0 packaging==23.1 parso==0.8.3 pathspec==0.11.1 pexpect==4.8.0 pickleshare==0.7.5 platformdirs==3.5.1 pluggy==1.0.0 prompt-toolkit==3.0.38 ptyprocess==0.7.0 pure-eval==0.2.2 pycodestyle==2.10.0 pyflakes==3.0.1 Pygments==2.15.1 pytest==7.3.1 six==1.16.0 stack-data==0.6.2 traitlets==5.9.0 wcwidth==0.2.6 bittwist-linux-3.8/tests/README.md0000644000175000017500000000534114451412432015202 0ustar opsops# Bit-Twist Python testing framework This framework is targeted for Linux systems to: - Validate packet generation between release and development version. - Benchmark large packet generation between release and development version. - Run tests for development version of bittwist and bittwiste. ## Setup ``` $ ~/.pyenv/versions/3.11.3/bin/python -m venv venv $ source venv/bin/activate $ pip install -r requirements.txt ``` ## Running tests Ensure ../src/bittwist and ../src/bittwiste has been built successfully prior to running tests. Some tests (../src/bittwist) will perform actual packet injection, so sudo is required. To run all available tests (sudo is required): ``` ./test.sh ``` To run tests for ../src/bittwist (sudo is required): ``` pytest test_bittwist.py ``` To run tests for ../src/bittwiste: ``` pytest test_bittwiste.py ``` To run specific test: ``` pytest test_bittwiste.py::test_bittwiste_copy ``` ## bittwist benchmark bittwist benchmark data can be generated using the command below: ``` ./benchmark_bittwist.py --iface IFACE --smac SMAC --dmac DMAC --sip SIP --dip DIP ``` Sample JSON output files (benchmark_*.json) are available in the same directory as this file. The JSON output files are also represented as charts in https://bittwist.sourceforge.io/benchmark.html ## bittwiste benchmark Result: Edit 10 million IP packets in 4 seconds. Method: ``` # Run the test.sh separately to generate packets to be captured. $ sudo tcpdump -i lo -w 10M.pcap -c 10000000 -v -n -B 65536 -Z "$(whoami)" 'tcp port 0' # 15GB of 10 million packets captured. $ du -h 10M.pcap 15G 10M.pcap # Inspect first few captured packets. $ tcpdump -v -r 10M.pcap -c 2 reading from file 10M.pcap, link-type EN10MB (Ethernet), snapshot length 262144 15:07:55.832237 IP (tos 0x0, ttl 64, id 12930, offset 0, flags [DF], proto TCP (6), length 1500) localhost.0 > localhost.0: tcp 1480 [bad hdr length 0 - too short, < 20] 15:07:55.832246 IP (tos 0x0, ttl 64, id 12930, offset 0, flags [DF], proto TCP (6), length 1500) localhost.0 > localhost.0: tcp 1480 [bad hdr length 0 - too short, < 20] # Run bittwiste to edit pcap to save up to layer 3 only (IP). $ time bittwiste -I 10M.pcap -O 10M.ip.pcap -L 3 input file: 10M.pcap output file: 10M.ip.pcap 10000000 packets (500000024 bytes) written real 0m4.276s user 0m2.389s sys 0m1.888s # Inspect first few edited packets. $ tcpdump -v -r 10M.ip.pcap -c 2 reading from file 10M.ip.pcap, link-type EN10MB (Ethernet), snapshot length 262144 15:07:55.832237 IP (tos 0x0, ttl 64, id 12930, offset 0, flags [DF], proto TCP (6), length 20) localhost > localhost: [|tcp] 15:07:55.832246 IP (tos 0x0, ttl 64, id 12930, offset 0, flags [DF], proto TCP (6), length 20) localhost > localhost: [|tcp] ``` bittwist-linux-3.8/tests/.flake80000644000175000017500000000006214434757763015115 0ustar opsops[flake8] max-line-length = 120 exclude = venv bittwist-linux-3.8/tests/test.sh0000755000175000017500000000157014446536203015250 0ustar opsops#!/bin/bash # SPDX-License-Identifier: GPL-2.0-or-later # # test.sh - Test runner script for Bit-Twist project # Copyright (C) 2006 - 2023 Addy Yeow # # 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. pytest -s bittwist-linux-3.8/tests/benchmark_max_throughput.json0000644000175000017500000002041714451424732021715 0ustar opsops[ { "10": [ { "pkt_len": 64, "pkts": 1171875, "pps": { "expected": 19531, "actual": 19531, "diff": 0.0 }, "mbps": { "expected": 10, "actual": 10.0, "diff": 0.0 }, "elapsed_s": { "expected": 60, "actual": 59.999981, "diff": -3.2e-05 } }, { "pkt_len": 128, "pkts": 585937, "pps": { "expected": 9765, "actual": 9765, "diff": 0.0 }, "mbps": { "expected": 10, "actual": 10.0, "diff": 0.0 }, "elapsed_s": { "expected": 60, "actual": 59.999866, "diff": -0.000223 } }, { "pkt_len": 256, "pkts": 292968, "pps": { "expected": 4882, "actual": 4882, "diff": 0.0 }, "mbps": { "expected": 10, "actual": 10.0, "diff": 0.0 }, "elapsed_s": { "expected": 60, "actual": 59.999672, "diff": -0.000547 } }, { "pkt_len": 512, "pkts": 146484, "pps": { "expected": 2441, "actual": 2441, "diff": 0.0 }, "mbps": { "expected": 10, "actual": 10.0001, "diff": 0.001 }, "elapsed_s": { "expected": 60, "actual": 59.999454, "diff": -0.00091 } }, { "pkt_len": 1024, "pkts": 73242, "pps": { "expected": 1220, "actual": 1220, "diff": 0.0 }, "mbps": { "expected": 10, "actual": 10.0001, "diff": 0.001 }, "elapsed_s": { "expected": 60, "actual": 59.999054, "diff": -0.001577 } }, { "pkt_len": 1280, "pkts": 58593, "pps": { "expected": 976, "actual": 976, "diff": 0.0 }, "mbps": { "expected": 10, "actual": 10.0002, "diff": 0.002 }, "elapsed_s": { "expected": 60, "actual": 59.998222, "diff": -0.002963 } }, { "pkt_len": 1514, "pkts": 49537, "pps": { "expected": 825, "actual": 825, "diff": 0.0 }, "mbps": { "expected": 10, "actual": 10.0002, "diff": 0.002 }, "elapsed_s": { "expected": 60, "actual": 59.998043, "diff": -0.003262 } } ] }, { "100": [ { "pkt_len": 64, "pkts": 11718750, "pps": { "expected": 195312, "actual": 195312, "diff": 0.0 }, "mbps": { "expected": 100, "actual": 99.9999, "diff": -0.0001 }, "elapsed_s": { "expected": 60, "actual": 60.000046, "diff": 7.7e-05 } }, { "pkt_len": 128, "pkts": 5859375, "pps": { "expected": 97656, "actual": 97656, "diff": 0.0 }, "mbps": { "expected": 100, "actual": 100.0, "diff": 0.0 }, "elapsed_s": { "expected": 60, "actual": 60.000011, "diff": 1.8e-05 } }, { "pkt_len": 256, "pkts": 2929687, "pps": { "expected": 48828, "actual": 48828, "diff": 0.0 }, "mbps": { "expected": 100, "actual": 100.0, "diff": 0.0 }, "elapsed_s": { "expected": 60, "actual": 60.000019, "diff": 3.2e-05 } }, { "pkt_len": 512, "pkts": 1464843, "pps": { "expected": 24414, "actual": 24414, "diff": 0.0 }, "mbps": { "expected": 100, "actual": 100.0, "diff": 0.0 }, "elapsed_s": { "expected": 60, "actual": 59.999977, "diff": -3.8e-05 } }, { "pkt_len": 1024, "pkts": 732421, "pps": { "expected": 12207, "actual": 12207, "diff": 0.0 }, "mbps": { "expected": 100, "actual": 100.0001, "diff": 0.0001 }, "elapsed_s": { "expected": 60, "actual": 59.999859, "diff": -0.000235 } }, { "pkt_len": 1280, "pkts": 585937, "pps": { "expected": 9765, "actual": 9765, "diff": 0.0 }, "mbps": { "expected": 100, "actual": 100.0001, "diff": 0.0001 }, "elapsed_s": { "expected": 60, "actual": 59.999866, "diff": -0.000223 } }, { "pkt_len": 1514, "pkts": 495376, "pps": { "expected": 8256, "actual": 8256, "diff": 0.0 }, "mbps": { "expected": 100, "actual": 100.0001, "diff": 0.0001 }, "elapsed_s": { "expected": 60, "actual": 59.999851, "diff": -0.000248 } } ] }, { "1000": [ { "pkt_len": 64, "pkts": 117187500, "pps": { "expected": 1953125, "actual": 1325046, "diff": -32.157645 }, "mbps": { "expected": 1000, "actual": 678.4238, "diff": -32.15762 }, "elapsed_s": { "expected": 60, "actual": 88.440308, "diff": 47.400513 } }, { "pkt_len": 128, "pkts": 58593750, "pps": { "expected": 976562, "actual": 976562, "diff": 0.0 }, "mbps": { "expected": 1000, "actual": 999.9995, "diff": -5e-05 }, "elapsed_s": { "expected": 60, "actual": 60.000034, "diff": 5.7e-05 } }, { "pkt_len": 256, "pkts": 29296875, "pps": { "expected": 488281, "actual": 488281, "diff": 0.0 }, "mbps": { "expected": 1000, "actual": 999.9998, "diff": -2e-05 }, "elapsed_s": { "expected": 60, "actual": 60.000015, "diff": 2.5e-05 } }, { "pkt_len": 512, "pkts": 14648437, "pps": { "expected": 244140, "actual": 244140, "diff": 0.0 }, "mbps": { "expected": 1000, "actual": 999.9993, "diff": -7e-05 }, "elapsed_s": { "expected": 60, "actual": 60.000042, "diff": 7e-05 } }, { "pkt_len": 1024, "pkts": 7324218, "pps": { "expected": 122070, "actual": 118261, "diff": -3.120341 }, "mbps": { "expected": 1000, "actual": 968.7999, "diff": -3.12001 }, "elapsed_s": { "expected": 60, "actual": 61.932289, "diff": 3.220482 } }, { "pkt_len": 1280, "pkts": 5859375, "pps": { "expected": 97656, "actual": 95761, "diff": -1.940485 }, "mbps": { "expected": 1000, "actual": 980.596, "diff": -1.9404 }, "elapsed_s": { "expected": 60, "actual": 61.187279, "diff": 1.978798 } }, { "pkt_len": 1514, "pkts": 4953764, "pps": { "expected": 82562, "actual": 82121, "diff": -0.534144 }, "mbps": { "expected": 1000, "actual": 994.6578, "diff": -0.53422 }, "elapsed_s": { "expected": 60, "actual": 60.322243, "diff": 0.537072 } } ] } ]bittwist-linux-3.8/tests/test_bittwiste.py0000644000175000017500000011551214446536203017363 0ustar opsops#!/usr/bin/env python # SPDX-License-Identifier: GPL-2.0-or-later # # test_bittwiste.py - bittwiste Linux test suite # Copyright (C) 2006 - 2023 Addy Yeow # # 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. import hashlib import subprocess import sys from pathlib import Path bin = Path(__file__).resolve().parent.parent / "src" / "bittwiste" if not bin.exists(): sys.stderr.write(f"{bin} is missing") sys.exit(1) out_pcap_file = Path(__file__).resolve().parent / "pcap" / "out.pcap" def test_bittwiste_copy(): in_pcap_file = Path(__file__).resolve().parent / "pcap" / "tcp.pcap" command = f"{bin} -I {in_pcap_file} -O {out_pcap_file}" subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT) in_checksum = hashlib.md5(open(in_pcap_file, "rb").read()).hexdigest() out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest() assert in_checksum == out_checksum def test_bittwiste_templates(): templates = [ ("eth", "ef94560b8178c9c5d1e913dcc945ce54"), ("arp", "d5b379f8a20376e1b63bb8338dbf877c"), ("ip", "f29a94f834e7cc8ea668520136d50eea"), ("ip6", "959d8f64bd4db5c0c735f3d7b6e8f0cb"), ("icmp", "cd039cc28047192408bb2e37d0ece168"), ("icmp6", "dd5f5162c0666731e7389e4441a00d0b"), ("tcp", "47eb30890319537e780e597843345189"), ("ip6tcp", "b3795eb0b5b315f6d156bb2c48424062"), ("udp", "f3f26f6e1234741ff89d5205d20a7802"), ("ip6udp", "01f51a34b5cea651547437288ad3431a"), ] for template, expected_checksum in templates: command = f"{bin} -I {template} -O {out_pcap_file}" subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT) out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest() assert out_checksum == expected_checksum def test_bittwiste_template_udp_sport(): command = f"{bin} -I udp -O {out_pcap_file} -T udp -s 0" subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT) out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest() assert out_checksum == "c132a9cc34f8f951c6835655a061e5fc" def test_bittwiste_template_repeat_tcp_dport_rand(): command = f"{bin} -I ip6tcp -O {out_pcap_file} -N 10000 -P 1 -T tcp -d rand" output = subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT) output = output.decode("utf-8") assert "10001 packets (1020126 bytes) written" in output out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest() assert out_checksum == "38f4a09f4228898971269ee920ec0c12" def test_bittwiste_max_ip_payload(): in_pcap_file = Path(__file__).resolve().parent / "pcap" / "tcp.pcap" payload = "0" * 1500 * 2 # 1500 bytes max payload. command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} -R 1 -L 3 -X {payload} -T ip" output = subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT) output = output.decode("utf-8") assert "1 packets (1554 bytes) written" in output out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest() assert out_checksum == "5386b37145d52979ade052383742b29c" def test_bittwiste_max_tcp_payload(): in_pcap_file = Path(__file__).resolve().parent / "pcap" / "tcp.pcap" payload = "f" * 1500 * 2 # 1500 bytes max payload. command = ( f"{bin} -I {in_pcap_file} -O {out_pcap_file} -R 1 -L 4 -X {payload} -T tcp" ) output = subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT) output = output.decode("utf-8") assert "1 packets (1554 bytes) written" in output out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest() assert out_checksum == "d66db2ba1345b1bb7e63270556915066" def test_bittwiste_icmp_echo(): in_pcap_file = Path(__file__).resolve().parent / "pcap" / "icmp.pcap" command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} -T icmp -t 0" subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT) out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest() assert out_checksum == "decdb8f44253801bcfdc845387d6f6cb" def test_bittwiste_layer_2(): in_pcap_file = Path(__file__).resolve().parent / "pcap" / "udp.pcap" command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} -L 2" output = subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT) output = output.decode("utf-8") # 24 bytes (pcap file header) # 16 bytes (pcap packet header) # 14 bytes (Ethernet header) assert "1 packets (54 bytes) written" in output out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest() assert out_checksum == "284991bf006227abc978df0531206f83" def test_bittwiste_layer_3(): in_pcap_file = Path(__file__).resolve().parent / "pcap" / "udp.pcap" command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} -L 3" output = subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT) output = output.decode("utf-8") # 24 bytes (pcap file header) # 16 bytes (pcap packet header) # 14 bytes (Ethernet header) # 20 bytes (IP header) assert "1 packets (74 bytes) written" in output out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest() assert out_checksum == "911ad3bc2d34ce31a33f46eeedf0d003" def test_bittwiste_layer_4(): in_pcap_file = Path(__file__).resolve().parent / "pcap" / "udp.pcap" command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} -L 4" output = subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT) output = output.decode("utf-8") # 24 bytes (pcap file header) # 16 bytes (pcap packet header) # 14 bytes (Ethernet header) # 20 bytes (IP header) # 8 bytes (UDP header) assert "1 packets (82 bytes) written" in output out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest() assert out_checksum == "4859e9da19006513d8836686ad10b87a" def test_bittwiste_no_checksum(): in_pcap_file = Path(__file__).resolve().parent / "pcap" / "udp.pcap" command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} -L 4 -C" subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT) out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest() assert out_checksum == "36858da4295e35c470b0de4955f3e44d" def test_bittwiste_link_type(): in_pcap_file = Path(__file__).resolve().parent / "pcap" / "udp.pcap" command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} -M 0" subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT) out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest() assert out_checksum == "58018d4978c0ac9bc517d0686505553a" def test_bittwiste_delete_offset(): in_pcap_file = Path(__file__).resolve().parent / "pcap" / "udp.pcap" command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} -D 15-9999" subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT) out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest() assert out_checksum == "284991bf006227abc978df0531206f83" def test_bittwiste_range(): in_pcap_file = Path(__file__).resolve().parent / "pcap" / "icmp.pcap" command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} -R 2-3" subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT) out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest() assert out_checksum == "011f483f5bf0a21039dfc345040c696c" def test_bittwiste_timeframe(): in_pcap_file = Path(__file__).resolve().parent / "pcap" / "icmp.pcap" command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} -S 01/06/2023,14:56:32-01/06/2023,14:56:33" subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT) out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest() assert out_checksum == "011f483f5bf0a21039dfc345040c696c" def test_bittwiste_nsec_ts_pcap(): in_pcap_file = Path(__file__).resolve().parent / "pcap" / "nanosecond-ts.pcap" command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} -S 08/06/2023,09:09:29-08/06/2023,09:09:29" subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT) out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest() assert out_checksum == "c621b20df8bc13975dbaefde03f17acc" def test_bittwiste_repeat(): in_pcap_file = Path(__file__).resolve().parent / "pcap" / "nanosecond-ts.pcap" opts = [ ("-N 1", "243327564647893dda909185b545f72c"), ("-R 1 -N 1", "73a57e6dbc987767164b504ad70cd6e0"), ("-R 2-4 -N 1000", "77e31e7c751cc03dbe888ae7dcb33d71"), ] for opt, expected_checksum in opts: command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} {opt}" subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT) out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest() assert out_checksum == expected_checksum def test_bittwiste_gaprange(): ranges = [ ("1", "0d088451053fca616c80a131d9559f81"), (f"{2 ** 31 - 1}", "6263c28e0fb912a06a7e55e4045d856f"), ("1000-10000", "5b89572f552e0f9698757f5e0c03fa92"), ("1000", "9567e89a9ce877370f35519bcb3b6c5a"), ] for range, expected_checksum in ranges: command = f"{bin} -I eth -O {out_pcap_file} -P 10000 -N 10 -G {range}" subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT) out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest() assert out_checksum == expected_checksum def test_bittwiste_nsec_gaprange(): in_pcap_file = Path(__file__).resolve().parent / "pcap" / "nanosecond-ts.pcap" ranges = [ ("1", "877575bc86617acb3548edd36cc72ad2"), (f"{2 ** 31 - 1}", "6a2d01e9da3e102d5fa47c8e87c285c0"), ("1000-10000", "4ef4a66489e5efafa70c32e9c198a142"), ("1000", "78d1f93cac85aeca05893f178d08c55d"), ] for range, expected_checksum in ranges: command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} -P 10000 -G {range}" subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT) out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest() assert out_checksum == expected_checksum def test_bittwiste_eth_dst_mac(): in_pcap_file = Path(__file__).resolve().parent / "pcap" / "udp.pcap" macs = [ ("aa:bb:cc:dd:ee:ff", "fecc1048a00288d602bf7b84e023e8a2"), ("bb:bb:bb:bb:bb:bb,aa:bb:cc:dd:ee:ff", "fecc1048a00288d602bf7b84e023e8a2"), ("rand", "e9afb8b4268ae1ae6d071447da6c7246"), ("bb:bb:bb:bb:bb:bb,rand", "e9afb8b4268ae1ae6d071447da6c7246"), ] for mac, expected_checksum in macs: command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} -P 10000 -T eth -d {mac}" subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT) out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest() assert out_checksum == expected_checksum def test_bittwiste_eth_src_mac(): in_pcap_file = Path(__file__).resolve().parent / "pcap" / "udp.pcap" macs = [ ("00:11:22:33:44:55", "933cb03f4ef0d2b57a1a0bd7763c20ec"), ("aa:aa:aa:aa:aa:aa,00:11:22:33:44:55", "933cb03f4ef0d2b57a1a0bd7763c20ec"), ("rand", "7f97cd336ae2090d26b97aaf988fdcb4"), ("aa:aa:aa:aa:aa:aa,rand", "7f97cd336ae2090d26b97aaf988fdcb4"), ] for mac, expected_checksum in macs: command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} -P 10000 -T eth -s {mac}" subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT) out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest() assert out_checksum == expected_checksum def test_bittwiste_eth_type(): in_pcap_file = Path(__file__).resolve().parent / "pcap" / "udp.pcap" types = [ ("ip", "2bedce3bb211e95b6b2f29978e6605e6"), ("ip6", "13adf4829975c161e903d0adf8712cde"), ("arp", "7cd8fe98e453ea62c8c70733e2eb6f38"), ] for type, expected_checksum in types: command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} -T eth -t {type}" subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT) out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest() assert out_checksum == expected_checksum def test_bittwiste_arp_opcode(): in_pcap_file = Path(__file__).resolve().parent / "pcap" / "arp.pcap" command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} -T arp -o 2" subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT) out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest() assert out_checksum == "94318ecfee20fb3b402639d8f91093dc" def test_bittwiste_arp_smac(): in_pcap_file = Path(__file__).resolve().parent / "pcap" / "arp.pcap" macs = [ ("22:22:22:22:22:22", "0538da849487db578c53b1678222f6f1"), ("aa:aa:aa:aa:aa:aa,22:22:22:22:22:22", "0538da849487db578c53b1678222f6f1"), ("rand", "46251c409ad5729f430cad94429d3dc5"), ("aa:aa:aa:aa:aa:aa,rand", "46251c409ad5729f430cad94429d3dc5"), ] for mac, expected_checksum in macs: command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} -P 10000 -T arp -s {mac}" subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT) out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest() assert out_checksum == expected_checksum def test_bittwiste_arp_sip(): in_pcap_file = Path(__file__).resolve().parent / "pcap" / "arp.pcap" command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} -T arp -p 192.168.0.1" subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT) out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest() assert out_checksum == "c6747231d370b90a70dbc442dd4bdf6d" def test_bittwiste_arp_tmac(): in_pcap_file = Path(__file__).resolve().parent / "pcap" / "arp.pcap" macs = [ ("22:22:22:22:22:22", "bc45ddf0a6488d8ba0107ce0f16a7a97"), ("00:00:00:00:00:00,22:22:22:22:22:22", "bc45ddf0a6488d8ba0107ce0f16a7a97"), ("rand", "d67087305a597c4c68b8b3bd2436a4f7"), ("00:00:00:00:00:00,rand", "d67087305a597c4c68b8b3bd2436a4f7"), ] for mac, expected_checksum in macs: command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} -P 10000 -T arp -t {mac}" subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT) out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest() assert out_checksum == expected_checksum def test_bittwiste_arp_tip(): in_pcap_file = Path(__file__).resolve().parent / "pcap" / "arp.pcap" command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} -T arp -q 192.168.0.1" subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT) out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest() assert out_checksum == "0670268132833a757e42580584463fa2" def test_bittwiste_ip_ds_field(): in_pcap_file = Path(__file__).resolve().parent / "pcap" / "ip.pcap" # From RFC 4594 # Some of the service class name mapping to DS field value: # --------------------------------------------------------------- # Service class name DSCP name DSCP value (binary, hex, int) # --------------------------------------------------------------- # Standard CS0 000000, 0x00, 0 # Low-priority data CS1 001000, 0x08, 8 # OAM CS2 010000, 0x10, 16 # Broadcast video CS3 011000, 0x18, 24 # Real-time interactive CS4 100000, 0x20, 32 # --------------------------------------------------------------- values = [ ("0", "f29a94f834e7cc8ea668520136d50eea"), ("0x00", "f29a94f834e7cc8ea668520136d50eea"), # Standard ("8", "b0c9c5d4adf93e8e1749bd7844015da7"), ("0x08", "b0c9c5d4adf93e8e1749bd7844015da7"), # Low-priority data ("16", "f247e8932c3b5405e82a9410d1250489"), ("0x10", "f247e8932c3b5405e82a9410d1250489"), # OAM ("24", "c579867ac611fdadf43c9ec3a6a2eb72"), ("0x18", "c579867ac611fdadf43c9ec3a6a2eb72"), # Broadcast video ("32", "d08af4ed967605d4a3e9c0ca24c4b886"), ("0x20", "d08af4ed967605d4a3e9c0ca24c4b886"), # Real-time interactive ] for value, expected_checksum in values: command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} -T ip -c {value}" subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT) out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest() assert out_checksum == expected_checksum def test_bittwiste_ip_ecn_field(): in_pcap_file = Path(__file__).resolve().parent / "pcap" / "ip.pcap" values = [ # From RFC 3168 # ECN: Explicit Congestion Notification # ECT: ECN-Capable Transport # CE: Congestion Experienced # To set ECN field, choose one of the 4 codepoints below: # ---------------------------------- # ECN FIELD # ---------------------------------- # ECT CE Hex value Codepoint name # ---------------------------------- # 0 0 0x00 Not-ECT # 0 1 0x01 ECT(1) # 1 0 0x02 ECT(0) # 1 1 0x03 CE # ---------------------------------- ("0", "f29a94f834e7cc8ea668520136d50eea"), ("0x00", "f29a94f834e7cc8ea668520136d50eea"), # 0b00000011 Not-ECT ("1", "c05a433d6fe82316368826af20fb57b3"), ("0x01", "c05a433d6fe82316368826af20fb57b3"), # 0b00000001 ECT(1) ("2", "6f9cdfa72ad388f581c1ee3332fec340"), ("0x02", "6f9cdfa72ad388f581c1ee3332fec340"), # 0b00000010 ECT(0) ("3", "8f738d20218243bfe371d01fe2134c3a"), ("0x03", "8f738d20218243bfe371d01fe2134c3a"), # 0b00000011 CE ] for value, expected_checksum in values: command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} -T ip -e {value}" subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT) out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest() assert out_checksum == expected_checksum def test_bittwiste_ip_id(): in_pcap_file = Path(__file__).resolve().parent / "pcap" / "ip.pcap" ids = [ ("0", "cce2b07dd766b216fc1393be020b1884"), ("65535", "3a856b4b58cb606fca2899fdb77de035"), ("12930,1", "9cc2bb65372c57a26811709caec42883"), ("1,2", "f29a94f834e7cc8ea668520136d50eea"), ("rand", "cc8d257889a156b6afe54e0c99a787b5"), ("0x3282,rand", "cc8d257889a156b6afe54e0c99a787b5"), ] for id, expected_checksum in ids: command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} -P 10000 -T ip -i {id}" subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT) out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest() assert out_checksum == expected_checksum def test_bittwiste_ip_flags(): in_pcap_file = Path(__file__).resolve().parent / "pcap" / "ip.pcap" flags = [ ("-", "5764a63494d331bae4ba6289583de7eb"), ("r", "273e0444f43862fd3a1adde85c259673"), ("d", "f29a94f834e7cc8ea668520136d50eea"), ("m", "a744c0b40197f83cbf08f48349346c07"), ("rdm", "027d1b001a781901aa1d3efabf938e9d"), ] for flag, expected_checksum in flags: command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} -T ip -f {flag}" subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT) out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest() assert out_checksum == expected_checksum def test_bittwiste_ip_offset(): in_pcap_file = Path(__file__).resolve().parent / "pcap" / "ip.pcap" command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} -T ip -o 7770" subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT) out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest() assert out_checksum == "8f6a26bb30dbc03fb10b7d39c8976fc8" def test_bittwiste_ip_ttl(): in_pcap_file = Path(__file__).resolve().parent / "pcap" / "ip.pcap" ttls = [ ("0", "a02573984fc285b214a27ed81945c772"), ("rand", "f9677b333bd64e71a8f8fa2bc38ef52b"), ("64,rand", "f9677b333bd64e71a8f8fa2bc38ef52b"), ("255", "b184b553c697ae010c10e6e6d11d228c"), ] for ttl, expected_checksum in ttls: command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} -P 10000 -T ip -t {ttl}" subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT) out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest() assert out_checksum == expected_checksum def test_bittwiste_ip_proto(): in_pcap_file = Path(__file__).resolve().parent / "pcap" / "ip.pcap" protos = [ ("0", "5593499dc569b12f5eb35e866c770719"), ("rand", "94851677eb42797816674e20e3d482d4"), ("6,rand", "94851677eb42797816674e20e3d482d4"), ("255", "6518e609dd35cbbae72fa363236a1d18"), ] for proto, expected_checksum in protos: command = ( f"{bin} -I {in_pcap_file} -O {out_pcap_file} -P 10000 -T ip -p {proto}" ) subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT) out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest() assert out_checksum == expected_checksum def test_bittwiste_ip_sip(): in_pcap_file = Path(__file__).resolve().parent / "pcap" / "ip.pcap" ips = [ ("1.1.1.1", "2f4d5757b9b291930850e5bc5d312081"), ("127.0.0.1,1.1.1.1", "2f4d5757b9b291930850e5bc5d312081"), ("1.1.0.0/16", "72fa1c10e88753417ecca1b3f859da68"), ("127.0.0.1,1.1.0.0/16", "72fa1c10e88753417ecca1b3f859da68"), ("0.0.0.0/0", "870a06f0e16038bc57a28f3b95b3db06"), ("0.0.0.0/32", "151499a5f22cec02c0544f3010c96989"), ] for ip, expected_checksum in ips: command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} -P 10000 -T ip -s {ip}" subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT) out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest() assert out_checksum == expected_checksum def test_bittwiste_ip_dip(): in_pcap_file = Path(__file__).resolve().parent / "pcap" / "ip.pcap" ips = [ ("1.1.1.1", "19ab454b058b686ff297cabf962d91ad"), ("127.0.0.1,1.1.1.1", "19ab454b058b686ff297cabf962d91ad"), ("1.1.0.0/16", "78e2760d8880e1b0b6a06459d51eba24"), ("127.0.0.1,1.1.0.0/16", "78e2760d8880e1b0b6a06459d51eba24"), ("0.0.0.0/0", "8cb8aec22dc1a7b2e81e20b1fbe4f4de"), ("0.0.0.0/32", "2d48b2186a0dcbf612737e0e0ee49bc4"), ] for ip, expected_checksum in ips: command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} -P 10000 -T ip -d {ip}" subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT) out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest() assert out_checksum == expected_checksum def test_bittwiste_ip6_ds_field(): in_pcap_file = Path(__file__).resolve().parent / "pcap" / "tcp6.pcap" # From RFC 4594 # Some of the service class name mapping to DS field value: # --------------------------------------------------------------- # Service class name DSCP name DSCP value (binary, hex, int) # --------------------------------------------------------------- # Standard CS0 000000, 0x00, 0 # Low-priority data CS1 001000, 0x08, 8 # OAM CS2 010000, 0x10, 16 # Broadcast video CS3 011000, 0x18, 24 # Real-time interactive CS4 100000, 0x20, 32 # --------------------------------------------------------------- values = [ ("0", "fd907f31094c4937012285acf200e28f"), ("0x00", "fd907f31094c4937012285acf200e28f"), # Standard ("8", "d4c72d18d6f83f7010ff3caccaa976b6"), ("0x08", "d4c72d18d6f83f7010ff3caccaa976b6"), # Low-priority data ("16", "c6bd6e916e953525253b003000a2a7ba"), ("0x10", "c6bd6e916e953525253b003000a2a7ba"), # OAM ("24", "2f4a2716507d916e0fcdca596c96163a"), ("0x18", "2f4a2716507d916e0fcdca596c96163a"), # Broadcast video ("32", "a80bb13607492a8c5d2108b23db532ba"), ("0x20", "a80bb13607492a8c5d2108b23db532ba"), # Real-time interactive ] for value, expected_checksum in values: command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} -T ip6 -c {value}" subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT) out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest() assert out_checksum == expected_checksum def test_bittwiste_ip6_ecn_field(): in_pcap_file = Path(__file__).resolve().parent / "pcap" / "tcp6.pcap" values = [ # From RFC 3168 # ECN: Explicit Congestion Notification # ECT: ECN-Capable Transport # CE: Congestion Experienced # To set ECN field, choose one of the 4 codepoints below: # ---------------------------------- # ECN FIELD # ---------------------------------- # ECT CE Hex value Codepoint name # ---------------------------------- # 0 0 0x00 Not-ECT # 0 1 0x01 ECT(1) # 1 0 0x02 ECT(0) # 1 1 0x03 CE # ---------------------------------- ("0", "fd907f31094c4937012285acf200e28f"), ("0x00", "fd907f31094c4937012285acf200e28f"), # 0b00000011 Not-ECT ("1", "30f782ab4c0d12ad7ab5e9cc1b00de8e"), ("0x01", "30f782ab4c0d12ad7ab5e9cc1b00de8e"), # 0b00000001 ECT(1) ("2", "f4d7480a83ddd35de40a3224be44b07a"), ("0x02", "f4d7480a83ddd35de40a3224be44b07a"), # 0b00000010 ECT(0) ("3", "e6aa1806cb7f3b7df354570e5ebd4d81"), ("0x03", "e6aa1806cb7f3b7df354570e5ebd4d81"), # 0b00000011 CE ] for value, expected_checksum in values: command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} -T ip6 -e {value}" subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT) out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest() assert out_checksum == expected_checksum def test_bittwiste_ip6_flow_label(): in_pcap_file = Path(__file__).resolve().parent / "pcap" / "tcp6.pcap" in_checksum = hashlib.md5(open(in_pcap_file, "rb").read()).hexdigest() flow_labels = [ # 217523, 0x351b3, 0x0351b3, and 0650663 are all equal but specified in # different notation. ("217523", in_checksum), # integer input ("0x351b3", in_checksum), # hexadecimal input, leading 0x ("0x0351b3", in_checksum), # hexadecimal input, leading 0x ("0650663", in_checksum), # octal input, leading 0 ("0x00000", "e426a6627f85ec9e81c92aba38c82809"), ("0", "e426a6627f85ec9e81c92aba38c82809"), ("0xfffff", "d46873fcd066f82302169e52bd235247"), ("1048575", "d46873fcd066f82302169e52bd235247"), ] for flow_label, expected_checksum in flow_labels: command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} -T ip6 -f {flow_label}" subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT) out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest() assert out_checksum == expected_checksum def test_bittwiste_ip6_next_header(): next_headers = [ ("0", "1c96458741482f6ae270b2162c2525ae"), ("6", "959d8f64bd4db5c0c735f3d7b6e8f0cb"), ("17", "4fcb7c47888cfd15d64805eadc595fc4"), ("50", "ca9a470a6a59fb6d100280c4d3777efe"), ("51", "fd2942e491ea9641d43be4fd93c80abc"), ("58", "3d74df02c928143f3a519f989cce1f1e"), ("255", "812c88133f034b831c732c5ad2bf2a3c"), ("rand", "9715fb341e3daa197da5de27744dc874"), ("6,rand", "9715fb341e3daa197da5de27744dc874"), ] for next_header, expected_checksum in next_headers: command = f"{bin} -I ip6 -O {out_pcap_file} -P 10000 -T ip6 -n {next_header}" subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT) out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest() assert out_checksum == expected_checksum def test_bittwiste_ip6_hop_limit(): in_pcap_file = Path(__file__).resolve().parent / "pcap" / "tcp6.pcap" hop_limits = [ ("0", "e3b0d298d4f3f994674b50dd69e9fdc5"), ("255", "e18371eabf1e0eff127feed105f256af"), ("rand", "a6041eb2f7b9341258362800102ca155"), ("53,rand", "a6041eb2f7b9341258362800102ca155"), ] for hop_limit, expected_checksum in hop_limits: command = ( f"{bin} -I {in_pcap_file} -O {out_pcap_file} -P 10000 -T ip6 -h {hop_limit}" ) subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT) out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest() assert out_checksum == expected_checksum def test_bittwiste_ip6_sip(): in_pcap_file = Path(__file__).resolve().parent / "pcap" / "tcp6.pcap" ips = [ ("fd00::1", "2945d7bf35e49e9d1370f43387b9e74e"), ("2606:4700:4700::64,::1", "4162c7086a1b07c1bcfed847bc561762"), ("2001:db8::/64", "e70dc9a77460bd7da04c43702f283c39"), ("2606:4700:4700::64,2001:db8::/64", "e70dc9a77460bd7da04c43702f283c39"), ("::/0", "2214a6eaf456a8e77bdbd835a8e635ba"), ("::/128", "66414ba3c39e8d41b34dea1822e9da60"), ] for ip, expected_checksum in ips: command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} -P 10000 -T ip6 -s {ip}" subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT) out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest() assert out_checksum == expected_checksum def test_bittwiste_ip6_dip(): in_pcap_file = Path(__file__).resolve().parent / "pcap" / "tcp6.pcap" ips = [ ("fd00::2", "a47dd221b2a8a9c20433d80b20a32a97"), ("2606:4700:4700::6400,::2", "7d7996cee18bbcd17cc3541da84b372c"), ("2001:db8::/64", "64b7b89f5b9221cc9f63aae4361b7bf6"), ("2606:4700:4700::6400,2001:db8::/64", "64b7b89f5b9221cc9f63aae4361b7bf6"), ("::/0", "ccd2ee2417f409883229a716b54beaef"), ("::/128", "ed7310991e521868b871d429e3d73932"), ] for ip, expected_checksum in ips: command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} -P 10000 -T ip6 -d {ip}" subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT) out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest() assert out_checksum == expected_checksum def test_bittwiste_icmp_type(): in_pcap_file = Path(__file__).resolve().parent / "pcap" / "icmp.pcap" command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} -T icmp -t 0" subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT) out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest() assert out_checksum == "decdb8f44253801bcfdc845387d6f6cb" def test_bittwiste_icmp_code(): in_pcap_file = Path(__file__).resolve().parent / "pcap" / "icmp.pcap" command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} -T icmp -c 255" subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT) out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest() assert out_checksum == "aef78c21afb648a5c554bd2256dc6389" def test_bittwiste_icmp6_type(): in_pcap_file = Path(__file__).resolve().parent / "pcap" / "icmp6.pcap" command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} -T icmp6 -t 129" subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT) out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest() assert out_checksum == "2458130f660da7acf73e984bfc2d858b" def test_bittwiste_icmp6_code(): in_pcap_file = Path(__file__).resolve().parent / "pcap" / "icmp6.pcap" command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} -T icmp6 -c 255" subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT) out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest() assert out_checksum == "5690afe231cd89d2a0b6dde31d1b6441" def test_bittwiste_tcp_sport(): in_pcap_file = Path(__file__).resolve().parent / "pcap" / "tcp6.pcap" opts = [ ("-T tcp -s 1000", "b064417c556ef7a068f7cda4c1aa0865"), ("-T tcp -s 30000,1000", "b064417c556ef7a068f7cda4c1aa0865"), ("-P 10000 -T tcp -s rand", "4ff6f9931fc2e0b7065eb613e5bd7a2a"), ("-P 10000 -T tcp -s 30000,rand", "4ff6f9931fc2e0b7065eb613e5bd7a2a"), ] for opt, expected_checksum in opts: command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} {opt}" subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT) out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest() assert out_checksum == expected_checksum def test_bittwiste_tcp_dport(): in_pcap_file = Path(__file__).resolve().parent / "pcap" / "tcp6.pcap" opts = [ ("-T tcp -d 1000", "f6f4d2431303b49c47d7e3a63178e2ff"), ("-T tcp -d 60000,1000", "f6f4d2431303b49c47d7e3a63178e2ff"), ("-P 10000 -T tcp -d rand", "737aae1e41f76e323da89ebb3df86dd7"), ("-P 10000 -T tcp -d 60000,rand", "737aae1e41f76e323da89ebb3df86dd7"), ] for opt, expected_checksum in opts: command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} {opt}" subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT) out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest() assert out_checksum == expected_checksum def test_bittwiste_tcp_seq(): in_pcap_file = Path(__file__).resolve().parent / "pcap" / "tcp.pcap" opts = [ ("-T tcp -q 0", "eab1ff9e93ada77cd598b1173fbd1e36"), ("-T tcp -q 4294967295", "c7dfbf484ecc6cabe6e2d7aa7db502fc"), ("-T tcp -q 2053058830,100", "e46cc50cb4c4159692b62deb09be511e"), ("-P 10000 -T tcp -q rand", "a21d1ae3d7dd4b5fae9c8dac9dd75c77"), ("-P 10000 -T tcp -q 2053058831,rand", "857cc97740470d7995c2fb7e9bb80374"), ] for opt, expected_checksum in opts: command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} {opt}" subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT) out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest() assert out_checksum == expected_checksum def test_bittwiste_tcp_ack(): in_pcap_file = Path(__file__).resolve().parent / "pcap" / "tcp.pcap" opts = [ ("-T tcp -a 0", "5b57c547f994a80dd284c176dfaade3e"), ("-T tcp -a 4294967295", "0b7f0b0b502f93e818cab70f147de02c"), ("-T tcp -a 0,100", "13e5873d42f2115a2a5dae4b2a78bf4a"), ("-P 10000 -T tcp -a rand", "ebbf176a8ec144e8e84b6ee0aa54561f"), ("-P 10000 -T tcp -a 143840249,rand", "e304ecb1942c875a8d73db1da7aeb9be"), ] for opt, expected_checksum in opts: command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} {opt}" subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT) out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest() assert out_checksum == expected_checksum def test_bittwiste_tcp_flags(): in_pcap_file = Path(__file__).resolve().parent / "pcap" / "tcp.pcap" flags = [ ("-", "5a62552112078b48e578909c8829a0bd"), ("c", "e512b62c0fd8ddb0084afddc0a1df163"), ("e", "a9e39e786441ddfb894437e4ca31cd03"), ("u", "90148eeee21504b0452fc074b033e828"), ("a", "9c171b01791a089c6d8896462a86a313"), ("p", "eb86309a48eeb86695cbc7ab124d50b3"), ("r", "74edf982a1b3bd504f2c7fb808040398"), ("s", "bffdb416fa9f142d5a5ab73668d051f5"), ("f", "58da6f385414ce1fdb7e9c0cfa1becf1"), ("ceuaprsf", "bd002c3a6fa2bf65f612c6c65063ba80"), ] for flag, expected_checksum in flags: command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} -T tcp -f {flag}" subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT) out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest() assert out_checksum == expected_checksum def test_bittwiste_tcp_win(): in_pcap_file = Path(__file__).resolve().parent / "pcap" / "tcp6.pcap" command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} -T tcp -w 65535" subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT) out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest() assert out_checksum == "77e7eccd00e2e58915d9ce66771c5892" def test_bittwiste_tcp_urg(): in_pcap_file = Path(__file__).resolve().parent / "pcap" / "tcp6.pcap" command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} -T tcp -u 65535" subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT) out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest() assert out_checksum == "f7c30cad1c6eca1a949e2a02bbeb3fb7" def test_bittwiste_udp_sport(): in_pcap_file = Path(__file__).resolve().parent / "pcap" / "udp.pcap" opts = [ ("-T udp -s 0", "f40f2330d6c7a5c6b912820fc75c520f"), ("-T udp -s 60935,0", "f40f2330d6c7a5c6b912820fc75c520f"), ("-P 10000 -T udp -s rand", "00235742347dc989f273bef864c15413"), ("-P 10000 -T udp -s 60935,rand", "00235742347dc989f273bef864c15413"), ] for opt, expected_checksum in opts: command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} {opt}" subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT) out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest() assert out_checksum == expected_checksum def test_bittwiste_udp_dport(): in_pcap_file = Path(__file__).resolve().parent / "pcap" / "udp6.pcap" opts = [ ("-T udp -d 65535", "e9875fa4b2f6f14f21335c1b3e84d7ec"), ("-T udp -d 53,65535", "e9875fa4b2f6f14f21335c1b3e84d7ec"), ("-P 10000 -T udp -d rand", "66d4781d2f88aadbb12416c398f34886"), ("-P 10000 -T udp -d 53,rand", "66d4781d2f88aadbb12416c398f34886"), ] for opt, expected_checksum in opts: command = f"{bin} -I {in_pcap_file} -O {out_pcap_file} {opt}" subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT) out_checksum = hashlib.md5(open(out_pcap_file, "rb").read()).hexdigest() assert out_checksum == expected_checksum bittwist-linux-3.8/tests/pcap/0000755000175000017500000000000014451424636014654 5ustar opsopsbittwist-linux-3.8/tests/pcap/tcp6.pcap0000644000175000017500000000017614437600340016371 0ustar opsopsÔò¡k†yddEVV»»»»»»ªªªªªª†Ý`Q³ 5&GGd&GGdu0ê`0ž²ûåZ€à­Å ‘_‰bittwist-linux-3.8/tests/pcap/arp.pcap0000644000175000017500000000012214437553366016305 0ustar opsopsÔò¡Ö~d3/ **»»»»»»ªªªªªªªªªªªªbittwist-linux-3.8/tests/pcap/icmp.pcap0000644000175000017500000000030614436034703016443 0ustar opsopsÔò¡ÿ$xd–3**»»»»»»ªªªªªªEC!@@ñºõŠ+J%xd<<**»»»»»»ªªªªªªED@@ðÕõŠ+J%xdšC**»»»»»»ªªªªªªEDò@@ïéõŠ+Jbittwist-linux-3.8/tests/pcap/1000us.pcap0000644000175000017500000000146014446536203016450 0ustar opsopsÔò¡€Cm8èJJE<2‚@@ 8èÐz_1 ªªFŸÿ× 7óot €Cm8ÐJJE<2‚@@ 8èÐz_1 ªªFŸÿ× 7óot €Cm8¸ JJE<2‚@@ 8èÐz_1 ªªFŸÿ× 7óot €Cm8 **EC!@@ù½õŠ+J€Cm8ˆ**EC!@@ù½õŠ+J€Cm8p**EC!@@ù½õŠ+J€Cm8X**EC!@@ù½õŠ+J€Cm8@**EC!@@ù½õŠ+J€Cm8(#**E¿†@½HèÐö#€Cm8'**E¿†@½HèÐö#€Cm8ø***E¿†@½HèÐö#€Cm8à.**E¿†@½HèÐö#bittwist-linux-3.8/tests/pcap/icmp6.pcap0000644000175000017500000000065214437730206016537 0ustar opsopsÔò¡u†ydc¶vv»»»»»»ªªªªªª†Ý`ïE@:0&GGd&GGd€fÿLv†ydײvv»»»»»»ªªªªªª†Ý`ïE@:0&GGd&GGd€fûLw†ydP³vv»»»»»»ªªªªªª†Ý`ïE@:0&GGd&GGd€f÷Lbittwist-linux-3.8/tests/pcap/ip.pcap0000644000175000017500000000011214434570544016124 0ustar opsopsÔò¡ªrd,t""E2‚@@ `bittwist-linux-3.8/tests/pcap/udp.pcap0000644000175000017500000000020314437533643016307 0ustar opsopsÔò¡—¤~d [[»»»»»»ªªªªªªEM¿†@µî59ql—µ1111in-addrarpa )Àbittwist-linux-3.8/tests/pcap/1514.pcap0000644000175000017500000000302214437546434016115 0ustar opsopsÔò¡ªrd,têêEÜ2‚@@˜D’Pz_1 ªªÕÿ× 7óot ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿbittwist-linux-3.8/tests/pcap/tcp.pcap0000644000175000017500000000030414434530577016307 0ustar opsopsÔò¡ªrd,tJJE<2‚@@ 8D’Pz_1 ªª uÿ× 7óot ªrd9tBBE42ƒ@@ ?D’Pz_1’Óù€+\ä 7óot7óotbittwist-linux-3.8/tests/pcap/nanosecond-ts.pcap0000644000175000017500000000100414442230044020253 0ustar opsopsM<²¡ d‡cR*BBE4@㒣蠠£UÞ&Œ^€ómð /p‘& dr˜R*BBE4çÜ@@Tå èÞ&Œ^ £{€S  ‘&f‚/p#dëi)BBE4ú@@@BÐèœÎqäy…ê €S‹ ™@6˜ t –#dsp›.BBE4öB@0VèÐy…ê œÎqå€F— t½Ú™?„T)dÇ€,BBE4„@@>@ˆF¹Ç r£vK€SÉú zÅ¡X:5.ž)dÇ&:7BBE4úy.Ȉ@r£vKF¹Ç € ,¦ :5æzÆybittwist-linux-3.8/tests/pcap/udp6.pcap0000644000175000017500000000022714437741447016406 0ustar opsopsÔò¡~†yd)oo»»»»»»ªªªªªª†Ý`§9@&GGd&GGdZE59>×µ1111in-addrarpa )Àbittwist-linux-3.8/tests/pytest.ini0000644000175000017500000000004514434754116015761 0ustar opsops[pytest] log_cli=true log_level=INFO bittwist-linux-3.8/tests/__init__.py0000644000175000017500000000000014450736430016025 0ustar opsopsbittwist-linux-3.8/tests/benchmark_pps_ipg.json0000644000175000017500000001275514451421666020311 0ustar opsops[ { "0.5": [ { "pkts": 10, "ipg_us": { "expected": 2000000, "actual": 2000076.2, "diff": 0.00381 }, "elapsed_s": { "expected": 20.0, "actual": 20.000843, "diff": 0.004215 } }, { "pkts": 20, "ipg_us": { "expected": 2000000, "actual": 2000057.2, "diff": 0.00286 }, "elapsed_s": { "expected": 40.0, "actual": 40.001225, "diff": 0.003062 } }, { "pkts": 30, "ipg_us": { "expected": 2000000, "actual": 2000054.0, "diff": 0.0027 }, "elapsed_s": { "expected": 60.0, "actual": 60.001698, "diff": 0.00283 } }, { "pkts": 40, "ipg_us": { "expected": 2000000, "actual": 2000056.675, "diff": 0.002834 }, "elapsed_s": { "expected": 80.0, "actual": 80.002342, "diff": 0.002927 } }, { "pkts": 50, "ipg_us": { "expected": 2000000, "actual": 2000050.18, "diff": 0.002509 }, "elapsed_s": { "expected": 100.0, "actual": 100.002548, "diff": 0.002548 } } ] }, { "1": [ { "pkts": 20, "ipg_us": { "expected": 1000000, "actual": 1000029.35, "diff": 0.002935 }, "elapsed_s": { "expected": 20.0, "actual": 20.000633, "diff": 0.003165 } }, { "pkts": 40, "ipg_us": { "expected": 1000000, "actual": 1000028.95, "diff": 0.002895 }, "elapsed_s": { "expected": 40.0, "actual": 40.001202, "diff": 0.003005 } }, { "pkts": 60, "ipg_us": { "expected": 1000000, "actual": 1000029.283333, "diff": 0.002928 }, "elapsed_s": { "expected": 60.0, "actual": 60.001801, "diff": 0.003002 } }, { "pkts": 80, "ipg_us": { "expected": 1000000, "actual": 1000029.7375, "diff": 0.002974 }, "elapsed_s": { "expected": 80.0, "actual": 80.002426, "diff": 0.003032 } }, { "pkts": 100, "ipg_us": { "expected": 1000000, "actual": 1000027.03, "diff": 0.002703 }, "elapsed_s": { "expected": 100.0, "actual": 100.002747, "diff": 0.002747 } } ] }, { "100": [ { "pkts": 2000, "ipg_us": { "expected": 10000, "actual": 10000.017, "diff": 0.00017 }, "elapsed_s": { "expected": 20.0, "actual": 20.000078, "diff": 0.00039 } }, { "pkts": 4000, "ipg_us": { "expected": 10000, "actual": 9999.9965, "diff": -3.5e-05 }, "elapsed_s": { "expected": 40.0, "actual": 40.000046, "diff": 0.000115 } }, { "pkts": 6000, "ipg_us": { "expected": 10000, "actual": 10000.002833, "diff": 2.8e-05 }, "elapsed_s": { "expected": 60.0, "actual": 60.000084, "diff": 0.00014 } }, { "pkts": 8000, "ipg_us": { "expected": 10000, "actual": 10000.00425, "diff": 4.2e-05 }, "elapsed_s": { "expected": 80.0, "actual": 80.000092, "diff": 0.000115 } }, { "pkts": 10000, "ipg_us": { "expected": 10000, "actual": 10000.0028, "diff": 2.8e-05 }, "elapsed_s": { "expected": 100.0, "actual": 100.000092, "diff": 9.2e-05 } } ] }, { "500000": [ { "pkts": 10000000, "ipg_us": { "expected": 2, "actual": 2.000001, "diff": 5e-05 }, "elapsed_s": { "expected": 20.0, "actual": 20.005051, "diff": 0.025255 } }, { "pkts": 20000000, "ipg_us": { "expected": 2, "actual": 2.000002, "diff": 0.0001 }, "elapsed_s": { "expected": 40.0, "actual": 40.011944, "diff": 0.02986 } }, { "pkts": 30000000, "ipg_us": { "expected": 2, "actual": 2.0, "diff": 0.0 }, "elapsed_s": { "expected": 60.0, "actual": 60.014664, "diff": 0.02444 } }, { "pkts": 40000000, "ipg_us": { "expected": 2, "actual": 2.0, "diff": 0.0 }, "elapsed_s": { "expected": 80.0, "actual": 80.01619, "diff": 0.020237 } }, { "pkts": 50000000, "ipg_us": { "expected": 2, "actual": 2.000001, "diff": 5e-05 }, "elapsed_s": { "expected": 100.0, "actual": 100.019691, "diff": 0.019691 } } ] } ]bittwist-linux-3.8/tests/benchmark_bittwist.py0000755000175000017500000002516414451416365020201 0ustar opsops#!/usr/bin/env python # SPDX-License-Identifier: GPL-2.0-or-later # # benchmark_bittwist.py - Generate bittwist benchmark data # Copyright (C) 2006 - 2023 Addy Yeow # # 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. import argparse import json import logging import re import subprocess import sys from pathlib import Path bittwist_bin = Path(__file__).resolve().parent.parent / "src" / "bittwist" if not bittwist_bin.exists(): sys.stderr.write(f"{bittwist_bin} is missing") sys.exit(1) bittwiste_bin = Path(__file__).resolve().parent.parent / "src" / "bittwiste" if not bittwiste_bin.exists(): sys.stderr.write(f"{bittwiste_bin} is missing") sys.exit(1) out_pcap_file = Path(__file__).resolve().parent / "pcap" / "out.pcap" def get_ipg_from_output(output): """ Parse bittwist verbose output and return a list of all inter-packet gap values in microseconds. """ ipg = [] # Look for lines matching pattern e.g. "10:53:46.295748 #6 (42 bytes)" pattern = r"(\d{2}):(\d{2}):(\d{2})\.(\d+) #\d+ \(\d+ bytes\)" prev_us = None lines = output.split("\n") for line in lines: match = re.match(pattern, line) if not match: continue hh, mm, ss, us = match.groups() curr_us = ( (int(hh) * 3_600_000_000) + (int(mm) * 60_000_000) + (int(ss) * 1_000_000) + int(us) ) if prev_us: ipg.append(curr_us - prev_us) prev_us = curr_us return ipg def get_stats_from_output(output): """ Parse bittwist output and return the stats. """ lines = [line.strip() for line in output.strip().split("\n")][-3:] """ sent = 585937 packets, 299999744 bits, 37499968 bytes throughput = 19531 pps, 10.0000 Mbps, 0.0100 Gbps elapsed time = 29.999981 seconds """ match = re.match(r"sent = (\d+) packets, (\d+) bits", lines[0]) sent_pkts, sent_bits = match.groups() match = re.match(r"throughput = (\d+) pps, (\d+\.\d+) Mbps", lines[1]) throughput_pps, throughput_mbps = match.groups() match = re.match(r"elapsed time = (\d+\.\d+) seconds", lines[2]) elapsed_s = match.groups()[0] stats = { "sent_pkts": int(sent_pkts), "sent_bits": int(sent_bits), "throughput_pps": int(throughput_pps), "throughput_mbps": float(throughput_mbps), "elapsed_s": float(elapsed_s), } return stats def percent_change(new_val, old_val): change = (new_val - old_val) / old_val * 100 return round(change, 6) def write_json(json_file, data): with open(json_file, "w") as f: f.write(json.dumps(data, indent=2)) logging.info(f"wrote {json_file}") def benchmark_pps_ipg(): """ Single CPU thread benchmark that generates benchmark data by sending packets using packets per second (PPS) inter-packet gap (IPG) with set number of packets onto the loopback interface. """ out_json_file = Path(__file__).resolve().parent / "benchmark_pps_ipg.json" benchmark_results = [] # Each pps takes about (20s + 40s + 60s + 80s + 100s) = 300s # In total, pps_vals takes 300s * 4 = 20 minutes pps_vals = (0.5, 1, 100, 500000) for pps in pps_vals: pps_data = {pps: []} for n in range(5): # Number of packets to send for this test. # This is formulated such that each increment of n increases the # test by 20 seconds in total expected duration. # As we are testing for IPG accuracy, such incremental duration # will allow us to check for possible drift in the benchmark # results; increasing drift may indicate buggy timing code. pkts = int((n + 1) * (pps * 20)) # Given the PPS, calculate its IPG in microseconds. expected_ipg_us = int(1_000_000 / pps) expected_elapsed_s = pkts * expected_ipg_us / 1_000_000.0 # Prepare the packets with the required IPG for this test. cmd = f"{bittwiste_bin} -I udp -O {out_pcap_file} -N {pkts} -G {expected_ipg_us}" subprocess.check_output(cmd, shell=True, stderr=subprocess.STDOUT) # Send the packets using their existing IPG. cmd = f"sudo taskset --cpu-list 0 nice -20 {bittwist_bin} -v -i lo {out_pcap_file}" logging.info(f"pps={pps} - pkts={pkts} - cmd={cmd}") output = subprocess.check_output(cmd, shell=True, stderr=subprocess.STDOUT) output = output.decode("utf-8") ipg = get_ipg_from_output(output) actual_ipg_us = round(sum(ipg) / len(ipg), 6) actual_elapsed_s = get_stats_from_output(output)["elapsed_s"] pps_data[pps].append( { "pkts": pkts, "ipg_us": { "expected": expected_ipg_us, "actual": actual_ipg_us, "diff": percent_change(actual_ipg_us, expected_ipg_us), }, "elapsed_s": { "expected": expected_elapsed_s, "actual": actual_elapsed_s, "diff": percent_change(actual_elapsed_s, expected_elapsed_s), }, } ) benchmark_results.append(pps_data) write_json(out_json_file, benchmark_results) def benchmark_max_throughput(iface, smac, dmac, sip, dip): """ On-wire benchmark that generates benchmark data by sending packets at the specified throughputs in Mbps with set number of packets onto an actual network interface card. """ out_json_file = Path(__file__).resolve().parent / "benchmark_max_throughput.json" benchmark_results = [] # 21 tests. mbps_vals = (10, 100, 1000) # Mbps pkt_lens = (64, 128, 256, 512, 1024, 1280, 1514) # bytes # We want each test to take about 60s; 21 minutes in total. for expected_mbps in mbps_vals: mbps_data = {expected_mbps: []} for pkt_len in pkt_lens: expected_elapsed_s = 60 # Number of packets to send for this test. total_bits = expected_mbps * 1000 * 1000 * expected_elapsed_s pkts = int(total_bits / (pkt_len * 8)) expected_pps = int(pkts / expected_elapsed_s) # 1 + 100K UDP packets with correct MAC addresses. cmd = f"{bittwiste_bin} -I udp -O {out_pcap_file}.1 -N {pkts - 1} -T eth -s {smac} -d {dmac}" subprocess.check_output(cmd, shell=True, stderr=subprocess.STDOUT) # Correct IP addresses. cmd = f"{bittwiste_bin} -I {out_pcap_file}.1 -O {out_pcap_file}.2 -T ip -s {sip} -d {dip}" subprocess.check_output(cmd, shell=True, stderr=subprocess.STDOUT) # Extend each packet to the required length. # e.g. 64 bytes packet: # - Ethernet header = 14 bytes # - IP header = 20 bytes # - UDP header = 8 bytes # - Payload = 64 - headers = 22 bytes payload = "0" * (pkt_len - (14 + 20 + 8)) * 2 cmd = f"{bittwiste_bin} -I {out_pcap_file}.2 -O {out_pcap_file} -X {payload} -L 4 -T udp" subprocess.check_output(cmd, shell=True, stderr=subprocess.STDOUT) # Remove intermediary pcap files. Path(f"{out_pcap_file}.1").unlink() Path(f"{out_pcap_file}.2").unlink() # Send the packets at the specified linerate in Mbps. cmd = f"sudo taskset --cpu-list 0 nice -20 {bittwist_bin} -i {iface} -r {expected_mbps} {out_pcap_file}" logging.info(f"pkt_len={pkt_len} - pkts={pkts} - cmd={cmd}") output = subprocess.check_output(cmd, shell=True, stderr=subprocess.STDOUT) output = output.decode("utf-8") stats = get_stats_from_output(output) assert stats["sent_pkts"] == pkts actual_pps = stats["throughput_pps"] actual_mbps = stats["throughput_mbps"] actual_elapsed_s = stats["elapsed_s"] mbps_data[expected_mbps].append( { "pkt_len": pkt_len, "pkts": pkts, "pps": { "expected": expected_pps, "actual": actual_pps, "diff": percent_change(actual_pps, expected_pps), }, "mbps": { "expected": expected_mbps, "actual": actual_mbps, "diff": percent_change(actual_mbps, expected_mbps), }, "elapsed_s": { "expected": expected_elapsed_s, "actual": actual_elapsed_s, "diff": percent_change(actual_elapsed_s, expected_elapsed_s), }, } ) benchmark_results.append(mbps_data) write_json(out_json_file, benchmark_results) def main(): log_format = "%(levelname)s - %(asctime)s - %(funcName)s - %(message)s" logging.basicConfig(level="DEBUG", format=log_format) parser = argparse.ArgumentParser() parser.add_argument( "--tests", nargs="+", default=[], help="Benchmark to run. Default: All" ) # On-wire setup. parser.add_argument("--iface", default="lo", help="Outbound network interface") parser.add_argument("--smac", default="00:00:00:00:00:00", help="Source MAC") parser.add_argument("--dmac", default="00:00:00:00:00:00", help="Destination MAC") parser.add_argument("--sip", default="127.0.0.1", help="Source IP") parser.add_argument("--dip", default="127.0.0.1", help="Destination IP") args = parser.parse_args() # If not set, run all tests. tests = args.tests if not tests or "benchmark_pps_ipg" in tests: benchmark_pps_ipg() if not tests or "benchmark_max_throughput" in tests: benchmark_max_throughput(args.iface, args.smac, args.dmac, args.sip, args.dip) if __name__ == "__main__": sys.exit(main()) bittwist-linux-3.8/tests/test_bittwist.py0000644000175000017500000001205014451215605017203 0ustar opsops#!/usr/bin/env python # SPDX-License-Identifier: GPL-2.0-or-later # # test_bittwist.py - bittwist Linux test suite # Copyright (C) 2006 - 2023 Addy Yeow # # 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. import hashlib import logging import subprocess import sys from pathlib import Path from .benchmark_bittwist import get_ipg_from_output from .benchmark_bittwist import get_stats_from_output relbin = Path("/usr/local/bin/bittwist") if not relbin.exists(): sys.stderr.write(f"{relbin} is missing") sys.exit(1) devbin = Path(__file__).resolve().parent.parent / "src" / "bittwist" if not devbin.exists(): sys.stderr.write(f"{devbin} is missing") sys.exit(1) executables = [ # Release version (publicly available) ("REL", relbin), # Dev version ("DEV", devbin), ] def test_bittwist(): """ Quick test to check output of dev bittwist. """ pcap_file = Path(__file__).resolve().parent / "pcap" / "ip.pcap" command = f"sudo {devbin} -vv -i lo -l 0 -p 100 -c 100 {pcap_file} {pcap_file}" output = subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT) output = output.decode("utf-8") assert "sent = 100 packets, 27200 bits, 3400 bytes" in output def test_bittwist_rel_vs_dev_hexdump(): """ Compare live hexdump of ip.pcap between release and dev bittwist. $ tcpdump -v -x -XX -r pcap/ip.pcap reading from file pcap/ip.pcap, link-type EN10MB (Ethernet), snapshot length 262144 11:10:39.422956 IP (tos 0x0, ttl 64, id 12930, offset 0, flags [DF], proto TCP (6), length 20) localhost > localhost: [|tcp] 0x0000: 0000 0000 0000 0000 0000 0000 0800 4500 ..............E. 0x0010: 0014 3282 4000 4006 0a60 7f00 0001 7f00 ..2.@.@..`...... 0x0020: 0001 .. """ hexdump_hashes = [] for ver, executable in executables: pcap_file = Path(__file__).resolve().parent / "pcap" / "ip.pcap" command = ( f"sudo {executable} -vv -i lo -l 5 {pcap_file} {pcap_file} {pcap_file}" ) hexdump = [] output = subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT) output = output.decode("utf-8") lines = [line.strip() for line in output.strip().split("\n")] for line in lines: if "0x" in line: hexdump.append(line) hexdump = "\n".join(hexdump) hexdump_hashes.append(hashlib.md5(hexdump.encode("utf-8")).hexdigest()) assert len(set(hexdump_hashes)) == 1, "Hash mismatch in hexdump_hashes." def test_bittwist_2M_speed(): """ Measure sending large number of packets on localhost. This should gives program's theoretical limit before hitting actual NIC. This throughput can be expected of current dev version: sent = 2000000 packets, 24224000000 bits, 3028000000 bytes throughput = 518668 pps, 6282.1104 Mbps, 6.2821 Gbps elapsed time = 3.856029 seconds """ values = [] # Elapsed times in seconds. for ver, executable in executables: pcap_file = Path(__file__).resolve().parent / "pcap" / "1514.pcap" # 1M loop through 2 pcap files each containing 1 packet, 2M packets in total. command = f"sudo {executable} -i lo -l 1000000 -r 0 {pcap_file} {pcap_file}" logging.info(f"command={command}") output = subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT) output = output.decode("utf-8") elapsed_s = get_stats_from_output(output)["elapsed_s"] logging.info(f"{ver} - {elapsed_s}") values.append(elapsed_s) # Elapsed times should be less than 1 second apart. assert all(abs(values[i] - values[i + 1]) <= 1 for i in range(len(values) - 1)) def test_bittwist_1000us(): """ Check actual inter-packet gap when sending packets with 1000 us captured inter-packet gap. """ pcap_file = Path(__file__).resolve().parent / "pcap" / "1000us.pcap" command = f"sudo {devbin} -v -i lo -l 10 {pcap_file}" output = subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT) output = output.decode("utf-8") ipg = get_ipg_from_output(output) # Skip low deltas from first packet in trace file. ipg = [v for v in ipg if v > 100] avg_ipg = int(sum(ipg) / len(ipg)) logging.info(f"ipg={len(ipg)} - avg_ipg={avg_ipg} us") assert len(ipg) == 110 assert 990 < avg_ipg < 1010 # 10 us tolerance. bittwist-linux-3.8/doc/0000755000175000017500000000000014451442047013330 5ustar opsopsbittwist-linux-3.8/doc/bittwiste.1.html0000644000175000017500000006510414451442047016401 0ustar opsops bittwiste.1
BITTWISTE(1)                General Commands Manual               BITTWISTE(1)



NAME
       bittwiste -- pcap capture file editor

SYNOPSIS
       bittwiste [ -I input ] [ -O output ] [ -L layer ] [ -X payload ]
                 [ -C ] [ -M linktype ] [ -D offset ] [ -R range ]
                 [ -S timeframe ] [ -N repeat ] [ -G gaprange ] [ -P seed ]
                 [ -T header ] [ header-specific-options ] [ -h ]

DESCRIPTION
       This document describes the bittwiste program, the pcap(3) capture file
       editor. Bittwiste is designed to work only with  Ethernet  frame,  e.g.
       link  type  DLT_EN10MB  in  pcap(3),  with a maximum frame size of 1514
       bytes which is equivalent to an MTU of 1500 bytes, 14 bytes for  Ether-
       net header.

       Bittwiste can currently edit Ethernet, ARP, IPv4, IPv6, ICMPv4, ICMPv6,
       TCP, and UDP headers. IPv6  packets  with  extension  headers  or  next
       header  field  not matching ICMPv6, TCP, or UDP are not supported; bit-
       twiste will simply write such packets as is to output trace file as  it
       encounters them in the input trace file.

       If  run  with the -X flag, you can append your own payload after any of
       the supported headers; specified using the -L and  -T  flag.  Bittwiste
       will,  if not run with the -C flag, recalculate the checksums for IPv4,
       ICMPv4, ICMPv6, TCP, and UDP headers, except for the last fragment of a
       fragmented IPv4 datagram; bittwiste does not currently support checksum
       correction for the last fragment of a fragmented IPv4 datagram.

       While parsing the packets in an input trace file, bittwiste will  skip,
       i.e.  write to output trace file as is, any truncated packet, for exam-
       ple, an ICMPv4 packet with a captured length of 25 bytes  (we  need  at
       least  28  bytes; 14 bytes for Ethernet header, minimum 20 bytes for IP
       header, and 4 bytes for ICMPv4 header) does not give enough information
       on its ICMPv4 header for bittwiste to read and modify it. In this case,
       you can utilize the -L and -T flag to copy the original  packet  up  to
       its  IP header and append your customized ICMPv4 header and data to the
       packet using the -X flag.  When  specifying  payload  that  covers  the
       ICMPv4,  ICMPv6,  TCP,  or  UDP header and its data, you can use zeros,
       e.g. 0000 for 2 bytes of zeros, for the header checksum which  is  then
       corrected automatically by bittwiste.

       In  order  to simplify the way options are specified, you can only edit
       packets of a specific type supplied to the -T  flag  per  execution  of
       bittwiste  on an input trace file. In addition, the -T flag must appear
       last among the general options which are the -I, -O, -L,  -X,  -C,  -M,
       -D, -R, -S, -N, -G, and -P flag.

OPTIONS
       -I input
              Input  pcap  based trace file. Typically, input should be a file
              path to a pcap based trace file. However, for  convenience,  the
              following  template  names  are also accepted to load trace file
              from one of the built-in templates:

              eth    : Ethernet header
              arp    : ARP header
              ip     : IPv4 header
              ip6    : IPv6 header
              icmp   : ICMPv4 header
              icmp6  : ICMPv6 header
              tcp    : IPv4 TCP header
              ip6tcp : IPv6 TCP header
              udp    : IPv4 UDP header
              ip6udp : IPv6 UDP header

              Example: -I icmp

       -O output
              Output trace file.

       -L layer
              Copy up to the specified layer and discard the  remaining  data.
              Value  for layer must be either 2, 3, or 4 where 2 for Ethernet,
              3 for ARP, IPv4, or IPv6, and 4 for ICMPv4, ICMPv6, TCP, or UDP.

       -X payload
              Append payload in hex digits to the end of each packet.
              Example: -X 0302aad1
              -X flag is ignored if -L and -T flag are not specified.

       -C     Specify this flag to disable checksum correction. Checksum  cor-
              rection is applicable for non-fragmented supported packets only.

       -M linktype
              Replace  the linktype stored in the pcap file header. Typically,
              value for linktype is 1 for Ethernet.
              Example: -M 12 (for raw IP), -M 51 (for PPPoE)

              For the complete list, see:
              https://www.tcpdump.org/linktypes.html

       -D offset
              Delete the specified byte offset from each packet.
              First byte (starting from link layer header) starts from 1.
              -L, -X, -C and -T flag are ignored if -D flag is specified.
              Example: -D 15-40, -D 10, or -D 18-9999

       -R range
              Save only the specified range of packets.
              Example: -R 5-21 or -R 9

       -S timeframe
              Save only the packets within the specified timeframe with up  to
              one-second  resolution  using  DD/MM/YYYY,HH:MM:SS as the format
              for start and end time in timeframe.
              Example: -S 22/10/2006,21:47:35-24/10/2006,13:16:05
              -S flag is evaluated after -R flag.

       -N repeat
              Duplicate packets from the input trace file  repeat  times.  Use
              this flag to create a stream of packets, each with, for example,
              a random tcp sequence number, from a 1-packet trace file.
              Example: -N 100000
              -N flag is evaluated after -R and -S flag.

       -G gaprange
              Apply inter-packet gap between packets in microseconds from 1 to
              (2^31  -  1). Values in gaprange are inclusive and selected ran-
              domly. A single value implies a fixed gap.
              Example: -G 1000-10000 or -G 1000
              -G flag is evaluated after -R, -S, and -N flag.

       -P seed
              Positive integer to seed the random number generator (RNG) used,
              for  example,  to generate random port number. If unset, current
              timestamp will be used as the RNG seed.

              bittwiste uses Mersenne Twister for  high-speed  uniformly  dis-
              tributed random number generation.

       -T header
              Edit  only  the  specified  header. Possible keywords for header
              are, eth, arp, ip, ip6, icmp, icmp6, tcp, or udp. -T  flag  must
              appear last among the general options.

       -h     Print version information and usage.

       header-specific-options
              Each  packet  that  matches  the type supplied to the -T flag is
              modified based on the options described below:

              Options for eth (RFC 894):

              -d dmac or omac,nmac
                     Destination MAC address. If omac and nmac are  specified,
                     any  instances  of  omac  in  the destination MAC address
                     field will be replaced with nmac. You can  also  use  the
                     string 'rand' for a random MAC address.

                     Examples:
                     -d 00:08:55:64:65:6a
                     -d rand
                     -d 00:08:55:64:65:6a,rand

              -s smac or omac,nmac
                     Source  MAC  address. If omac and nmac are specified, any
                     instances of omac in the source MAC address field will be
                     replaced  with  nmac.  You can also use the string 'rand'
                     for a random MAC address.

                     Examples:
                     -s 00:13:20:3e:ab:cf
                     -s rand
                     -s 00:13:20:3e:ab:cf,rand

              -t type
                     EtherType. Possible keywords for type are, ip,  ip6,  and
                     arp only.

              Options for arp (RFC 826):

              -o opcode
                     Operation  code  in integer value between 0 to 65535. For
                     example, you can set opcode to 1 for ARP request,  2  for
                     ARP reply.

              -s smac or omac,nmac
                     Sender  MAC  address. If omac and nmac are specified, any
                     instances of omac in the sender MAC address field will be
                     replaced  with  nmac.  You can also use the string 'rand'
                     for a random MAC address.

                     Examples:
                     -s 00:13:20:3e:ab:cf
                     -s rand
                     -s 00:13:20:3e:ab:cf,rand

              -p sip or oip,nip
                     Sender IP address. Example: -p 192.168.0.1
                     If oip and nip are specified, any instances of oip in the
                     sender IP address field will be replaced with nip.

              -t tmac or omac,nmac
                     Target  MAC  address. If omac and nmac are specified, any
                     instances of omac in the target MAC address field will be
                     replaced  with  nmac.  You can also use the string 'rand'
                     for a random MAC address.

                     Examples:
                     -t 00:08:55:64:65:6a
                     -t rand
                     -t 00:08:55:64:65:6a,rand

              -q tip or oip,nip
                     Target IP address. Example: -q 192.168.0.2
                     If oip and nip are specified, any instances of oip in the
                     target IP address field will be replaced with nip.

              Options for ip (RFC 791):

              -c ds_field
                     6-bit  DS  field  (first  6-bit  of 8-bit type of service
                     field).

                     Some of the service class name mapping to ds_field  value
                     from RFC 4594:

                     0  : Standard (CS0)
                     8  : Low-priority data (CS1)
                     16 : OAM (CS2)
                     24 : Broadcast video (CS3)
                     32 : Real-time interactive (CS4)

                     Example:  -c 16 or -c 0x10 (to classify packet for opera-
                     tion and management of the network)

                     For more information on DS field, see RFC  2474  and  RFC
                     4594.

              -e ecn_field
                     2-bit  ECN  field  (last  2-bit  of 8-bit type of service
                     field).

                     ecn_field can be set to one of the 4 values below:

                     0 : Not-ECT
                     1 : ECT(1)
                     2 : ECT(0)
                     3 : CE

                     Example: -e 3 or -e 0x03 (to indicate congestion  to  the
                     end hosts)

                     For more information on ECN field, see RFC 3168.

              -i id or oi,ni
                     Identification in integer value between 0 to 65535. If oi
                     and ni are specified, any instances of oi in the  identi-
                     fication field will be replaced with ni. You can also use
                     the string 'rand' for a random identification.

                     Example: -i 2000, -i rand, or -i 1000,rand

              -f flags
                     Control flags. Possible characters for flags are:

                     - : remove all flags
                     r : set the reserved flag
                     d : set the don't fragment flag
                     m : set the more fragment flag

                     Example: -f d
                     If any of the flags is specified, all original flags  are
                     removed automatically.

              -o offset
                     Fragment offset in integer value between 0 to 7770. Value
                     for offset represents the number of 64-bit segments  con-
                     tained  in  earlier  fragments which must not exceed 7770
                     (62160 bytes).

              -t ttl or ot,nt
                     Time to live in integer value between 0 to 255 (millisec-
                     onds). If ot and nt are specified, any instances of ot in
                     the time to live field will be replaced with nt. You  can
                     also use the string 'rand' for a random time to live.

                     Example: -t 64, -i rand, or -i 64,rand

              -p proto or op,np
                     Protocol  number in integer value between 0 to 255. If op
                     and np are specified, any instances of op in the protocol
                     number  field  will be replaced with np. You can also use
                     the string 'rand' for a random protocol number. Some com-
                     mon protocol numbers are:

                     1  : Internet Control Message (ICMP)
                     6  : Transmission Control (TCP)
                     17 : User Datagram (UDP)

                     For the complete list, see:
                     https://www.iana.org/assignments/protocol-numbers

              -s sip or oip,nip
                     Source  IP address. If oip and nip are specified, any in-
                     stances of oip in the source IP address field will be re-
                     placed  with  nip.  If CIDR notation (RFC 4632) is speci-
                     fied, e.g. 192.168.0.0/16, an IP address will be selected
                     at random from the range.

                     Examples:
                     -s 192.168.0.1
                     -s 127.0.0.1,192.168.0.0/16
                     -s 0.0.0.0/0 (random IPv4 throughout the entire range)

              -d dip or oip,nip
                     Destination IP address. If oip and nip are specified, any
                     instances of oip in the destination IP address field will
                     be  replaced  with  nip.  If  CIDR notation (RFC 4632) is
                     specified, e.g. 192.168.0.0/16, an IP address will be se-
                     lected at random from the range.

                     Examples:
                     -d 192.168.0.2
                     -d 127.0.0.2,192.168.0.0/16
                     -d 0.0.0.0/0 (random IPv4 throughout the entire range)

              Options for ip6 (RFC 8200):

              -c ds_field
                     6-bit  DS  field  (first  6-bit  of  8-bit  traffic class
                     field).

                     Some of the service class name mapping to ds_field  value
                     from RFC 4594:

                     0  : Standard (CS0)
                     8  : Low-priority data (CS1)
                     16 : OAM (CS2)
                     24 : Broadcast video (CS3)
                     32 : Real-time interactive (CS4)

                     Example:  -c 16 or -c 0x10 (to classify packet for opera-
                     tion and management of the network)

                     For more information on DS field, see RFC  2474  and  RFC
                     4594.

              -e ecn_field
                     2-bit  ECN  field  (last  2-bit  of  8-bit  traffic class
                     field).

                     ecn_field can be set to one of the 4 values below:

                     0 : Not-ECT
                     1 : ECT(1)
                     2 : ECT(0)
                     3 : CE

                     Example: -e 3 or -e 0x03 (to indicate congestion  to  the
                     end hosts)

                     For more information on ECN field, see RFC 3168.

              -f flow_label
                     Flow label in integer value between 0 to 1048575 or hexa-
                     decimal value between 0x00000 to 0xfffff (20-bit).
                     Example: -f 0

                     Value of 0 is to indicate that the packet does not belong
                     to any flow. For more information, see RFC 6437.

              -n next_header or on,nn
                     Next  header number in integer value between 0 to 255. If
                     on and nn are specified, any instances of on in the  next
                     header  field  will be replaced with nn. You can also use
                     the string 'rand' for a random next header number.  Exam-
                     ple of next header numbers:

                     0  : IPv6 Hop-by-Hop Option (HOPOPT)
                     6  : Transmission Control (TCP)
                     17 : User Datagram (UDP)
                     50 : Encap Security Payload (ESP)
                     51 : Authentication Header (AH)
                     58 : ICMP for IPv6 (IPv6-ICMP)

                     For the complete list, see:
                     https://www.iana.org/assignments/protocol-numbers

              -h hop_limit or oh,nh
                     Hop limit in integer value between 0 to 255. If oh and nh
                     are specified, any instances of oh in the hop limit field
                     will  be  replaced  with  nh. You can also use the string
                     'rand' for a random hop limit.  Destination  host  should
                     not discard a packet with hop limit equal to 0.

              -s sip or oip,nip
                     Source  IP address. If oip and nip are specified, any in-
                     stances of oip in the source IP address field will be re-
                     placed  with  nip.  If CIDR notation (RFC 4291) is speci-
                     fied, e.g. 2001:db8::/64, an IP address will be  selected
                     at random from the range.

                     Examples:
                     -s fd00::1
                     -s ::1,2001:db8::/64
                     -s ::/0 (random IPv6 throughout the entire range)

              -d dip or oip,nip
                     Destination IP address. If oip and nip are specified, any
                     instances of oip in the destination IP address field will
                     be  replaced  with  nip.  If  CIDR notation (RFC 4291) is
                     specified, e.g. 2001:db8::/64, an IP address will be  se-
                     lected at random from the range.

                     Examples:
                     -d fd00::2
                     -d ::2,2001:db8::/64
                     -d ::/0 (random IPv6 throughout the entire range)

              Options for icmp (RFC 792):

              -t type
                     Type  of  message in integer value between 0 to 255. Some
                     common messages are:

                     0  : Echo reply
                     3  : Destination unreachable
                     8  : Echo
                     11 : Time exceeded

                     For the complete list, see:
                     https://www.iana.org/assignments/icmp-parameters

              -c code
                     Error code for this ICMPv4 message in integer  value  be-
                     tween  0 to 255. For example, code for time exceeded mes-
                     sage may have one of the following values:

                     0 : transit TTL exceeded
                     1 : reassembly TTL exceeded

                     For the complete list, see:
                     https://www.iana.org/assignments/icmp-parameters

              Options for icmp6 (RFC 4443):

              -t type
                     Type of message in integer value between 0 to  255.  Some
                     common messages are:

                     3   : Time Exceeded
                     128 : Echo Request
                     129 : Echo Reply

                     For the complete list, see:
                     https://www.iana.org/assignments/icmpv6-parameters

              -c code
                     Code  for  this ICMPv6 message in integer value between 0
                     to 255. For example, code for Time Exceeded  message  may
                     have one of the following values:

                     0 : hop limit exceeded in transit
                     1 : fragment reassembly time exceeded

                     For the complete list, see:
                     https://www.iana.org/assignments/icmpv6-parameters

              Options for tcp (RFC 9293):

              -s sport or op,np
                     Source  port  number in integer value between 0 to 65535.
                     If op and np are specified, any instances of  op  in  the
                     source  port field will be replaced with np. You can also
                     use the string 'rand' for a random port number.

                     Example: -s 2000, -s rand, or -s 1000,rand

              -d dport or op,np
                     Destination port number in integer  value  between  0  to
                     65535. If op and np are specified, any instances of op in
                     the destination port field will be replaced with np.  You
                     can also use the string 'rand' for a random port number.

                     Example: -d 2000, -d rand, or -d 1000,rand

              -q seq or os,ns
                     Sequence number in integer value between 0 to 4294967295.
                     If SYN control bit is set, e.g. character s  is  supplied
                     to  the -f flag, seq represents the initial sequence num-
                     ber (ISN) and the first data byte is ISN + 1. If  os  and
                     ns  are  specified,  any  instances of os in the sequence
                     number field will be replaced with ns. You can  also  use
                     the string 'rand' for a random sequence number.

                     Example: -q 100000, -q rand, or -q 100000,rand

              -a ack or oa,na
                     Acknowledgment  number  in  integer  value  between  0 to
                     4294967295. If ACK control bit is set, e.g.  character  a
                     is  supplied  to the -f flag, ack represents the value of
                     the next sequence number that the receiver  is  expecting
                     to  receive. If oa and na are specified, any instances of
                     oa in the acknowledgment number field  will  be  replaced
                     with  na. You can also use the string 'rand' for a random
                     acknowledgment number.

                     Example: -a 100000, -a rand, or -a 100000,rand

              -f flags
                     Control flags. Possible characters for flags are:

                     - : remove all flags
                     c : congestion window reduced
                     e : explicit congestion notification echo
                     u : urgent pointer field is significant
                     a : acknowledgment field is significant
                     p : push function
                     r : resets the connection
                     s : synchronizes the sequence numbers
                     f : no more data from sender

                     Example: -f s
                     If any of the flags is specified, all original flags  are
                     removed automatically.

              -w win
                     Window  size  in integer value between 0 to 65535. If ACK
                     control bit is set, e.g. character a is supplied  to  the
                     -f  flag, win represents the number of data bytes, begin-
                     ning with the one indicated in the acknowledgment  number
                     field that the receiver is willing to accept.

              -u urg
                     Urgent  pointer  in  integer value between 0 to 65535. If
                     URG control bit is set, e.g. character u is  supplied  to
                     the  -f flag, urg represents a pointer that points to the
                     first data byte following the urgent data.

              Options for udp (RFC 768):

              -s sport or op,np
                     Source port number in integer value between 0  to  65535.
                     If  op  and  np are specified, any instances of op in the
                     source port field will be replaced with np. You can  also
                     use the string 'rand' for a random port number.

                     Example: -s 2000, -s rand, or -s 1000,rand

              -d dport or op,np
                     Destination  port  number  in  integer value between 0 to
                     65535. If op and np are specified, any instances of op in
                     the  destination port field will be replaced with np. You
                     can also use the string 'rand' for a random port number.

                     Example: -d 2000, -d rand, or -d 1000,rand

SEE ALSO
       bittwist(1), pcap(3), tcpdump(1)

BUGS
       File your bug report and send to:

              Addy Yeow <ayeowch@gmail.com>

       Make sure you are using the latest  stable  version  before  submitting
       your bug report.

       When  running  bittwiste  with  both  the -N and -G flags, large inter-
       packet gap may  result  in  the  packet  timestamp  beyond  Unix  epoch
       2147483647  (2038-01-19  03:14:07  UTC) to overflow. This is due to the
       use of signed 32-bit integer to store timestamp in pcap(3) header. Sim-
       ply  changing  the data type, e.g. using unsigned 64-bit integer, would
       break the compatibility of the output trace file with existing systems.

       The workaround built into bittwiste is  to  use  Unix  epoch  946684800
       (2020-01-01  00:00:00  UTC) as the starting reference timestamp when -G
       flag is specified. This translates to a maximum timespan of 38 years or
       559165  packets  in the output trace file when using the maximum inter-
       packet gap, i.e. -G 2147483647.

COPYRIGHT
       Copyright (C) 2006 - 2023 Addy Yeow <ayeowch@gmail.com>

       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  MER-
       CHANTABILITY  or FITNESS FOR A PARTICULAR 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.

AUTHORS
       Original author and current maintainer:

              Addy Yeow

       The current version is available from https://bittwist.sourceforge.io



                                  6 July 2023                     BITTWISTE(1)
bittwist-linux-3.8/doc/bittwist.1.html0000644000175000017500000001334014451442047016227 0ustar opsops bittwist.1
BITTWIST(1)                 General Commands Manual                BITTWIST(1)



NAME
       bittwist -- pcap based ethernet packet generator

SYNOPSIS
       bittwist [ -dvh ] [ -i interface ] [ -s length ] [ -l loop ]
                [ -c count ] [ -p pps ] [ -t gap ] [ -r rate ]
                pcap-file(s)

DESCRIPTION
       This  document describes the bittwist program, the pcap(3) based Ether-
       net packet generator. Bittwist is designed to work  under  Ethernet  II
       (IEEE 802.3) network with an MTU of up to 1500 bytes on 10Mbps (10Base-
       T), 100Mbps (100BASE-TX), or 1000Mbps (1000BASE-T) link speed.  Packets
       are  generated  from saved tcpdump(1) capture file referred to as trace
       file in this document. Some familiarity with tcpdump(1) and  its  basic
       options  are  assumed  in  this document. Since bittwist uses functions
       provided by  pcap(3)  library,  e.g.  pcap_open_live()  and  pcap_send-
       packet(),  to  write  packets onto the network, it may require that you
       have special privileges, e.g. read access to /dev/bpf* on BSD  or  root
       access  on  Linux,  to generate packets or to enumerate network devices
       with, for example, the -d flag.

       Bittwist will, if not run with the -s flag, inject each  packet  up  to
       its actual length (on-wire) instead of its captured length. If the cap-
       tured length is less than the actual  length,  bittwist  will  pad  the
       packet  with zeros up to its actual length prior to injection. Bittwist
       will, if not run with the -p, -t, or -r flag,  inject  packets  from  a
       trace  file  based  on the captured intervals, in particular, the time-
       stamp difference between two adjacent packets,  except  for  the  first
       packet  in a trace file, which is always injected immediately. Bittwist
       is designed to not to interfere with the packet data; it merely read  a
       packet and inject it as is onto the network. If modification is desired
       prior to injection of a packet, you can use the  bittwiste(1)  program,
       which does just that.

OPTIONS
       -d     Print a list of network interfaces available.

       -v     Print timestamp for each packet.

       -vv    Print timestamp and hex data for each packet.

       -i interface
              Send pcap-file(s) out onto the network through interface.

       -s length
              Packet length to send (in bytes). Set length to:

               0 : Send the actual packet length. This is the default.
              -1 : Send the captured length.

              or any other value from 14 to 1514.

       -l loop
              Send  pcap-file(s) out onto the network for loop times. Set loop
              to 0 to send pcap-file(s) until stopped. To stop, type  Control-
              C.

       -c count
              Send  up  to  count packets. Default is to send all packets from
              pcap-file(s).

       -p pps Send pps packets per second. Value for pps must be between 1  to
              1000000.

       -t gap Set  inter-packet gap in seconds, ignoring the captured interval
              between packets in pcap-file(s). Value for gap must be between 1
              to 86400.

       -r rate
              Limit the sending to rate Mbps. Value for rate must be between 0
              to 10000. This flag  is  intended  to  set  the  desired  packet
              throughput.  If  you  want  to send packets at line rate of 1000
              Mbps, try -r 1000. If you want  to  send  packets  without  rate
              limit,  try  -r 0. This flag is typically used with -l 0 to send
              pcap-file(s) until stopped.

       -h     Print version information and usage.

SEE ALSO
       bittwiste(1), pcap(3), tcpdump(1)

BUGS
       File your bug report and send to:

              Addy Yeow <ayeowch@gmail.com>

       Make sure you are using the latest  stable  version  before  submitting
       your bug report.

       If you run bittwist with -r flag, pcap_sendpacket() may return an error
       with the following error string:

              send: No buffer space available

       We recommend that you adjust the value for -r flag to limit the  packet
       throughput  to  a lower level that is supported by your interface, e.g.
       start from -r 10 to inject packets at a maximum rate of 10Mbps and  ad-
       just the value accordingly.

COPYRIGHT
       Copyright (C) 2006 - 2023 Addy Yeow <ayeowch@gmail.com>

       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 MER-
       CHANTABILITY or FITNESS FOR A PARTICULAR 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.

AUTHORS
       Original author and current maintainer:

              Addy Yeow

       The current version is available from https://bittwist.sourceforge.io



                                  6 July 2023                      BITTWIST(1)
bittwist-linux-3.8/doc/bittwist.10000644000175000017500000001342514451442047015270 0ustar opsops.\" .\" bittwist.1 - manpage for the bittwist program .\" Copyright (C) 2006 - 2023 Addy Yeow .\" .\" 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. .\" .TH BITTWIST 1 "6 July 2023" .SH NAME .B bittwist \-- pcap based ethernet packet generator .SH SYNOPSIS .B bittwist [ .B \-dvh ] [ .B \-i .I interface ] [ .B \-s .I length ] [ .B \-l .I loop ] .ti +9 [ .B \-c .I count ] [ .B \-p .I pps ] [ .B \-t .I gap ] [ .B \-r .I rate ] .ti +9 .I pcap-file(s) .SH DESCRIPTION This document describes the \fIbittwist\fP program, the \fIpcap\fP(3) based Ethernet packet generator. \fIBittwist\fP is designed to work under Ethernet II (IEEE 802.3) network with an MTU of up to 1500 bytes on 10Mbps (10Base-T), 100Mbps (100BASE-TX), or 1000Mbps (1000BASE-T) link speed. Packets are generated from saved \fItcpdump\fP(1) capture file referred to as trace file in this document. Some familiarity with \fItcpdump\fP(1) and its basic options are assumed in this document. Since \fIbittwist\fP uses functions provided by \fIpcap\fP(3) library, e.g. \fBpcap_open_live()\fP and \fBpcap_sendpacket()\fP, to write packets onto the network, it may require that you have special privileges, e.g. read access to \fI/dev/bpf*\fP on BSD or root access on Linux, to generate packets or to enumerate network devices with, for example, the \fB-d\fP flag. .PP \fIBittwist\fP will, if not run with the \fB-s\fP flag, inject each packet up to its actual length (on-wire) instead of its captured length. If the captured length is less than the actual length, \fIbittwist\fP will pad the packet with zeros up to its actual length prior to injection. \fIBittwist\fP will, if not run with the \fB-p\fP, \fB-t\fP, or \fB-r\fP flag, inject packets from a trace file based on the captured intervals, in particular, the timestamp difference between two adjacent packets, except for the first packet in a trace file, which is always injected immediately. \fIBittwist\fP is designed to not to interfere with the packet data; it merely read a packet and inject it as is onto the network. If modification is desired prior to injection of a packet, you can use the \fIbittwiste\fP(1) program, which does just that. .SH OPTIONS .TP .B \-d Print a list of network interfaces available. .TP .B \-v Print timestamp for each packet. .TP .B \-vv Print timestamp and hex data for each packet. .TP .B \-i \fIinterface\fP Send \fIpcap-file(s)\fP out onto the network through \fIinterface\fP. .TP .B \-s \fIlength\fP Packet length to send (in bytes). Set \fIlength\fP to: .IP \fB 0\fP : Send the actual packet length. This is the default. .br \fB-1\fP : Send the captured length. .IP or any other value from 14 to 1514. .TP .B \-l \fIloop\fP Send \fIpcap-file(s)\fP out onto the network for \fIloop\fP times. Set \fIloop\fP to 0 to send \fIpcap-file(s)\fP until stopped. To stop, type Control-C. .TP .B \-c \fIcount\fP Send up to \fIcount\fP packets. Default is to send all packets from \fIpcap-file(s)\fP. .TP .B \-p \fIpps\fP Send \fIpps\fP packets per second. Value for \fIpps\fP must be between 1 to 1000000. .TP .B \-t \fIgap\fP Set inter-packet \fIgap\fP in seconds, ignoring the captured interval between packets in \fIpcap-file(s)\fP. Value for \fIgap\fP must be between 1 to 86400. .TP .B \-r \fIrate\fP Limit the sending to \fIrate\fP Mbps. Value for \fIrate\fP must be between 0 to 10000. This flag is intended to set the desired packet throughput. If you want to send packets at line rate of 1000 Mbps, try \fB-r\fP 1000. If you want to send packets without rate limit, try \fB-r\fP 0. This flag is typically used with \fB-l\fP 0 to send \fIpcap-file(s)\fP until stopped. .TP .B \-h Print version information and usage. .SH SEE ALSO bittwiste(1), pcap(3), tcpdump(1) .SH BUGS File your bug report and send to: .IP Addy Yeow .PP Make sure you are using the latest stable version before submitting your bug report. .PP If you run \fIbittwist\fP with \fB-r\fP flag, \fBpcap_sendpacket\fP() may return an error with the following error string: .IP send: No buffer space available .PP We recommend that you adjust the value for \fB-r\fP flag to limit the packet throughput to a lower level that is supported by your interface, e.g. start from \fB-r\fP 10 to inject packets at a maximum rate of 10Mbps and adjust the value accordingly. .SH COPYRIGHT Copyright (C) 2006 - 2023 Addy Yeow .PP 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. .PP 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. .PP 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. .SH AUTHORS Original author and current maintainer: .IP Addy Yeow .PP The current version is available from https://bittwist.sourceforge.io bittwist-linux-3.8/doc/bittwiste.10000644000175000017500000005545114451442047015442 0ustar opsops.\" .\" bittwiste.1 - manpage for the bittwiste program .\" Copyright (C) 2006 - 2023 Addy Yeow .\" .\" 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. .\" .TH BITTWISTE 1 "6 July 2023" .SH NAME .B bittwiste \-- pcap capture file editor .SH SYNOPSIS .B bittwiste [ .B \-I .I input ] [ .B \-O .I output ] [ .B \-L .I layer ] [ .B \-X .I payload ] .ti +10 [ .B \-C ] [ .B \-M .I linktype ] [ .B \-D .I offset ] [ .B \-R .I range ] .ti +10 [ .B \-S .I timeframe ] [ .B \-N .I repeat ] [ .B \-G .I gaprange ] [ .B \-P .I seed ] .ti +10 [ .B \-T .I header ] [ .I header-specific-options ] [ .B \-h ] .SH DESCRIPTION This document describes the \fIbittwiste\fP program, the \fIpcap\fP(3) capture file editor. \fIBittwiste\fP is designed to work only with Ethernet frame, e.g. link type DLT_EN10MB in \fIpcap\fP(3), with a maximum frame size of 1514 bytes which is equivalent to an MTU of 1500 bytes, 14 bytes for Ethernet header. .PP \fIBittwiste\fP can currently edit Ethernet, ARP, IPv4, IPv6, ICMPv4, ICMPv6, TCP, and UDP headers. IPv6 packets with extension headers or next header field not matching ICMPv6, TCP, or UDP are not supported; \fIbittwiste\fP will simply write such packets as is to output trace file as it encounters them in the input trace file. .PP If run with the \fB-X\fP flag, you can append your own payload after any of the supported headers; specified using the \fB-L\fP and \fB-T\fP flag. \fIBittwiste\fP will, if not run with the \fB-C\fP flag, recalculate the checksums for IPv4, ICMPv4, ICMPv6, TCP, and UDP headers, except for the last fragment of a fragmented IPv4 datagram; \fIbittwiste\fP does not currently support checksum correction for the last fragment of a fragmented IPv4 datagram. .PP While parsing the packets in an input trace file, \fIbittwiste\fP will skip, i.e. write to output trace file as is, any truncated packet, for example, an ICMPv4 packet with a captured length of 25 bytes (we need at least 28 bytes; 14 bytes for Ethernet header, minimum 20 bytes for IP header, and 4 bytes for ICMPv4 header) does not give enough information on its ICMPv4 header for \fIbittwiste\fP to read and modify it. In this case, you can utilize the \fB-L\fP and \fB-T\fP flag to copy the original packet up to its IP header and append your customized ICMPv4 header and data to the packet using the \fB-X\fP flag. When specifying payload that covers the ICMPv4, ICMPv6, TCP, or UDP header and its data, you can use zeros, e.g. 0000 for 2 bytes of zeros, for the header checksum which is then corrected automatically by \fIbittwiste\fP. .PP In order to simplify the way options are specified, you can only edit packets of a specific type supplied to the \fB-T\fP flag per execution of \fIbittwiste\fP on an input trace file. In addition, the \fB-T\fP flag must appear last among the general options which are the \fB-I\fP, \fB-O\fP, \fB-L\fP, \fB-X\fP, \fB-C\fP, \fB-M\fP, \fB-D\fP, \fB-R\fP, \fB-S\fP, \fB-N\fP, \fB-G\fP, and \fB-P\fP flag. .SH OPTIONS .TP .B \-I \fIinput\fP Input pcap based trace file. Typically, \fIinput\fP should be a file path to a pcap based trace file. However, for convenience, the following template names are also accepted to load trace file from one of the built-in templates: .IP \fBeth\fP : Ethernet header .br \fBarp\fP : ARP header .br \fBip\fP : IPv4 header .br \fBip6\fP : IPv6 header .br \fBicmp\fP : ICMPv4 header .br \fBicmp6\fP : ICMPv6 header .br \fBtcp\fP : IPv4 TCP header .br \fBip6tcp\fP : IPv6 TCP header .br \fBudp\fP : IPv4 UDP header .br \fBip6udp\fP : IPv6 UDP header .IP Example: \fB-I\fP icmp .TP .B \-O \fIoutput\fP Output trace file. .TP .B \-L \fIlayer\fP Copy up to the specified \fIlayer\fP and discard the remaining data. Value for \fIlayer\fP must be either 2, 3, or 4 where 2 for Ethernet, 3 for ARP, IPv4, or IPv6, and 4 for ICMPv4, ICMPv6, TCP, or UDP. .TP .B \-X \fIpayload\fP Append \fIpayload\fP in hex digits to the end of each packet. .br Example: \fB-X\fP 0302aad1 .br \fB-X\fP flag is ignored if \fB-L\fP and \fB-T\fP flag are not specified. .TP .B \-C Specify this flag to disable checksum correction. Checksum correction is applicable for non-fragmented supported packets only. .TP .B \-M \fIlinktype\fP Replace the \fIlinktype\fP stored in the pcap file header. Typically, value for \fIlinktype\fP is 1 for Ethernet. .br Example: \fB-M\fP 12 (for raw IP), \fB-M\fP 51 (for PPPoE) .IP For the complete list, see: .br \fIhttps://www.tcpdump.org/linktypes.html\fP .TP .B \-D \fIoffset\fP Delete the specified byte \fIoffset\fP from each packet. .br First byte (starting from link layer header) starts from 1. .br \fB-L\fP, \fB-X\fP, \fB-C\fP and \fB-T\fP flag are ignored if \fB-D\fP flag is specified. .br Example: \fB-D\fP 15-40, \fB-D\fP 10, or \fB-D\fP 18-9999 .TP .B \-R \fIrange\fP Save only the specified \fIrange\fP of packets. .br Example: \fB-R\fP 5-21 or \fB-R\fP 9 .TP .B \-S \fItimeframe\fP Save only the packets within the specified \fItimeframe\fP with up to one-second resolution using DD/MM/YYYY,HH:MM:SS as the format for start and end time in \fItimeframe\fP. .br Example: \fB-S\fP 22/10/2006,21:47:35-24/10/2006,13:16:05 .br \fB-S\fP flag is evaluated after \fB-R\fP flag. .TP .B \-N \fIrepeat\fP Duplicate packets from the \fIinput\fP trace file \fIrepeat\fP times. Use this flag to create a stream of packets, each with, for example, a random tcp sequence number, from a 1-packet trace file. .br Example: \fB-N\fP 100000 .br \fB-N\fP flag is evaluated after \fB-R\fP and \fB-S\fP flag. .TP .B \-G \fIgaprange\fP Apply inter-packet gap between packets in microseconds from 1 to (2^31 - 1). Values in \fIgaprange\fP are inclusive and selected randomly. A single value implies a fixed gap. .br Example: \fB-G\fP 1000-10000 or \fB-G\fP 1000 .br \fB-G\fP flag is evaluated after \fB-R\fP, \fB-S\fP, and \fB-N\fP flag. .TP .B \-P \fIseed\fP Positive integer to seed the random number generator (RNG) used, for example, to generate random port number. If unset, current timestamp will be used as the RNG seed. .IP bittwiste uses Mersenne Twister for high-speed uniformly distributed random number generation. .TP .B \-T \fIheader\fP Edit only the specified \fIheader\fP. Possible keywords for \fIheader\fP are, \fBeth\fP, \fBarp\fP, \fBip\fP, \fBip6\fP, \fBicmp\fP, \fBicmp6\fP, \fBtcp\fP, or \fBudp\fP. \fB-T\fP flag must appear last among the general options. .TP .B \-h Print version information and usage. .TP \fIheader-specific-options\fP Each packet that matches the type supplied to the \fB-T\fP flag is modified based on the options described below: .IP Options for \fBeth\fP (RFC 894): .RS .TP .B \-d \fIdmac\fP or \fIomac\fP,\fInmac\fP .br Destination MAC address. If \fIomac\fP and \fInmac\fP are specified, any instances of \fIomac\fP in the destination MAC address field will be replaced with \fInmac\fP. You can also use the string 'rand' for a random MAC address. .IP Examples: .br \fB-d\fP 00:08:55:64:65:6a .br \fB-d\fP rand .br \fB-d\fP 00:08:55:64:65:6a,rand .TP .B \-s \fIsmac\fP or \fIomac\fP,\fInmac\fP .br Source MAC address. If \fIomac\fP and \fInmac\fP are specified, any instances of \fIomac\fP in the source MAC address field will be replaced with \fInmac\fP. You can also use the string 'rand' for a random MAC address. .IP Examples: .br \fB-s\fP 00:13:20:3e:ab:cf .br \fB-s\fP rand .br \fB-s\fP 00:13:20:3e:ab:cf,rand .TP .B \-t \fItype\fP EtherType. Possible keywords for type are, \fBip\fP, \fBip6\fP, and \fBarp\fP only. .TP Options for \fBarp\fP (RFC 826): .TP .B \-o \fIopcode\fP Operation code in integer value between 0 to 65535. For example, you can set \fIopcode\fP to 1 for ARP request, 2 for ARP reply. .TP .B \-s \fIsmac\fP or \fIomac\fP,\fInmac\fP .br Sender MAC address. If \fIomac\fP and \fInmac\fP are specified, any instances of \fIomac\fP in the sender MAC address field will be replaced with \fInmac\fP. You can also use the string 'rand' for a random MAC address. .IP Examples: .br \fB-s\fP 00:13:20:3e:ab:cf .br \fB-s\fP rand .br \fB-s\fP 00:13:20:3e:ab:cf,rand .TP .B \-p \fIsip\fP or \fIoip\fP,\fInip\fP .br Sender IP address. Example: \fB-p\fP 192.168.0.1 .br If \fIoip\fP and \fInip\fP are specified, any instances of \fIoip\fP in the sender IP address field will be replaced with \fInip\fP. .TP .B \-t \fItmac\fP or \fIomac\fP,\fInmac\fP .br Target MAC address. If \fIomac\fP and \fInmac\fP are specified, any instances of \fIomac\fP in the target MAC address field will be replaced with \fInmac\fP. You can also use the string 'rand' for a random MAC address. .IP Examples: .br \fB-t\fP 00:08:55:64:65:6a .br \fB-t\fP rand .br \fB-t\fP 00:08:55:64:65:6a,rand .TP .B \-q \fItip\fP or \fIoip\fP,\fInip\fP .br Target IP address. Example: \fB-q\fP 192.168.0.2 .br If \fIoip\fP and \fInip\fP are specified, any instances of \fIoip\fP in the target IP address field will be replaced with \fInip\fP. .TP Options for \fBip\fP (RFC 791): .TP .B \-c \fIds_field\fP .br 6-bit DS field (first 6-bit of 8-bit type of service field). .IP Some of the service class name mapping to \fIds_field\fP value from RFC 4594: .IP \fB0\fP : Standard (CS0) .br \fB8\fP : Low-priority data (CS1) .br \fB16\fP : OAM (CS2) .br \fB24\fP : Broadcast video (CS3) .br \fB32\fP : Real-time interactive (CS4) .IP Example: \fB-c\fP 16 or \fB-c\fP 0x10 (to classify packet for operation and management of the network) .IP For more information on DS field, see RFC 2474 and RFC 4594. .TP .B \-e \fIecn_field\fP .br 2-bit ECN field (last 2-bit of 8-bit type of service field). .IP \fIecn_field\fP can be set to one of the 4 values below: .IP \fB0\fP : Not-ECT .br \fB1\fP : ECT(1) .br \fB2\fP : ECT(0) .br \fB3\fP : CE .IP Example: \fB-e\fP 3 or \fB-e\fP 0x03 (to indicate congestion to the end hosts) .IP For more information on ECN field, see RFC 3168. .TP .B \-i \fIid\fP or \fIoi\fP,\fIni\fP Identification in integer value between 0 to 65535. If \fIoi\fP and \fIni\fP are specified, any instances of \fIoi\fP in the identification field will be replaced with \fIni\fP. You can also use the string 'rand' for a random identification. .IP Example: \fB-i\fP 2000, \fB-i\fP rand, or \fB-i\fP 1000,rand .TP .B \-f \fIflags\fP Control flags. Possible characters for \fIflags\fP are: .IP \fB-\fP : remove all flags .br \fBr\fP : set the reserved flag .br \fBd\fP : set the don't fragment flag .br \fBm\fP : set the more fragment flag .IP Example: \fB-f\fP d .br If any of the flags is specified, all original flags are removed automatically. .TP .B \-o \fIoffset\fP Fragment offset in integer value between 0 to 7770. Value for \fIoffset\fP represents the number of 64-bit segments contained in earlier fragments which must not exceed 7770 (62160 bytes). .TP .B \-t \fIttl\fP or \fIot\fP,\fInt\fP Time to live in integer value between 0 to 255 (milliseconds). If \fIot\fP and \fInt\fP are specified, any instances of \fIot\fP in the time to live field will be replaced with \fInt\fP. You can also use the string 'rand' for a random time to live. .IP Example: \fB-t\fP 64, \fB-i\fP rand, or \fB-i\fP 64,rand .TP .B \-p \fIproto\fP or \fIop\fP,\fInp\fP Protocol number in integer value between 0 to 255. If \fIop\fP and \fInp\fP are specified, any instances of \fIop\fP in the protocol number field will be replaced with \fInp\fP. You can also use the string 'rand' for a random protocol number. Some common protocol numbers are: .IP \fB1\fP : Internet Control Message (ICMP) .br \fB6\fP : Transmission Control (TCP) .br \fB17\fP : User Datagram (UDP) .IP For the complete list, see: .br \fIhttps://www.iana.org/assignments/protocol-numbers\fP .TP .B \-s \fIsip\fP or \fIoip\fP,\fInip\fP .br Source IP address. If \fIoip\fP and \fInip\fP are specified, any instances of \fIoip\fP in the source IP address field will be replaced with \fInip\fP. If CIDR notation (RFC 4632) is specified, e.g. 192.168.0.0/16, an IP address will be selected at random from the range. .IP Examples: .br \fB-s\fP 192.168.0.1 .br \fB-s\fP 127.0.0.1,192.168.0.0/16 .br \fB-s\fP 0.0.0.0/0 (random IPv4 throughout the entire range) .TP .B \-d \fIdip\fP or \fIoip\fP,\fInip\fP .br Destination IP address. If \fIoip\fP and \fInip\fP are specified, any instances of \fIoip\fP in the destination IP address field will be replaced with \fInip\fP. If CIDR notation (RFC 4632) is specified, e.g. 192.168.0.0/16, an IP address will be selected at random from the range. .IP Examples: .br \fB-d\fP 192.168.0.2 .br \fB-d\fP 127.0.0.2,192.168.0.0/16 .br \fB-d\fP 0.0.0.0/0 (random IPv4 throughout the entire range) .TP Options for \fBip6\fP (RFC 8200): .TP .B \-c \fIds_field\fP .br 6-bit DS field (first 6-bit of 8-bit traffic class field). .IP Some of the service class name mapping to \fIds_field\fP value from RFC 4594: .IP \fB0\fP : Standard (CS0) .br \fB8\fP : Low-priority data (CS1) .br \fB16\fP : OAM (CS2) .br \fB24\fP : Broadcast video (CS3) .br \fB32\fP : Real-time interactive (CS4) .IP Example: \fB-c\fP 16 or \fB-c\fP 0x10 (to classify packet for operation and management of the network) .IP For more information on DS field, see RFC 2474 and RFC 4594. .TP .B \-e \fIecn_field\fP .br 2-bit ECN field (last 2-bit of 8-bit traffic class field). .IP \fIecn_field\fP can be set to one of the 4 values below: .IP \fB0\fP : Not-ECT .br \fB1\fP : ECT(1) .br \fB2\fP : ECT(0) .br \fB3\fP : CE .IP Example: \fB-e\fP 3 or \fB-e\fP 0x03 (to indicate congestion to the end hosts) .IP For more information on ECN field, see RFC 3168. .TP .B \-f \fIflow_label\fP .br Flow label in integer value between 0 to 1048575 or hexadecimal value between 0x00000 to 0xfffff (20-bit). .br Example: \fB-f\fP 0 .IP Value of 0 is to indicate that the packet does not belong to any flow. For more information, see RFC 6437. .TP .B \-n \fInext_header\fP or \fIon\fP,\fInn\fP .br Next header number in integer value between 0 to 255. If \fIon\fP and \fInn\fP are specified, any instances of \fIon\fP in the next header field will be replaced with \fInn\fP. You can also use the string 'rand' for a random next header number. Example of next header numbers: .IP \fB0\fP : IPv6 Hop-by-Hop Option (HOPOPT) .br \fB6\fP : Transmission Control (TCP) .br \fB17\fP : User Datagram (UDP) .br \fB50\fP : Encap Security Payload (ESP) .br \fB51\fP : Authentication Header (AH) .br \fB58\fP : ICMP for IPv6 (IPv6-ICMP) .IP For the complete list, see: .br \fIhttps://www.iana.org/assignments/protocol-numbers\fP .TP .B \-h \fIhop_limit\fP or \fIoh\fP,\fInh\fP .br Hop limit in integer value between 0 to 255. If \fIoh\fP and \fInh\fP are specified, any instances of \fIoh\fP in the hop limit field will be replaced with \fInh\fP. You can also use the string 'rand' for a random hop limit. Destination host should not discard a packet with hop limit equal to 0. .TP .B \-s \fIsip\fP or \fIoip\fP,\fInip\fP .br Source IP address. If \fIoip\fP and \fInip\fP are specified, any instances of \fIoip\fP in the source IP address field will be replaced with \fInip\fP. If CIDR notation (RFC 4291) is specified, e.g. 2001:db8::/64, an IP address will be selected at random from the range. .IP Examples: .br \fB-s\fP fd00::1 .br \fB-s\fP ::1,2001:db8::/64 .br \fB-s\fP ::/0 (random IPv6 throughout the entire range) .TP .B \-d \fIdip\fP or \fIoip\fP,\fInip\fP .br Destination IP address. If \fIoip\fP and \fInip\fP are specified, any instances of \fIoip\fP in the destination IP address field will be replaced with \fInip\fP. If CIDR notation (RFC 4291) is specified, e.g. 2001:db8::/64, an IP address will be selected at random from the range. .IP Examples: .br \fB-d\fP fd00::2 .br \fB-d\fP ::2,2001:db8::/64 .br \fB-d\fP ::/0 (random IPv6 throughout the entire range) .TP Options for \fBicmp\fP (RFC 792): .TP .B \-t \fItype\fP Type of message in integer value between 0 to 255. Some common messages are: .IP \fB0\fP : Echo reply .br \fB3\fP : Destination unreachable .br \fB8\fP : Echo .br \fB11\fP : Time exceeded .IP For the complete list, see: .br \fIhttps://www.iana.org/assignments/icmp-parameters\fP .TP .B \-c \fIcode\fP Error code for this ICMPv4 message in integer value between 0 to 255. For example, \fIcode\fP for \fBtime exceeded\fP message may have one of the following values: .IP \fB0\fP : transit TTL exceeded .br \fB1\fP : reassembly TTL exceeded .IP For the complete list, see: .br \fIhttps://www.iana.org/assignments/icmp-parameters\fP .TP Options for \fBicmp6\fP (RFC 4443): .TP .B \-t \fItype\fP Type of message in integer value between 0 to 255. Some common messages are: .IP \fB3\fP : Time Exceeded .br \fB128\fP : Echo Request .br \fB129\fP : Echo Reply .IP For the complete list, see: .br \fIhttps://www.iana.org/assignments/icmpv6-parameters\fP .TP .B \-c \fIcode\fP Code for this ICMPv6 message in integer value between 0 to 255. For example, \fIcode\fP for \fBTime Exceeded\fP message may have one of the following values: .IP \fB0\fP : hop limit exceeded in transit .br \fB1\fP : fragment reassembly time exceeded .IP For the complete list, see: .br \fIhttps://www.iana.org/assignments/icmpv6-parameters\fP .TP Options for \fBtcp\fP (RFC 9293): .TP .B \-s \fIsport\fP or \fIop\fP,\fInp\fP Source port number in integer value between 0 to 65535. If \fIop\fP and \fInp\fP are specified, any instances of \fIop\fP in the source port field will be replaced with \fInp\fP. You can also use the string 'rand' for a random port number. .IP Example: \fB-s\fP 2000, \fB-s\fP rand, or \fB-s\fP 1000,rand .TP .B \-d \fIdport\fP or \fIop\fP,\fInp\fP Destination port number in integer value between 0 to 65535. If \fIop\fP and \fInp\fP are specified, any instances of \fIop\fP in the destination port field will be replaced with \fInp\fP. You can also use the string 'rand' for a random port number. .IP Example: \fB-d\fP 2000, \fB-d\fP rand, or \fB-d\fP 1000,rand .TP .B \-q \fIseq\fP or \fIos\fP,\fIns\fP .br Sequence number in integer value between 0 to 4294967295. If SYN control bit is set, e.g. character \fBs\fP is supplied to the \fB-f\fP flag, \fIseq\fP represents the initial sequence number (ISN) and the first data byte is ISN + 1. If \fIos\fP and \fIns\fP are specified, any instances of \fIos\fP in the sequence number field will be replaced with \fIns\fP. You can also use the string 'rand' for a random sequence number. .IP Example: \fB-q\fP 100000, \fB-q\fP rand, or \fB-q\fP 100000,rand .TP .B \-a \fIack\fP or \fIoa\fP,\fIna\fP .br Acknowledgment number in integer value between 0 to 4294967295. If ACK control bit is set, e.g. character \fBa\fP is supplied to the \fB-f\fP flag, \fIack\fP represents the value of the next sequence number that the receiver is expecting to receive. If \fIoa\fP and \fIna\fP are specified, any instances of \fIoa\fP in the acknowledgment number field will be replaced with \fIna\fP. You can also use the string 'rand' for a random acknowledgment number. .IP Example: \fB-a\fP 100000, \fB-a\fP rand, or \fB-a\fP 100000,rand .TP .B \-f \fIflags\fP Control flags. Possible characters for \fIflags\fP are: .IP \fB-\fP : remove all flags .br \fBc\fP : congestion window reduced .br \fBe\fP : explicit congestion notification echo .br \fBu\fP : urgent pointer field is significant .br \fBa\fP : acknowledgment field is significant .br \fBp\fP : push function .br \fBr\fP : resets the connection .br \fBs\fP : synchronizes the sequence numbers .br \fBf\fP : no more data from sender .IP Example: \fB-f\fP s .br If any of the flags is specified, all original flags are removed automatically. .TP .B \-w \fIwin\fP .br Window size in integer value between 0 to 65535. If ACK control bit is set, e.g. character \fBa\fP is supplied to the \fB-f\fP flag, \fIwin\fP represents the number of data bytes, beginning with the one indicated in the acknowledgment number field that the receiver is willing to accept. .TP .B \-u \fIurg\fP .br Urgent pointer in integer value between 0 to 65535. If URG control bit is set, e.g. character \fBu\fP is supplied to the \fB-f\fP flag, \fIurg\fP represents a pointer that points to the first data byte following the urgent data. .TP Options for \fBudp\fP (RFC 768): .TP .B \-s \fIsport\fP or \fIop\fP,\fInp\fP Source port number in integer value between 0 to 65535. If \fIop\fP and \fInp\fP are specified, any instances of \fIop\fP in the source port field will be replaced with \fInp\fP. You can also use the string 'rand' for a random port number. .IP Example: \fB-s\fP 2000, \fB-s\fP rand, or \fB-s\fP 1000,rand .TP .B \-d \fIdport\fP or \fIop\fP,\fInp\fP Destination port number in integer value between 0 to 65535. If \fIop\fP and \fInp\fP are specified, any instances of \fIop\fP in the destination port field will be replaced with \fInp\fP. You can also use the string 'rand' for a random port number. .IP Example: \fB-d\fP 2000, \fB-d\fP rand, or \fB-d\fP 1000,rand .RE .SH SEE ALSO bittwist(1), pcap(3), tcpdump(1) .SH BUGS File your bug report and send to: .IP Addy Yeow .PP Make sure you are using the latest stable version before submitting your bug report. .PP When running \fIbittwiste\fP with both the \fB-N\fP and \fB-G\fP flags, large inter-packet gap may result in the packet timestamp beyond Unix epoch 2147483647 (2038-01-19 03:14:07 UTC) to overflow. This is due to the use of signed 32-bit integer to store timestamp in \fIpcap\fP(3) header. Simply changing the data type, e.g. using unsigned 64-bit integer, would break the compatibility of the output trace file with existing systems. .PP The workaround built into \fIbittwiste\fP is to use Unix epoch 946684800 (2020-01-01 00:00:00 UTC) as the starting reference timestamp when \fB-G\fP flag is specified. This translates to a maximum timespan of 38 years or 559165 packets in the output trace file when using the maximum inter-packet gap, i.e. \fB-G\fP 2147483647. .SH COPYRIGHT Copyright (C) 2006 - 2023 Addy Yeow .PP 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. .PP 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. .PP 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. .SH AUTHORS Original author and current maintainer: .IP Addy Yeow .PP The current version is available from https://bittwist.sourceforge.io bittwist-linux-3.8/AUTHORS0000644000175000017500000000016014451442047013630 0ustar opsopsAuthors of Bit-Twist Addy Yeow wrote the original Bit-Twist and is the current maintainer. bittwist-linux-3.8/.clang-format0000644000175000017500000000011514451442047015133 0ustar opsopsBasedOnStyle: LLVM BreakBeforeBraces: Allman ColumnLimit: 100 IndentWidth: 4 bittwist-linux-3.8/VERSION0000644000175000017500000000000414451442047013625 0ustar opsops3.8 bittwist-linux-3.8/src/0000755000175000017500000000000014451442047013352 5ustar opsopsbittwist-linux-3.8/src/template_pcap.h0000644000175000017500000000362414451442047016346 0ustar opsops/* * SPDX-License-Identifier: GPL-2.0-or-later * * template_pcap - Template pcap files to be used as input files for bittwiste * Copyright (C) 2006 - 2023 Addy Yeow * * 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. */ #ifndef _TEMPLATE_PCAP_H_ #define _TEMPLATE_PCAP_H_ extern const unsigned char TEMPLATE_PCAP_ETH[]; extern const size_t TEMPLATE_PCAP_ETH_LEN; extern const unsigned char TEMPLATE_PCAP_ARP[]; extern const size_t TEMPLATE_PCAP_ARP_LEN; extern const unsigned char TEMPLATE_PCAP_IP[]; extern const size_t TEMPLATE_PCAP_IP_LEN; extern const unsigned char TEMPLATE_PCAP_IP6[]; extern const size_t TEMPLATE_PCAP_IP6_LEN; extern const unsigned char TEMPLATE_PCAP_ICMP[]; extern const size_t TEMPLATE_PCAP_ICMP_LEN; extern const unsigned char TEMPLATE_PCAP_ICMP6[]; extern const size_t TEMPLATE_PCAP_ICMP6_LEN; extern const unsigned char TEMPLATE_PCAP_TCP[]; extern const size_t TEMPLATE_PCAP_TCP_LEN; extern const unsigned char TEMPLATE_PCAP_IP6_TCP[]; extern const size_t TEMPLATE_PCAP_IP6_TCP_LEN; extern const unsigned char TEMPLATE_PCAP_UDP[]; extern const size_t TEMPLATE_PCAP_UDP_LEN; extern const unsigned char TEMPLATE_PCAP_IP6_UDP[]; extern const size_t TEMPLATE_PCAP_IP6_UDP_LEN; #endif /* _TEMPLATE_PCAP_H_ */ bittwist-linux-3.8/src/bittwist.c0000644000175000017500000005357214451442047015403 0ustar opsops/* * SPDX-License-Identifier: GPL-2.0-or-later * * bittwist - pcap based ethernet packet generator * Copyright (C) 2006 - 2023 Addy Yeow * * 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. */ #include "bittwist.h" #include "token_bucket.h" char *program_name; int32_t thiszone; /* offset from GMT to local time in seconds */ char ebuf[PCAP_ERRBUF_SIZE]; /* pcap error buffer */ /* options */ int vflag = 0; /* 1 = print timestamp, 2 = print timestamp and hex data */ int len = 0; /* packet length to send (-1 = captured, 0 = on wire, or positive value <= 1514) */ int pps = 0; /* packets per second (1 to 1000000) */ int gap = 0; /* gap/interval between packets in seconds (1 to 86400) */ int linerate = -1; /* limit packet throughput at the specified Mbps (0 means no limit) */ uint64_t bps = 0; /* bits per second converted from linerate */ bool default_gap = true; /* true = use captured interval, false = custom interval */ uint64_t max_pkts = 0; /* send up to the specified number of packets */ /* data */ int trace_files_count = 0; trace_file_t **trace_files = NULL; /* pointers to trace files */ pcap_t *pd = NULL; /* pcap descriptor */ uint8_t pkt_data[ETH_MAX_LEN]; /* packet data including the link-layer header */ struct pcap_sf_pkthdr header; /* pcap header per packet */ struct token_bucket tb_pps; /* token bucket for shaping throughput at pps */ struct token_bucket tb_linerate; /* token bucket for shaping throughput at linerate */ /* stats */ static uint64_t pkts_sent = 0; static uint64_t bytes_sent = 0; static uint64_t failed = 0; struct timeval start = {0, 0}; struct timeval end = {0, 0}; int main(int argc, char **argv) { char *cp; int c; pcap_if_t *devptr; int i; int devnum; char *device = NULL; int loop = 1; thiszone = gmt2local(0); if ((cp = strrchr(argv[0], '/')) != NULL) program_name = cp + 1; else program_name = argv[0]; /* process options */ while ((c = getopt(argc, argv, "dvi:s:l:c:p:t:r:h")) != -1) { switch (c) { case 'd': if (pcap_findalldevs(&devptr, ebuf) < 0) error("%s", ebuf); else { for (i = 0; devptr != 0; i++) { (void)printf("%d. %s", i + 1, devptr->name); if (devptr->description != NULL) (void)printf(" (%s)", devptr->description); (void)putchar('\n'); devptr = devptr->next; } } exit(EXIT_SUCCESS); case 'v': ++vflag; break; case 'i': if ((devnum = atoi(optarg)) != 0) { if (devnum < 0) error("invalid adapter index"); if (pcap_findalldevs(&devptr, ebuf) < 0) error("%s", ebuf); else { for (i = 0; i < devnum - 1; i++) { devptr = devptr->next; if (devptr == NULL) error("invalid adapter index"); } } device = devptr->name; } else { device = optarg; } break; case 's': len = strtol(optarg, NULL, 0); if (len != -1 && len != 0) { if (len < ETH_HDR_LEN || len > ETH_MAX_LEN) error("value for length must be between %d to %d", ETH_HDR_LEN, ETH_MAX_LEN); } break; case 'l': loop = strtol(optarg, NULL, 0); /* loop infinitely if loop <= 0 */ break; case 'c': max_pkts = strtoul(optarg, NULL, 0); /* send all packets if max_pkts <= 0 */ break; case 'p': pps = strtol(optarg, NULL, 0); if (pps < 1 || pps > PPS_MAX) error("value for pps must be between 1 to %d", PPS_MAX); break; case 't': gap = strtol(optarg, NULL, 0); if (gap < 1 || gap > GAP_MAX) error("value for gap must be between 1 to %d", GAP_MAX); break; case 'r': linerate = strtol(optarg, NULL, 0); if (linerate < LINERATE_MIN || linerate > LINERATE_MAX) error("value for rate must be between %d to %d", LINERATE_MIN, LINERATE_MAX); if (linerate > 0) bps = (uint64_t)linerate * 1000000; break; case 'h': default: usage(); } } /* don't use captured interval if any of the custom interval options is set */ if (pps > 0 || gap > 0 || linerate >= 0) default_gap = false; if (device == NULL) error("device not specified"); if (argv[optind] == NULL) error("trace file not specified"); /* set signal handler for SIGINT (Control-C) */ (void)signal(SIGINT, cleanup); load_trace_files(argc, argv); init_pcap(device); notice("sending packets through %s", device); if (gettimeofday(&start, NULL) == -1) notice("gettimeofday(): %s", strerror(errno)); clock_gettime(CLOCK_MONOTONIC, &tb_pps.last_add); clock_gettime(CLOCK_MONOTONIC, &tb_linerate.last_add); /* send infinitely if loop <= 0 until user Control-C */ while (1) { for (i = 0; i < trace_files_count; i++) /* for each trace file */ send_packets(trace_files[i]); if (loop > 1) loop--; else if (loop == 1) break; } cleanup(0); /* NOTREACHED */ exit(EXIT_SUCCESS); } void load_trace_files(int argc, char **argv) { struct pcap_file_header preamble; /* pcap file header per trace file */ trace_files = malloc((argc - optind) * sizeof(trace_file_t *)); if (trace_files == NULL) error("malloc(): cannot allocate memory for trace_files"); for (int i = optind; i < argc; i++) { trace_file_t *trace_file = malloc(sizeof(trace_file_t)); if (trace_file == NULL) error("malloc(): cannot allocate memory for trace_file"); trace_file->filename = argv[i]; if ((trace_file->fp = fopen(trace_file->filename, "rb")) == NULL) error("fopen(): error reading %s", trace_file->filename); /* * check preamble of each trace file. * preamble occupies the first 24 bytes of a trace file */ if (fread(&preamble, PCAP_PREAMBLE_LEN, 1, trace_file->fp) == 0) error("fread(): error reading %s", trace_file->filename); if (preamble.magic != PCAP_MAGIC && preamble.magic != NSEC_PCAP_MAGIC) error("%s is not a valid pcap based trace file", trace_file->filename); if (preamble.magic == NSEC_PCAP_MAGIC) trace_file->nsec = true; else trace_file->nsec = false; /* load all inter-packet gaps if we are using captured interval */ if (default_gap) load_ipg(trace_file); else trace_file->ipg = NULL; trace_files[trace_files_count++] = trace_file; } } void load_ipg(trace_file_t *trace_file) { struct timespec curr_ts; /* timestamp of current packet */ struct timespec prev_ts; /* timestamp of previous packet */ uint64_t pkts = 0; /* packet index */ uint64_t i = 0; /* ipg index */ uint64_t ipg_cap = 10000; /* initial capacity to store ipg values; doubled as needed */ trace_file->ipg = malloc(ipg_cap * sizeof(ipg_t)); if (trace_file->ipg == NULL) error("malloc(): cannot allocate memory for ipg"); /* * file pointer has moved past the pcap file header. * loop through the remaining data by reading the packet header first. * packet header (16 bytes) = timestamp + length */ while (fread(&header, PCAP_HDR_LEN, 1, trace_file->fp) == 1) { curr_ts.tv_sec = header.ts.tv_sec; if (trace_file->nsec) curr_ts.tv_nsec = header.ts.tv_usec; else curr_ts.tv_nsec = header.ts.tv_usec * 1000; /* move file pointer to the end of this packet data */ if (fseek(trace_file->fp, header.caplen, SEEK_CUR) != 0) error("fseek(): error reading %s", trace_file->filename); if (pkts > 0) { /* double the capacity to store ipg values if current capacity is hit */ if (i >= ipg_cap) { ipg_cap *= 2; ipg_t *new_ipg = realloc(trace_file->ipg, ipg_cap * sizeof(ipg_t)); if (new_ipg == NULL) error("realloc(): cannot allocate memory for new_ipg"); trace_file->ipg = new_ipg; } trace_file->ipg[i].ns = (curr_ts.tv_sec - prev_ts.tv_sec) * 1000000000L + (curr_ts.tv_nsec - prev_ts.tv_nsec); /* pps value dictates how next packet is to be throttled during the send */ if (trace_file->ipg[i].ns == 0) trace_file->ipg[i].pps = 0; /* send immediately */ else if (trace_file->ipg[i].ns <= 1000000000) trace_file->ipg[i].pps = 1000000000 / trace_file->ipg[i].ns; /* throttle at pps */ else trace_file->ipg[i].pps = -1; /* pps is less than 1, fallback to sleep for ns */ ++i; } ++pkts; prev_ts = curr_ts; } } void init_pcap(char *device) { /* empty error buffer to grab warning message (if exist) from pcap_open_live() below */ *ebuf = '\0'; /* note that we are doing this for sending packets, not capture */ pd = pcap_open_live(device, ETH_MAX_LEN, /* portion of packet to capture */ 1, /* promiscuous mode is on */ 1000, /* read timeout, in milliseconds */ ebuf); if (pd == NULL) error("%s", ebuf); else if (*ebuf) notice("%s", ebuf); /* warning message from pcap_open_live() above */ } void send_packets(trace_file_t *trace_file) { int pkt_len; /* packet length to send */ struct timeval tv; /* current timestamp for verbose output */ uint64_t pkts = 0; /* packet index */ /* reset trace file pointer moving past the pcap file header */ if (fseek(trace_file->fp, PCAP_PREAMBLE_LEN, SEEK_SET) != 0) error("fseek(): error reading %s", trace_file->filename); /* * loop through the remaining data by reading the packet header first. * packet header (16 bytes) = timestamp + length */ while (fread(&header, PCAP_HDR_LEN, 1, trace_file->fp) == 1) { if (len < 0) /* captured length */ pkt_len = header.caplen; else if (len == 0) /* actual length */ pkt_len = header.len; else /* user specified length */ pkt_len = len; /* skip throttling if linerate is set to 0, i.e. send each packet immediately */ if (linerate != 0) throttle(trace_file, pkts, pkt_len * 8); load_packet(trace_file, pkt_len, &header); if (pcap_sendpacket(pd, pkt_data, pkt_len) == -1) { notice("%s", pcap_geterr(pd)); ++failed; } else { ++pkts_sent; bytes_sent += pkt_len; /* verbose output */ if (vflag) { if (gettimeofday(&tv, NULL) == -1) notice("gettimeofday(): %s", strerror(errno)); else ts_print(&tv); (void)printf("#%lu (%d bytes)", pkts_sent, pkt_len); if (vflag > 1) hex_print(pkt_data, pkt_len); else putchar('\n'); fflush(stdout); } } ++pkts; if ((max_pkts > 0) && (pkts_sent >= max_pkts)) cleanup(0); } /* end while */ } void sleep_ns(uint64_t ns) { /* cumulative drift to adjust ns accordingly based on previous elapsed_ns */ static int64_t drift_ns = 0; struct timespec ts, rem_ts, start_ts, end_ts; ts.tv_sec = (ns + drift_ns) / 1000000000; ts.tv_nsec = (ns + drift_ns) % 1000000000; clock_gettime(CLOCK_MONOTONIC, &start_ts); while (nanosleep(&ts, &rem_ts) == -1 && errno == EINTR) ts = rem_ts; clock_gettime(CLOCK_MONOTONIC, &end_ts); uint64_t elapsed_ns = (end_ts.tv_sec - start_ts.tv_sec) * 1000000000 + (end_ts.tv_nsec - start_ts.tv_nsec); /* * if elapsed_ns > ns, the negative drift will shorten the next sleep_ns(). * if elapsed_ns < ns, the positive drift will lengthen the next sleep_ns() */ drift_ns += ns - elapsed_ns; } void throttle(trace_file_t *trace_file, uint64_t pkts, int bits) { /* always send first packet immediately */ if (pkts_sent == 0) return; if (pps) { /* throttle using token bucket algorithm if pps is specified */ while (!token_bucket_remove(&tb_pps, 1, pps)) usleep(1); } else if (bps) { /* throttle using token bucket algorithm if linerate is specified */ while (!token_bucket_remove(&tb_linerate, bits, bps)) usleep(1); } else if (gap) /* user specified inter-packet gap in seconds */ { sleep_ns(gap * 1000000000L); } else /* use captured interval */ { /* * note that the first packet in this trace file is always sent immediately. * this applies even if we are looping the same trace file multiple times */ if (pkts > 0) { if (trace_file->ipg[pkts - 1].pps == 0) /* send immediately */ return; else if (trace_file->ipg[pkts - 1].pps < 0) /* sleep for ns */ sleep_ns(trace_file->ipg[pkts - 1].ns); else /* throttle using token bucket algorithm at pps */ { while (!token_bucket_remove(&tb_pps, 1, trace_file->ipg[pkts - 1].pps)) usleep(1); } } } } void load_packet(trace_file_t *trace_file, int pkt_len, struct pcap_sf_pkthdr *header) { int copy_len = pkt_len < header->caplen ? pkt_len : header->caplen; if (fread(pkt_data, 1, copy_len, trace_file->fp) != copy_len) error("fread(): error reading %s", trace_file->filename); /* pad trailing bytes with zeros */ if (copy_len < pkt_len) memset(pkt_data + copy_len, PKT_PAD, pkt_len - copy_len); /* move file pointer to the end of this packet data */ if (copy_len < header->caplen) { if (fseek(trace_file->fp, header->caplen - copy_len, SEEK_CUR) != 0) error("fseek(): error reading %s", trace_file->filename); } } void info(void) { struct timeval elapsed; float seconds; unsigned long bits_sent, actual_pps; float mbps, gbps; if (gettimeofday(&end, NULL) == -1) notice("gettimeofday(): %s", strerror(errno)); timersub(&end, &start, &elapsed); seconds = elapsed.tv_sec + (float)elapsed.tv_usec / 1000000; actual_pps = pkts_sent / seconds; bits_sent = bytes_sent * 8; mbps = bits_sent / seconds / 1000000; gbps = bits_sent / seconds / 1000000000; (void)putchar('\n'); notice("sent = %lu packets, %lu bits, %lu bytes", pkts_sent, bits_sent, bytes_sent); notice("throughput = %lu pps, %.4f Mbps, %.4f Gbps", actual_pps, mbps, gbps); if (failed) notice("%lu write attempts failed", failed); notice("elapsed time = %f seconds", seconds); } void cleanup(int signum) { for (int i = 0; i < trace_files_count; i++) { fclose(trace_files[i]->fp); if (trace_files[i]->ipg != NULL) free(trace_files[i]->ipg); } free(trace_files); trace_files = NULL; if (signum == -1) exit(EXIT_FAILURE); else info(); exit(EXIT_SUCCESS); } /* * Reference: tcpdump's gmt2local.c * * Copyright (c) 1990, 1991, 1993, 1994, 1995, 1996, 1997 * The Regents of the University of California. All rights reserved. * */ int32_t gmt2local(time_t t) { int dt, dir; struct tm *gmt, *loc; struct tm sgmt; if (t == 0) t = time(NULL); gmt = &sgmt; *gmt = *gmtime(&t); loc = localtime(&t); dt = (loc->tm_hour - gmt->tm_hour) * 60 * 60 + (loc->tm_min - gmt->tm_min) * 60; /* * If the year or julian day is different, we span 00:00 GMT and must add or subtract a day. * Check the year first to avoid problems when the julian day wraps. */ dir = loc->tm_year - gmt->tm_year; if (dir == 0) dir = loc->tm_yday - gmt->tm_yday; dt += dir * 24 * 60 * 60; return (dt); } /* * Reference: tcpdump's print-ascii.c * * Copyright (c) 1990, 1991, 1993, 1994, 1995, 1996, 1997 * The Regents of the University of California. All rights reserved. * */ void hex_print(const uint8_t *cp, uint32_t length) { uint32_t i, s; uint32_t nshorts; uint32_t oset = 0; nshorts = length / sizeof(uint16_t); i = 0; while (nshorts > 0) { if ((i++ % 8) == 0) { (void)printf("\n\t0x%04x: ", oset); oset += 16; } s = *cp; (void)printf(" %02x%02x", s, *(cp + 1)); cp += 2; nshorts--; } if (length & 1) { if ((i % 8) == 0) (void)printf("\n\t0x%04x: ", oset); (void)printf(" %02x", *cp); } (void)putchar('\n'); } /* * Reference: tcpdump's util.c * * Copyright (c) 1990, 1991, 1993, 1994, 1995, 1996, 1997 * The Regents of the University of California. All rights reserved. * */ void ts_print(const struct timeval *tvp) { int s; s = (tvp->tv_sec + thiszone) % 86400; (void)printf("%02d:%02d:%02d.%06u ", s / 3600, (s % 3600) / 60, s % 60, (unsigned)tvp->tv_usec); } void notice(const char *fmt, ...) { va_list ap; va_start(ap, fmt); (void)vprintf(fmt, ap); va_end(ap); if (*fmt) { fmt += strlen(fmt); if (fmt[-1] != '\n') (void)puts(""); } } /* * Reference: tcpdump's util.c * * Copyright (c) 1990, 1991, 1993, 1994, 1995, 1996, 1997 * The Regents of the University of California. All rights reserved. * */ void error(const char *fmt, ...) { va_list ap; (void)fprintf(stderr, "%s: ", program_name); va_start(ap, fmt); (void)vfprintf(stderr, fmt, ap); va_end(ap); if (*fmt) { fmt += strlen(fmt); if (fmt[-1] != '\n') (void)fputc('\n', stderr); } cleanup(-1); } void usage(void) { (void)fprintf(stderr, "%s version %s, Copyright (C) 2006 - 2023 Addy Yeow \n" "%s\n" "Usage: %s [-d] [-v] [-i interface] [-s length] [-l loop] [-c count]\n" " [-p pps] [-t gap] [-r rate] [-h] pcap-file(s)\n" "\nOptions:\n" " -d Print a list of network interfaces available.\n" " -v Print timestamp for each packet.\n" " -vv Print timestamp and hex data for each packet.\n" " -i interface Send 'pcap-file(s)' out onto the network through 'interface'.\n" " -s length Packet length to send (in bytes). Set 'length' to:\n" " 0 : Send the actual packet length. This is the default.\n" " -1 : Send the captured length.\n" " or any other value from %d to %d.\n" " -l loop Send 'pcap-file(s)' out onto the network for 'loop' times.\n" " Set 'loop' to 0 to send 'pcap-file(s)' until stopped.\n" " To stop, type Control-C.\n" " -c count Send up to 'count' packets.\n" " Default is to send all packets from 'pcap-file(s)'.\n" " -p pps Send 'pps' packets per second.\n" " Value for 'pps' must be between 1 to %d.\n" " -t gap Set inter-packet 'gap' in seconds, ignoring the captured\n" " interval between packets in 'pcap-file(s)'.\n" " Value for 'gap' must be between 1 to %d.\n" " -r rate Limit the sending to 'rate' Mbps.\n" " Value for 'rate' must be between %d to %d.\n" " This flag is intended to set the desired packet throughput.\n" " If you want to send packets at line rate of 1000 Mbps,\n" " try -r 1000. If you want to send packets without rate limit,\n" " try -r 0. This flag is typically used with -l 0 to send\n" " 'pcap-file(s)' until stopped.\n" " -h Print version information and usage.\n", program_name, BITTWIST_VERSION, pcap_lib_version(), program_name, ETH_HDR_LEN, ETH_MAX_LEN, PPS_MAX, GAP_MAX, LINERATE_MIN, LINERATE_MAX); exit(EXIT_SUCCESS); } bittwist-linux-3.8/src/token_bucket.c0000644000175000017500000000300014451442047016164 0ustar opsops/* * SPDX-License-Identifier: GPL-2.0-or-later * * token_bucket - token bucket algorithm * Copyright (C) 2006 - 2023 Addy Yeow * * 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. */ #include "token_bucket.h" void token_bucket_add(struct token_bucket *tb, uint64_t bps) { struct timespec now; clock_gettime(CLOCK_MONOTONIC, &now); uint64_t elapsed_ns = (now.tv_sec - tb->last_add.tv_sec) * 1000000000 + (now.tv_nsec - tb->last_add.tv_nsec); tb->tokens += bps * (double)elapsed_ns / 1000000000; tb->tokens = (tb->tokens < bps) ? tb->tokens : bps; tb->last_add = now; } bool token_bucket_remove(struct token_bucket *tb, uint64_t bits, uint64_t bps) { if (tb->tokens < bits) token_bucket_add(tb, bps); if (tb->tokens < bits) return false; tb->tokens -= bits; return true; } bittwist-linux-3.8/src/def.h0000644000175000017500000004422414451442047014267 0ustar opsops/* * SPDX-License-Identifier: GPL-2.0-or-later * * def.h - Definition header file for Bit-Twist project * Copyright (C) 2006 - 2023 Addy Yeow * * 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. */ #ifndef _DEF_H_ #define _DEF_H_ #include #include #include #include #include #include #include #include #include #include #include #define __USE_XOPEN /* using strptime from time.h */ #include #include #define _NET_IF_ARP_H_ /* OpenBSD's if.h takes in if_arp.h */ #include #include #include #include #ifdef __BSD_VISIBLE /* Linux does not have net/if_dl.h */ #include #endif #include struct pcap_timeval { bpf_int32 tv_sec; /* seconds (WARNING: will overflow by 2038-01-19 03:14:07 UTC) */ /* * PCAP_MAGIC: 6-digit microseconds * NSEC_PCAP_MAGIC: 9-digit nanoseconds */ bpf_int32 tv_usec; }; struct pcap_sf_pkthdr { struct pcap_timeval ts; /* timestamp */ bpf_u_int32 caplen; /* length of portion present */ bpf_u_int32 len; /* length this packet (off wire) */ }; #define BITTWIST_VERSION "3.8" #define BITTWISTE_VERSION BITTWIST_VERSION #define ETH_ADDR_LEN 6 /* Ethernet address length */ #define ETH_HDR_LEN 14 /* Ethernet header length */ #define ETH_MAX_LEN 1514 /* maximum frame length, excluding CRC */ #define ARP_HDR_LEN 28 /* Ethernet ARP header length */ #define IP_ADDR_LEN 4 /* IP address length */ #define IP_HDR_LEN 20 /* default IP header length */ #define IP6_HDR_LEN 40 /* default IPv6 header length */ #define ICMP_HDR_LEN 4 /* ICMP header length (up to checksum field only) */ #define ICMP6_HDR_LEN 4 /* ICMPv6 header length (up to checksum field only) */ #define TCP_HDR_LEN 20 /* default TCP header length */ #define UDP_HDR_LEN 8 /* UDP header length */ #define ETH_TYPE_IP 0x0800 /* IP protocol */ #define ETH_TYPE_IPV6 0x86dd /* IPv6 protocol */ #define ETH_TYPE_ARP 0x0806 /* address resolution protocol */ #ifndef IPPROTO_ICMP #define IPPROTO_ICMP 1 /* internet control message protocol */ #endif #ifndef IPPROTO_TCP #define IPPROTO_TCP 6 /* transmission control protocol */ #endif #ifndef IPPROTO_UDP #define IPPROTO_UDP 17 /* user datagram protocol */ #endif #ifndef IPPROTO_ICMPV6 #define IPPROTO_ICMPV6 58 /* internet control message protocol version 6 */ #endif /* bittwist */ #define LINERATE_MIN 0 /* Mbps (0 means no limit) */ #define LINERATE_MAX 10000 /* Mbps */ #define PKT_PAD 0x00 /* packet padding */ #define PPS_MAX 1000000 /* max. packets per second */ #define GAP_MAX 86400 /* arbitrary maximum inter-packet gap in seconds */ /* bittwiste */ #define FIELD_SET 1 /* flag to overwrite field with new value */ #define FIELD_REPLACE 2 /* flag to overwrite matching value in field with new value */ #define FIELD_SET_RAND 3 /* flag to overwrite field with random value */ #define FIELD_REPLACE_RAND 4 /* flag to overwrite matching value in field with random value */ #define PAYLOAD_MAX 1500 /* maximum payload in bytes */ #define GAP_START 946684800 /* arbitrary start time when using custom inter-packet gap */ /* supported header specification (dummy values) */ #define ETH 1 #define ARP 2 #define IP 3 #define IP6 30 #define ICMP 4 #define ICMP6 40 #define TCP 5 #define UDP 6 #define IP_FO_MAX 7770 /* maximum IP fragment offset (number of 64-bit segments) */ #define IP6_FLOW_LABEL_MAX 1048575 /* 20-bit flow label: 0x00000 to 0xfffff (1048575) */ #define DS_FIELD_MAX 63 /* 6-bit DS field */ #define ECN_FIELD_MAX 3 /* 2-bit ECN field */ #define PCAP_PREAMBLE_LEN 24 /* pcap file header length */ #define PCAP_HDR_LEN 16 /* pcap header length */ #define PCAP_MAGIC 0xa1b2c3d4 /* pcap magic number (timestamps in microsecond resolution)*/ #define NSEC_PCAP_MAGIC 0xa1b23c4d /* pcap magic number (timestamps in nanosecond resolution) */ #ifndef timespecisset #define timespecisset(tsp) ((tsp)->tv_sec || (tsp)->tv_nsec) #endif #ifndef timespeccmp #define timespeccmp(ctsp, ptsp, cmp) \ (((ctsp)->tv_sec == (ptsp)->tv_sec) ? ((ctsp)->tv_nsec cmp(ptsp)->tv_nsec) \ : ((ctsp)->tv_sec cmp(ptsp)->tv_sec)) #endif #ifndef timespecsub #define timespecsub(ctsp, ptsp, vtsp) \ do \ { \ (vtsp)->tv_sec = (ctsp)->tv_sec - (ptsp)->tv_sec; \ (vtsp)->tv_nsec = (ctsp)->tv_nsec - (ptsp)->tv_nsec; \ if ((vtsp)->tv_nsec < 0) \ { \ (vtsp)->tv_sec--; \ (vtsp)->tv_nsec += 1000000000L; \ } \ } while (0) #endif #define pcap_timeval_usadd(ts, us) \ do \ { \ uint64_t total_usec = (ts)->tv_usec + (us); \ (ts)->tv_sec += total_usec / 1000000; \ (ts)->tv_usec = total_usec % 1000000; \ } while (0) #define pcap_timeval_nsadd(ts, ns) \ do \ { \ uint64_t total_usec = (ts)->tv_usec + (ns); \ (ts)->tv_sec += total_usec / 1000000000L; \ (ts)->tv_usec = total_usec % 1000000000L; \ } while (0) /* Ethernet header */ struct ethhdr { uint8_t eth_dhost[ETH_ADDR_LEN]; uint8_t eth_shost[ETH_ADDR_LEN]; uint16_t eth_type; }; /* Ethernet ARP header */ struct arphdr { uint16_t ar_hrd; /* format of hardware address */ #define ARPHRD_ETHER 1 /* ethernet hardware format */ #define ARPHRD_IEEE802 6 /* token-ring hardware format */ #define ARPHRD_ARCNET 7 /* arcnet hardware format */ #define ARPHRD_FRELAY 15 /* frame relay hardware format */ #define ARPHRD_IEEE1394 24 /* firewire hardware format */ uint16_t ar_pro; /* format of protocol address */ uint8_t ar_hln; /* length of hardware address */ uint8_t ar_pln; /* length of protocol address */ uint16_t ar_op; /* one of: */ #define ARPOP_REQUEST 1 /* request to resolve address */ #define ARPOP_REPLY 2 /* response to previous request */ #define ARPOP_REVREQUEST 3 /* request protocol address given hardware */ #define ARPOP_REVREPLY 4 /* response giving protocol address */ #define ARPOP_INVREQUEST 8 /* request to identify peer */ #define ARPOP_INVREPLY 9 /* response identifying peer */ uint8_t ar_sha[ETH_ADDR_LEN]; /* sender hardware address */ uint8_t ar_spa[IP_ADDR_LEN]; /* sender protocol address */ uint8_t ar_tha[ETH_ADDR_LEN]; /* target hardware address */ uint8_t ar_tpa[IP_ADDR_LEN]; /* target protocol address */ }; /* IPv4 header */ struct ip { #if BYTE_ORDER == LITTLE_ENDIAN uint8_t ip_hl : 4, /* header length */ ip_v : 4; /* version */ #endif #if BYTE_ORDER == BIG_ENDIAN uint8_t ip_v : 4, /* version */ ip_hl : 4; /* header length */ #endif uint8_t ip_tos; /* type of service */ uint16_t ip_len; /* total length */ uint16_t ip_id; /* identification */ uint16_t ip_off; /* fragment offset field */ #define IP_RF 0x8000 /* reserved fragment flag */ #define IP_DF 0x4000 /* dont fragment flag */ #define IP_MF 0x2000 /* more fragments flag */ #define IP_OFFMASK 0x1fff /* mask for fragmenting bits */ uint8_t ip_ttl; /* time to live */ uint8_t ip_p; /* protocol */ uint16_t ip_sum; /* checksum */ struct in_addr ip_src, ip_dst; /* source and destination address */ } __packed_ip; /* IPv6 header */ struct ip6 { union { struct ip6_hdrctl { uint32_t ip6_un1_flow; /* 4-bit version, 8-bit traffic class, 20-bit flow label */ uint16_t ip6_un1_plen; /* 16-bit payload length */ uint8_t ip6_un1_nxt; /* 8-bit next header */ uint8_t ip6_un1_hlim; /* 8-bit hop limit */ } ip6_un1; uint8_t ip6_un2_vfc; /* 4-bit version, top 4-bit traffic class */ } ip6_ctlun; struct in6_addr ip6_src; /* 128-bit source address */ struct in6_addr ip6_dst; /* 128-bit destination address */ } __packed_ip6; /* Sample IPv6 packet showing how Wireshark decodes flow info (ip6_flow): Frame 1: 86 bytes on wire (688 bits), 86 bytes captured (688 bits) Ethernet II, Src: aa:aa:aa:aa:aa:aa (aa:aa:aa:aa:aa:aa), Dst: bb:bb:bb:bb:bb:bb (bb:bb:bb:bb:bb:bb) Internet Protocol Version 6, Src: 2606:4700:4700::64, Dst: 2606:4700:4700::6400 0110 .... = Version: 6 .... 0000 0000 .... .... .... .... .... = Traffic Class: 0x00 (DSCP: CS0, ECN: Not-ECT) .... .... .... 0011 0101 0001 1011 0011 = Flow Label: 0x351b3 Payload Length: 32 Next Header: TCP (6) Hop Limit: 53 Source Address: 2606:4700:4700::64 Destination Address: 2606:4700:4700::6400 Transmission Control Protocol, Src Port: 30000, Dst Port: 60000, Seq: 1, Ack: 1, Len: 0 */ #define ip6_flow ip6_ctlun.ip6_un1.ip6_un1_flow #define ip6_plen ip6_ctlun.ip6_un1.ip6_un1_plen #define ip6_nxt ip6_ctlun.ip6_un1.ip6_un1_nxt #define ip6_hlim ip6_ctlun.ip6_un1.ip6_un1_hlim #define IP6_FLOWLABEL_MASK (htonl(0x000fffff)) /* 20-bit flow label (network byte order) */ /* IPv4 pseudo header for computing TCP and UDP checksums */ struct ippseudo { struct in_addr ippseudo_src; /* source address */ struct in_addr ippseudo_dst; /* destination address */ uint8_t ippseudo_pad; /* pad, must be zero */ uint8_t ippseudo_p; /* protocol */ uint16_t ippseudo_len; /* protocol length */ }; /* IPv6 pseudo header for computing ICMPv6, TCP and UDP checksums */ struct ip6pseudo { struct in6_addr ip6pseudo_src; /* 128-bit source address */ struct in6_addr ip6pseudo_dst; /* 128-bit destination address */ uint32_t ip6pseudo_len; /* 32-bit upper-layer packet length */ uint8_t ip6pseudo_zero[3]; /* 24-bit zeros */ uint8_t ip6pseudo_nxt; /* 8-bit next header */ }; /* ICMP header (up to checksum field only) */ struct icmphdr { uint8_t icmp_type; /* type field */ uint8_t icmp_code; /* code field */ uint16_t icmp_cksum; /* checksum field */ }; /* ICMPv6 header (up to checksum field only) */ struct icmp6hdr { uint8_t icmp6_type; /* type field */ uint8_t icmp6_code; /* code field */ uint16_t icmp6_cksum; /* checksum field */ }; /* TCP header */ struct tcphdr { uint16_t th_sport; /* source port */ uint16_t th_dport; /* destination port */ uint32_t th_seq; /* sequence number */ uint32_t th_ack; /* acknowledgment number */ #if BYTE_ORDER == LITTLE_ENDIAN uint8_t th_x2 : 4, /* (unused) */ th_off : 4; /* data offset in number of 32-bit words */ #endif #if BYTE_ORDER == BIG_ENDIAN uint8_t th_off : 4, /* data offset in number of 32-bit words */ th_x2 : 4; /* (unused) */ #endif uint8_t th_flags; #define TH_FIN 0x01 /* no more data from sender */ #define TH_SYN 0x02 /* synchronize sequence numbers */ #define TH_RST 0x04 /* reset the connection */ #define TH_PUSH 0x08 /* push function */ #define TH_ACK 0x10 /* acknowledgment field is significant */ #define TH_URG 0x20 /* urgent pointer field is significant */ #define TH_ECE 0x40 /* explicit congestion notification echo */ #define TH_CWR 0x80 /* congestion window reduced */ #define TH_FLAGS (TH_FIN | TH_SYN | TH_RST | TH_PUSH | TH_ACK | TH_URG | TH_ECE | TH_CWR) uint16_t th_win; /* window */ uint16_t th_sum; /* checksum */ uint16_t th_urp; /* urgent pointer */ }; /* UDP header */ struct udphdr { uint16_t uh_sport; /* source port */ uint16_t uh_dport; /* destination port */ uint16_t uh_ulen; /* udp length */ uint16_t uh_sum; /* udp checksum */ }; /* * Structures for bittwiste header specific options. */ struct eth_addr_opt { uint8_t old[ETH_ADDR_LEN]; /* 48-bit Ethernet address */ uint8_t new[ETH_ADDR_LEN]; uint8_t flag; }; struct ethopt { struct eth_addr_opt dhost; struct eth_addr_opt shost; uint16_t eth_type; }; struct arpopt { uint16_t ar_op; /* opcode */ uint8_t ar_op_flag; struct eth_addr_opt sha; /* sender hardware address */ uint8_t ar_old_spa[IP_ADDR_LEN]; /* sender protocol address */ uint8_t ar_new_spa[IP_ADDR_LEN]; uint8_t ar_spa_flag; struct eth_addr_opt tha; /* target hardware address */ uint8_t ar_old_tpa[IP_ADDR_LEN]; /* target protocol address */ uint8_t ar_new_tpa[IP_ADDR_LEN]; uint8_t ar_tpa_flag; }; struct in_addr_opt { struct in_addr old; /* 32-bit IPv4 address */ struct in_addr new; struct in_addr netmask; /* netmask to use when randomizing address */ uint8_t rand_bits; /* number of bits available to the right that can be randomized */ uint8_t flag; }; struct ipopt { uint8_t ip_ds_field; /* 6-bit DS field (first 6-bit of 8-bit type of service field) */ uint8_t ip_ds_field_flag; uint8_t ip_ecn_field; /* 2-bit ECN field (last 2-bit of 8-bit type of service field) */ uint8_t ip_ecn_field_flag; uint16_t ip_old_id; /* identification */ uint16_t ip_new_id; uint8_t ip_id_flag; uint8_t ip_flag_r; /* reserved bit */ uint8_t ip_flag_d; /* don't fragment bit */ uint8_t ip_flag_m; /* more fragment bit */ uint8_t ip_flags_flag; uint16_t ip_fo; /* fragment offset in bytes */ uint8_t ip_fo_flag; uint8_t ip_old_ttl; /* time to live */ uint8_t ip_new_ttl; uint8_t ip_ttl_flag; uint8_t ip_old_p; /* protocol */ uint8_t ip_new_p; uint8_t ip_p_flag; struct in_addr_opt ip_src; /* options for source address */ struct in_addr_opt ip_dst; /* options for destination address */ }; struct in6_addr_opt { struct in6_addr old; /* 128-bit IPv6 address */ struct in6_addr new; struct in6_addr netmask; /* netmask to use when randomizing address */ uint8_t rand_bits; /* number of bits available to the right that can be randomized */ uint8_t flag; }; struct ip6opt { uint8_t ip6_ds_field; /* 6-bit DS field (first 6-bit of 8-bit traffic class field) */ uint8_t ip6_ds_field_flag; uint8_t ip6_ecn_field; /* 2-bit ECN field (last 2-bit of 8-bit traffic class field) */ uint8_t ip6_ecn_field_flag; uint32_t ip6_flow_label; /* 20-bit flow label */ uint8_t ip6_flow_label_flag; uint8_t ip6_old_next_header; /* 8-bit next header */ uint8_t ip6_new_next_header; uint8_t ip6_next_header_flag; uint8_t ip6_old_hop_limit; /* 8-bit hop limit */ uint8_t ip6_new_hop_limit; uint8_t ip6_hop_limit_flag; struct in6_addr_opt ip6_src; /* options for source address */ struct in6_addr_opt ip6_dst; /* options for destination address */ }; struct icmpopt { uint8_t icmp_type; /* type of message */ uint8_t icmp_type_flag; uint8_t icmp_code; /* type sub code */ uint8_t icmp_code_flag; }; struct icmp6opt { uint8_t icmp6_type; /* type of message */ uint8_t icmp6_type_flag; uint8_t icmp6_code; /* type sub code */ uint8_t icmp6_code_flag; }; struct tcpopt { uint16_t th_old_sport; /* source port */ uint16_t th_new_sport; uint8_t th_sport_flag; uint16_t th_old_dport; /* destination port */ uint16_t th_new_dport; uint8_t th_dport_flag; uint32_t th_old_seq; /* sequence number */ uint32_t th_new_seq; uint8_t th_seq_flag; uint32_t th_old_ack; /* acknowledgment number */ uint32_t th_new_ack; uint8_t th_ack_flag; uint8_t th_flag_c; /* CWR */ uint8_t th_flag_e; /* ECE */ uint8_t th_flag_u; /* URG */ uint8_t th_flag_a; /* ACK */ uint8_t th_flag_p; /* PSH */ uint8_t th_flag_r; /* RST */ uint8_t th_flag_s; /* SYN */ uint8_t th_flag_f; /* FIN */ uint8_t th_flags_flag; uint16_t th_win; /* window */ uint8_t th_win_flag; uint16_t th_urp; /* urgent pointer */ uint8_t th_urp_flag; }; struct udpopt { uint16_t uh_old_sport; /* source port */ uint16_t uh_new_sport; uint8_t uh_sport_flag; uint16_t uh_old_dport; /* destination port */ uint16_t uh_new_dport; uint8_t uh_dport_flag; }; #endif /* !_DEF_H_ */ bittwist-linux-3.8/src/token_bucket.h0000644000175000017500000000423614451442047016205 0ustar opsops/* * SPDX-License-Identifier: GPL-2.0-or-later * * token_bucket - token bucket algorithm * Copyright (C) 2006 - 2023 Addy Yeow * * 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. */ /* Reference: https://ee.lbl.gov/papers/congavoid.pdf (Congestion Avoidance and Control) https://en.wikipedia.org/wiki/Token_bucket Sample usage with sample.c below: --- #include "token_bucket.h" int main() { struct token_bucket tb; uint64_t bps = 1000000000; // Target throughput at 1 Gbps uint64_t packet_size = 12112; // 1514 bytes uint64_t packets = 1000000; // 1 million packets int i = 1; clock_gettime(CLOCK_MONOTONIC, &tb.last_add); do { while (!token_bucket_remove(&tb, packet_size, bps)) usleep(1); i++; // packet sent } while (i <= packets); return 0; } $ gcc -I. sample.c token_bucket.c && time ./a.out real 0m12.113s user 0m0.391s sys 0m0.392s --- The time taken should be close to (packet_size_in_bits * packets) / bits_per_second >>> 12112 * 1_000_000 / 1_000_000_000 12.112 */ #ifndef _TOKEN_BUCKET_H_ #define _TOKEN_BUCKET_H_ #include #include #include #include struct token_bucket { double tokens; /* available tokens (bits) in bucket */ struct timespec last_add; /* timestamp */ }; void token_bucket_add(struct token_bucket *tb, uint64_t bps); bool token_bucket_remove(struct token_bucket *tb, uint64_t bits, uint64_t bps); #endif /* !_TOKEN_BUCKET_H_ */ bittwist-linux-3.8/src/bittwist.h0000644000175000017500000000353414451442047015401 0ustar opsops/* * SPDX-License-Identifier: GPL-2.0-or-later * * bittwist - pcap based ethernet packet generator * Copyright (C) 2006 - 2023 Addy Yeow * * 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. */ #ifndef _BITTWIST_H_ #define _BITTWIST_H_ #include "def.h" /* inter-packet gap from trace file represented in nanoseconds and packets per second */ typedef struct { uint64_t ns; int pps; } ipg_t; typedef struct { FILE *fp; char *filename; bool nsec; /* set to true if we have timestamps in nanosecond resolution */ ipg_t *ipg; } trace_file_t; void load_trace_files(int argc, char **argv); void load_ipg(trace_file_t *trace_file); void init_pcap(char *device); void send_packets(trace_file_t *trace_file); void sleep_ns(uint64_t ns); void throttle(trace_file_t *trace_file, uint64_t pkts, int bits); void load_packet(trace_file_t *trace_file, int pkt_len, struct pcap_sf_pkthdr *header); void info(void); void cleanup(int signum); int32_t gmt2local(time_t t); void hex_print(const uint8_t *cp, uint32_t length); void ts_print(const struct timeval *tvp); void notice(const char *fmt, ...); void error(const char *fmt, ...); void usage(void); #endif /* !_BITTWIST_H_ */ bittwist-linux-3.8/src/bittwiste.c0000644000175000017500000041737214451442047015552 0ustar opsops/* * SPDX-License-Identifier: GPL-2.0-or-later * * bittwiste - pcap capture file editor * Copyright (C) 2006 - 2023 Addy Yeow * * 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. */ #include "bittwiste.h" #include "template_pcap.h" #include "tinymt64.h" char *program_name; /* general options */ int header_opt = -1; /* specifies which header to edit, -1 -> no header selected */ int layer_opt = 0; /* copy up to the specified layer only */ int start_oset_opt = 0, end_oset_opt = 0; /* delete the specified byte offset */ int start_opt = 0, end_opt = 0; /* copy the specified range of packets only */ time_t start_sec_opt = 0, end_sec_opt = 0; /* copy packets within the specified timeframe only */ uint32_t gap_start_opt, gap_end_opt; /* inter-packet gap range in microseconds (inclusive) */ struct pcap_timeval gap_last_ts = {0, 0}; /* track last timestamp when applying custom gap */ int repeat_opt = 0; /* duplicate packets for the specified times */ int csum_opt = 1; /* set to 0 to disable checksum correction */ uint8_t *payload_opt = NULL; /* payload in hex digits *NOTFREED* */ uint16_t payload_len_opt = 0; /* length of payload in bytes */ int linktype_opt = -1; /* pcap preamble link type field, -1 -> no override */ bool nsec = false; /* set to true if we have timestamps in nanosecond resolution */ /* TinyMT as random number generator (RNG) */ tinymt64_t tinymt; /* header specific options *NOTFREED* */ struct ethopt *ethopt; /* Ethernet options */ struct arpopt *arpopt; /* ARP options */ struct ipopt *ipopt; /* IP options */ struct ip6opt *ip6opt; /* IPv6 options */ struct icmpopt *icmpopt; /* ICMP options */ struct icmp6opt *icmp6opt; /* ICMPv6 options */ struct tcpopt *tcpopt; /* TCP options */ struct udpopt *udpopt; /* UDP options */ /* stats */ static uint64_t pkts = 0; static uint64_t bytes = 0; int main(int argc, char **argv) { char *cp; int c, i; char *str = NULL; char *infile = NULL; char *outfile = NULL; struct tm *tm = NULL; /* use current time as default seed for RNG, can be optionally set using -P flag */ unsigned int seed = time(NULL); if ((cp = strrchr(argv[0], '/')) != NULL) program_name = cp + 1; else program_name = argv[0]; /* process general options */ while ((c = getopt(argc, argv, "I:O:L:X:CM:D:R:S:N:G:P:T:h")) != -1) { switch (c) { case 'I': infile = optarg; /* file path or template name */ break; case 'O': outfile = optarg; break; case 'L': layer_opt = strtol(optarg, NULL, 0); /* * 2 - Ethernet * 3 - ARP, IP * 4 - ICMP, TCP, UDP */ if (layer_opt < 2 || layer_opt > 4) error("layer is out of range"); break; case 'X': /* ignored if option -L and -T are not specified */ c = strlen(optarg); if (c > (PAYLOAD_MAX * 2) || (c % 2) != 0) error("invalid payload specification"); payload_len_opt = c / 2; payload_opt = (uint8_t *)malloc(sizeof(uint8_t) * payload_len_opt); if (payload_opt == NULL) error("malloc(): cannot allocate memory for payload_opt"); /* make a byte of data from every 2 characters of optarg */ for (i = 0; i < payload_len_opt; i++) { uint8_t hex_byte[3] = {optarg[i * 2], optarg[i * 2 + 1], '\0'}; if (!isxdigit(hex_byte[0]) || !isxdigit(hex_byte[1])) error("invalid payload specification"); sscanf((char *)hex_byte, "%hhx", &payload_opt[i]); } break; case 'C': csum_opt = 0; /* DISABLE checksum correction */ break; case 'M': linktype_opt = strtol(optarg, NULL, 0); /* * 1 - Ethernet * 9 - PPP * 12 - Raw IP * 51 - PPPoE * 105 - IEEE 802.11 wireless * 117 - OpenBSD pflog * 118 - Cisco IOS * 119 - 802.11 with Prism hdr */ if (linktype_opt < 0 || linktype_opt > UCHAR_MAX) error("linktype is out of range"); break; case 'D': /* * -D 15-18, delete from byte 15th through byte 18th (inclusive), * starting from link-layer hdr */ str = strdup(optarg); if (str == NULL) error("strdup(): cannot allocate memory for str"); if ((cp = (char *)strtok(str, "-")) == NULL) error("invalid offset specification"); start_oset_opt = strtol(cp, NULL, 0); if ((cp = (char *)strtok(NULL, "-")) == NULL) end_oset_opt = start_oset_opt; /* delete a single byte, e.g. -D 15 */ else end_oset_opt = strtol(cp, NULL, 0); free(str); str = NULL; if (start_oset_opt <= 0 || end_oset_opt <= 0 || (start_oset_opt > end_oset_opt)) error("invalid offset specification"); break; case 'R': /* e.g. -R 5-21 or -R 9 */ str = strdup(optarg); if (str == NULL) error("strdup(): cannot allocate memory for str"); if ((cp = (char *)strtok(str, "-")) == NULL) error("invalid range specification"); start_opt = strtol(cp, NULL, 0); if ((cp = (char *)strtok(NULL, "-")) == NULL) end_opt = start_opt; /* only one packet */ else end_opt = strtol(cp, NULL, 0); free(str); str = NULL; if (start_opt <= 0 || end_opt <= 0 || (start_opt > end_opt)) error("invalid range specification"); break; case 'S': /* * time frame with one-second resolution: -S 22/10/2006,21:47:35-24/10/2006,13:16:05 * format: -S DD/MM/YYYY,HH:MM:SS-DD/MM/YYYY,HH:MM:SS * note that -S 22/10/2006-24/10/2006 is equivalent to -S * 22/10/2006,00:00:00-24/10/2006,00:00:00 */ str = strdup(optarg); if (str == NULL) error("strdup(): cannot allocate memory for str"); if ((cp = (char *)strtok(str, "-")) == NULL) error("invalid timeframe specification"); tm = (struct tm *)malloc(sizeof(struct tm)); if (tm == NULL) error("malloc(): cannot allocate memory for tm"); if (!strptime(cp, "%d/%m/%Y,%T", tm)) error("invalid timeframe specification"); start_sec_opt = mktime(tm); if ((cp = (char *)strtok(NULL, "-")) == NULL) end_sec_opt = start_sec_opt; /* only the packets within the one-second resolution */ else { if (!strptime(cp, "%d/%m/%Y,%T", tm)) error("invalid timeframe specification"); } end_sec_opt = mktime(tm); free(tm); tm = NULL; free(str); str = NULL; if (start_sec_opt > end_sec_opt) error("invalid timeframe specification"); break; case 'N': /* e.g. -N 10, duplicate packet for 10 times */ repeat_opt = strtol(optarg, NULL, 0); if (repeat_opt < 0) error("invalid repeat specification"); break; case 'G': /* inter-packet gap in microseconds, e.g. -G 1000-10000 or -G 1000 */ str = strdup(optarg); if (str == NULL) error("strdup(): cannot allocate memory for str"); if ((cp = (char *)strtok(str, "-")) == NULL) error("invalid gap range specification"); gap_start_opt = strtol(cp, NULL, 0); if ((cp = (char *)strtok(NULL, "-")) == NULL) gap_end_opt = gap_start_opt; /* fixed gap */ else gap_end_opt = strtol(cp, NULL, 0); /* ranged random gap */ free(str); str = NULL; if (gap_start_opt <= 0 || gap_end_opt <= 0 || gap_start_opt > INT32_MAX || gap_end_opt > INT32_MAX || (gap_start_opt > gap_end_opt)) error("invalid gap range specification"); gap_last_ts.tv_sec = GAP_START; break; case 'P': /* optional positive integer to seed RNG */ seed = strtol(optarg, NULL, 0); if (seed < 0) error("invalid seed specification"); break; case 'T': if (strcasecmp(optarg, "eth") == 0) header_opt = ETH; else if (strcasecmp(optarg, "arp") == 0) header_opt = ARP; else if (strcasecmp(optarg, "ip") == 0) header_opt = IP; else if (strcasecmp(optarg, "ip6") == 0) header_opt = IP6; else if (strcasecmp(optarg, "icmp") == 0) header_opt = ICMP; else if (strcasecmp(optarg, "icmp6") == 0) header_opt = ICMP6; else if (strcasecmp(optarg, "tcp") == 0) header_opt = TCP; else if (strcasecmp(optarg, "udp") == 0) header_opt = UDP; else error("invalid header specification"); /* process hdr specific options */ parse_header_options(argc, argv); break; case 'h': default: usage(); } } if (infile == NULL) error("input file not specified"); if (outfile == NULL) error("output file not specified"); if (strcmp(infile, outfile) == 0) error("invalid outfile specification"); /* initialize RNG */ tinymt64_init(&tinymt, seed); parse_trace(infile, outfile); info(); exit(EXIT_SUCCESS); } void set_eth_addr_options(char *optarg, struct eth_addr_opt *opt) { /* * optarg: * - 11:11:11:11:11:11 (overwrite MAC), flag = FIELD_SET * - 11:11:11:11:11:11,22:22:22:22:22:22 (overwrite matching MAC), flag = FIELD_REPLACE * - rand (overwrite MAC with random MAC), flag = FIELD_SET_RAND * - 11:11:11:11:11:11,rand (overwrite matching MAC with random MAC), flag = FIELD_REPLACE_RAND */ char *str = strdup(optarg); if (str == NULL) error("strdup(): cannot allocate memory for str"); char *cp = strtok(str, ","); if (cp == NULL) error("invalid MAC address"); if (strcasecmp(cp, "rand") == 0) opt->flag = FIELD_SET_RAND; /* overwrite MAC with random MAC */ else { if (eth_aton(cp, opt->old) != 1) error("invalid MAC address"); cp = strtok(NULL, ","); if (cp == NULL) opt->flag = FIELD_SET; /* overwrite MAC */ else if (strcasecmp(cp, "rand") == 0) opt->flag = FIELD_REPLACE_RAND; /* overwrite matching MAC with random MAC */ else { opt->flag = FIELD_REPLACE; /* overwrite matching MAC */ if (eth_aton(cp, opt->new) != 1) error("invalid MAC address"); } } free(str); } void set_rand_in_addr_options(char *cp, struct in_addr *netnum, struct in_addr *netmask, uint8_t *rand_bits) { uint8_t netlen; /* * parse CIDR notation in the form of /, e.g. 1.0.0.0/8 * 0.0.0.0/0 will result in random IPv4 selected from the entire range */ char *input_netnum = strtok(cp, "/"); char *input_netlen = strtok(NULL, "/"); if (input_netnum == NULL || input_netlen == NULL) error("invalid CIDR notation"); if (inet_pton(AF_INET, input_netnum, netnum) != 1) error("invalid CIDR notation"); /* extract prefix length to calculate netmask */ netlen = atoi(input_netlen); if (netlen < 0 || netlen > 32) error("invalid CIDR notation"); /* number of bits available to the right that can be randomized */ *rand_bits = 32 - netlen; /* calculate network mask and update the network number */ if (netlen == 0) netmask->s_addr = 0; /* special handling for /0 */ else { netmask->s_addr = htonl((uint32_t)(0xffffffffu << (32 - netlen))); if (netnum->s_addr != (netnum->s_addr & netmask->s_addr)) netnum->s_addr &= netmask->s_addr; } } void set_in_addr_options(char *optarg, struct in_addr_opt *opt) { /* * optarg: * - 1.1.1.1 (overwrite IP), flag = FIELD_SET * - 1.1.1.1,2.2.2.2 (overwrite matching IP), flag = FIELD_REPLACE * - 1.0.0.0/8 (overwrite IP with IP from CIDR), flag = FIELD_SET_RAND * - 1.1.1.1,2.0.0.0/8 (overwrite matching IP with IP from CIDR), flag = FIELD_REPLACE_RAND */ char *str = strdup(optarg); if (str == NULL) error("strdup(): cannot allocate memory for str"); char *cp = strtok(str, ","); if (cp == NULL) error("invalid IPv4 address"); if (strstr(cp, "/") != NULL && strchr(cp, ',') == NULL) { opt->flag = FIELD_SET_RAND; /* overwrite IP with IP from CIDR */ set_rand_in_addr_options(cp, &opt->new, &opt->netmask, &opt->rand_bits); } else { if (inet_pton(AF_INET, cp, &opt->old) != 1) error("invalid IPv4 address"); cp = strtok(NULL, ","); if (cp == NULL) opt->flag = FIELD_SET; /* overwrite IP */ else if (strstr(cp, "/") != NULL && strchr(cp, ',') == NULL) { opt->flag = FIELD_REPLACE_RAND; /* overwrite matching IP with IP from CIDR */ set_rand_in_addr_options(cp, &opt->new, &opt->netmask, &opt->rand_bits); } else { opt->flag = FIELD_REPLACE; /* overwrite matching IP */ if (inet_pton(AF_INET, cp, &opt->new) != 1) error("invalid IPv4 address"); } } free(str); } void set_rand_in6_addr_options(char *cp, struct in6_addr *netnum, struct in6_addr *netmask, uint8_t *rand_bits) { uint8_t netlen, shift, i, s; /* * parse CIDR notation in the form of /, e.g. 2001:db8::/48 * ::/0 will result in random IPv6 selected from the entire range */ char *input_netnum = strtok(cp, "/"); char *input_netlen = strtok(NULL, "/"); if (input_netnum == NULL || input_netlen == NULL) error("invalid CIDR notation"); if (inet_pton(AF_INET6, input_netnum, netnum) != 1) error("invalid CIDR notation"); /* extract prefix length to calculate netmask */ netlen = atoi(input_netlen); if (netlen < 0 || netlen > 128) error("invalid CIDR notation"); /* number of bits available to the right that can be randomized */ *rand_bits = 128 - netlen; /* calculate network mask and update the network number */ shift = netlen; for (i = 0; i < 16; i++) /* 16 octets in IPv6 */ { s = (shift > 8) ? 8 : shift; shift -= s; netmask->s6_addr[i] = (uint8_t)(0xffu << (8 - s)); if (netnum->s6_addr[i] != (netnum->s6_addr[i] & netmask->s6_addr[i])) netnum->s6_addr[i] &= netmask->s6_addr[i]; } } void set_in6_addr_options(char *optarg, struct in6_addr_opt *opt) { /* * optarg: * - ::1 (overwrite IP), flag = FIELD_SET * - ::1,::2 (overwrite matching IP), flag = FIELD_REPLACE * - ::2/64 (overwrite IP with IP from CIDR), flag = FIELD_SET_RAND * - ::1,::2/64 (overwrite matching IP with IP from CIDR), flag = FIELD_REPLACE_RAND */ char *str = strdup(optarg); if (str == NULL) error("strdup(): cannot allocate memory for str"); char *cp = strtok(str, ","); if (cp == NULL) error("invalid IPv6 address"); if (strstr(cp, "/") != NULL && strchr(cp, ',') == NULL) { opt->flag = FIELD_SET_RAND; /* overwrite IP with IP from CIDR */ set_rand_in6_addr_options(cp, &opt->new, &opt->netmask, &opt->rand_bits); } else { if (inet_pton(AF_INET6, cp, &opt->old) != 1) error("invalid IPv6 address"); cp = strtok(NULL, ","); if (cp == NULL) opt->flag = FIELD_SET; /* overwrite IP */ else if (strstr(cp, "/") != NULL && strchr(cp, ',') == NULL) { opt->flag = FIELD_REPLACE_RAND; /* overwrite matching IP with IP from CIDR */ set_rand_in6_addr_options(cp, &opt->new, &opt->netmask, &opt->rand_bits); } else { opt->flag = FIELD_REPLACE; /* overwrite matching IP */ if (inet_pton(AF_INET6, cp, &opt->new) != 1) error("invalid IPv6 address"); } } free(str); } void set_number_options(char *optarg, void *val_a, void *val_b, uint8_t *flag, size_t val_size) { /* * optarg: * - 1 (overwrite value), flag = FIELD_SET * - 1,2 (overwrite matching value), flag = FIELD_REPLACE * - rand (overwrite value with random value), flag = FIELD_SET_RAND * - 1,rand (overwrite matching value with random value), flag = FIELD_REPLACE_RAND */ char *str = strdup(optarg); if (str == NULL) error("strdup(): cannot allocate memory for str"); char *cp = strtok(str, ","); if (cp == NULL) error("invalid number specification"); if (strcasecmp(cp, "rand") == 0) *flag = FIELD_SET_RAND; /* overwrite value with random value */ else { /* input value can be integer, hexadecimal, or octal */ uint64_t v = strtoul(cp, NULL, 0); /* * accept sizeof(uint8_t) for e.g. protocol number * accept sizeof(uint16_t) for e.g. tcp port number * accept sizeof(uint32_t) for e.g. tcp sequence number */ uint32_t max_val; if (val_size == sizeof(uint8_t)) max_val = UINT8_MAX; else if (val_size == sizeof(uint16_t)) max_val = UINT16_MAX; else max_val = UINT32_MAX; if (v < 0 || v > max_val) error("number is out of range: %lu", v); if (val_size == sizeof(uint8_t)) *((uint8_t *)val_a) = (uint8_t)v; else if (val_size == sizeof(uint16_t)) *((uint16_t *)val_a) = (uint16_t)v; else *((uint32_t *)val_a) = (uint32_t)v; cp = strtok(NULL, ","); if (cp == NULL) *flag = FIELD_SET; /* overwrite value */ else if (strcasecmp(cp, "rand") == 0) *flag = FIELD_REPLACE_RAND; /* overwrite matching value with random value */ else { v = strtoul(cp, NULL, 0); if (v < 0 || v > max_val) error("number is out of range: %lu", v); if (val_size == sizeof(uint8_t)) *((uint8_t *)val_b) = (uint8_t)v; else if (val_size == sizeof(uint16_t)) *((uint16_t *)val_b) = (uint16_t)v; else *((uint32_t *)val_b) = (uint32_t)v; *flag = FIELD_REPLACE; /* overwrite matching value */ } } free(str); } void parse_header_options(int argc, char **argv) { char *cp; int c; char *str = NULL; uint32_t v; /* input value (can be integer, hexadecimal, or octal) returned by strtol */ if (header_opt == ETH) { ethopt = (struct ethopt *)malloc(sizeof(struct ethopt)); if (ethopt == NULL) error("malloc(): cannot allocate memory for ethopt"); memset(ethopt, 0, sizeof(struct ethopt)); while ((c = getopt(argc, argv, "d:s:t:")) != -1) { switch (c) { case 'd': /* destination MAC */ set_eth_addr_options(optarg, ðopt->dhost); break; case 's': /* source MAC */ set_eth_addr_options(optarg, ðopt->shost); break; case 't': /* type */ if (strcasecmp(optarg, "ip") == 0) ethopt->eth_type = ETH_TYPE_IP; else if (strcasecmp(optarg, "ip6") == 0) ethopt->eth_type = ETH_TYPE_IPV6; else if (strcasecmp(optarg, "arp") == 0) ethopt->eth_type = ETH_TYPE_ARP; else error("invalid Ethernet type specification"); break; default: usage(); } } } else if (header_opt == ARP) { arpopt = (struct arpopt *)malloc(sizeof(struct arpopt)); if (arpopt == NULL) error("malloc(): cannot allocate memory for arpopt"); memset(arpopt, 0, sizeof(struct arpopt)); while ((c = getopt(argc, argv, "o:s:p:t:q:")) != -1) { switch (c) { case 'o': /* opcode */ v = strtol(optarg, NULL, 0); if (v < 0 || v > USHRT_MAX) error("ARP opcode is out of range"); arpopt->ar_op = (uint16_t)v; arpopt->ar_op_flag = 1; break; case 's': /* sender MAC */ set_eth_addr_options(optarg, &arpopt->sha); break; case 'p': /* sender IP */ str = strdup(optarg); if (str == NULL) error("strdup(): cannot allocate memory for str"); if ((cp = (char *)strtok(str, ",")) == NULL) error("invalid sender IP address"); if (inet_pton(AF_INET, cp, &(arpopt->ar_old_spa)) != 1) error("invalid sender IP address"); if ((cp = (char *)strtok(NULL, ",")) == NULL) /* overwrite all sender IP address */ arpopt->ar_spa_flag = 1; else { /* overwrite matching IP address only */ arpopt->ar_spa_flag = 2; if (inet_pton(AF_INET, cp, &(arpopt->ar_new_spa)) != 1) error("invalid sender IP address"); } free(str); str = NULL; break; case 't': /* target MAC */ set_eth_addr_options(optarg, &arpopt->tha); break; case 'q': /* target IP */ str = strdup(optarg); if (str == NULL) error("strdup(): cannot allocate memory for str"); if ((cp = (char *)strtok(str, ",")) == NULL) error("invalid target IP address"); if (inet_pton(AF_INET, cp, &(arpopt->ar_old_tpa)) != 1) error("invalid target IP address"); if ((cp = (char *)strtok(NULL, ",")) == NULL) /* overwrite all target IP address */ arpopt->ar_tpa_flag = 1; else { /* overwrite matching IP address only */ arpopt->ar_tpa_flag = 2; if (inet_pton(AF_INET, cp, &(arpopt->ar_new_tpa)) != 1) error("invalid target IP address"); } free(str); str = NULL; break; default: usage(); } } } else if (header_opt == IP) { ipopt = (struct ipopt *)malloc(sizeof(struct ipopt)); if (ipopt == NULL) error("malloc(): cannot allocate memory for ipopt"); memset(ipopt, 0, sizeof(struct ipopt)); while ((c = getopt(argc, argv, "c:e:i:f:o:t:p:s:d:")) != -1) { switch (c) { case 'c': /* 6-bit DS field ('c' for codepoints; 'd' taken by destination IP) */ v = strtol(optarg, NULL, 0); if (v < 0 || v > DS_FIELD_MAX) error("DS field is out of range"); ipopt->ip_ds_field = (uint8_t)v; ipopt->ip_ds_field_flag = 1; break; case 'e': /* 2-bit ECN field */ v = strtol(optarg, NULL, 0); if (v < 0 || v > ECN_FIELD_MAX) error("ECN field is out of range"); ipopt->ip_ecn_field = (uint8_t)v; ipopt->ip_ecn_field_flag = 1; break; case 'i': /* identification */ set_number_options(optarg, &ipopt->ip_old_id, &ipopt->ip_new_id, &ipopt->ip_id_flag, sizeof(uint16_t)); break; case 'f': /* flags */ for (c = 0; optarg[c]; c++) optarg[c] = tolower(optarg[c]); if (strchr(optarg, 'r') != NULL) /* reserved bit */ ipopt->ip_flag_r = 1; if (strchr(optarg, 'd') != NULL) /* don't fragment bit */ ipopt->ip_flag_d = 1; if (strchr(optarg, 'm') != NULL) /* more fragment bit */ ipopt->ip_flag_m = 1; if (strchr(optarg, '-') != NULL) { /* remove flags */ ipopt->ip_flag_r = 0; ipopt->ip_flag_d = 0; ipopt->ip_flag_m = 0; } ipopt->ip_flags_flag = 1; break; case 'o': /* fragment offset */ v = strtol(optarg, NULL, 0); if (v < 0 || v > IP_FO_MAX) error("IP fragment offset is out of range"); ipopt->ip_fo = (uint16_t)v; ipopt->ip_fo_flag = 1; break; case 't': /* time to live */ set_number_options(optarg, &ipopt->ip_old_ttl, &ipopt->ip_new_ttl, &ipopt->ip_ttl_flag, sizeof(uint8_t)); break; case 'p': /* protocol */ set_number_options(optarg, &ipopt->ip_old_p, &ipopt->ip_new_p, &ipopt->ip_p_flag, sizeof(uint8_t)); break; case 's': /* source IP */ set_in_addr_options(optarg, &ipopt->ip_src); break; case 'd': /* destination IP */ set_in_addr_options(optarg, &ipopt->ip_dst); break; default: usage(); } } } else if (header_opt == IP6) { ip6opt = (struct ip6opt *)malloc(sizeof(struct ip6opt)); if (ip6opt == NULL) error("malloc(): cannot allocate memory for ip6opt"); memset(ip6opt, 0, sizeof(struct ip6opt)); while ((c = getopt(argc, argv, "c:e:f:n:h:s:d:")) != -1) { switch (c) { case 'c': /* 6-bit DS field ('c' for codepoints; 'd' taken by destination IP) */ v = strtol(optarg, NULL, 0); if (v < 0 || v > DS_FIELD_MAX) error("DS field is out of range"); ip6opt->ip6_ds_field = (uint8_t)v; ip6opt->ip6_ds_field_flag = 1; break; case 'e': /* 2-bit ECN field */ v = strtol(optarg, NULL, 0); if (v < 0 || v > ECN_FIELD_MAX) error("ECN field is out of range"); ip6opt->ip6_ecn_field = (uint8_t)v; ip6opt->ip6_ecn_field_flag = 1; break; case 'f': /* 20-bit flow label: 0x00000 to 0xfffff (1048575) */ v = strtol(optarg, NULL, 0); if (v < 0 || v > IP6_FLOW_LABEL_MAX) error("IPv6 flow label is out of range"); ip6opt->ip6_flow_label = (uint32_t)v; ip6opt->ip6_flow_label_flag = 1; break; case 'n': /* 8-bit next header */ set_number_options(optarg, &ip6opt->ip6_old_next_header, &ip6opt->ip6_new_next_header, &ip6opt->ip6_next_header_flag, sizeof(uint8_t)); break; case 'h': /* 8-bit hop limit */ set_number_options(optarg, &ip6opt->ip6_old_hop_limit, &ip6opt->ip6_new_hop_limit, &ip6opt->ip6_hop_limit_flag, sizeof(uint8_t)); break; case 's': /* source IP */ set_in6_addr_options(optarg, &ip6opt->ip6_src); break; case 'd': /* destination IP */ set_in6_addr_options(optarg, &ip6opt->ip6_dst); break; default: usage(); } } } else if (header_opt == ICMP) { icmpopt = (struct icmpopt *)malloc(sizeof(struct icmpopt)); if (icmpopt == NULL) error("malloc(): cannot allocate memory for icmpopt"); memset(icmpopt, 0, sizeof(struct icmpopt)); while ((c = getopt(argc, argv, "t:c:")) != -1) { switch (c) { case 't': /* type, e.g. 8 for echo request, 0 for echo reply */ v = strtol(optarg, NULL, 0); if (v < 0 || v > UCHAR_MAX) error("ICMP type is out of range"); icmpopt->icmp_type = (uint8_t)v; icmpopt->icmp_type_flag = 1; break; case 'c': /* code */ v = strtol(optarg, NULL, 0); if (v < 0 || v > UCHAR_MAX) error("ICMP code is out of range"); icmpopt->icmp_code = (uint8_t)v; icmpopt->icmp_code_flag = 1; break; default: usage(); } } } else if (header_opt == ICMP6) { icmp6opt = (struct icmp6opt *)malloc(sizeof(struct icmp6opt)); if (icmp6opt == NULL) error("malloc(): cannot allocate memory for icmp6opt"); memset(icmp6opt, 0, sizeof(struct icmp6opt)); while ((c = getopt(argc, argv, "t:c:")) != -1) { switch (c) { case 't': /* type, e.g. 128 for echo request, 129 for echo reply */ v = strtol(optarg, NULL, 0); if (v < 0 || v > UCHAR_MAX) error("ICMPv6 type is out of range"); icmp6opt->icmp6_type = (uint8_t)v; icmp6opt->icmp6_type_flag = 1; break; case 'c': /* code */ v = strtol(optarg, NULL, 0); if (v < 0 || v > UCHAR_MAX) error("ICMPv6 code is out of range"); icmp6opt->icmp6_code = (uint8_t)v; icmp6opt->icmp6_code_flag = 1; break; default: usage(); } } } else if (header_opt == TCP) { tcpopt = (struct tcpopt *)malloc(sizeof(struct tcpopt)); if (tcpopt == NULL) error("malloc(): cannot allocate memory for tcpopt"); memset(tcpopt, 0, sizeof(struct tcpopt)); while ((c = getopt(argc, argv, "s:d:q:a:f:w:u:")) != -1) { switch (c) { case 's': /* source port */ set_number_options(optarg, &tcpopt->th_old_sport, &tcpopt->th_new_sport, &tcpopt->th_sport_flag, sizeof(uint16_t)); break; case 'd': /* destination port */ set_number_options(optarg, &tcpopt->th_old_dport, &tcpopt->th_new_dport, &tcpopt->th_dport_flag, sizeof(uint16_t)); break; case 'q': /* sequence number */ set_number_options(optarg, &tcpopt->th_old_seq, &tcpopt->th_new_seq, &tcpopt->th_seq_flag, sizeof(uint32_t)); break; case 'a': /* acknowledgment number */ set_number_options(optarg, &tcpopt->th_old_ack, &tcpopt->th_new_ack, &tcpopt->th_ack_flag, sizeof(uint32_t)); break; case 'f': /* flags */ for (c = 0; optarg[c]; c++) optarg[c] = tolower(optarg[c]); if (strchr(optarg, 'c') != NULL) /* CWR */ tcpopt->th_flag_c = 1; if (strchr(optarg, 'e') != NULL) /* ECE */ tcpopt->th_flag_e = 1; if (strchr(optarg, 'u') != NULL) /* URG */ tcpopt->th_flag_u = 1; if (strchr(optarg, 'a') != NULL) /* ACK */ tcpopt->th_flag_a = 1; if (strchr(optarg, 'p') != NULL) /* PSH */ tcpopt->th_flag_p = 1; if (strchr(optarg, 'r') != NULL) /* RST */ tcpopt->th_flag_r = 1; if (strchr(optarg, 's') != NULL) /* SYN */ tcpopt->th_flag_s = 1; if (strchr(optarg, 'f') != NULL) /* FIN */ tcpopt->th_flag_f = 1; if (strchr(optarg, '-') != NULL) { /* remove flags */ tcpopt->th_flag_c = 0; tcpopt->th_flag_e = 0; tcpopt->th_flag_u = 0; tcpopt->th_flag_a = 0; tcpopt->th_flag_p = 0; tcpopt->th_flag_r = 0; tcpopt->th_flag_s = 0; tcpopt->th_flag_f = 0; } tcpopt->th_flags_flag = 1; break; case 'w': /* window size */ v = strtol(optarg, NULL, 0); if (v < 0 || v > USHRT_MAX) error("TCP window size is out of range"); tcpopt->th_win = (uint16_t)v; tcpopt->th_win_flag = 1; break; case 'u': /* urgent pointer */ v = strtol(optarg, NULL, 0); if (v < 0 || v > USHRT_MAX) error("TCP urgent pointer is out of range"); tcpopt->th_urp = (uint16_t)v; tcpopt->th_urp_flag = 1; break; default: usage(); } } } else if (header_opt == UDP) { udpopt = (struct udpopt *)malloc(sizeof(struct udpopt)); if (udpopt == NULL) error("malloc(): cannot allocate memory for udpopt"); memset(udpopt, 0, sizeof(struct udpopt)); while ((c = getopt(argc, argv, "s:d:")) != -1) { switch (c) { case 's': /* source port */ set_number_options(optarg, &udpopt->uh_old_sport, &udpopt->uh_new_sport, &udpopt->uh_sport_flag, sizeof(uint16_t)); break; case 'd': /* destination port */ set_number_options(optarg, &udpopt->uh_old_dport, &udpopt->uh_new_dport, &udpopt->uh_dport_flag, sizeof(uint16_t)); break; default: usage(); } } } /* NOTREACHED */ } void parse_trace(char *infile, char *outfile) { FILE *fp; /* file pointer to input file */ FILE *fp_outfile; /* file pointer to output file */ struct pcap_file_header preamble; struct pcap_sf_pkthdr *header; uint8_t *pkt_data; /* original packet data starting from link-layer hdr */ int repeat_index = 0; /* to track number of times we have read input file */ int pkt_index; /* to check if we are within start_opt and end_opt for range specification */ load_input_file(infile, &fp); notice("output file: %s", outfile); if ((fp_outfile = fopen(outfile, "wb")) == NULL) error("fopen(): error creating %s", outfile); /* preamble occupies the first 24 bytes of a trace file */ if (fread(&preamble, sizeof(preamble), 1, fp) == 0) error("fread(): error reading %s", infile); if (preamble.magic != PCAP_MAGIC && preamble.magic != NSEC_PCAP_MAGIC) error("%s is not a valid pcap based trace file", infile); /* we have timestamps in nanosecond resolution */ if (preamble.magic == NSEC_PCAP_MAGIC) nsec = true; /* override pcap preamble link type with user specified link type */ if (linktype_opt >= 0) preamble.linktype = linktype_opt; /* write preamble to output file */ if (fwrite(&preamble, sizeof(preamble), 1, fp_outfile) != 1) error("fwrite(): error writing %s", outfile); /* pcap hdr */ header = (struct pcap_sf_pkthdr *)calloc(1, PCAP_HDR_LEN); if (header == NULL) error("calloc(): cannot allocate memory for header"); /* check -N to duplicate packets */ while (repeat_index <= repeat_opt) { /* * loop through the remaining data by reading the pcap hdr first. * pcap hdr (16 bytes) = secs. + usecs./nsecs. + caplen + len */ pkt_index = 1; while (fread(header, PCAP_HDR_LEN, 1, fp)) { /* original packet data starting from link-layer hdr */ pkt_data = (uint8_t *)malloc(sizeof(uint8_t) * header->caplen); if (pkt_data == NULL) error("malloc(): cannot allocate memory for pkt_data"); /* copy captured packet data starting from link-layer hdr into pkt_data */ if (fread(pkt_data, header->caplen, 1, fp) == 0) error("fread(): error reading %s", infile); /* check -R to select range of packets */ if ((pkt_index >= start_opt && pkt_index <= end_opt) || (start_opt == 0 && end_opt == 0)) { /* check -S to select packets within a timeframe */ if ((header->ts.tv_sec >= start_sec_opt && header->ts.tv_sec <= end_sec_opt) || (start_sec_opt == 0 && end_sec_opt == 0)) { /* check -D to truncate packet */ if (start_oset_opt != 0 && end_oset_opt != 0 && start_oset_opt <= header->caplen) truncate_packet(pkt_data, header, outfile, &fp_outfile); else modify_packet(pkt_data, header, outfile, &fp_outfile); ++pkts; /* packets written */ } } free(pkt_data); pkt_data = NULL; ++pkt_index; } /* reset to start of input file and skip preamble */ if (fseek(fp, sizeof(preamble), SEEK_SET) != 0) error("fseek(): error reading %s", infile); ++repeat_index; } /* get bytes written */ if (fseek(fp_outfile, 0, SEEK_END) != 0) error("fseek(): error writing %s", outfile); bytes = ftell(fp_outfile); if (bytes == -1) error("ftell(): error writing %s", outfile); free(header); header = NULL; (void)fclose(fp); (void)fclose(fp_outfile); } void truncate_packet(const uint8_t *pkt_data, struct pcap_sf_pkthdr *header, char *outfile, FILE **fp_outfile) { int i; int len; /* original header->caplen */ int end_o; /* aligned end_oset_opt */ /* align end_oset_opt so that it does not go beyond header->caplen */ if (end_oset_opt > header->caplen) end_o = header->caplen; else end_o = end_oset_opt; len = header->caplen; /* original capture length (before byte deletion) */ header->caplen = header->len = len - ((end_o - start_oset_opt) + 1); /* write pcap header */ if (fwrite(header, PCAP_HDR_LEN, 1, *fp_outfile) != 1) error("fwrite(): error writing %s", outfile); for (i = 0; i < start_oset_opt - 1; i++) { if (fputc(pkt_data[i], *fp_outfile) == EOF) error("fputc(): error writing %s", outfile); } for (i = end_o; i < len; i++) { if (fputc(pkt_data[i], *fp_outfile) == EOF) error("fputc(): error writing %s", outfile); } } void modify_packet(const uint8_t *pkt_data, struct pcap_sf_pkthdr *header, char *outfile, FILE **fp_outfile) { uint8_t *new_pkt_data; /* modified pkt_data inclusive of pcap hdr is written here */ int ret; int i; /* modified pkt_data inclusive of pcap hdr */ new_pkt_data = (uint8_t *)malloc(sizeof(uint8_t) * (PCAP_HDR_LEN + ETH_MAX_LEN)); /* 16 + 1514 bytes */ if (new_pkt_data == NULL) error("malloc(): cannot allocate memory for new_pkt_data"); memset(new_pkt_data, 0, PCAP_HDR_LEN + ETH_MAX_LEN); /* * encapsulated editing function starting from link-layer hdr. * parse_eth() returns bytes written in new_pkt_data starting from link-layer hdr */ ret = parse_eth(pkt_data, new_pkt_data, header) + PCAP_HDR_LEN; /* we are editing pcap hdr to apply custom inter-packet gap */ if (gap_start_opt > 0) update_pcap_hdr(header); /* copy pcap hdr into new_pkt_data */ memcpy(new_pkt_data, header, PCAP_HDR_LEN); /* no changes */ if (ret == PCAP_HDR_LEN) { /* parse_eth() returns 0 */ /* write pcap hdr */ if (fwrite(header, PCAP_HDR_LEN, 1, *fp_outfile) != 1) error("fwrite(): error writing %s", outfile); if (fwrite(pkt_data, header->caplen, 1, *fp_outfile) != 1) error("fwrite(): error writing %s", outfile); } /* overwrite the entire pkt_data with new_pkt_data */ else if (ret == header->caplen + PCAP_HDR_LEN) { if (fwrite(new_pkt_data, ret, 1, *fp_outfile) != 1) error("fwrite(): error writing %s", outfile); } else { if (fwrite(new_pkt_data, ret, 1, *fp_outfile) != 1) error("fwrite(): error writing %s", outfile); /* write remaining bytes from pkt_data */ for (i = ret - PCAP_HDR_LEN; i < header->caplen; i++) { if (fputc(pkt_data[i], *fp_outfile) == EOF) error("fputc(): error writing %s", outfile); } } free(new_pkt_data); new_pkt_data = NULL; } void load_input_file(char *infile, FILE **fp) { /* attempt to load from built-in template first, i.e. without actual input file */ if (strcasecmp(infile, "eth") == 0) { notice("input file: %s (Ethernet header template)", infile); *fp = fmemopen((void *)TEMPLATE_PCAP_ETH, TEMPLATE_PCAP_ETH_LEN, "r"); } else if (strcasecmp(infile, "arp") == 0) { notice("input file: %s (ARP header template)", infile); *fp = fmemopen((void *)TEMPLATE_PCAP_ARP, TEMPLATE_PCAP_ARP_LEN, "r"); } else if (strcasecmp(infile, "ip") == 0) { notice("input file: %s (IPv4 header template)", infile); *fp = fmemopen((void *)TEMPLATE_PCAP_IP, TEMPLATE_PCAP_IP_LEN, "r"); } else if (strcasecmp(infile, "ip6") == 0) { notice("input file: %s (IPv6 header template)", infile); *fp = fmemopen((void *)TEMPLATE_PCAP_IP6, TEMPLATE_PCAP_IP6_LEN, "r"); } else if (strcasecmp(infile, "icmp") == 0) { notice("input file: %s (ICMPv4 header template)", infile); *fp = fmemopen((void *)TEMPLATE_PCAP_ICMP, TEMPLATE_PCAP_ICMP_LEN, "r"); } else if (strcasecmp(infile, "icmp6") == 0) { notice("input file: %s (ICMPv6 header template)", infile); *fp = fmemopen((void *)TEMPLATE_PCAP_ICMP6, TEMPLATE_PCAP_ICMP6_LEN, "r"); } else if (strcasecmp(infile, "tcp") == 0) { notice("input file: %s (IPv4 TCP header template)", infile); *fp = fmemopen((void *)TEMPLATE_PCAP_TCP, TEMPLATE_PCAP_TCP_LEN, "r"); } else if (strcasecmp(infile, "ip6tcp") == 0) { notice("input file: %s (IPv6 TCP header template)", infile); *fp = fmemopen((void *)TEMPLATE_PCAP_IP6_TCP, TEMPLATE_PCAP_IP6_TCP_LEN, "r"); } else if (strcasecmp(infile, "udp") == 0) { notice("input file: %s (IPv4 UDP header template)", infile); *fp = fmemopen((void *)TEMPLATE_PCAP_UDP, TEMPLATE_PCAP_UDP_LEN, "r"); } else if (strcasecmp(infile, "ip6udp") == 0) { notice("input file: %s (IPv6 UDP header template)", infile); *fp = fmemopen((void *)TEMPLATE_PCAP_IP6_UDP, TEMPLATE_PCAP_IP6_UDP_LEN, "r"); } else { /* load actual input file */ notice("input file: %s", infile); if ((*fp = fopen(infile, "rb")) == NULL) error("fopen(): error reading %s", infile); } } void update_pcap_hdr(struct pcap_sf_pkthdr *header) { uint64_t us; if (gap_start_opt == gap_end_opt) us = gap_start_opt; else us = gap_start_opt + get_random_number(gap_end_opt - gap_start_opt); if (nsec) pcap_timeval_nsadd(&gap_last_ts, us * 1000); else pcap_timeval_usadd(&gap_last_ts, us); header->ts = gap_last_ts; } uint16_t parse_eth(const uint8_t *pkt_data, uint8_t *new_pkt_data, struct pcap_sf_pkthdr *header) { /* * Ethernet header (14 bytes) * 1. destination MAC (6 bytes) * 2. source MAC (6 bytes) * 3. type (2 bytes) */ struct ethhdr *eth_hdr; uint16_t eth_type; int i; /* do nothing if Ethernet hdr is truncated */ if (header->caplen < ETH_HDR_LEN) return (0); eth_hdr = (struct ethhdr *)malloc(ETH_HDR_LEN); if (eth_hdr == NULL) error("malloc(): cannot allocate memory for eth_hdr"); /* copy Ethernet hdr from pkt_data into eth_hdr */ memcpy(eth_hdr, pkt_data, ETH_HDR_LEN); /* we are editing Ethernet hdr */ if (header_opt == ETH) update_eth_hdr(eth_hdr); eth_type = ntohs(eth_hdr->eth_type); /* * go pass pcap hdr in new_pkt_data * then copy eth_hdr into new_pkt_data * and reset pointer to the beginning of new_pkt_data */ i = 0; while (i++ < PCAP_HDR_LEN) (void)*new_pkt_data++; memcpy(new_pkt_data, eth_hdr, ETH_HDR_LEN); free(eth_hdr); eth_hdr = NULL; i = 0; while (i++ < PCAP_HDR_LEN) (void)*new_pkt_data--; /* copy up to layer 2 only, discard remaining data */ if (layer_opt == 2) { /* we are editing Ethernet hdr and we have payload */ if (header_opt == ETH && payload_len_opt > 0) { /* truncate payload if it is too large */ if ((payload_len_opt + ETH_HDR_LEN) > ETH_MAX_LEN) payload_len_opt -= (payload_len_opt + ETH_HDR_LEN) - ETH_MAX_LEN; /* * go pass pcap hdr and Ethernet hdr in new_pkt_data * then copy payload_opt into new_pkt_data * and reset pointer to the beginning of new_pkt_data */ i = 0; while (i++ < PCAP_HDR_LEN + ETH_HDR_LEN) (void)*new_pkt_data++; memcpy(new_pkt_data, payload_opt, payload_len_opt); i = 0; while (i++ < PCAP_HDR_LEN + ETH_HDR_LEN) (void)*new_pkt_data--; header->caplen = header->len = ETH_HDR_LEN + payload_len_opt; } else header->caplen = header->len = ETH_HDR_LEN; return (header->caplen); } /* parse ARP datagram */ if (eth_type == ETH_TYPE_ARP) return (parse_arp(pkt_data, new_pkt_data, header)); /* parse IP datagram */ else if (eth_type == ETH_TYPE_IP) return (parse_ip(pkt_data, new_pkt_data, header, NULL, 0)); /* parse IPv6 datagram */ else if (eth_type == ETH_TYPE_IPV6) return (parse_ip6(pkt_data, new_pkt_data, header)); /* no further editing support for other datagram */ else return (ETH_HDR_LEN); } void update_eth_hdr(struct ethhdr *eth_hdr) { /* overwrite destination MAC */ if (ethopt->dhost.flag == FIELD_SET) memcpy(eth_hdr->eth_dhost, ethopt->dhost.old, ETH_ADDR_LEN); else if (ethopt->dhost.flag == FIELD_REPLACE && memcmp(eth_hdr->eth_dhost, ethopt->dhost.old, ETH_ADDR_LEN) == 0) memcpy(eth_hdr->eth_dhost, ethopt->dhost.new, ETH_ADDR_LEN); else if (ethopt->dhost.flag == FIELD_SET_RAND || (ethopt->dhost.flag == FIELD_REPLACE_RAND && memcmp(eth_hdr->eth_dhost, ethopt->dhost.old, ETH_ADDR_LEN) == 0)) set_random_eth_addr(eth_hdr->eth_dhost); /* overwrite source MAC */ if (ethopt->shost.flag == FIELD_SET) memcpy(eth_hdr->eth_shost, ethopt->shost.old, ETH_ADDR_LEN); else if (ethopt->shost.flag == FIELD_REPLACE && memcmp(eth_hdr->eth_shost, ethopt->shost.old, ETH_ADDR_LEN) == 0) memcpy(eth_hdr->eth_shost, ethopt->shost.new, ETH_ADDR_LEN); else if (ethopt->shost.flag == FIELD_SET_RAND || (ethopt->shost.flag == FIELD_REPLACE_RAND && memcmp(eth_hdr->eth_shost, ethopt->shost.old, ETH_ADDR_LEN) == 0)) set_random_eth_addr(eth_hdr->eth_shost); /* overwrite Ethernet type */ if (ethopt->eth_type != 0) eth_hdr->eth_type = htons(ethopt->eth_type); } uint16_t parse_arp(const uint8_t *pkt_data, uint8_t *new_pkt_data, struct pcap_sf_pkthdr *header) { /* * Ethernet ARP header (28 bytes) * 1. hardware type (2 bytes) * 2. protocol type (2 bytes) * 3. hardware address length (1 byte) * 4. protocol address length (1 byte) * 5. opcode (2 bytes) * 6. sender hardware address (6 bytes) * 7. sender protocol address (4 bytes) * 8. target hardware address (6 bytes) * 9. target protocol address (4 bytes) */ struct arphdr *arp_hdr; int i; /* do nothing if ARP hdr is truncated */ if (header->caplen < ETH_HDR_LEN + ARP_HDR_LEN) return (ETH_HDR_LEN); /* go pass Ethernet hdr in pkt_data */ i = 0; while (i++ < ETH_HDR_LEN) (void)*pkt_data++; arp_hdr = (struct arphdr *)malloc(ARP_HDR_LEN); if (arp_hdr == NULL) error("malloc(): cannot allocate memory for arp_hdr"); /* copy ARP hdr from pkt_data into arp_hdr */ memcpy(arp_hdr, pkt_data, ARP_HDR_LEN); /* reset pointer to the beginning of pkt_data */ i = 0; while (i++ < ETH_HDR_LEN) (void)*pkt_data--; /* do nothing if this is an unsupported ARP hdr */ if (arp_hdr->ar_hln != ETH_ADDR_LEN || arp_hdr->ar_pln != IP_ADDR_LEN) { free(arp_hdr); arp_hdr = NULL; return (ETH_HDR_LEN); } /* we are editing ARP hdr */ if (header_opt == ARP) update_arp_hdr(arp_hdr); /* * go pass pcap hdr and Ethernet hdr in new_pkt_data * then copy arp_hdr into new_pkt_data * and reset pointer to the beginning of new_pkt_data */ i = 0; while (i++ < PCAP_HDR_LEN + ETH_HDR_LEN) (void)*new_pkt_data++; memcpy(new_pkt_data, arp_hdr, ARP_HDR_LEN); free(arp_hdr); arp_hdr = NULL; i = 0; while (i++ < PCAP_HDR_LEN + ETH_HDR_LEN) (void)*new_pkt_data--; /* copy up to layer 3 only, discard remaining data */ if (layer_opt == 3) { /* we are editing ARP hdr and we have payload */ if (header_opt == ARP && payload_len_opt > 0) { /* truncate payload if it is too large */ if ((payload_len_opt + ETH_HDR_LEN + ARP_HDR_LEN) > ETH_MAX_LEN) payload_len_opt -= (payload_len_opt + ETH_HDR_LEN + ARP_HDR_LEN) - ETH_MAX_LEN; /* * go pass pcap hdr, Ethernet hdr and ARP hdr in new_pkt_data * then copy payload_opt into new_pkt_data * and reset pointer to the beginning of new_pkt_data */ i = 0; while (i++ < PCAP_HDR_LEN + ETH_HDR_LEN + ARP_HDR_LEN) (void)*new_pkt_data++; memcpy(new_pkt_data, payload_opt, payload_len_opt); i = 0; while (i++ < PCAP_HDR_LEN + ETH_HDR_LEN + ARP_HDR_LEN) (void)*new_pkt_data--; header->caplen = header->len = ETH_HDR_LEN + ARP_HDR_LEN + payload_len_opt; } else header->caplen = header->len = ETH_HDR_LEN + ARP_HDR_LEN; return (header->caplen); } /* no further editing support after ARP hdr */ return (ETH_HDR_LEN + ARP_HDR_LEN); } void update_arp_hdr(struct arphdr *arp_hdr) { /* overwrite opcode */ if (arpopt->ar_op_flag) arp_hdr->ar_op = htons(arpopt->ar_op); /* overwrite sender MAC */ if (arpopt->sha.flag == FIELD_SET) memcpy(arp_hdr->ar_sha, arpopt->sha.old, ETH_ADDR_LEN); else if (arpopt->sha.flag == FIELD_REPLACE && memcmp(arp_hdr->ar_sha, arpopt->sha.old, ETH_ADDR_LEN) == 0) memcpy(arp_hdr->ar_sha, arpopt->sha.new, ETH_ADDR_LEN); else if (arpopt->sha.flag == FIELD_SET_RAND || (arpopt->sha.flag == FIELD_REPLACE_RAND && memcmp(arp_hdr->ar_sha, arpopt->sha.old, ETH_ADDR_LEN) == 0)) set_random_eth_addr(arp_hdr->ar_sha); /* overwrite sender IP */ if (arpopt->ar_spa_flag == 1) /* overwrite all sender IP */ memcpy(arp_hdr->ar_spa, arpopt->ar_old_spa, IP_ADDR_LEN); else if (arpopt->ar_spa_flag == 2 && /* overwrite matching IP only */ memcmp(arp_hdr->ar_spa, arpopt->ar_old_spa, IP_ADDR_LEN) == 0) memcpy(arp_hdr->ar_spa, arpopt->ar_new_spa, IP_ADDR_LEN); /* overwrite target MAC */ if (arpopt->tha.flag == FIELD_SET) memcpy(arp_hdr->ar_tha, arpopt->tha.old, ETH_ADDR_LEN); else if (arpopt->tha.flag == FIELD_REPLACE && memcmp(arp_hdr->ar_tha, arpopt->tha.old, ETH_ADDR_LEN) == 0) memcpy(arp_hdr->ar_tha, arpopt->tha.new, ETH_ADDR_LEN); else if (arpopt->tha.flag == FIELD_SET_RAND || (arpopt->tha.flag == FIELD_REPLACE_RAND && memcmp(arp_hdr->ar_tha, arpopt->tha.old, ETH_ADDR_LEN) == 0)) set_random_eth_addr(arp_hdr->ar_tha); /* overwrite target IP */ if (arpopt->ar_tpa_flag == 1) /* overwrite all target IP */ memcpy(arp_hdr->ar_tpa, arpopt->ar_old_tpa, IP_ADDR_LEN); else if (arpopt->ar_tpa_flag == 2 && /* overwrite matching IP only */ memcmp(arp_hdr->ar_tpa, arpopt->ar_old_tpa, IP_ADDR_LEN) == 0) memcpy(arp_hdr->ar_tpa, arpopt->ar_new_tpa, IP_ADDR_LEN); } uint16_t parse_ip(const uint8_t *pkt_data, uint8_t *new_pkt_data, struct pcap_sf_pkthdr *header, struct ip *ip_hdr, int flag) { /* * IP header (20 bytes + optional X bytes for options) * 1. version (4 bits) * 2. header length (4 bits) * 3. service type (1 byte) * 4. total length (2 bytes) * 5. id (2 bytes) * 6. flag (3 bits) * 7. fragment offset (13 bits) * 8. ttl (1 byte) * 9. protocol (1 byte) * 10. header checksum (2 bytes) * 11. source IP (4 bytes) * 12. destination IP (4 bytes) * 13. options (X bytes) */ uint16_t ip_hlb; /* hdr length in bytes */ uint8_t r = '\0'; /* flags */ uint8_t d = '\0'; uint8_t m = '\0'; uint8_t ip_p = '\0'; /* protocol */ uint8_t *ip_o = NULL; /* options (X bytes) */ int i, j; /* * flag is 0; entry from Ethernet hdr to edit IP hdr. * flag is 1; entry from ICMP, TCP or UDP hdr to update IP total length and recalculate * checksum for IP hdr. */ if (flag == 0 && ip_hdr == NULL) { /* do nothing if IP hdr is truncated */ if (header->caplen < ETH_HDR_LEN + IP_HDR_LEN) return (ETH_HDR_LEN); /* go pass Ethernet hdr in pkt_data */ i = 0; while (i++ < ETH_HDR_LEN) (void)*pkt_data++; ip_hdr = (struct ip *)malloc(IP_HDR_LEN); if (ip_hdr == NULL) error("malloc(): cannot allocate memory for ip_hdr"); /* copy IP hdr from pkt_data into ip_hdr */ memcpy(ip_hdr, pkt_data, IP_HDR_LEN); } ip_hlb = ip_hdr->ip_hl * 4; /* convert to bytes */ /* have IP options */ if (ip_hlb > IP_HDR_LEN) { /* do nothing if IP hdr with options is truncated */ if (header->caplen < ETH_HDR_LEN + ip_hlb) { /* reset pointer to the beginning of pkt_data */ i = 0; while (i++ < ETH_HDR_LEN) (void)*pkt_data--; free(ip_hdr); ip_hdr = NULL; return (ETH_HDR_LEN); } ip_o = (uint8_t *)malloc(sizeof(uint8_t) * (ip_hlb - IP_HDR_LEN)); if (ip_o == NULL) error("malloc(): cannot allocate memory for ip_o"); /* copy IP options into ip_o */ for (i = 0, j = IP_HDR_LEN; i < (ip_hlb - IP_HDR_LEN); i++, j++) ip_o[i] = pkt_data[j]; } if (flag == 0) { /* reset pointer to the beginning of pkt_data */ i = 0; while (i++ < ETH_HDR_LEN) (void)*pkt_data--; /* we are editing IP hdr */ if (header_opt == IP) { /* original flags */ r = (ntohs(ip_hdr->ip_off) & IP_RF) > 0 ? 1 : 0; d = (ntohs(ip_hdr->ip_off) & IP_DF) > 0 ? 1 : 0; m = (ntohs(ip_hdr->ip_off) & IP_MF) > 0 ? 1 : 0; update_ip_hdr(ip_hdr, &r, &d, &m); } /* * if more fragment flag is set, we should not parse the protocol hdr * (ICMP, TCP, or UDP) just yet since this is a fragmented packet */ m = (ntohs(ip_hdr->ip_off) & IP_MF) > 0 ? 1 : 0; ip_p = ip_hdr->ip_p; /* we are going to copy up to layer 3 only, change total length */ if (layer_opt == 3) { /* we are editing IP hdr and we have payload, include its length in total length */ if (header_opt == IP && payload_len_opt > 0) { /* truncate payload if it is too large */ if ((payload_len_opt + ETH_HDR_LEN + ip_hlb) > ETH_MAX_LEN) payload_len_opt -= (payload_len_opt + ETH_HDR_LEN + ip_hlb) - ETH_MAX_LEN; ip_hdr->ip_len = htons(ip_hlb + payload_len_opt); } else ip_hdr->ip_len = htons(ip_hlb); } } /* recalculate checksum (cover IP hdr only) */ if (csum_opt) update_ip_cksum(ip_hdr, ip_o, &ip_hlb); /* * go pass pcap hdr and Ethernet hdr in new_pkt_data * then copy ip_hdr and ip_o (if exist) into new_pkt_data * and reset pointer to the beginning of new_pkt_data */ i = 0; while (i++ < PCAP_HDR_LEN + ETH_HDR_LEN) (void)*new_pkt_data++; memcpy(new_pkt_data, ip_hdr, IP_HDR_LEN); /* have IP options */ if (ip_hlb > IP_HDR_LEN) { i = 0; while (i++ < IP_HDR_LEN) (void)*new_pkt_data++; memcpy(new_pkt_data, ip_o, ip_hlb - IP_HDR_LEN); free(ip_o); ip_o = NULL; i = 0; while (i++ < IP_HDR_LEN) (void)*new_pkt_data--; } i = 0; while (i++ < PCAP_HDR_LEN + ETH_HDR_LEN) (void)*new_pkt_data--; if (flag == 0) { /* copy up to layer 3 only, discard remaining data */ if (layer_opt == 3) { /* we are editing IP hdr and we have payload */ if (header_opt == IP && payload_len_opt > 0) { /* * go pass pcap hdr, Ethernet hdr and IP hdr in new_pkt_data * then copy payload_opt into new_pkt_data * and reset pointer to the beginning of new_pkt_data */ i = 0; while (i++ < PCAP_HDR_LEN + ETH_HDR_LEN + ip_hlb) (void)*new_pkt_data++; memcpy(new_pkt_data, payload_opt, payload_len_opt); i = 0; while (i++ < PCAP_HDR_LEN + ETH_HDR_LEN + ip_hlb) (void)*new_pkt_data--; header->caplen = header->len = ETH_HDR_LEN + ip_hlb + payload_len_opt; /* * if payload is specified and it applies to ICMP, TCP, or UDP hdr + data, * and checksum correction on this payload is needed, * and more fragment flag is not set -> not a fragmented packet */ if (csum_opt && !m) { /* parse ICMP datagram */ if (ip_p == IPPROTO_ICMP) return (parse_icmp(pkt_data, new_pkt_data, header, ip_hdr)); /* parse TCP datagram */ else if (ip_p == IPPROTO_TCP) return (parse_tcp(pkt_data, new_pkt_data, header, ip_hdr)); /* parse UDP datagram */ else if (ip_p == IPPROTO_UDP) return (parse_udp(pkt_data, new_pkt_data, header, ip_hdr)); } } else header->caplen = header->len = ETH_HDR_LEN + ip_hlb; free(ip_hdr); ip_hdr = NULL; return (header->caplen); } /* !m means more fragment flag is not set -> not a fragmented packet */ if (!m) { /* parse ICMP datagram */ if (ip_p == IPPROTO_ICMP) return (parse_icmp(pkt_data, new_pkt_data, header, ip_hdr)); /* parse TCP datagram */ else if (ip_p == IPPROTO_TCP) return (parse_tcp(pkt_data, new_pkt_data, header, ip_hdr)); /* parse UDP datagram */ else if (ip_p == IPPROTO_UDP) return (parse_udp(pkt_data, new_pkt_data, header, ip_hdr)); } /* no further editing support for other datagram or fragmented packet */ free(ip_hdr); return (ETH_HDR_LEN + ip_hlb); } return (0); /* flag is 1 */ } void update_ip_cksum(struct ip *ip_hdr, uint8_t *ip_o, uint16_t *ip_hlb) { uint8_t *ip_hdr_o; /* IP hdr with options (for hdr checksum calculation) */ int i; ip_hdr->ip_sum = 0x0000; /* clear checksum field */ /* have IP options */ if (*ip_hlb > IP_HDR_LEN) { ip_hdr_o = (uint8_t *)malloc(sizeof(uint8_t) * (*ip_hlb)); if (ip_hdr_o == NULL) error("malloc(): cannot allocate memory for ip_hdr_o"); /* * copy ip_hdr into ip_hdr_o, go pass IP hdr in ip_hdr_o * then copy ip_o into ip_hdr_o * and reset pointer to the beginning of ip_hdr_o * and finally calculate checksum of ip_hdr_o */ memcpy(ip_hdr_o, ip_hdr, IP_HDR_LEN); i = 0; while (i++ < IP_HDR_LEN) (void)*ip_hdr_o++; memcpy(ip_hdr_o, ip_o, *ip_hlb - IP_HDR_LEN); i = 0; while (i++ < IP_HDR_LEN) (void)*ip_hdr_o--; ip_hdr->ip_sum = cksum(ip_hdr_o, *ip_hlb); free(ip_hdr_o); ip_hdr_o = NULL; } else ip_hdr->ip_sum = cksum((uint8_t *)ip_hdr, *ip_hlb); } void update_ip_hdr(struct ip *ip_hdr, uint8_t *r, uint8_t *d, uint8_t *m) { uint16_t ip_fo; /* fragment offset (number of 64-bit segments) */ /* overwrite first 6-bit (DS field) of 8-bit type of service field */ if (ipopt->ip_ds_field_flag) /* left shifted DS field value by 2-bit ECN field */ ip_hdr->ip_tos |= ipopt->ip_ds_field << 2; /* overwrite last 2-bit (ECN field) of 8-bit type of service field */ if (ipopt->ip_ecn_field_flag) ip_hdr->ip_tos |= ipopt->ip_ecn_field; /* overwrite identification */ if (ipopt->ip_id_flag == FIELD_SET) ip_hdr->ip_id = htons(ipopt->ip_old_id); else if (ipopt->ip_id_flag == FIELD_REPLACE && ip_hdr->ip_id == htons(ipopt->ip_old_id)) ip_hdr->ip_id = htons(ipopt->ip_new_id); else if (ipopt->ip_id_flag == FIELD_SET_RAND || (ipopt->ip_id_flag == FIELD_REPLACE_RAND && ip_hdr->ip_id == htons(ipopt->ip_old_id))) ip_hdr->ip_id = htons(get_random_number(UINT16_MAX)); /* original fragment offset */ ip_fo = ntohs(ip_hdr->ip_off) & IP_OFFMASK; /* overwrite fragment offset only */ if (ipopt->ip_fo_flag && !ipopt->ip_flags_flag) { ip_hdr->ip_off = htons((ipopt->ip_fo & IP_OFFMASK) | (*r ? IP_RF : 0) | (*d ? IP_DF : 0) | (*m ? IP_MF : 0)); } /* overwrite flags only */ else if (!ipopt->ip_fo_flag && ipopt->ip_flags_flag) { ip_hdr->ip_off = htons((ip_fo & IP_OFFMASK) | ((ipopt->ip_flag_r) ? IP_RF : 0) | ((ipopt->ip_flag_d) ? IP_DF : 0) | ((ipopt->ip_flag_m) ? IP_MF : 0)); } /* overwrite fragment offset and flags */ else if (ipopt->ip_fo_flag && ipopt->ip_flags_flag) { ip_hdr->ip_off = htons((ipopt->ip_fo & IP_OFFMASK) | ((ipopt->ip_flag_r) ? IP_RF : 0) | ((ipopt->ip_flag_d) ? IP_DF : 0) | ((ipopt->ip_flag_m) ? IP_MF : 0)); } /* overwrite time to live */ if (ipopt->ip_ttl_flag == FIELD_SET) ip_hdr->ip_ttl = ipopt->ip_old_ttl; else if (ipopt->ip_ttl_flag == FIELD_REPLACE && ip_hdr->ip_ttl == ipopt->ip_old_ttl) ip_hdr->ip_ttl = ipopt->ip_new_ttl; else if (ipopt->ip_ttl_flag == FIELD_SET_RAND || (ipopt->ip_ttl_flag == FIELD_REPLACE_RAND && ip_hdr->ip_ttl == ipopt->ip_old_ttl)) ip_hdr->ip_ttl = get_random_number(UINT8_MAX); /* overwrite protocol */ if (ipopt->ip_p_flag == FIELD_SET) ip_hdr->ip_p = ipopt->ip_old_p; else if (ipopt->ip_p_flag == FIELD_REPLACE && ip_hdr->ip_p == htons(ipopt->ip_old_p)) ip_hdr->ip_p = ipopt->ip_new_p; else if (ipopt->ip_p_flag == FIELD_SET_RAND || (ipopt->ip_p_flag == FIELD_REPLACE_RAND && ip_hdr->ip_p == ipopt->ip_old_p)) ip_hdr->ip_p = get_random_number(UINT8_MAX); /* overwrite source IP */ if (ipopt->ip_src.flag == FIELD_SET) memcpy(&ip_hdr->ip_src, &ipopt->ip_src.old, sizeof(struct in_addr)); else if (ipopt->ip_src.flag == FIELD_REPLACE && memcmp(&ip_hdr->ip_src, &ipopt->ip_src.old, sizeof(struct in_addr)) == 0) memcpy(&ip_hdr->ip_src, &ipopt->ip_src.new, sizeof(struct in_addr)); else if (ipopt->ip_src.flag == FIELD_SET_RAND || (ipopt->ip_src.flag == FIELD_REPLACE_RAND && memcmp(&ip_hdr->ip_src, &ipopt->ip_src.old, sizeof(struct in_addr)) == 0)) set_random_in_addr(&ip_hdr->ip_src, &ipopt->ip_src); /* overwrite destination IP */ if (ipopt->ip_dst.flag == FIELD_SET) memcpy(&ip_hdr->ip_dst, &ipopt->ip_dst.old, sizeof(struct in_addr)); else if (ipopt->ip_dst.flag == FIELD_REPLACE && memcmp(&ip_hdr->ip_dst, &ipopt->ip_dst.old, sizeof(struct in_addr)) == 0) memcpy(&ip_hdr->ip_dst, &ipopt->ip_dst.new, sizeof(struct in_addr)); else if (ipopt->ip_dst.flag == FIELD_SET_RAND || (ipopt->ip_dst.flag == FIELD_REPLACE_RAND && memcmp(&ip_hdr->ip_dst, &ipopt->ip_dst.old, sizeof(struct in_addr)) == 0)) set_random_in_addr(&ip_hdr->ip_dst, &ipopt->ip_dst); } uint16_t parse_ip6(const uint8_t *pkt_data, uint8_t *new_pkt_data, struct pcap_sf_pkthdr *header) { /* * IPv6 header (40 bytes + optional X bytes for extension headers) * 1. version (4 bits) * 2. traffic class (8 bits) * 3. flow label (20 bits) * 4. payload length (16 bits) - rest of packet after 40 bytes headers * 5. next header (8 bits) - same values as IPv4 protocol field * 6. hop limit (8 bits) * 7. source address (128 bits) * 8. destination address (128 bits) * 9. extension headers (X bytes) */ struct ip6 *ip6_hdr; int i; /* do nothing if IPv6 hdr is truncated */ if (header->caplen < ETH_HDR_LEN + IP6_HDR_LEN) return (ETH_HDR_LEN); /* go pass Ethernet hdr in pkt_data */ i = 0; while (i++ < ETH_HDR_LEN) (void)*pkt_data++; ip6_hdr = (struct ip6 *)malloc(sizeof(struct ip6)); if (ip6_hdr == NULL) error("malloc(): cannot allocate memory for ip6_hdr"); /* copy IPv6 hdr from pkt_data into ip6_hdr */ memcpy(ip6_hdr, pkt_data, IP6_HDR_LEN); /* reset pointer to the beginning of pkt_data */ i = 0; while (i++ < ETH_HDR_LEN) (void)*pkt_data--; /* do nothing if next hdr is unsupported */ if (ip6_hdr->ip6_nxt != IPPROTO_TCP && ip6_hdr->ip6_nxt != IPPROTO_UDP && ip6_hdr->ip6_nxt != IPPROTO_ICMPV6) { free(ip6_hdr); ip6_hdr = NULL; return (ETH_HDR_LEN); } /* we are editing IPv6 hdr */ if (header_opt == IP6) update_ip6_hdr(ip6_hdr); /* we are going to copy up to layer 3 only, change payload length */ if (layer_opt == 3) { /* we are editing IPv6 hdr and we have payload, use its length as payload length */ if (header_opt == IP6 && payload_len_opt > 0) { /* truncate payload if it is too large */ if ((payload_len_opt + ETH_HDR_LEN + IP6_HDR_LEN) > ETH_MAX_LEN) payload_len_opt -= (payload_len_opt + ETH_HDR_LEN + IP6_HDR_LEN) - ETH_MAX_LEN; ip6_hdr->ip6_plen = htons(payload_len_opt); } else ip6_hdr->ip6_plen = 0; } write_ip6_hdr(new_pkt_data, ip6_hdr); /* copy up to layer 3 only, discard remaining data */ if (layer_opt == 3) { /* we are editing IPv6 hdr and we have payload */ if (header_opt == IP6 && payload_len_opt > 0) { /* * go pass pcap hdr, Ethernet hdr and IPv6 hdr in new_pkt_data * then copy payload_opt into new_pkt_data * and reset pointer to the beginning of new_pkt_data */ i = 0; while (i++ < PCAP_HDR_LEN + ETH_HDR_LEN + IP6_HDR_LEN) (void)*new_pkt_data++; memcpy(new_pkt_data, payload_opt, payload_len_opt); i = 0; while (i++ < PCAP_HDR_LEN + ETH_HDR_LEN + IP6_HDR_LEN) (void)*new_pkt_data--; header->caplen = header->len = ETH_HDR_LEN + IP6_HDR_LEN + payload_len_opt; /* * if payload is specified and it applies to ICMPv6, TCP, or UDP hdr + data, * and checksum correction on this payload is needed */ if (csum_opt) { /* parse ICMPv6 datagram */ if (ip6_hdr->ip6_nxt == IPPROTO_ICMPV6) return (parse_icmp6(pkt_data, new_pkt_data, header, ip6_hdr)); /* parse TCP datagram */ else if (ip6_hdr->ip6_nxt == IPPROTO_TCP) return (parse_tcp6(pkt_data, new_pkt_data, header, ip6_hdr)); /* parse UDP datagram */ else if (ip6_hdr->ip6_nxt == IPPROTO_UDP) return (parse_udp6(pkt_data, new_pkt_data, header, ip6_hdr)); } } else header->caplen = header->len = ETH_HDR_LEN + IP6_HDR_LEN; free(ip6_hdr); ip6_hdr = NULL; return (header->caplen); } /* parse ICMPv6 datagram */ if (ip6_hdr->ip6_nxt == IPPROTO_ICMPV6) return (parse_icmp6(pkt_data, new_pkt_data, header, ip6_hdr)); /* parse TCP datagram */ else if (ip6_hdr->ip6_nxt == IPPROTO_TCP) return (parse_tcp6(pkt_data, new_pkt_data, header, ip6_hdr)); /* parse UDP datagram */ else if (ip6_hdr->ip6_nxt == IPPROTO_UDP) return (parse_udp6(pkt_data, new_pkt_data, header, ip6_hdr)); /* no further editing support for other datagram */ free(ip6_hdr); ip6_hdr = NULL; return (ETH_HDR_LEN + IP6_HDR_LEN); } void update_ip6_hdr(struct ip6 *ip6_hdr) { /* overwrite first 6-bit (DS field) of 8-bit traffic class field */ if (ip6opt->ip6_ds_field_flag) /* left shifted DS field value by 2-bit ECN field + 20-bit flow label */ ip6_hdr->ip6_flow |= htonl(ip6opt->ip6_ds_field << 22); /* overwrite last 2-bit (ECN field) of 8-bit traffic class field */ if (ip6opt->ip6_ecn_field_flag) /* left shifted ECN field value by 20-bit flow label */ ip6_hdr->ip6_flow |= htonl(ip6opt->ip6_ecn_field << 20); /* overwrite flow label */ if (ip6opt->ip6_flow_label_flag) { /* keep 4-bit version and 8-bit traffic class but overwrite 20-bit flow label */ ip6_hdr->ip6_flow = (ip6_hdr->ip6_flow & ~IP6_FLOWLABEL_MASK) | (htonl(ip6opt->ip6_flow_label) & IP6_FLOWLABEL_MASK); } /* overwrite next header */ if (ip6opt->ip6_next_header_flag == FIELD_SET) ip6_hdr->ip6_nxt = ip6opt->ip6_old_next_header; else if (ip6opt->ip6_next_header_flag == FIELD_REPLACE && ip6_hdr->ip6_nxt == ip6opt->ip6_old_next_header) ip6_hdr->ip6_nxt = ip6opt->ip6_new_next_header; else if (ip6opt->ip6_next_header_flag == FIELD_SET_RAND || (ip6opt->ip6_next_header_flag == FIELD_REPLACE_RAND && ip6_hdr->ip6_nxt == ip6opt->ip6_old_next_header)) ip6_hdr->ip6_nxt = get_random_number(UINT8_MAX); /* overwrite hop limit */ if (ip6opt->ip6_hop_limit_flag == FIELD_SET) ip6_hdr->ip6_hlim = ip6opt->ip6_old_hop_limit; else if (ip6opt->ip6_hop_limit_flag == FIELD_REPLACE && ip6_hdr->ip6_hlim == ip6opt->ip6_old_hop_limit) ip6_hdr->ip6_hlim = ip6opt->ip6_new_hop_limit; else if (ip6opt->ip6_hop_limit_flag == FIELD_SET_RAND || (ip6opt->ip6_hop_limit_flag == FIELD_REPLACE_RAND && ip6_hdr->ip6_hlim == ip6opt->ip6_old_hop_limit)) ip6_hdr->ip6_hlim = get_random_number(UINT8_MAX); /* overwrite source IP */ if (ip6opt->ip6_src.flag == FIELD_SET) memcpy(&ip6_hdr->ip6_src, &ip6opt->ip6_src.old, sizeof(struct in6_addr)); else if (ip6opt->ip6_src.flag == FIELD_REPLACE && memcmp(&ip6_hdr->ip6_src, &ip6opt->ip6_src.old, sizeof(struct in6_addr)) == 0) memcpy(&ip6_hdr->ip6_src, &ip6opt->ip6_src.new, sizeof(struct in6_addr)); else if (ip6opt->ip6_src.flag == FIELD_SET_RAND || (ip6opt->ip6_src.flag == FIELD_REPLACE_RAND && memcmp(&ip6_hdr->ip6_src, &ip6opt->ip6_src.old, sizeof(struct in6_addr)) == 0)) set_random_in6_addr(&ip6_hdr->ip6_src, &ip6opt->ip6_src); /* overwrite destination IP */ if (ip6opt->ip6_dst.flag == FIELD_SET) memcpy(&ip6_hdr->ip6_dst, &ip6opt->ip6_dst.old, sizeof(struct in6_addr)); else if (ip6opt->ip6_dst.flag == FIELD_REPLACE && memcmp(&ip6_hdr->ip6_dst, &ip6opt->ip6_dst.old, sizeof(struct in6_addr)) == 0) memcpy(&ip6_hdr->ip6_dst, &ip6opt->ip6_dst.new, sizeof(struct in6_addr)); else if (ip6opt->ip6_dst.flag == FIELD_SET_RAND || (ip6opt->ip6_dst.flag == FIELD_REPLACE_RAND && memcmp(&ip6_hdr->ip6_dst, &ip6opt->ip6_dst.old, sizeof(struct in6_addr)) == 0)) set_random_in6_addr(&ip6_hdr->ip6_dst, &ip6opt->ip6_dst); } void write_ip6_hdr(uint8_t *new_pkt_data, struct ip6 *ip6_hdr) { int i; /* * go pass pcap hdr and Ethernet hdr in new_pkt_data * then copy ip6_hdr into new_pkt_data * and reset pointer to the beginning of new_pkt_data */ i = 0; while (i++ < PCAP_HDR_LEN + ETH_HDR_LEN) (void)*new_pkt_data++; memcpy(new_pkt_data, ip6_hdr, IP6_HDR_LEN); i = 0; while (i++ < PCAP_HDR_LEN + ETH_HDR_LEN) (void)*new_pkt_data--; } uint16_t parse_icmp(const uint8_t *pkt_data, uint8_t *new_pkt_data, struct pcap_sf_pkthdr *header, struct ip *ip_hdr) { /* * ICMP header (4 bytes) * 1. type (1 byte) * 2. code (1 byte) * 3. checksum (2 bytes) */ struct icmphdr *icmp_hdr; uint16_t ip_hlb; /* IP hdr length in bytes */ uint16_t ip_fo; /* IP fragment offset (number of 64-bit segments) */ int i; ip_hlb = ip_hdr->ip_hl * 4; /* convert to bytes */ /* do nothing if ICMP hdr is truncated */ if (header->caplen < ETH_HDR_LEN + ip_hlb + ICMP_HDR_LEN) { free(ip_hdr); ip_hdr = NULL; return (ETH_HDR_LEN + ip_hlb); } icmp_hdr = (struct icmphdr *)malloc(ICMP_HDR_LEN); if (icmp_hdr == NULL) error("malloc(): cannot allocate memory for icmp_hdr"); /* * we have payload which covers ICMP hdr + data, * use that payload instead of pkt_data */ if (layer_opt == 3 && header_opt == IP && payload_len_opt > 0) { /* * go pass pcap hdr, Ethernet hdr and IP hdr in new_pkt_data * then copy ICMP hdr from new_pkt_data into icmp_hdr * and reset pointer to the beginning of new_pkt_data */ i = 0; while (i++ < PCAP_HDR_LEN + ETH_HDR_LEN + ip_hlb) (void)*new_pkt_data++; memcpy(icmp_hdr, new_pkt_data, ICMP_HDR_LEN); i = 0; while (i++ < PCAP_HDR_LEN + ETH_HDR_LEN + ip_hlb) (void)*new_pkt_data--; } else { /* * go pass Ethernet hdr and IP hdr in pkt_data * then copy ICMP hdr from pkt_data into icmp_hdr * and reset pointer to the beginning of pkt_data */ i = 0; while (i++ < (ETH_HDR_LEN + ip_hlb)) (void)*pkt_data++; memcpy(icmp_hdr, pkt_data, ICMP_HDR_LEN); i = 0; while (i++ < (ETH_HDR_LEN + ip_hlb)) (void)*pkt_data--; /* we are editing ICMP hdr */ if (header_opt == ICMP) { /* overwrite type */ if (icmpopt->icmp_type_flag) icmp_hdr->icmp_type = icmpopt->icmp_type; /* overwrite code */ if (icmpopt->icmp_code_flag) icmp_hdr->icmp_code = icmpopt->icmp_code; } /* we are going to copy up to layer 4 only */ if (layer_opt == 4) { /* * we are editing ICMP hdr and we have payload, * attach the payload first before checksum calculation */ if (header_opt == ICMP && payload_len_opt > 0) { /* truncate payload if it is too large */ if ((payload_len_opt + ETH_HDR_LEN + ip_hlb + ICMP_HDR_LEN) > ETH_MAX_LEN) payload_len_opt -= (payload_len_opt + ETH_HDR_LEN + ip_hlb + ICMP_HDR_LEN) - ETH_MAX_LEN; /* * go pass pcap hdr, Ethernet hdr, IP hdr and ICMP hdr in new_pkt_data * then copy payload_opt into new_pkt_data * and reset pointer to the beginning of new_pkt_data */ i = 0; while (i++ < PCAP_HDR_LEN + ETH_HDR_LEN + ip_hlb + ICMP_HDR_LEN) (void)*new_pkt_data++; memcpy(new_pkt_data, payload_opt, payload_len_opt); i = 0; while (i++ < PCAP_HDR_LEN + ETH_HDR_LEN + ip_hlb + ICMP_HDR_LEN) (void)*new_pkt_data--; header->caplen = header->len = ETH_HDR_LEN + ip_hlb + ICMP_HDR_LEN + payload_len_opt; } else header->caplen = header->len = ETH_HDR_LEN + ip_hlb + ICMP_HDR_LEN; /* update IP total length */ ip_hdr->ip_len = htons(header->caplen - ETH_HDR_LEN); /* go pass Ethernet hdr in pkt_data */ i = 0; while (i++ < ETH_HDR_LEN) (void)*pkt_data++; /* * reuse parsing function for IP hdr * to update IP total length in new_pkt_data * and recalculate checksum for IP hdr if required */ (void)parse_ip(pkt_data, new_pkt_data, header, ip_hdr, 1); /* reset pointer to the beginning of pkt_data */ i = 0; while (i++ < ETH_HDR_LEN) (void)*pkt_data--; } } /* we have no support for checksum calculation for fragmented packet */ ip_fo = ntohs(ip_hdr->ip_off) & IP_OFFMASK; /* * recalculate checksum for ICMP hdr (cover ICMP hdr + trailing data) * if we have enough data */ if (csum_opt && ip_fo == 0 && header->caplen >= (ETH_HDR_LEN + ntohs(ip_hdr->ip_len))) update_icmp_cksum(pkt_data, ip_hdr, icmp_hdr, &ip_hlb); free(ip_hdr); ip_hdr = NULL; /* * go pass pcap hdr, Ethernet hdr and IP hdr in new_pkt_data * then copy icmp_hdr into new_pkt_data * and reset pointer to the beginning of new_pkt_data */ i = 0; while (i++ < PCAP_HDR_LEN + ETH_HDR_LEN + ip_hlb) (void)*new_pkt_data++; memcpy(new_pkt_data, icmp_hdr, ICMP_HDR_LEN); free(icmp_hdr); icmp_hdr = NULL; i = 0; while (i++ < PCAP_HDR_LEN + ETH_HDR_LEN + ip_hlb) (void)*new_pkt_data--; /* no further editing support after ICMP hdr */ if (layer_opt == 4) return (header->caplen); /* * we have written payload_opt (payload after IP hdr) which covers ICMP hdr + data, * checksum for ICMP hdr corrected above, * while ICMP data is written to new_pkt_data in parse_ip() */ else if (layer_opt == 3) return (header->caplen); else return (ETH_HDR_LEN + ip_hlb + ICMP_HDR_LEN); } void update_icmp_cksum(const uint8_t *pkt_data, struct ip *ip_hdr, struct icmphdr *icmp_hdr, uint16_t *ip_hlb) { uint8_t *icmpp; /* ICMP hdr + trailing data */ uint16_t icmpp_len; int i; icmpp_len = ntohs(ip_hdr->ip_len) - *ip_hlb; icmpp = (uint8_t *)malloc(sizeof(uint8_t) * icmpp_len); if (icmpp == NULL) error("malloc(): cannot allocate memory for icmpp"); memset(icmpp, 0, icmpp_len); /* clear checksum field */ icmp_hdr->icmp_cksum = 0x0000; /* copy ICMP hdr from icmp_hdr into icmpp */ memcpy(icmpp, icmp_hdr, ICMP_HDR_LEN); /* copy trailing data from payload_opt into icmpp */ if (layer_opt == 4 && header_opt == ICMP && payload_len_opt > 0) { for (i = ICMP_HDR_LEN; i < (ICMP_HDR_LEN + payload_len_opt); i++) icmpp[i] = payload_opt[i - ICMP_HDR_LEN]; } /* copy trailing data from payload_opt (payload after IP hdr) into icmpp */ else if (layer_opt == 3 && header_opt == IP && payload_len_opt > 0) { for (i = ICMP_HDR_LEN; i < payload_len_opt; i++) icmpp[i] = payload_opt[i]; } /* copy trailing data from pkt_data into icmpp */ else { for (i = ICMP_HDR_LEN; i < icmpp_len; i++) icmpp[i] = pkt_data[ETH_HDR_LEN + *ip_hlb + i]; } /* recalculate checksum */ icmp_hdr->icmp_cksum = cksum(icmpp, icmpp_len); free(icmpp); icmpp = NULL; } uint16_t parse_icmp6(const uint8_t *pkt_data, uint8_t *new_pkt_data, struct pcap_sf_pkthdr *header, struct ip6 *ip6_hdr) { /* * ICMPv6 header (4 bytes) * 1. type (1 byte) * 2. code (1 byte) * 3. checksum (2 bytes) */ struct icmp6hdr *icmp6_hdr; int i; /* do nothing if ICMPv6 hdr is truncated */ if (header->caplen < ETH_HDR_LEN + IP6_HDR_LEN + ICMP6_HDR_LEN) { free(ip6_hdr); ip6_hdr = NULL; return (ETH_HDR_LEN + IP6_HDR_LEN); } icmp6_hdr = (struct icmp6hdr *)malloc(ICMP6_HDR_LEN); if (icmp6_hdr == NULL) error("malloc(): cannot allocate memory for icmp6_hdr"); /* * we have payload which covers ICMPv6 hdr + data, * use that payload instead of pkt_data */ if (layer_opt == 3 && header_opt == IP6 && payload_len_opt > 0) { /* * go pass pcap hdr, Ethernet hdr and IPv6 hdr in new_pkt_data * then copy ICMPv6 hdr from new_pkt_data into icmp6_hdr * and reset pointer to the beginning of new_pkt_data */ i = 0; while (i++ < PCAP_HDR_LEN + ETH_HDR_LEN + IP6_HDR_LEN) (void)*new_pkt_data++; memcpy(icmp6_hdr, new_pkt_data, ICMP6_HDR_LEN); i = 0; while (i++ < PCAP_HDR_LEN + ETH_HDR_LEN + IP6_HDR_LEN) (void)*new_pkt_data--; } else { /* * go pass Ethernet hdr and IPv6 hdr in pkt_data * then copy ICMPv6 hdr from pkt_data into icmp6_hdr * and reset pointer to the beginning of pkt_data */ i = 0; while (i++ < (ETH_HDR_LEN + IP6_HDR_LEN)) (void)*pkt_data++; memcpy(icmp6_hdr, pkt_data, ICMP6_HDR_LEN); i = 0; while (i++ < (ETH_HDR_LEN + IP6_HDR_LEN)) (void)*pkt_data--; /* we are editing ICMPv6 hdr */ if (header_opt == ICMP6) { /* overwrite type */ if (icmp6opt->icmp6_type_flag) icmp6_hdr->icmp6_type = icmp6opt->icmp6_type; /* overwrite code */ if (icmp6opt->icmp6_code_flag) icmp6_hdr->icmp6_code = icmp6opt->icmp6_code; } /* we are going to copy up to layer 4 only */ if (layer_opt == 4) { /* * we are editing ICMPv6 hdr and we have payload, * attach the payload first before checksum calculation */ if (header_opt == ICMP6 && payload_len_opt > 0) { /* truncate payload if it is too large */ if ((payload_len_opt + ETH_HDR_LEN + IP6_HDR_LEN + ICMP6_HDR_LEN) > ETH_MAX_LEN) payload_len_opt -= (payload_len_opt + ETH_HDR_LEN + IP6_HDR_LEN + ICMP6_HDR_LEN) - ETH_MAX_LEN; /* * go pass pcap hdr, Ethernet hdr, IPv6 hdr and ICMPv6 hdr in new_pkt_data * then copy payload_opt into new_pkt_data * and reset pointer to the beginning of new_pkt_data */ i = 0; while (i++ < PCAP_HDR_LEN + ETH_HDR_LEN + IP6_HDR_LEN + ICMP6_HDR_LEN) (void)*new_pkt_data++; memcpy(new_pkt_data, payload_opt, payload_len_opt); i = 0; while (i++ < PCAP_HDR_LEN + ETH_HDR_LEN + IP6_HDR_LEN + ICMP6_HDR_LEN) (void)*new_pkt_data--; header->caplen = header->len = ETH_HDR_LEN + IP6_HDR_LEN + ICMP6_HDR_LEN + payload_len_opt; } else header->caplen = header->len = ETH_HDR_LEN + IP6_HDR_LEN + ICMP6_HDR_LEN; /* update IPv6 payload length */ ip6_hdr->ip6_plen = htons(header->caplen - ETH_HDR_LEN); write_ip6_hdr(new_pkt_data, ip6_hdr); } } /* * recalculate checksum for ICMPv6 hdr (cover IPv6 pseudo hdr + ICMPv6 hdr + trailing data) * if we have enough data */ if (csum_opt && header->caplen >= (ETH_HDR_LEN + IP6_HDR_LEN + ntohs(ip6_hdr->ip6_plen))) update_icmp6_cksum(pkt_data, ip6_hdr, icmp6_hdr); free(ip6_hdr); ip6_hdr = NULL; /* * go pass pcap hdr, Ethernet hdr and IPv6 hdr in new_pkt_data * then copy icmp6_hdr into new_pkt_data * and reset pointer to the beginning of new_pkt_data */ i = 0; while (i++ < PCAP_HDR_LEN + ETH_HDR_LEN + IP6_HDR_LEN) (void)*new_pkt_data++; memcpy(new_pkt_data, icmp6_hdr, ICMP6_HDR_LEN); free(icmp6_hdr); icmp6_hdr = NULL; i = 0; while (i++ < PCAP_HDR_LEN + ETH_HDR_LEN + IP6_HDR_LEN) (void)*new_pkt_data--; /* no further editing support after ICMPv6 hdr */ if (layer_opt == 4) return (header->caplen); /* * we have written payload_opt (payload after IPv6 hdr) which covers ICMPv6 hdr + data, * checksum for ICMPv6 hdr corrected above, * while ICMPv6 data is written to new_pkt_data in parse_ip6() */ else if (layer_opt == 3) return (header->caplen); else return (ETH_HDR_LEN + IP6_HDR_LEN + ICMP6_HDR_LEN); } void update_icmp6_cksum(const uint8_t *pkt_data, struct ip6 *ip6_hdr, struct icmp6hdr *icmp6_hdr) { struct ip6pseudo *ip6p; /* IPv6 pseudo hdr */ uint8_t *icmp6p; /* IPv6 pseudo hdr + ICMPv6 hdr + trailing data */ uint16_t icmp6p_len; int i; /* create IP pseudo hdr */ ip6p = create_ip6pseudo(ip6_hdr); icmp6p_len = sizeof(struct ip6pseudo) + ntohs(ip6p->ip6pseudo_len); icmp6p = (uint8_t *)malloc(sizeof(uint8_t) * icmp6p_len); if (icmp6p == NULL) error("malloc(): cannot allocate memory for icmp6p"); memset(icmp6p, 0, icmp6p_len); /* copy IPv6 pseudo hdr from ip6p into icmp6p */ memcpy(icmp6p, ip6p, sizeof(struct ip6pseudo)); free(ip6p); ip6p = NULL; /* go pass IPv6 pseudo hdr in icmp6p */ i = 0; while (i++ < sizeof(struct ip6pseudo)) (void)*icmp6p++; /* clear checksum field */ icmp6_hdr->icmp6_cksum = 0x0000; /* copy ICMPv6 hdr from icmp6_hdr into icmp6p */ memcpy(icmp6p, icmp6_hdr, ICMP6_HDR_LEN); /* reset pointer to the beginning of icmp6p */ i = 0; while (i++ < sizeof(struct ip6pseudo)) (void)*icmp6p--; /* copy trailing data from payload_opt into icmp6p */ if (layer_opt == 4 && header_opt == ICMP6 && payload_len_opt > 0) { for (i = ICMP6_HDR_LEN; i < (icmp6p_len - sizeof(struct ip6pseudo)); i++) icmp6p[i + sizeof(struct ip6pseudo)] = payload_opt[i - ICMP6_HDR_LEN]; } /* copy trailing data from payload_opt (payload after IPv6 hdr) into icmp6p */ else if (layer_opt == 3 && header_opt == IP6 && payload_len_opt > 0) { for (i = ICMP6_HDR_LEN; i < payload_len_opt; i++) icmp6p[i + sizeof(struct ip6pseudo)] = payload_opt[i]; } /* copy trailing data from pkt_data into icmp6p */ else { for (i = ICMP6_HDR_LEN; i < (icmp6p_len - sizeof(struct ip6pseudo)); i++) icmp6p[i + sizeof(struct ip6pseudo)] = pkt_data[ETH_HDR_LEN + IP6_HDR_LEN + i]; } /* recalculate checksum */ icmp6_hdr->icmp6_cksum = cksum(icmp6p, icmp6p_len); free(icmp6p); icmp6p = NULL; } uint16_t parse_tcp(const uint8_t *pkt_data, uint8_t *new_pkt_data, struct pcap_sf_pkthdr *header, struct ip *ip_hdr) { /* * TCP header (20 bytes + optional X bytes for options) * 1. source port (2 bytes) * 2. destination port (2 bytes) * 3. sequence number (4 bytes) * 4. acknowledgment number (4 bytes) * 5. data offset (4 bits) - number of 32-bit segments in TCP header * 6. reserved (6 bits) * 7. flags (6 bits) * 8. window (2 bytes) * 9. checksum (2 bytes) * 10. urgent pointer (2 bytes) * 11. options (X bytes) */ struct tcphdr *tcp_hdr; uint8_t *tcp_o = NULL; /* options (X bytes) */ uint16_t tcp_hlb; /* TCP hdr length in bytes */ uint16_t ip_hlb; /* IP hdr length in bytes */ uint16_t ip_fo; /* IP fragment offset (number of 64-bit segments) */ int i, j; ip_hlb = ip_hdr->ip_hl * 4; /* convert to bytes */ /* do nothing if TCP hdr is truncated */ if (header->caplen < ETH_HDR_LEN + ip_hlb + TCP_HDR_LEN) { free(ip_hdr); ip_hdr = NULL; return (ETH_HDR_LEN + ip_hlb); } tcp_hdr = (struct tcphdr *)malloc(TCP_HDR_LEN); if (tcp_hdr == NULL) error("malloc(): cannot allocate memory for tcp_hdr"); /* * we have payload which covers TCP hdr + data, * use that payload instead of pkt_data */ if (layer_opt == 3 && header_opt == IP && payload_len_opt > 0) { /* * go pass pcap hdr, Ethernet hdr and IP hdr in new_pkt_data * then copy TCP hdr from new_pkt_data into tcp_hdr * and reset pointer to the beginning of new_pkt_data */ i = 0; while (i++ < PCAP_HDR_LEN + ETH_HDR_LEN + ip_hlb) (void)*new_pkt_data++; memcpy(tcp_hdr, new_pkt_data, TCP_HDR_LEN); i = 0; while (i++ < PCAP_HDR_LEN + ETH_HDR_LEN + ip_hlb) (void)*new_pkt_data--; } else { /* * go pass Ethernet hdr and IP hdr in pkt_data * then copy TCP hdr from pkt_data into tcp_hdr * and reset pointer to the beginning of pkt_data */ i = 0; while (i++ < (ETH_HDR_LEN + ip_hlb)) (void)*pkt_data++; memcpy(tcp_hdr, pkt_data, TCP_HDR_LEN); i = 0; while (i++ < (ETH_HDR_LEN + ip_hlb)) (void)*pkt_data--; } tcp_hlb = tcp_hdr->th_off * 4; /* convert to bytes */ /* have TCP options */ if (tcp_hlb > TCP_HDR_LEN) { /* do nothing if TCP hdr with options is truncated */ if (header->caplen < (ETH_HDR_LEN + ip_hlb + tcp_hlb)) { free(ip_hdr); ip_hdr = NULL; free(tcp_hdr); tcp_hdr = NULL; return (ETH_HDR_LEN + ip_hlb); } tcp_o = (uint8_t *)malloc(sizeof(uint8_t) * (tcp_hlb - TCP_HDR_LEN)); if (tcp_o == NULL) error("malloc(): cannot allocate memory for tcp_o"); if (layer_opt == 3 && header_opt == IP && payload_len_opt > 0) { /* copy TCP options from new_pkt_data into tcp_o */ for (i = 0, j = TCP_HDR_LEN; i < (tcp_hlb - TCP_HDR_LEN); i++, j++) tcp_o[i] = new_pkt_data[PCAP_HDR_LEN + ETH_HDR_LEN + ip_hlb + j]; } else { /* copy TCP options from pkt_data into tcp_o */ for (i = 0, j = TCP_HDR_LEN; i < (tcp_hlb - TCP_HDR_LEN); i++, j++) tcp_o[i] = pkt_data[ETH_HDR_LEN + ip_hlb + j]; } } /* we are editing TCP hdr */ if (header_opt == TCP) update_tcp_hdr(tcp_hdr); /* we are going to copy up to layer 4 only */ if (layer_opt == 4) { /* * we are editing TCP hdr and we have payload, * attach the payload first before checksum calculation */ if (header_opt == TCP && payload_len_opt > 0) { /* truncate payload if it is too large */ if ((payload_len_opt + ETH_HDR_LEN + ip_hlb + tcp_hlb) > ETH_MAX_LEN) payload_len_opt -= (payload_len_opt + ETH_HDR_LEN + ip_hlb + tcp_hlb) - ETH_MAX_LEN; /* * go pass pcap hdr, Ethernet hdr, IP hdr and TCP hdr in new_pkt_data * then copy payload_opt into new_pkt_data * and reset pointer to the beginning of new_pkt_data */ i = 0; while (i++ < PCAP_HDR_LEN + ETH_HDR_LEN + ip_hlb + tcp_hlb) (void)*new_pkt_data++; memcpy(new_pkt_data, payload_opt, payload_len_opt); i = 0; while (i++ < PCAP_HDR_LEN + ETH_HDR_LEN + ip_hlb + tcp_hlb) (void)*new_pkt_data--; header->caplen = header->len = ETH_HDR_LEN + ip_hlb + tcp_hlb + payload_len_opt; } else header->caplen = header->len = ETH_HDR_LEN + ip_hlb + tcp_hlb; /* update IP total length */ ip_hdr->ip_len = htons(header->caplen - ETH_HDR_LEN); /* go pass Ethernet hdr in pkt_data */ i = 0; while (i++ < ETH_HDR_LEN) (void)*pkt_data++; /* * reuse parsing function for IP hdr * to update IP total length in new_pkt_data * and recalculate checksum for IP hdr if required */ (void)parse_ip(pkt_data, new_pkt_data, header, ip_hdr, 1); /* reset pointer to the beginning of pkt_data */ i = 0; while (i++ < ETH_HDR_LEN) (void)*pkt_data--; } /* we have no support for checksum calculation for fragmented packet */ ip_fo = ntohs(ip_hdr->ip_off) & IP_OFFMASK; /* * recalculate checksum for TCP hdr (cover IP pseudo hdr + TCP hdr + trailing data) * if we have enough data */ if (csum_opt && ip_fo == 0 && header->caplen >= (ETH_HDR_LEN + ntohs(ip_hdr->ip_len))) update_tcp_cksum(pkt_data, ip_hdr, tcp_hdr, &ip_hlb, &tcp_hlb, tcp_o); free(ip_hdr); ip_hdr = NULL; /* * go pass pcap hdr, Ethernet hdr and IP hdr in new_pkt_data * then copy tcp_hdr and tcp_o (if exist) into new_pkt_data * and reset pointer to the beginning of new_pkt_data */ i = 0; while (i++ < PCAP_HDR_LEN + ETH_HDR_LEN + ip_hlb) (void)*new_pkt_data++; memcpy(new_pkt_data, tcp_hdr, TCP_HDR_LEN); free(tcp_hdr); tcp_hdr = NULL; /* have TCP options */ if (tcp_hlb > TCP_HDR_LEN) { i = 0; while (i++ < TCP_HDR_LEN) (void)*new_pkt_data++; memcpy(new_pkt_data, tcp_o, tcp_hlb - TCP_HDR_LEN); free(tcp_o); tcp_o = NULL; i = 0; while (i++ < TCP_HDR_LEN) (void)*new_pkt_data--; } i = 0; while (i++ < PCAP_HDR_LEN + ETH_HDR_LEN + ip_hlb) (void)*new_pkt_data--; /* no further editing support after TCP hdr */ if (layer_opt == 4) return (header->caplen); /* * we have written payload_opt (payload after IP hdr) which covers TCP hdr + data, * checksum for TCP hdr corrected above, * while TCP data is written to new_pkt_data in parse_ip() */ else if (layer_opt == 3) return (header->caplen); else return (ETH_HDR_LEN + ip_hlb + tcp_hlb); } void update_tcp_cksum(const uint8_t *pkt_data, struct ip *ip_hdr, struct tcphdr *tcp_hdr, uint16_t *ip_hlb, uint16_t *tcp_hlb, uint8_t *tcp_o) { struct ippseudo *ipp; /* IP pseudo hdr */ uint8_t *tcpp; /* IP pseudo hdr + TCP hdr (with options if exist) + trailing data */ uint16_t tcpp_len; int i; /* create IP pseudo hdr */ ipp = create_ippseudo(ip_hdr, ip_hlb); tcpp_len = sizeof(struct ippseudo) + ntohs(ipp->ippseudo_len); tcpp = (uint8_t *)malloc(sizeof(uint8_t) * tcpp_len); if (tcpp == NULL) error("malloc(): cannot allocate memory for tcpp"); memset(tcpp, 0, tcpp_len); /* copy IP pseudo hdr from ipp into tcpp */ memcpy(tcpp, ipp, sizeof(struct ippseudo)); free(ipp); ipp = NULL; /* go pass IP pseudo hdr in tcpp */ i = 0; while (i++ < sizeof(struct ippseudo)) (void)*tcpp++; /* clear checksum field */ tcp_hdr->th_sum = 0x0000; /* copy TCP hdr from tcp_hdr into tcpp */ memcpy(tcpp, tcp_hdr, TCP_HDR_LEN); /* * have TCP options, * go pass TCP hdr in tcpp * then copy tcp_o into tcpp * and reset pointer of tcpp to go pass IP pseudo hdr only */ if (*tcp_hlb > TCP_HDR_LEN) { i = 0; while (i++ < TCP_HDR_LEN) (void)*tcpp++; memcpy(tcpp, tcp_o, *tcp_hlb - TCP_HDR_LEN); i = 0; while (i++ < TCP_HDR_LEN) (void)*tcpp--; } /* reset pointer to the beginning of tcpp */ i = 0; while (i++ < sizeof(struct ippseudo)) (void)*tcpp--; /* copy trailing data from payload_opt into tcpp */ if (layer_opt == 4 && header_opt == TCP && payload_len_opt > 0) { for (i = *tcp_hlb; i < (tcpp_len - sizeof(struct ippseudo)); i++) tcpp[i + sizeof(struct ippseudo)] = payload_opt[i - *tcp_hlb]; } /* copy trailing data from payload_opt (payload after IP hdr) into tcpp */ else if (layer_opt == 3 && header_opt == IP && payload_len_opt > 0) { for (i = *tcp_hlb; i < payload_len_opt; i++) tcpp[i + sizeof(struct ippseudo)] = payload_opt[i]; } /* copy trailing data from pkt_data into tcpp */ else { for (i = *tcp_hlb; i < (tcpp_len - sizeof(struct ippseudo)); i++) tcpp[i + sizeof(struct ippseudo)] = pkt_data[ETH_HDR_LEN + *ip_hlb + i]; } /* recalculate checksum */ tcp_hdr->th_sum = cksum(tcpp, tcpp_len); free(tcpp); tcpp = NULL; } uint16_t parse_tcp6(const uint8_t *pkt_data, uint8_t *new_pkt_data, struct pcap_sf_pkthdr *header, struct ip6 *ip6_hdr) { /* * TCP header (20 bytes + optional X bytes for options) * 1. source port (2 bytes) * 2. destination port (2 bytes) * 3. sequence number (4 bytes) * 4. acknowledgment number (4 bytes) * 5. data offset (4 bits) - number of 32-bit segments in TCP header * 6. reserved (6 bits) * 7. flags (6 bits) * 8. window (2 bytes) * 9. checksum (2 bytes) * 10. urgent pointer (2 bytes) * 11. options (X bytes) */ struct tcphdr *tcp_hdr; uint8_t *tcp_o = NULL; /* options (X bytes) */ uint16_t tcp_hlb; /* TCP hdr length in bytes */ int i, j; /* do nothing if TCP hdr is truncated */ if (header->caplen < ETH_HDR_LEN + IP6_HDR_LEN + TCP_HDR_LEN) { free(ip6_hdr); ip6_hdr = NULL; return (ETH_HDR_LEN + IP6_HDR_LEN); } tcp_hdr = (struct tcphdr *)malloc(TCP_HDR_LEN); if (tcp_hdr == NULL) error("malloc(): cannot allocate memory for tcp_hdr"); /* * we have payload which covers TCP hdr + data, * use that payload instead of pkt_data */ if (layer_opt == 3 && header_opt == IP6 && payload_len_opt > 0) { /* * go pass pcap hdr, Ethernet hdr and IPv6 hdr in new_pkt_data * then copy TCP hdr from new_pkt_data into tcp_hdr * and reset pointer to the beginning of new_pkt_data */ i = 0; while (i++ < PCAP_HDR_LEN + ETH_HDR_LEN + IP6_HDR_LEN) (void)*new_pkt_data++; memcpy(tcp_hdr, new_pkt_data, TCP_HDR_LEN); i = 0; while (i++ < PCAP_HDR_LEN + ETH_HDR_LEN + IP6_HDR_LEN) (void)*new_pkt_data--; } else { /* * go pass Ethernet hdr and IPv6 hdr in pkt_data * then copy TCP hdr from pkt_data into tcp_hdr * and reset pointer to the beginning of pkt_data */ i = 0; while (i++ < (ETH_HDR_LEN + IP6_HDR_LEN)) (void)*pkt_data++; memcpy(tcp_hdr, pkt_data, TCP_HDR_LEN); i = 0; while (i++ < (ETH_HDR_LEN + IP6_HDR_LEN)) (void)*pkt_data--; } tcp_hlb = tcp_hdr->th_off * 4; /* convert to bytes */ /* have TCP options */ if (tcp_hlb > TCP_HDR_LEN) { /* do nothing if TCP hdr with options is truncated */ if (header->caplen < (ETH_HDR_LEN + IP6_HDR_LEN + tcp_hlb)) { free(ip6_hdr); ip6_hdr = NULL; free(tcp_hdr); tcp_hdr = NULL; return (ETH_HDR_LEN + IP6_HDR_LEN); } tcp_o = (uint8_t *)malloc(sizeof(uint8_t) * (tcp_hlb - TCP_HDR_LEN)); if (tcp_o == NULL) error("malloc(): cannot allocate memory for tcp_o"); if (layer_opt == 3 && header_opt == IP6 && payload_len_opt > 0) { /* copy TCP options from new_pkt_data into tcp_o */ for (i = 0, j = TCP_HDR_LEN; i < (tcp_hlb - TCP_HDR_LEN); i++, j++) tcp_o[i] = new_pkt_data[PCAP_HDR_LEN + ETH_HDR_LEN + IP6_HDR_LEN + j]; } else { /* copy TCP options from pkt_data into tcp_o */ for (i = 0, j = TCP_HDR_LEN; i < (tcp_hlb - TCP_HDR_LEN); i++, j++) tcp_o[i] = pkt_data[ETH_HDR_LEN + IP6_HDR_LEN + j]; } } /* we are editing TCP hdr */ if (header_opt == TCP) update_tcp_hdr(tcp_hdr); /* we are going to copy up to layer 4 only */ if (layer_opt == 4) { /* * we are editing TCP hdr and we have payload, * attach the payload first before checksum calculation */ if (header_opt == TCP && payload_len_opt > 0) { /* truncate payload if it is too large */ if ((payload_len_opt + ETH_HDR_LEN + IP6_HDR_LEN + tcp_hlb) > ETH_MAX_LEN) payload_len_opt -= (payload_len_opt + ETH_HDR_LEN + IP6_HDR_LEN + tcp_hlb) - ETH_MAX_LEN; /* * go pass pcap hdr, Ethernet hdr, IPv6 hdr and TCP hdr in new_pkt_data * then copy payload_opt into new_pkt_data * and reset pointer to the beginning of new_pkt_data */ i = 0; while (i++ < PCAP_HDR_LEN + ETH_HDR_LEN + IP6_HDR_LEN + tcp_hlb) (void)*new_pkt_data++; memcpy(new_pkt_data, payload_opt, payload_len_opt); i = 0; while (i++ < PCAP_HDR_LEN + ETH_HDR_LEN + IP6_HDR_LEN + tcp_hlb) (void)*new_pkt_data--; header->caplen = header->len = ETH_HDR_LEN + IP6_HDR_LEN + tcp_hlb + payload_len_opt; } else header->caplen = header->len = ETH_HDR_LEN + IP6_HDR_LEN + tcp_hlb; /* update IPv6 payload length */ ip6_hdr->ip6_plen = htons(header->caplen - (ETH_HDR_LEN + IP6_HDR_LEN)); write_ip6_hdr(new_pkt_data, ip6_hdr); } /* * recalculate checksum for TCP hdr (cover IPv6 pseudo hdr + TCP hdr + trailing data) * if we have enough data */ if (csum_opt && header->caplen >= (ETH_HDR_LEN + IP6_HDR_LEN + ntohs(ip6_hdr->ip6_plen))) update_tcp6_cksum(pkt_data, ip6_hdr, tcp_hdr, &tcp_hlb, tcp_o); free(ip6_hdr); ip6_hdr = NULL; /* * go pass pcap hdr, Ethernet hdr and IPv6 hdr in new_pkt_data * then copy tcp_hdr and tcp_o (if exist) into new_pkt_data * and reset pointer to the beginning of new_pkt_data */ i = 0; while (i++ < PCAP_HDR_LEN + ETH_HDR_LEN + IP6_HDR_LEN) (void)*new_pkt_data++; memcpy(new_pkt_data, tcp_hdr, TCP_HDR_LEN); free(tcp_hdr); tcp_hdr = NULL; /* have TCP options */ if (tcp_hlb > TCP_HDR_LEN) { i = 0; while (i++ < TCP_HDR_LEN) (void)*new_pkt_data++; memcpy(new_pkt_data, tcp_o, tcp_hlb - TCP_HDR_LEN); free(tcp_o); tcp_o = NULL; i = 0; while (i++ < TCP_HDR_LEN) (void)*new_pkt_data--; } i = 0; while (i++ < PCAP_HDR_LEN + ETH_HDR_LEN + IP6_HDR_LEN) (void)*new_pkt_data--; /* no further editing support after TCP hdr */ if (layer_opt == 4) return (header->caplen); /* * we have written payload_opt (payload after IPv6 hdr) which covers TCP hdr + data, * checksum for TCP hdr corrected above, * while TCP data is written to new_pkt_data in parse_ip6() */ else if (layer_opt == 3) return (header->caplen); else return (ETH_HDR_LEN + IP6_HDR_LEN + tcp_hlb); } void update_tcp6_cksum(const uint8_t *pkt_data, struct ip6 *ip6_hdr, struct tcphdr *tcp_hdr, uint16_t *tcp_hlb, uint8_t *tcp_o) { struct ip6pseudo *ip6p; /* IPv6 pseudo hdr */ uint8_t *tcpp; /* IPv6 pseudo hdr + TCP hdr (with options if exist) + trailing data */ uint16_t tcpp_len; int i; /* create IP pseudo hdr */ ip6p = create_ip6pseudo(ip6_hdr); tcpp_len = sizeof(struct ip6pseudo) + ntohs(ip6p->ip6pseudo_len); tcpp = (uint8_t *)malloc(sizeof(uint8_t) * tcpp_len); if (tcpp == NULL) error("malloc(): cannot allocate memory for tcpp"); memset(tcpp, 0, tcpp_len); /* copy IPv6 pseudo hdr from ipp into tcpp */ memcpy(tcpp, ip6p, sizeof(struct ip6pseudo)); free(ip6p); ip6p = NULL; /* go pass IPv6 pseudo hdr in tcpp */ i = 0; while (i++ < sizeof(struct ip6pseudo)) (void)*tcpp++; /* clear checksum field */ tcp_hdr->th_sum = 0x0000; /* copy TCP hdr from tcp_hdr into tcpp */ memcpy(tcpp, tcp_hdr, TCP_HDR_LEN); /* * have TCP options, * go pass TCP hdr in tcpp * then copy tcp_o into tcpp * and reset pointer of tcpp to go pass IPv6 pseudo hdr only */ if (*tcp_hlb > TCP_HDR_LEN) { i = 0; while (i++ < TCP_HDR_LEN) (void)*tcpp++; memcpy(tcpp, tcp_o, *tcp_hlb - TCP_HDR_LEN); i = 0; while (i++ < TCP_HDR_LEN) (void)*tcpp--; } /* reset pointer to the beginning of tcpp */ i = 0; while (i++ < sizeof(struct ip6pseudo)) (void)*tcpp--; /* copy trailing data from payload_opt into tcpp */ if (layer_opt == 4 && header_opt == TCP && payload_len_opt > 0) { for (i = *tcp_hlb; i < (tcpp_len - sizeof(struct ip6pseudo)); i++) tcpp[i + sizeof(struct ip6pseudo)] = payload_opt[i - *tcp_hlb]; } /* copy trailing data from payload_opt (payload after IPv6 hdr) into tcpp */ else if (layer_opt == 3 && header_opt == IP6 && payload_len_opt > 0) { for (i = *tcp_hlb; i < payload_len_opt; i++) tcpp[i + sizeof(struct ip6pseudo)] = payload_opt[i]; } /* copy trailing data from pkt_data into tcpp */ else { for (i = *tcp_hlb; i < (tcpp_len - sizeof(struct ip6pseudo)); i++) tcpp[i + sizeof(struct ip6pseudo)] = pkt_data[ETH_HDR_LEN + IP6_HDR_LEN + i]; } /* recalculate checksum */ tcp_hdr->th_sum = cksum(tcpp, tcpp_len); free(tcpp); tcpp = NULL; } void update_tcp_hdr(struct tcphdr *tcp_hdr) { /* overwrite source port */ if (tcpopt->th_sport_flag == FIELD_SET) tcp_hdr->th_sport = htons(tcpopt->th_old_sport); else if (tcpopt->th_sport_flag == FIELD_REPLACE && tcp_hdr->th_sport == htons(tcpopt->th_old_sport)) tcp_hdr->th_sport = htons(tcpopt->th_new_sport); else if (tcpopt->th_sport_flag == FIELD_SET_RAND || (tcpopt->th_sport_flag == FIELD_REPLACE_RAND && tcp_hdr->th_sport == htons(tcpopt->th_old_sport))) tcp_hdr->th_sport = htons(get_random_number(UINT16_MAX)); /* overwrite destination port */ if (tcpopt->th_dport_flag == FIELD_SET) tcp_hdr->th_dport = htons(tcpopt->th_old_dport); else if (tcpopt->th_dport_flag == FIELD_REPLACE && tcp_hdr->th_dport == htons(tcpopt->th_old_dport)) tcp_hdr->th_dport = htons(tcpopt->th_new_dport); else if (tcpopt->th_dport_flag == FIELD_SET_RAND || (tcpopt->th_dport_flag == FIELD_REPLACE_RAND && tcp_hdr->th_dport == htons(tcpopt->th_old_dport))) tcp_hdr->th_dport = htons(get_random_number(UINT16_MAX)); /* overwrite sequence number */ if (tcpopt->th_seq_flag == FIELD_SET) tcp_hdr->th_seq = htonl(tcpopt->th_old_seq); else if (tcpopt->th_seq_flag == FIELD_REPLACE && tcp_hdr->th_seq == htonl(tcpopt->th_old_seq)) tcp_hdr->th_seq = htonl(tcpopt->th_new_seq); else if (tcpopt->th_seq_flag == FIELD_SET_RAND || (tcpopt->th_seq_flag == FIELD_REPLACE_RAND && tcp_hdr->th_seq == htonl(tcpopt->th_old_seq))) tcp_hdr->th_seq = htonl(get_random_number(UINT32_MAX)); /* overwrite acknowledgment number */ if (tcpopt->th_ack_flag == FIELD_SET) tcp_hdr->th_ack = htonl(tcpopt->th_old_ack); else if (tcpopt->th_ack_flag == FIELD_REPLACE && tcp_hdr->th_ack == htonl(tcpopt->th_old_ack)) tcp_hdr->th_ack = htonl(tcpopt->th_new_ack); else if (tcpopt->th_ack_flag == FIELD_SET_RAND || (tcpopt->th_ack_flag == FIELD_REPLACE_RAND && tcp_hdr->th_ack == htonl(tcpopt->th_old_ack))) tcp_hdr->th_ack = htonl(get_random_number(UINT32_MAX)); /* overwrite flags */ if (tcpopt->th_flags_flag) tcp_hdr->th_flags = ((tcpopt->th_flag_c ? TH_CWR : 0) | (tcpopt->th_flag_e ? TH_ECE : 0) | (tcpopt->th_flag_u ? TH_URG : 0) | (tcpopt->th_flag_a ? TH_ACK : 0) | (tcpopt->th_flag_p ? TH_PUSH : 0) | (tcpopt->th_flag_r ? TH_RST : 0) | (tcpopt->th_flag_s ? TH_SYN : 0) | (tcpopt->th_flag_f ? TH_FIN : 0)); /* overwrite window size */ if (tcpopt->th_win_flag) tcp_hdr->th_win = htons(tcpopt->th_win); /* overwrite urgent pointer */ if (tcpopt->th_urp_flag) tcp_hdr->th_urp = htons(tcpopt->th_urp); } uint16_t parse_udp(const uint8_t *pkt_data, uint8_t *new_pkt_data, struct pcap_sf_pkthdr *header, struct ip *ip_hdr) { /* * UDP header (8 bytes) * 1. source port (2 bytes) * 2. destination port (2 bytes) * 3. length (2 bytes) * 4. checksum (2 bytes) */ struct udphdr *udp_hdr; uint16_t ip_hlb; /* IP hdr length in bytes */ uint16_t ip_fo; /* IP fragment offset (number of 64-bit segments) */ int i; ip_hlb = ip_hdr->ip_hl * 4; /* convert to bytes */ /* do nothing if UDP hdr is truncated */ if (header->caplen < ETH_HDR_LEN + ip_hlb + UDP_HDR_LEN) { free(ip_hdr); ip_hdr = NULL; return (ETH_HDR_LEN + ip_hlb); } udp_hdr = (struct udphdr *)malloc(UDP_HDR_LEN); if (udp_hdr == NULL) error("malloc(): cannot allocate memory for udp_hdr"); /* * we have payload which covers UDP hdr + data, * use that payload instead of pkt_data */ if (layer_opt == 3 && header_opt == IP && payload_len_opt > 0) { /* * go pass pcap hdr, Ethernet hdr and IP hdr in new_pkt_data * then copy UDP hdr from new_pkt_data into udp_hdr * and reset pointer to the beginning of new_pkt_data */ i = 0; while (i++ < PCAP_HDR_LEN + ETH_HDR_LEN + ip_hlb) (void)*new_pkt_data++; memcpy(udp_hdr, new_pkt_data, UDP_HDR_LEN); i = 0; while (i++ < PCAP_HDR_LEN + ETH_HDR_LEN + ip_hlb) (void)*new_pkt_data--; } else { /* * go pass Ethernet hdr and IP hdr in pkt_data * then copy UDP hdr from pkt_data into udp_hdr * and reset pointer to the beginning of pkt_data */ i = 0; while (i++ < (ETH_HDR_LEN + ip_hlb)) (void)*pkt_data++; memcpy(udp_hdr, pkt_data, UDP_HDR_LEN); i = 0; while (i++ < (ETH_HDR_LEN + ip_hlb)) (void)*pkt_data--; } /* we are editing UDP hdr */ if (header_opt == UDP) update_udp_hdr(udp_hdr); /* we are going to copy up to layer 4 only */ if (layer_opt == 4) { /* * we are editing UDP hdr and we have payload, * attach the payload first before checksum calculation */ if (header_opt == UDP && payload_len_opt > 0) { /* truncate payload if it is too large */ if ((payload_len_opt + ETH_HDR_LEN + ip_hlb + UDP_HDR_LEN) > ETH_MAX_LEN) payload_len_opt -= (payload_len_opt + ETH_HDR_LEN + ip_hlb + UDP_HDR_LEN) - ETH_MAX_LEN; /* * go pass pcap hdr, Ethernet hdr, IP hdr and UDP hdr in new_pkt_data * then copy payload_opt into new_pkt_data * and reset pointer to the beginning of new_pkt_data */ i = 0; while (i++ < PCAP_HDR_LEN + ETH_HDR_LEN + ip_hlb + UDP_HDR_LEN) (void)*new_pkt_data++; memcpy(new_pkt_data, payload_opt, payload_len_opt); i = 0; while (i++ < PCAP_HDR_LEN + ETH_HDR_LEN + ip_hlb + UDP_HDR_LEN) (void)*new_pkt_data--; header->caplen = header->len = ETH_HDR_LEN + ip_hlb + UDP_HDR_LEN + payload_len_opt; } else header->caplen = header->len = ETH_HDR_LEN + ip_hlb + UDP_HDR_LEN; /* update UDP length */ udp_hdr->uh_ulen = htons(header->caplen - (ETH_HDR_LEN + ip_hlb)); /* update IP total length */ ip_hdr->ip_len = htons(header->caplen - ETH_HDR_LEN); /* go pass Ethernet hdr in pkt_data */ i = 0; while (i++ < ETH_HDR_LEN) (void)*pkt_data++; /* * reuse parsing function for IP hdr * to update IP total length in new_pkt_data * and recalculate checksum for IP hdr if required */ (void)parse_ip(pkt_data, new_pkt_data, header, ip_hdr, 1); /* reset pointer to the beginning of pkt_data */ i = 0; while (i++ < ETH_HDR_LEN) (void)*pkt_data--; } /* we have no support for checksum calculation for fragmented packet */ ip_fo = ntohs(ip_hdr->ip_off) & IP_OFFMASK; /* * recalculate checksum for UDP hdr (cover IP pseudo hdr + UDP hdr + trailing data) * if we have enough data */ if (csum_opt && ip_fo == 0 && header->caplen >= (ETH_HDR_LEN + ntohs(ip_hdr->ip_len))) update_udp_cksum(pkt_data, ip_hdr, udp_hdr, &ip_hlb); free(ip_hdr); ip_hdr = NULL; /* * go pass pcap hdr, Ethernet hdr and IP hdr in new_pkt_data * then copy udp_hdr into new_pkt_data * and reset pointer to the beginning of new_pkt_data */ i = 0; while (i++ < PCAP_HDR_LEN + ETH_HDR_LEN + ip_hlb) (void)*new_pkt_data++; memcpy(new_pkt_data, udp_hdr, UDP_HDR_LEN); free(udp_hdr); udp_hdr = NULL; i = 0; while (i++ < PCAP_HDR_LEN + ETH_HDR_LEN + ip_hlb) (void)*new_pkt_data--; /* no further editing support after UDP hdr */ if (layer_opt == 4) return (header->caplen); /* * we have written payload_opt (payload after IP hdr) which covers UDP hdr + data, * checksum for UDP hdr corrected above, * while UDP data is written to new_pkt_data in parse_ip() */ else if (layer_opt == 3) return (header->caplen); else return (ETH_HDR_LEN + ip_hlb + UDP_HDR_LEN); } void update_udp_cksum(const uint8_t *pkt_data, struct ip *ip_hdr, struct udphdr *udp_hdr, uint16_t *ip_hlb) { struct ippseudo *ipp; /* IP pseudo hdr */ uint8_t *udpp; /* IP pseudo hdr + UDP hdr + trailing data */ uint16_t udpp_len; int i; /* create IP pseudo hdr */ ipp = create_ippseudo(ip_hdr, ip_hlb); udpp_len = sizeof(struct ippseudo) + ntohs(ipp->ippseudo_len); udpp = (uint8_t *)malloc(sizeof(uint8_t) * udpp_len); if (udpp == NULL) error("malloc(): cannot allocate memory for udpp"); memset(udpp, 0, udpp_len); /* copy IP pseudo hdr from ipp into udpp */ memcpy(udpp, ipp, sizeof(struct ippseudo)); free(ipp); ipp = NULL; /* go pass IP pseudo hdr in udpp */ i = 0; while (i++ < sizeof(struct ippseudo)) (void)*udpp++; /* clear checksum field */ udp_hdr->uh_sum = 0x0000; /* copy UDP hdr from udp_hdr into udpp */ memcpy(udpp, udp_hdr, UDP_HDR_LEN); /* reset pointer to the beginning of udpp */ i = 0; while (i++ < sizeof(struct ippseudo)) (void)*udpp--; /* copy trailing data from payload_opt into udpp */ if (layer_opt == 4 && header_opt == UDP && payload_len_opt > 0) { for (i = UDP_HDR_LEN; i < (udpp_len - sizeof(struct ippseudo)); i++) udpp[i + sizeof(struct ippseudo)] = payload_opt[i - UDP_HDR_LEN]; } /* copy trailing data from payload_opt (payload after IP hdr) into udpp */ else if (layer_opt == 3 && header_opt == IP && payload_len_opt > 0) { for (i = UDP_HDR_LEN; i < payload_len_opt; i++) udpp[i + sizeof(struct ippseudo)] = payload_opt[i]; } /* copy trailing data from pkt_data into udpp */ else { for (i = UDP_HDR_LEN; i < (udpp_len - sizeof(struct ippseudo)); i++) udpp[i + sizeof(struct ippseudo)] = pkt_data[ETH_HDR_LEN + *ip_hlb + i]; } /* recalculate checksum */ udp_hdr->uh_sum = cksum(udpp, udpp_len); free(udpp); udpp = NULL; } uint16_t parse_udp6(const uint8_t *pkt_data, uint8_t *new_pkt_data, struct pcap_sf_pkthdr *header, struct ip6 *ip6_hdr) { /* * UDP header (8 bytes) * 1. source port (2 bytes) * 2. destination port (2 bytes) * 3. length (2 bytes) * 4. checksum (2 bytes) */ struct udphdr *udp_hdr; int i; /* do nothing if UDP hdr is truncated */ if (header->caplen < ETH_HDR_LEN + IP6_HDR_LEN + UDP_HDR_LEN) { free(ip6_hdr); ip6_hdr = NULL; return (ETH_HDR_LEN + IP6_HDR_LEN); } udp_hdr = (struct udphdr *)malloc(UDP_HDR_LEN); if (udp_hdr == NULL) error("malloc(): cannot allocate memory for udp_hdr"); /* * we have payload which covers UDP hdr + data, * use that payload instead of pkt_data */ if (layer_opt == 3 && header_opt == IP6 && payload_len_opt > 0) { /* * go pass pcap hdr, Ethernet hdr and IPv6 hdr in new_pkt_data * then copy UDP hdr from new_pkt_data into udp_hdr * and reset pointer to the beginning of new_pkt_data */ i = 0; while (i++ < PCAP_HDR_LEN + ETH_HDR_LEN + IP6_HDR_LEN) (void)*new_pkt_data++; memcpy(udp_hdr, new_pkt_data, UDP_HDR_LEN); i = 0; while (i++ < PCAP_HDR_LEN + ETH_HDR_LEN + IP6_HDR_LEN) (void)*new_pkt_data--; } else { /* * go pass Ethernet hdr and IPv6 hdr in pkt_data * then copy UDP hdr from pkt_data into udp_hdr * and reset pointer to the beginning of pkt_data */ i = 0; while (i++ < (ETH_HDR_LEN + IP6_HDR_LEN)) (void)*pkt_data++; memcpy(udp_hdr, pkt_data, UDP_HDR_LEN); i = 0; while (i++ < (ETH_HDR_LEN + IP6_HDR_LEN)) (void)*pkt_data--; } /* we are editing UDP hdr */ if (header_opt == UDP) update_udp_hdr(udp_hdr); /* we are going to copy up to layer 4 only */ if (layer_opt == 4) { /* * we are editing UDP hdr and we have payload, * attach the payload first before checksum calculation */ if (header_opt == UDP && payload_len_opt > 0) { /* truncate payload if it is too large */ if ((payload_len_opt + ETH_HDR_LEN + IP6_HDR_LEN + UDP_HDR_LEN) > ETH_MAX_LEN) payload_len_opt -= (payload_len_opt + ETH_HDR_LEN + IP6_HDR_LEN + UDP_HDR_LEN) - ETH_MAX_LEN; /* * go pass pcap hdr, Ethernet hdr, IPv6 hdr and UDP hdr in new_pkt_data * then copy payload_opt into new_pkt_data * and reset pointer to the beginning of new_pkt_data */ i = 0; while (i++ < PCAP_HDR_LEN + ETH_HDR_LEN + IP6_HDR_LEN + UDP_HDR_LEN) (void)*new_pkt_data++; memcpy(new_pkt_data, payload_opt, payload_len_opt); i = 0; while (i++ < PCAP_HDR_LEN + ETH_HDR_LEN + IP6_HDR_LEN + UDP_HDR_LEN) (void)*new_pkt_data--; header->caplen = header->len = ETH_HDR_LEN + IP6_HDR_LEN + UDP_HDR_LEN + payload_len_opt; } else header->caplen = header->len = ETH_HDR_LEN + IP6_HDR_LEN + UDP_HDR_LEN; /* update UDP length and IPv6 payload length */ udp_hdr->uh_ulen = ip6_hdr->ip6_plen = htons(header->caplen - (ETH_HDR_LEN + IP6_HDR_LEN)); write_ip6_hdr(new_pkt_data, ip6_hdr); } /* * recalculate checksum for UDP hdr (cover IPv6 pseudo hdr + UDP hdr + trailing data) * if we have enough data */ if (csum_opt && header->caplen >= (ETH_HDR_LEN + IP6_HDR_LEN + ntohs(ip6_hdr->ip6_plen))) update_udp6_cksum(pkt_data, ip6_hdr, udp_hdr); free(ip6_hdr); ip6_hdr = NULL; /* * go pass pcap hdr, Ethernet hdr and IPv6 hdr in new_pkt_data * then copy udp_hdr into new_pkt_data * and reset pointer to the beginning of new_pkt_data */ i = 0; while (i++ < PCAP_HDR_LEN + ETH_HDR_LEN + IP6_HDR_LEN) (void)*new_pkt_data++; memcpy(new_pkt_data, udp_hdr, UDP_HDR_LEN); free(udp_hdr); udp_hdr = NULL; i = 0; while (i++ < PCAP_HDR_LEN + ETH_HDR_LEN + IP6_HDR_LEN) (void)*new_pkt_data--; /* no further editing support after UDP hdr */ if (layer_opt == 4) return (header->caplen); /* * we have written payload_opt (payload after IPv6 hdr) which covers UDP hdr + data, * checksum for UDP hdr corrected above, * while UDP data is written to new_pkt_data in parse_ip6() */ else if (layer_opt == 3) return (header->caplen); else return (ETH_HDR_LEN + IP6_HDR_LEN + UDP_HDR_LEN); } void update_udp6_cksum(const uint8_t *pkt_data, struct ip6 *ip6_hdr, struct udphdr *udp_hdr) { struct ip6pseudo *ip6p; /* IPv6 pseudo hdr */ uint8_t *udpp; /* IPv6 pseudo hdr + UDP hdr + trailing data */ uint16_t udpp_len; int i; /* create IPv6 pseudo hdr */ ip6p = create_ip6pseudo(ip6_hdr); udpp_len = sizeof(struct ip6pseudo) + ntohs(ip6p->ip6pseudo_len); udpp = (uint8_t *)malloc(sizeof(uint8_t) * udpp_len); if (udpp == NULL) error("malloc(): cannot allocate memory for udpp"); memset(udpp, 0, udpp_len); /* copy IPv6 pseudo hdr from ipp into udpp */ memcpy(udpp, ip6p, sizeof(struct ip6pseudo)); free(ip6p); ip6p = NULL; /* go pass IPv6 pseudo hdr in udpp */ i = 0; while (i++ < sizeof(struct ip6pseudo)) (void)*udpp++; /* clear checksum field */ udp_hdr->uh_sum = 0x0000; /* copy UDP hdr from udp_hdr into udpp */ memcpy(udpp, udp_hdr, UDP_HDR_LEN); /* reset pointer to the beginning of udpp */ i = 0; while (i++ < sizeof(struct ip6pseudo)) (void)*udpp--; /* copy trailing data from payload_opt into udpp */ if (layer_opt == 4 && header_opt == UDP && payload_len_opt > 0) { for (i = UDP_HDR_LEN; i < (udpp_len - sizeof(struct ip6pseudo)); i++) udpp[i + sizeof(struct ip6pseudo)] = payload_opt[i - UDP_HDR_LEN]; } /* copy trailing data from payload_opt (payload after IPv6 hdr) into udpp */ else if (layer_opt == 3 && header_opt == IP6 && payload_len_opt > 0) { for (i = UDP_HDR_LEN; i < payload_len_opt; i++) udpp[i + sizeof(struct ip6pseudo)] = payload_opt[i]; } /* copy trailing data from pkt_data into udpp */ else { for (i = UDP_HDR_LEN; i < (udpp_len - sizeof(struct ip6pseudo)); i++) udpp[i + sizeof(struct ip6pseudo)] = pkt_data[ETH_HDR_LEN + IP6_HDR_LEN + i]; } /* recalculate checksum */ udp_hdr->uh_sum = cksum(udpp, udpp_len); free(udpp); udpp = NULL; } void update_udp_hdr(struct udphdr *udp_hdr) { /* overwrite source port */ if (udpopt->uh_sport_flag == FIELD_SET) udp_hdr->uh_sport = htons(udpopt->uh_old_sport); else if (udpopt->uh_sport_flag == FIELD_REPLACE && udp_hdr->uh_sport == htons(udpopt->uh_old_sport)) udp_hdr->uh_sport = htons(udpopt->uh_new_sport); else if (udpopt->uh_sport_flag == FIELD_SET_RAND || (udpopt->uh_sport_flag == FIELD_REPLACE_RAND && udp_hdr->uh_sport == htons(udpopt->uh_old_sport))) udp_hdr->uh_sport = htons(get_random_number(UINT16_MAX)); /* overwrite destination port */ if (udpopt->uh_dport_flag == FIELD_SET) udp_hdr->uh_dport = htons(udpopt->uh_old_dport); else if (udpopt->uh_dport_flag == FIELD_REPLACE && udp_hdr->uh_dport == htons(udpopt->uh_old_dport)) udp_hdr->uh_dport = htons(udpopt->uh_new_dport); else if (udpopt->uh_dport_flag == FIELD_SET_RAND || (udpopt->uh_dport_flag == FIELD_REPLACE_RAND && udp_hdr->uh_dport == htons(udpopt->uh_old_dport))) udp_hdr->uh_dport = htons(get_random_number(UINT16_MAX)); } void set_random_eth_addr(uint8_t *eth_addr) { uint64_t r = tinymt64_generate_uint64(&tinymt); /* 8 segments of random 8 bits */ for (uint8_t i = 0; i < ETH_ADDR_LEN; i++) { eth_addr[i] = (uint8_t)(r & 0xff); r >>= 8; /* use next segment of random 8 bits */ } } void set_random_in_addr(struct in_addr *addr, struct in_addr_opt *opt) { uint8_t rem_bits = opt->rand_bits; /* remaining last/right bits to randomize */ uint64_t r = tinymt64_generate_uint64(&tinymt); /* 8 segments of random 8 bits */ for (uint8_t i = 0; i < 4; i++) /* loop 4 octets */ { rem_bits -= (rem_bits > 8) ? 8 : rem_bits; opt->new.s_addr = opt->new.s_addr ^ ((opt->new.s_addr ^ r) & ~opt->netmask.s_addr); if (rem_bits == 0) break; r >>= 8; /* use next segment of random 8 bits */ } memcpy(addr, &opt->new, sizeof(struct in_addr)); } void set_random_in6_addr(struct in6_addr *addr, struct in6_addr_opt *opt) { uint8_t rem_bits = opt->rand_bits; /* remaining last/right bits to randomize */ uint64_t r = tinymt64_generate_uint64(&tinymt); /* 8 segments of random 8 bits */ for (uint8_t i = 15; i >= 0; i--) /* loop 16 octets starting from last octet */ { rem_bits -= (rem_bits > 8) ? 8 : rem_bits; opt->new.s6_addr[i] = opt->new.s6_addr[i] ^ ((opt->new.s6_addr[i] ^ r) & ~opt->netmask.s6_addr[i]); if (rem_bits == 0) break; r >>= 8; /* use next segment of random 8 bits */ /* exhausted all 8 segments, regenerate new segments of random 8 bits */ if (i % 8 == 0) r = tinymt64_generate_uint64(&tinymt); } memcpy(addr, &opt->new, sizeof(struct in6_addr)); } uint64_t get_random_number(uint64_t max_val) { /* return uniformly distributed random number between 0 and max_val inclusive */ return tinymt64_generate_double(&tinymt) * (max_val + 1); } struct ippseudo *create_ippseudo(struct ip *ip_hdr, uint16_t *ip_hlb) { struct ippseudo *ipp = (struct ippseudo *)malloc(sizeof(struct ippseudo)); if (ipp == NULL) error("malloc(): cannot allocate memory for ipp"); memcpy(&ipp->ippseudo_src, &ip_hdr->ip_src, sizeof(struct in_addr)); memcpy(&ipp->ippseudo_dst, &ip_hdr->ip_dst, sizeof(struct in_addr)); ipp->ippseudo_pad = 0x00; ipp->ippseudo_p = ip_hdr->ip_p; ipp->ippseudo_len = htons(ntohs(ip_hdr->ip_len) - *ip_hlb); return ipp; } struct ip6pseudo *create_ip6pseudo(struct ip6 *ip6_hdr) { struct ip6pseudo *ip6p = (struct ip6pseudo *)malloc(sizeof(struct ip6pseudo)); if (ip6p == NULL) error("malloc(): cannot allocate memory for ip6p"); memset(ip6p, 0, sizeof(struct ip6pseudo)); memcpy(&ip6p->ip6pseudo_src, &ip6_hdr->ip6_src, sizeof(struct in6_addr)); memcpy(&ip6p->ip6pseudo_dst, &ip6_hdr->ip6_dst, sizeof(struct in6_addr)); ip6p->ip6pseudo_len = ip6_hdr->ip6_plen; ip6p->ip6pseudo_nxt = ip6_hdr->ip6_nxt; return ip6p; } /* Reference: rfc1071.txt */ uint16_t cksum(const void *cp, uint16_t len) { const uint16_t *word_16 = cp; /* 16-bit word at a time */ uint16_t rem = len; unsigned int sum = 0; /* add all 16-bit words */ while (rem > 1) { sum += *word_16; word_16++; rem -= 2; } /* add last byte if len is odd */ if (rem) sum += *(uint8_t *)word_16; /* fold 32-bit sum into 16 bits in network byte order */ while (sum > 0xffff) sum = (sum >> 16) + (sum & 0xffff); /* one's complement the sum */ return (uint16_t)(~sum); } void info(void) { (void)putchar('\n'); notice("%lu packets (%lu bytes) written", pkts, bytes); } void notice(const char *fmt, ...) { va_list ap; va_start(ap, fmt); (void)vprintf(fmt, ap); va_end(ap); if (*fmt) { fmt += strlen(fmt); if (fmt[-1] != '\n') (void)puts(""); } } /* * Reference: tcpdump's util.c * * Copyright (c) 1990, 1991, 1993, 1994, 1995, 1996, 1997 * The Regents of the University of California. All rights reserved. * */ void error(const char *fmt, ...) { va_list ap; (void)fprintf(stderr, "%s: ", program_name); va_start(ap, fmt); (void)vfprintf(stderr, fmt, ap); va_end(ap); if (*fmt) { fmt += strlen(fmt); if (fmt[-1] != '\n') (void)fputc('\n', stderr); } exit(EXIT_FAILURE); } int eth_aton(const char *cp, uint8_t *eth_addr) { int i; unsigned int o0, o1, o2, o3, o4, o5; i = sscanf(cp, "%x:%x:%x:%x:%x:%x", &o0, &o1, &o2, &o3, &o4, &o5); if (i != 6) { eth_addr = NULL; return 0; } eth_addr[0] = o0; eth_addr[1] = o1; eth_addr[2] = o2; eth_addr[3] = o3; eth_addr[4] = o4; eth_addr[5] = o5; return 1; } void usage(void) { (void)fprintf(stderr, "%s version %s, Copyright (C) 2006 - 2023 Addy Yeow \n" "%s\n" "Usage: %s [-I input] [-O output] [-L layer] [-X payload] [-C]\n" " [-M linktype] [-D offset] [-R range] [-S timeframe]\n" " [-N repeat] [-G gaprange] [-P seed] [-T header]\n" " [header-specific-options] [-h]\n" "\nOptions:\n" " -I input Input pcap based trace file. Typically, input should be a\n" " file path to a pcap based trace file. However, for\n" " convenience, the following template names are also\n" " accepted to load trace file from one of the built-in\n" " templates:\n" " eth : Ethernet header\n" " arp : ARP header\n" " ip : IPv4 header\n" " ip6 : IPv6 header\n" " icmp : ICMPv4 header\n" " icmp6 : ICMPv6 header\n" " tcp : IPv4 TCP header\n" " ip6tcp : IPv6 TCP header\n" " udp : IPv4 UDP header\n" " ip6udp : IPv6 UDP header\n" " Example: -I icmp\n" " -O output Output trace file.\n" " -L layer Copy up to the specified 'layer' and discard the remaining\n" " data. Value for 'layer' must be either 2, 3, or 4 where\n" " 2 for Ethernet, 3 for ARP, IPv4, or IPv6, and 4 for ICMPv4,\n" " ICMPv6, TCP, or UDP.\n" " -X payload Append 'payload' in hex digits to the end of each packet.\n" " Example: -X 0302aad1\n" " -X flag is ignored if -L and -T flag are not specified.\n" " -C Specify this flag to disable checksum correction.\n" " Checksum correction is applicable for non-fragmented\n" " supported packets only.\n" " -M linktype Replace the 'linktype' stored in the pcap file header.\n" " Typically, value for 'linktype' is 1 for Ethernet.\n" " Example: -M 12 (for raw IP), -M 51 (for PPPoE)\n" " -D offset Delete the specified byte 'offset' from each packet.\n" " First byte (starting from link layer header) starts from 1.\n" " -L, -X, -C and -T flag are ignored if -D flag is specified.\n" " Example: -D 15-40, -D 10, or -D 18-9999\n" " -R range Save only the specified 'range' of packets.\n" " Example: -R 5-21 or -R 9\n" " -S timeframe Save only the packets within the specified 'timeframe' with\n" " up to one-second resolution using DD/MM/YYYY,HH:MM:SS as the\n" " format for start and end time in 'timeframe'.\n" " Example: -S 22/10/2006,21:47:35-24/10/2006,13:16:05\n" " -S flag is evaluated after -R flag.\n" " -N repeat Duplicate packets from the 'input' trace file 'repeat'\n" " times. Use this flag to create a stream of packets,\n" " each with, for example, a random tcp sequence number, from\n" " a 1-packet trace file.\n" " Example: -N 100000\n" " -N flag is evaluated after -R and -S flag.\n" " -G gaprange Apply inter-packet gap between packets in microseconds from\n" " 1 to (2^31 - 1). Values in 'gaprange' are inclusive and\n" " selected randomly. A single value implies a fixed gap.\n" " Example: -G 1000-10000 or -G 1000\n" " -G flag is evaluated after -R, -S, and -N flag.\n" " -P seed Positive integer to seed the random number generator (RNG)\n" " used, for example, to generate random port number.\n" " If unset, current timestamp will be used as the RNG seed.\n" " bittwiste uses Mersenne Twister for high-speed uniformly\n" " distributed random number generation.\n" " -T header Edit only the specified 'header'. Possible keywords for\n" " 'header' are, eth, arp, ip, ip6, icmp, icmp6, tcp, or udp.\n" " -T flag must appear last among the general options.\n" " -h Print version information and usage.\n" " header-specific-options\n" " See bittwiste manual page for header specific options.\n", program_name, BITTWISTE_VERSION, pcap_lib_version(), program_name); exit(EXIT_SUCCESS); } bittwist-linux-3.8/src/bittwiste.h0000644000175000017500000001162614451442047015547 0ustar opsops/* * SPDX-License-Identifier: GPL-2.0-or-later * * bittwiste - pcap capture file editor * Copyright (C) 2006 - 2023 Addy Yeow * * 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. */ #ifndef _BITTWISTE_H_ #define _BITTWISTE_H_ #include "def.h" void set_rand_in_addr_options(char *cp, struct in_addr *netnum, struct in_addr *netmask, uint8_t *rand_bits); void set_in_addr_options(char *optarg, struct in_addr_opt *opt); void set_rand_in6_addr_options(char *cp, struct in6_addr *netaddr, struct in6_addr *netmask, uint8_t *rand_bits); void set_in6_addr_options(char *optarg, struct in6_addr_opt *opt); void set_number_options(char *optarg, void *val_a, void *val_b, uint8_t *flag, size_t val_size); void parse_header_options(int argc, char **argv); void parse_trace(char *infile, char *outfile); void truncate_packet(const uint8_t *pkt_data, struct pcap_sf_pkthdr *header, char *outfile, FILE **fp_outfile); void modify_packet(const uint8_t *pkt_data, struct pcap_sf_pkthdr *header, char *outfile, FILE **fp_outfile); void load_input_file(char *infile, FILE **fp); void update_pcap_hdr(struct pcap_sf_pkthdr *header); uint16_t parse_eth(const uint8_t *pkt_data, uint8_t *new_pkt_data, struct pcap_sf_pkthdr *header); void update_eth_hdr(struct ethhdr *eth_hdr); uint16_t parse_arp(const uint8_t *pkt_data, uint8_t *new_pkt_data, struct pcap_sf_pkthdr *header); void update_arp_hdr(struct arphdr *arp_hdr); uint16_t parse_ip(const uint8_t *pkt_data, uint8_t *new_pkt_data, struct pcap_sf_pkthdr *header, struct ip *ip_hdr, int flag); void update_ip_cksum(struct ip *ip_hdr, uint8_t *ip_o, uint16_t *ip_hlb); void update_ip_hdr(struct ip *ip_hdr, uint8_t *r, uint8_t *d, uint8_t *m); uint16_t parse_ip6(const uint8_t *pkt_data, uint8_t *new_pkt_data, struct pcap_sf_pkthdr *header); void update_ip6_hdr(struct ip6 *ip6_hdr); void write_ip6_hdr(uint8_t *new_pkt_data, struct ip6 *ip6_hdr); uint16_t parse_icmp(const uint8_t *pkt_data, uint8_t *new_pkt_data, struct pcap_sf_pkthdr *header, struct ip *ip_hdr); void update_icmp_cksum(const uint8_t *pkt_data, struct ip *ip_hdr, struct icmphdr *icmp_hdr, uint16_t *ip_hlb); uint16_t parse_icmp6(const uint8_t *pkt_data, uint8_t *new_pkt_data, struct pcap_sf_pkthdr *header, struct ip6 *ip6_hdr); void update_icmp6_cksum(const uint8_t *pkt_data, struct ip6 *ip6_hdr, struct icmp6hdr *icmp6_hdr); uint16_t parse_tcp(const uint8_t *pkt_data, uint8_t *new_pkt_data, struct pcap_sf_pkthdr *header, struct ip *ip_hdr); void update_tcp_cksum(const uint8_t *pkt_data, struct ip *ip_hdr, struct tcphdr *tcp_hdr, uint16_t *ip_hlb, uint16_t *tcp_hlb, uint8_t *tcp_o); uint16_t parse_tcp6(const uint8_t *pkt_data, uint8_t *new_pkt_data, struct pcap_sf_pkthdr *header, struct ip6 *ip6_hdr); void update_tcp6_cksum(const uint8_t *pkt_data, struct ip6 *ip6_hdr, struct tcphdr *tcp_hdr, uint16_t *tcp_hlb, uint8_t *tcp_o); void update_tcp_hdr(struct tcphdr *tcp_hdr); uint16_t parse_udp(const uint8_t *pkt_data, uint8_t *new_pkt_data, struct pcap_sf_pkthdr *header, struct ip *ip_hdr); void update_udp_cksum(const uint8_t *pkt_data, struct ip *ip_hdr, struct udphdr *udp_hdr, uint16_t *ip_hlb); uint16_t parse_udp6(const uint8_t *pkt_data, uint8_t *new_pkt_data, struct pcap_sf_pkthdr *header, struct ip6 *ip6_hdr); void update_udp6_cksum(const uint8_t *pkt_data, struct ip6 *ip6_hdr, struct udphdr *udp_hdr); void update_udp_hdr(struct udphdr *udp_hdr); void set_random_eth_addr(uint8_t *eth_addr); void set_random_in_addr(struct in_addr *addr, struct in_addr_opt *opt); void set_random_in6_addr(struct in6_addr *addr, struct in6_addr_opt *opt); uint64_t get_random_number(uint64_t max_val); struct ippseudo *create_ippseudo(struct ip *ip_hdr, uint16_t *ip_hlb); struct ip6pseudo *create_ip6pseudo(struct ip6 *ip6_hdr); uint16_t cksum(const void *cp, uint16_t len); void info(void); void notice(const char *, ...); void error(const char *, ...); int eth_aton(const char *a, uint8_t *eth_addr); void usage(void); #endif /* !_BITTWISTE_H_ */ bittwist-linux-3.8/src/template_pcap.c0000644000175000017500000004277114451442047016347 0ustar opsops/* * SPDX-License-Identifier: GPL-2.0-or-later * * template_pcap - Template pcap files to be used as input files for bittwiste * Copyright (C) 2006 - 2023 Addy Yeow * * 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. */ #include #include "template_pcap.h" /* To define a new template using packet from e.g. out.pcap: $ xxd -p out.pcap | tr -d '\n' d4c3b2a102000400000000000000000000000400010000002f467864d41404000e0000000e000000 $ python >>> s = "d4c3b2a102000400000000000000000000000400010000002f467864d41404000e0000000e000000" >>> v = ', '.join([f"0x{b:02x}" for b in bytes.fromhex(s)]) >>> print(f"const unsigned char TEMPLATE[] = {{{v}}};") Fields in templates have been scrubbed as follow using bittwiste: 1) MAC address = 00:00:00:00:00:00 2) IPv4 address = 127.0.0.1 3) IPv6 address = ::1 4) Source port = 1000 5) Destination port = 2000 */ /* Template name: TEMPLATE_PCAP_ETH (-I eth) Description: Ethernet header Wireshark output: Frame 1: 14 bytes on wire (112 bits), 14 bytes captured (112 bits) Ethernet II, Src: 00:00:00_00:00:00 (00:00:00:00:00:00), Dst: 00:00:00_00:00:00 (00:00:00:00:00:00) Destination: 00:00:00_00:00:00 (00:00:00:00:00:00) Source: 00:00:00_00:00:00 (00:00:00:00:00:00) Type: IPv4 (0x0800) */ const unsigned char TEMPLATE_PCAP_ETH[] = { 0xd4, 0xc3, 0xb2, 0xa1, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2f, 0x46, 0x78, 0x64, 0xd4, 0x14, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00}; const size_t TEMPLATE_PCAP_ETH_LEN = sizeof(TEMPLATE_PCAP_ETH); /* Template name: TEMPLATE_PCAP_ARP (-I arp) Description: ARP header Wireshark output: Frame 1: 42 bytes on wire (336 bits), 42 bytes captured (336 bits) Ethernet II, Src: 00:00:00_00:00:00 (00:00:00:00:00:00), Dst: 00:00:00_00:00:00 (00:00:00:00:00:00) Destination: 00:00:00_00:00:00 (00:00:00:00:00:00) Source: 00:00:00_00:00:00 (00:00:00:00:00:00) Type: ARP (0x0806) Address Resolution Protocol (ARP Announcement) Hardware type: Ethernet (1) Protocol type: IPv4 (0x0800) Hardware size: 6 Protocol size: 4 Opcode: request (1) Sender MAC address: 00:00:00_00:00:00 (00:00:00:00:00:00) Sender IP address: 127.0.0.1 Target MAC address: 00:00:00_00:00:00 (00:00:00:00:00:00) Target IP address: 127.0.0.1 */ const unsigned char TEMPLATE_PCAP_ARP[] = { 0xd4, 0xc3, 0xb2, 0xa1, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0xd6, 0x7e, 0x64, 0x33, 0x2f, 0x0a, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x06, 0x00, 0x01, 0x08, 0x00, 0x06, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x01}; const size_t TEMPLATE_PCAP_ARP_LEN = sizeof(TEMPLATE_PCAP_ARP); /* Template name: TEMPLATE_PCAP_IP (-I ip) Description: IPv4 header Wireshark output: Frame 1: 34 bytes on wire (272 bits), 34 bytes captured (272 bits) Ethernet II, Src: 00:00:00_00:00:00 (00:00:00:00:00:00), Dst: 00:00:00_00:00:00 (00:00:00:00:00:00) Destination: 00:00:00_00:00:00 (00:00:00:00:00:00) Source: 00:00:00_00:00:00 (00:00:00:00:00:00) Type: IPv4 (0x0800) Internet Protocol Version 4, Src: 127.0.0.1, Dst: 127.0.0.1 0100 .... = Version: 4 .... 0101 = Header Length: 20 bytes (5) Differentiated Services Field: 0x00 (DSCP: CS0, ECN: Not-ECT) Total Length: 20 Identification: 0x3282 (12930) Flags: 0x40, Don't fragment ...0 0000 0000 0000 = Fragment Offset: 0 Time to Live: 64 Protocol: TCP (6) Header Checksum: 0x0a60 Source Address: 127.0.0.1 Destination Address: 127.0.0.1 */ const unsigned char TEMPLATE_PCAP_IP[] = { 0xd4, 0xc3, 0xb2, 0xa1, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0f, 0xaa, 0x72, 0x64, 0x2c, 0x74, 0x06, 0x00, 0x22, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x45, 0x00, 0x00, 0x14, 0x32, 0x82, 0x40, 0x00, 0x40, 0x06, 0x0a, 0x60, 0x7f, 0x00, 0x00, 0x01, 0x7f, 0x00, 0x00, 0x01}; const size_t TEMPLATE_PCAP_IP_LEN = sizeof(TEMPLATE_PCAP_IP); /* Template name: TEMPLATE_PCAP_IP6 (-I ip6) Description: IPv6 header Wireshark output: Frame 1: 54 bytes on wire (432 bits), 54 bytes captured (432 bits) Ethernet II, Src: 00:00:00_00:00:00 (00:00:00:00:00:00), Dst: 00:00:00_00:00:00 (00:00:00:00:00:00) Destination: 00:00:00_00:00:00 (00:00:00:00:00:00) Source: 00:00:00_00:00:00 (00:00:00:00:00:00) Type: IPv6 (0x86dd) Internet Protocol Version 6, Src: ::1, Dst: ::1 0110 .... = Version: 6 .... 0000 0000 .... .... .... .... .... = Traffic Class: 0x00 (DSCP: CS0, ECN: Not-ECT) .... .... .... 0011 0101 0001 1011 0011 = Flow Label: 0x351b3 Payload Length: 0 Next Header: TCP (6) Hop Limit: 53 Source Address: ::1 Destination Address: ::1 */ const unsigned char TEMPLATE_PCAP_IP6[] = { 0xd4, 0xc3, 0xb2, 0xa1, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, 0x6b, 0x86, 0x79, 0x64, 0x64, 0x45, 0x02, 0x00, 0x36, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86, 0xdd, 0x60, 0x03, 0x51, 0xb3, 0x00, 0x00, 0x06, 0x35, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}; const size_t TEMPLATE_PCAP_IP6_LEN = sizeof(TEMPLATE_PCAP_IP6); /* Template name: TEMPLATE_PCAP_ICMP (-I icmp) Description: ICMPv4 header Wireshark output: Frame 1: 42 bytes on wire (336 bits), 42 bytes captured (336 bits) Ethernet II, Src: 00:00:00_00:00:00 (00:00:00:00:00:00), Dst: 00:00:00_00:00:00 (00:00:00:00:00:00) Destination: 00:00:00_00:00:00 (00:00:00:00:00:00) Source: 00:00:00_00:00:00 (00:00:00:00:00:00) Type: IPv4 (0x0800) Internet Protocol Version 4, Src: 127.0.0.1, Dst: 127.0.0.1 0100 .... = Version: 4 .... 0101 = Header Length: 20 bytes (5) Differentiated Services Field: 0x00 (DSCP: CS0, ECN: Not-ECT) Total Length: 28 Identification: 0x4321 (17185) Flags: 0x40, Don't fragment ...0 0000 0000 0000 = Fragment Offset: 0 Time to Live: 64 Protocol: ICMP (1) Header Checksum: 0xf9bd Source Address: 127.0.0.1 Destination Address: 127.0.0.1 Internet Control Message Protocol Type: 8 (Echo (ping) request) Code: 0 Checksum: 0xf58a Identifier (BE): 299 (0x012b) Sequence Number (BE): 330 (0x014a) */ const unsigned char TEMPLATE_PCAP_ICMP[] = { 0xd4, 0xc3, 0xb2, 0xa1, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, 0xff, 0x24, 0x78, 0x64, 0x96, 0x33, 0x05, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x45, 0x00, 0x00, 0x1c, 0x43, 0x21, 0x40, 0x00, 0x40, 0x01, 0xf9, 0xbd, 0x7f, 0x00, 0x00, 0x01, 0x7f, 0x00, 0x00, 0x01, 0x08, 0x00, 0xf5, 0x8a, 0x01, 0x2b, 0x01, 0x4a}; const size_t TEMPLATE_PCAP_ICMP_LEN = sizeof(TEMPLATE_PCAP_ICMP); /* Template name: TEMPLATE_PCAP_ICMP6 (-I icmp6) Description: ICMPv6 header Wireshark output: Frame 1: 118 bytes on wire (944 bits), 118 bytes captured (944 bits) Ethernet II, Src: 00:00:00_00:00:00 (00:00:00:00:00:00), Dst: 00:00:00_00:00:00 (00:00:00:00:00:00) Destination: 00:00:00_00:00:00 (00:00:00:00:00:00) Source: 00:00:00_00:00:00 (00:00:00:00:00:00) Type: IPv6 (0x86dd) Internet Protocol Version 6, Src: ::1, Dst: ::1 0110 .... = Version: 6 .... 0000 0000 .... .... .... .... .... = Traffic Class: 0x00 (DSCP: CS0, ECN: Not-ECT) .... .... .... 0100 1110 1111 0100 0101 = Flow Label: 0x4ef45 Payload Length: 64 Next Header: ICMPv6 (58) Hop Limit: 48 Source Address: ::1 Destination Address: ::1 Internet Control Message Protocol v6 Type: Echo (ping) request (128) Code: 0 Checksum: 0x336f Identifier: 0x4c01 Sequence: 19 Data (56 bytes) */ const unsigned char TEMPLATE_PCAP_ICMP6[] = { 0xd4, 0xc3, 0xb2, 0xa1, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, 0x75, 0x86, 0x79, 0x64, 0x63, 0xb6, 0x00, 0x00, 0x76, 0x00, 0x00, 0x00, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86, 0xdd, 0x60, 0x04, 0xef, 0x45, 0x00, 0x40, 0x3a, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x00, 0x33, 0x6f, 0x4c, 0x01, 0x00, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; const size_t TEMPLATE_PCAP_ICMP6_LEN = sizeof(TEMPLATE_PCAP_ICMP6); /* Template name: TEMPLATE_PCAP_TCP (-I tcp) Description: IPv4 TCP header Wireshark output: Frame 1: 74 bytes on wire (592 bits), 74 bytes captured (592 bits) Ethernet II, Src: 00:00:00_00:00:00 (00:00:00:00:00:00), Dst: 00:00:00_00:00:00 (00:00:00:00:00:00) Destination: 00:00:00_00:00:00 (00:00:00:00:00:00) Source: 00:00:00_00:00:00 (00:00:00:00:00:00) Type: IPv4 (0x0800) Internet Protocol Version 4, Src: 127.0.0.1, Dst: 127.0.0.1 0100 .... = Version: 4 .... 0101 = Header Length: 20 bytes (5) Differentiated Services Field: 0x00 (DSCP: CS0, ECN: Not-ECT) Total Length: 60 Identification: 0x3282 (12930) Flags: 0x40, Don't fragment ...0 0000 0000 0000 = Fragment Offset: 0 Time to Live: 64 Protocol: TCP (6) Header Checksum: 0x0a38 Source Address: 127.0.0.1 Destination Address: 127.0.0.1 Transmission Control Protocol, Src Port: 1000, Dst Port: 2000, Seq: 0, Len: 0 Source Port: 1000 Destination Port: 2000 Sequence Number (raw): 2053058830 Acknowledgment number (raw): 0 1010 .... = Header Length: 40 bytes (10) Flags: 0x002 (SYN) Window: 43690 Checksum: 0x469f Urgent Pointer: 0 Options: (20 bytes), Maximum segment size, SACK permitted, Timestamps, No-Operation (NOP), Window scale */ const unsigned char TEMPLATE_PCAP_TCP[] = { 0xd4, 0xc3, 0xb2, 0xa1, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0f, 0xaa, 0x72, 0x64, 0x2c, 0x74, 0x06, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x45, 0x00, 0x00, 0x3c, 0x32, 0x82, 0x40, 0x00, 0x40, 0x06, 0x0a, 0x38, 0x7f, 0x00, 0x00, 0x01, 0x7f, 0x00, 0x00, 0x01, 0x03, 0xe8, 0x07, 0xd0, 0x7a, 0x5f, 0x31, 0x0e, 0x00, 0x00, 0x00, 0x00, 0xa0, 0x02, 0xaa, 0xaa, 0x46, 0x9f, 0x00, 0x00, 0x02, 0x04, 0xff, 0xd7, 0x04, 0x02, 0x08, 0x0a, 0x37, 0xf3, 0x6f, 0x74, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x03, 0x0a}; const size_t TEMPLATE_PCAP_TCP_LEN = sizeof(TEMPLATE_PCAP_TCP); /* Template name: TEMPLATE_PCAP_IP6_TCP (-I ip6tcp) Description: IPv6 TCP header Wireshark output: Frame 1: 86 bytes on wire (688 bits), 86 bytes captured (688 bits) Ethernet II, Src: 00:00:00_00:00:00 (00:00:00:00:00:00), Dst: 00:00:00_00:00:00 (00:00:00:00:00:00) Destination: 00:00:00_00:00:00 (00:00:00:00:00:00) Source: 00:00:00_00:00:00 (00:00:00:00:00:00) Type: IPv6 (0x86dd) Internet Protocol Version 6, Src: ::1, Dst: ::1 0110 .... = Version: 6 .... 0000 0000 .... .... .... .... .... = Traffic Class: 0x00 (DSCP: CS0, ECN: Not-ECT) .... .... .... 0011 0101 0001 1011 0011 = Flow Label: 0x351b3 Payload Length: 32 Next Header: TCP (6) Hop Limit: 53 Source Address: ::1 Destination Address: ::1 Transmission Control Protocol, Src Port: 1000, Dst Port: 2000, Seq: 1, Ack: 1, Len: 0 Source Port: 1000 Destination Port: 2000 Sequence Number (raw): 815706875 Acknowledgment number (raw): 484792852 1000 .... = Header Length: 32 bytes (8) Flags: 0x018 (PSH, ACK) Window: 224 Checksum: 0xce0e Urgent Pointer: 0 Options: (12 bytes), No-Operation (NOP), No-Operation (NOP), Timestamps */ const unsigned char TEMPLATE_PCAP_IP6_TCP[] = { 0xd4, 0xc3, 0xb2, 0xa1, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, 0x6b, 0x86, 0x79, 0x64, 0x64, 0x45, 0x02, 0x00, 0x56, 0x00, 0x00, 0x00, 0x56, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86, 0xdd, 0x60, 0x03, 0x51, 0xb3, 0x00, 0x20, 0x06, 0x35, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0xe8, 0x07, 0xd0, 0x30, 0x9e, 0xb2, 0xfb, 0x1c, 0xe5, 0x5a, 0x14, 0x80, 0x18, 0x00, 0xe0, 0xce, 0x0e, 0x00, 0x00, 0x01, 0x01, 0x08, 0x0a, 0x0e, 0x90, 0x91, 0x5f, 0x14, 0x01, 0x8d, 0x89}; const size_t TEMPLATE_PCAP_IP6_TCP_LEN = sizeof(TEMPLATE_PCAP_IP6_TCP); /* Template name: TEMPLATE_PCAP_UDP (-I udp) Description: IPv4 UDP header Wireshark output: Frame 1: 42 bytes on wire (336 bits), 42 bytes captured (336 bits) Ethernet II, Src: 00:00:00_00:00:00 (00:00:00:00:00:00), Dst: 00:00:00_00:00:00 (00:00:00:00:00:00) Destination: 00:00:00_00:00:00 (00:00:00:00:00:00) Source: 00:00:00_00:00:00 (00:00:00:00:00:00) Type: IPv4 (0x0800) Internet Protocol Version 4, Src: 127.0.0.1, Dst: 127.0.0.1 0100 .... = Version: 4 .... 0101 = Header Length: 20 bytes (5) Differentiated Services Field: 0x00 (DSCP: CS0, ECN: Not-ECT) Total Length: 28 Identification: 0xbf86 (49030) Flags: 0x00 ...0 0000 0000 0000 = Fragment Offset: 0 Time to Live: 64 Protocol: UDP (17) Header Checksum: 0xbd48 Source Address: 127.0.0.1 Destination Address: 127.0.0.1 User Datagram Protocol, Src Port: 1000, Dst Port: 2000 Source Port: 1000 Destination Port: 2000 Length: 8 Checksum: 0xf623 */ const unsigned char TEMPLATE_PCAP_UDP[] = { 0xd4, 0xc3, 0xb2, 0xa1, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, 0x97, 0xa4, 0x7e, 0x64, 0x15, 0x05, 0x0d, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x45, 0x00, 0x00, 0x1c, 0xbf, 0x86, 0x00, 0x00, 0x40, 0x11, 0xbd, 0x48, 0x7f, 0x00, 0x00, 0x01, 0x7f, 0x00, 0x00, 0x01, 0x03, 0xe8, 0x07, 0xd0, 0x00, 0x08, 0xf6, 0x23}; const size_t TEMPLATE_PCAP_UDP_LEN = sizeof(TEMPLATE_PCAP_UDP); /* Template name: TEMPLATE_PCAP_IP6_UDP (-I ip6udp) Description: IPv6 UDP header Wireshark output: Frame 1: 62 bytes on wire (496 bits), 62 bytes captured (496 bits) Ethernet II, Src: 00:00:00_00:00:00 (00:00:00:00:00:00), Dst: 00:00:00_00:00:00 (00:00:00:00:00:00) Destination: 00:00:00_00:00:00 (00:00:00:00:00:00) Source: 00:00:00_00:00:00 (00:00:00:00:00:00) Type: IPv6 (0x86dd) Internet Protocol Version 6, Src: ::1, Dst: ::1 0110 .... = Version: 6 .... 0000 0000 .... .... .... .... .... = Traffic Class: 0x00 (DSCP: CS0, ECN: Not-ECT) .... .... .... 0110 0001 0010 1010 0111 = Flow Label: 0x612a7 Payload Length: 8 Next Header: UDP (17) Hop Limit: 64 Source Address: ::1 Destination Address: ::1 User Datagram Protocol, Src Port: 1000, Dst Port: 2000 Source Port: 1000 Destination Port: 2000 Length: 8 Checksum: 0xf424 */ const unsigned char TEMPLATE_PCAP_IP6_UDP[] = { 0xd4, 0xc3, 0xb2, 0xa1, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, 0x7e, 0x86, 0x79, 0x64, 0x29, 0x81, 0x06, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86, 0xdd, 0x60, 0x06, 0x12, 0xa7, 0x00, 0x08, 0x11, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0xe8, 0x07, 0xd0, 0x00, 0x08, 0xf4, 0x24}; const size_t TEMPLATE_PCAP_IP6_UDP_LEN = sizeof(TEMPLATE_PCAP_IP6_UDP); bittwist-linux-3.8/src/tinymt/0000755000175000017500000000000014451442047014676 5ustar opsopsbittwist-linux-3.8/src/tinymt/tinymt64.h0000644000175000017500000001430414451442047016547 0ustar opsops#ifndef TINYMT64_H #define TINYMT64_H /** * @file tinymt64.h * * @brief Tiny Mersenne Twister only 127 bit internal state * * @author Mutsuo Saito (Hiroshima University) * @author Makoto Matsumoto (The University of Tokyo) * * Copyright (C) 2011 Mutsuo Saito, Makoto Matsumoto, * Hiroshima University and The University of Tokyo. * All rights reserved. * * The 3-clause BSD License is applied to this software, see * LICENSE.txt */ #include #include #define TINYMT64_MEXP 127 #define TINYMT64_SH0 12 #define TINYMT64_SH1 11 #define TINYMT64_SH8 8 #define TINYMT64_MASK UINT64_C(0x7fffffffffffffff) #define TINYMT64_MUL (1.0 / 9007199254740992.0) #if defined(__cplusplus) extern "C" { #endif /* * tinymt64 internal state vector and parameters */ struct TINYMT64_T { uint64_t status[2]; uint32_t mat1; uint32_t mat2; uint64_t tmat; }; typedef struct TINYMT64_T tinymt64_t; void tinymt64_init(tinymt64_t * random, uint64_t seed); void tinymt64_init_by_array(tinymt64_t * random, const uint64_t init_key[], int key_length); #if defined(__GNUC__) /** * This function always returns 127 * @param random not used * @return always 127 */ inline static int tinymt64_get_mexp( tinymt64_t * random __attribute__((unused))) { return TINYMT64_MEXP; } #else inline static int tinymt64_get_mexp(tinymt64_t * random) { return TINYMT64_MEXP; } #endif /** * This function changes internal state of tinymt64. * Users should not call this function directly. * @param random tinymt internal status */ inline static void tinymt64_next_state(tinymt64_t * random) { uint64_t x; random->status[0] &= TINYMT64_MASK; x = random->status[0] ^ random->status[1]; x ^= x << TINYMT64_SH0; x ^= x >> 32; x ^= x << 32; x ^= x << TINYMT64_SH1; random->status[0] = random->status[1]; random->status[1] = x; if ((x & 1) != 0) { random->status[0] ^= random->mat1; random->status[1] ^= ((uint64_t)random->mat2 << 32); } } /** * This function outputs 64-bit unsigned integer from internal state. * Users should not call this function directly. * @param random tinymt internal status * @return 64-bit unsigned pseudorandom number */ inline static uint64_t tinymt64_temper(tinymt64_t * random) { uint64_t x; #if defined(LINEARITY_CHECK) x = random->status[0] ^ random->status[1]; #else x = random->status[0] + random->status[1]; #endif x ^= random->status[0] >> TINYMT64_SH8; if ((x & 1) != 0) { x ^= random->tmat; } return x; } /** * This function outputs floating point number from internal state. * Users should not call this function directly. * @param random tinymt internal status * @return floating point number r (1.0 <= r < 2.0) */ inline static double tinymt64_temper_conv(tinymt64_t * random) { uint64_t x; union { uint64_t u; double d; } conv; #if defined(LINEARITY_CHECK) x = random->status[0] ^ random->status[1]; #else x = random->status[0] + random->status[1]; #endif x ^= random->status[0] >> TINYMT64_SH8; if ((x & 1) != 0) { conv.u = ((x ^ random->tmat) >> 12) | UINT64_C(0x3ff0000000000000); } else { conv.u = (x >> 12) | UINT64_C(0x3ff0000000000000); } return conv.d; } /** * This function outputs floating point number from internal state. * Users should not call this function directly. * @param random tinymt internal status * @return floating point number r (1.0 < r < 2.0) */ inline static double tinymt64_temper_conv_open(tinymt64_t * random) { uint64_t x; union { uint64_t u; double d; } conv; #if defined(LINEARITY_CHECK) x = random->status[0] ^ random->status[1]; #else x = random->status[0] + random->status[1]; #endif x ^= random->status[0] >> TINYMT64_SH8; if ((x & 1) != 0) { conv.u = ((x ^ random->tmat) >> 12) | UINT64_C(0x3ff0000000000001); } else { conv.u = (x >> 12) | UINT64_C(0x3ff0000000000001); } return conv.d; } /** * This function outputs 64-bit unsigned integer from internal state. * @param random tinymt internal status * @return 64-bit unsigned integer r (0 <= r < 2^64) */ inline static uint64_t tinymt64_generate_uint64(tinymt64_t * random) { tinymt64_next_state(random); return tinymt64_temper(random); } /** * This function outputs floating point number from internal state. * This function is implemented using multiplying by (1 / 2^53). * @param random tinymt internal status * @return floating point number r (0.0 <= r < 1.0) */ inline static double tinymt64_generate_double(tinymt64_t * random) { tinymt64_next_state(random); return (double)(tinymt64_temper(random) >> 11) * TINYMT64_MUL; } /** * This function outputs floating point number from internal state. * This function is implemented using union trick. * @param random tinymt internal status * @return floating point number r (0.0 <= r < 1.0) */ inline static double tinymt64_generate_double01(tinymt64_t * random) { tinymt64_next_state(random); return tinymt64_temper_conv(random) - 1.0; } /** * This function outputs floating point number from internal state. * This function is implemented using union trick. * @param random tinymt internal status * @return floating point number r (1.0 <= r < 2.0) */ inline static double tinymt64_generate_double12(tinymt64_t * random) { tinymt64_next_state(random); return tinymt64_temper_conv(random); } /** * This function outputs floating point number from internal state. * This function is implemented using union trick. * @param random tinymt internal status * @return floating point number r (0.0 < r <= 1.0) */ inline static double tinymt64_generate_doubleOC(tinymt64_t * random) { tinymt64_next_state(random); return 2.0 - tinymt64_temper_conv(random); } /** * This function outputs floating point number from internal state. * This function is implemented using union trick. * @param random tinymt internal status * @return floating point number r (0.0 < r < 1.0) */ inline static double tinymt64_generate_doubleOO(tinymt64_t * random) { tinymt64_next_state(random); return tinymt64_temper_conv_open(random) - 1.0; } #if defined(__cplusplus) } #endif #endif bittwist-linux-3.8/src/tinymt/README.md0000644000175000017500000000457314451442047016166 0ustar opsops# TinyMT Bit-Twist uses a small-sized variant of Mersenne Twister known as Tiny Mersenne Twister (TinyMT), included in this directory, for generating uniformly distributed random numbers, for example, to generate a sequence of random port numbers. TinyMT is authored by Mutsuo Saito (Hiroshima University) and Makoto Matsumoto (The University of Tokyo). 3-clause BSD License applies. For more information, see: http://www.math.sci.hiroshima-u.ac.jp/m-mat/MT/TINYMT/index.html ## File list - LICENSE.txt - URL=https://github.com/MersenneTwister-Lab/TinyMT/blob/0f056950cdbe293a3e58c178444014a9907cdc69/LICENSE.txt - SHA256=1f1a07592b8d80d07268e1bcfbee73b5ddbfcdc2b238730d0c1941a8a4db4bfd - tinymt64.h - URL=https://github.com/MersenneTwister-Lab/TinyMT/blob/0f056950cdbe293a3e58c178444014a9907cdc69/tinymt/tinymt64.h - SHA256=258e9d21bf492132181530839ea061015d9a728886d99a42b2348bee84eb8ef1 - tinymt64.c - URL=https://github.com/MersenneTwister-Lab/TinyMT/blob/0f056950cdbe293a3e58c178444014a9907cdc69/tinymt/tinymt64.c - SHA256=78531e368089b8b1b6ac3d9499079258795c95373bdeb96fcedd97347ee1b1f3 ## Verify uniform distribution Generate 20M random port numbers using seed 10000: generate.c: ``` #include #include "tinymt64.h" int main(int argc, char *argv[]) { tinymt64_t tinymt; tinymt64_init(&tinymt, 10000); unsigned int rand; for (unsigned int i = 0; i < 20000000; i++) { rand = (unsigned int)(tinymt64_generate_double(&tinymt) * 65536); printf("%u\n", rand); } return 0; } ``` ``` $ cc -O2 generate.c tinymt64.c -o generate $ ./generate > out.txt ``` Verify uniform distribution using chi-square test with p-value 0.05: check.py: ``` #!/usr/bin/env python import numpy as np from scipy.stats import chisquare def is_uniformly_distributed(nums): hist, _ = np.histogram(nums, bins="auto") _, p_value = chisquare(hist) significance_level = 0.05 return p_value > significance_level def main(): with open("out.txt", "r") as file: nums = [int(line.strip()) for line in file] print(is_uniformly_distributed(nums)) if __name__ == "__main__": main() ``` ``` $ python check.py True ``` In a test setup on a Linux system, TinyMT performs 65% faster than rand(); TinyMT took 4.6 ns to return a random number whereas rand() took 13 ns. Additionally, rand() did not pass the same chi-square test described above. bittwist-linux-3.8/src/tinymt/tinymt64.c0000644000175000017500000000740614451442047016547 0ustar opsops/** * @file tinymt64.c * * @brief 64-bit Tiny Mersenne Twister only 127 bit internal state * * @author Mutsuo Saito (Hiroshima University) * @author Makoto Matsumoto (The University of Tokyo) * * Copyright (C) 2011 Mutsuo Saito, Makoto Matsumoto, * Hiroshima University and The University of Tokyo. * All rights reserved. * * The 3-clause BSD License is applied to this software, see * LICENSE.txt */ #include "tinymt64.h" #define MIN_LOOP 8 /** * This function represents a function used in the initialization * by init_by_array * @param[in] x 64-bit integer * @return 64-bit integer */ static uint64_t ini_func1(uint64_t x) { return (x ^ (x >> 59)) * UINT64_C(2173292883993); } /** * This function represents a function used in the initialization * by init_by_array * @param[in] x 64-bit integer * @return 64-bit integer */ static uint64_t ini_func2(uint64_t x) { return (x ^ (x >> 59)) * UINT64_C(58885565329898161); } /** * This function certificate the period of 2^127-1. * @param random tinymt state vector. */ static void period_certification(tinymt64_t * random) { if ((random->status[0] & TINYMT64_MASK) == 0 && random->status[1] == 0) { random->status[0] = 'T'; random->status[1] = 'M'; } } /** * This function initializes the internal state array with a 64-bit * unsigned integer seed. * @param random tinymt state vector. * @param seed a 64-bit unsigned integer used as a seed. */ void tinymt64_init(tinymt64_t * random, uint64_t seed) { random->status[0] = seed ^ ((uint64_t)random->mat1 << 32); random->status[1] = random->mat2 ^ random->tmat; for (unsigned int i = 1; i < MIN_LOOP; i++) { random->status[i & 1] ^= i + UINT64_C(6364136223846793005) * (random->status[(i - 1) & 1] ^ (random->status[(i - 1) & 1] >> 62)); } period_certification(random); } /** * This function initializes the internal state array, * with an array of 64-bit unsigned integers used as seeds * @param random tinymt state vector. * @param init_key the array of 64-bit integers, used as a seed. * @param key_length the length of init_key. */ void tinymt64_init_by_array(tinymt64_t * random, const uint64_t init_key[], int key_length) { const unsigned int lag = 1; const unsigned int mid = 1; const unsigned int size = 4; unsigned int i, j; unsigned int count; uint64_t r; uint64_t st[4]; st[0] = 0; st[1] = random->mat1; st[2] = random->mat2; st[3] = random->tmat; if (key_length + 1 > MIN_LOOP) { count = (unsigned int)key_length + 1; } else { count = MIN_LOOP; } r = ini_func1(st[0] ^ st[mid % size] ^ st[(size - 1) % size]); st[mid % size] += r; r += (unsigned int)key_length; st[(mid + lag) % size] += r; st[0] = r; count--; for (i = 1, j = 0; (j < count) && (j < (unsigned int)key_length); j++) { r = ini_func1(st[i] ^ st[(i + mid) % size] ^ st[(i + size - 1) % size]); st[(i + mid) % size] += r; r += init_key[j] + i; st[(i + mid + lag) % size] += r; st[i] = r; i = (i + 1) % size; } for (; j < count; j++) { r = ini_func1(st[i] ^ st[(i + mid) % size] ^ st[(i + size - 1) % size]); st[(i + mid) % size] += r; r += i; st[(i + mid + lag) % size] += r; st[i] = r; i = (i + 1) % size; } for (j = 0; j < size; j++) { r = ini_func2(st[i] + st[(i + mid) % size] + st[(i + size - 1) % size]); st[(i + mid) % size] ^= r; r -= i; st[(i + mid + lag) % size] ^= r; st[i] = r; i = (i + 1) % size; } random->status[0] = st[0] ^ st[1]; random->status[1] = st[2] ^ st[3]; period_certification(random); } bittwist-linux-3.8/src/tinymt/LICENSE.txt0000644000175000017500000000311114451442047016515 0ustar opsopsCopyright (c) 2011, 2013 Mutsuo Saito, Makoto Matsumoto, Hiroshima University and The University of Tokyo. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the Hiroshima University nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. bittwist-linux-3.8/BUGS0000644000175000017500000000022514451442047013245 0ustar opsopsFile your bug report and send to Addy Yeow . Make sure you are using the latest stable version before submitting your bug report.