pax_global_header00006660000000000000000000000064132131567520014517gustar00rootroot0000000000000052 comment=f32c50ea24af250aea0b9a7b754ebe346379c6ff ostinato-0.9/000077500000000000000000000000001321315675200132275ustar00rootroot00000000000000ostinato-0.9/.gitignore000066400000000000000000000005371321315675200152240ustar00rootroot00000000000000# generated object files *.pyc *.o *.a *.dll *.so *.exe *.app drone ostinato # Qt generated files ui_*.h moc_*.cpp qrc_*.cpp # QMake generated files Makefile* *\object_script.* # protobuf generated files *.pb.h *.pb.cc *_pb2.py # ostinato generated files version.cpp pkg_info.json # vim swap files *.swp .DS_Store # ctags/cscope tags cscope.out ostinato-0.9/.travis.yml000066400000000000000000000024141321315675200153410ustar00rootroot00000000000000language: cpp osx_image: xcode7.3 os: - linux - osx compiler: - gcc - clang matrix: exclude: - os: osx compiler: gcc before_install: - "if [ $TRAVIS_OS_NAME = 'osx' ]; then \ brew update && \ brew tap cartr/qt4 && \ brew tap-pin cartr/qt4 && \ brew install qt@4 && \ brew install protobuf && \ ls -lR /usr/local/include; \ fi" addons: apt: packages: - libqt4-dev - qt4-qmake - libpcap-dev - libprotobuf-dev - protobuf-compiler script: - qmake - make notifications: email: - secure: "LUIBAz/phzOdxlnFuoC6hfNfl3HAGYTkacVfLu0GsrOgNBhqPqsRszhdFuX/kEdGb18GIcqCQn4tLfFVD9YyjZKFCTjjfTEaUlbHYrear4VU6HihBvhu4I+F8nffJ9y77kyZhUfC9RdgTwWEVdwYMi3rjIK8UggPVV1s/FyE2t+UAjmzdGPAb6uxu9znYpldKtY9FosqccPe7tB5uLLcdbX6ojvsOH7lyQPLclFFS7F2yM9nNNxnRwl1v4KlN6vYwBF2scKB8altEMEGnLJKB41S6piVWyQlXc8FotGJf4fg/6lwmKWBzT/aIw8UH8cxJW2q3sHmdcf/nBhSKojLN5HNIQOJ45Xfw0MKQy2uXGHG15DzYRKjsw6zdb7oVJJCVDiwh1wDvfExgJwpIZJuNsMaGEbSo/TxN+6iPaCf2iyFaCW/KkBt5gmcgck2dA0Xc8qd0z3Zjii7cvuM8awrsmtE7UuX7M/lV9M8w5EaYjhr0R2NwgqvRa0hQv+1Ycec/1eVVfMCMFWNsxmVSkQFFbFP2db195axMzeU13G8RIbPY3JsIUEGw1ykj4nXiH77CLYRfZuKvomjhBkzE+P+EyIxRMH65M4zO9lO7SW01hrHty60jKajt5ljPI5AfuKAFHQvoLOwEMwLwbtEy/ggI/SRAXjsKJbueTHaI4719Gg=" ostinato-0.9/.vimrc000066400000000000000000000001161321315675200143460ustar00rootroot00000000000000set shiftwidth=4 set tabstop=8 set softtabstop=4 set expandtab set cindent ostinato-0.9/COPYING000066400000000000000000001045131321315675200142660ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. 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 them 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 prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. 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. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey 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; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If 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 convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU 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 that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. 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. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 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. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. 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 state 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 3 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, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program 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, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU 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. But first, please read . ostinato-0.9/README.md000066400000000000000000000011231321315675200145030ustar00rootroot00000000000000# Ostinato [![Build Status](https://travis-ci.org/pstavirs/ostinato.svg?branch=master)](https://travis-ci.org/pstavirs/ostinato) Ostinato is an open-source, cross-platform network packet crafter/traffic generator and analyzer with a friendly GUI and powerful python API. Craft and send packets of several streams with different protocols at different rates. Ostinato aims to be "Wireshark in Reverse" and become complementary to Wireshark. License: GPLv3+ (see [COPYING](https://raw.githubusercontent.com/pstavirs/ostinato/master/COPYING)) For more information visit http://ostinato.org. ostinato-0.9/client/000077500000000000000000000000001321315675200145055ustar00rootroot00000000000000ostinato-0.9/client/about.ui000066400000000000000000000152711321315675200161640ustar00rootroot00000000000000 About 0 0 500 327 0 0 About Ostinato 0 Ostinato 0 0 :/icons/logo.png false Qt::AlignCenter Qt::Vertical 20 21 :/icons/name.png Qt::AlignCenter Version/Revision Placeholder Qt::AlignCenter Copyright (c) 2007-2016 Srivats P. Qt::AlignCenter <a href="http://ostinato.org">http://ostinato.org</a><br><a href="http://twitter.com/ostinato">@ostinato</a> Qt::RichText Qt::AlignCenter true Qt::Vertical 20 21 Logo (c): Dhiman Sengupta Icons (c): Mark James (http://www.famfamfam.com/lab/icons/silk/) Qt::AlignCenter License <p>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 3 of the License, or (at your option) any later version.</p><p>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.</p><p>You should have received a copy of the GNU General Public License along with this program. If not, see <a href="http://www.gnu.org/licenses/">http://www.gnu.org/licenses/</a></p> Qt::RichText Qt::AlignCenter true true Qt::Horizontal QDialogButtonBox::Ok buttonBox accepted() About accept() 353 280 286 262 ostinato-0.9/client/arpstatusmodel.cpp000066400000000000000000000071051321315675200202630ustar00rootroot00000000000000/* Copyright (C) 2016 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "arpstatusmodel.h" #include "port.h" #include "emulproto.pb.h" #include enum { kIp4Address, kMacAddress, kStatus, kFieldCount }; static QStringList columns_ = QStringList() << "IPv4 Address" << "Mac Address" << "Status"; ArpStatusModel::ArpStatusModel(QObject *parent) : QAbstractTableModel(parent) { port_ = NULL; deviceIndex_ = -1; neighbors_ = NULL; } int ArpStatusModel::rowCount(const QModelIndex &parent) const { if (!port_ || deviceIndex_ < 0 || parent.isValid()) return 0; return port_->numArp(deviceIndex_); } int ArpStatusModel::columnCount(const QModelIndex &parent) const { if (parent.isValid()) return 0; return columns_.size(); } QVariant ArpStatusModel::headerData( int section, Qt::Orientation orientation, int role) const { if (role != Qt::DisplayRole) return QVariant(); switch (orientation) { case Qt::Horizontal: return columns_[section]; case Qt::Vertical: return QString("%1").arg(section + 1); default: Q_ASSERT(false); // Unreachable } return QVariant(); } QVariant ArpStatusModel::data(const QModelIndex &index, int role) const { QString str; if (!port_ || deviceIndex_ < 0 || !index.isValid()) return QVariant(); int arpIdx = index.row(); int field = index.column(); Q_ASSERT(arpIdx < port_->numArp(deviceIndex_)); Q_ASSERT(field < kFieldCount); const OstEmul::ArpEntry &arp = neighbors_->arp(arpIdx); switch (field) { case kIp4Address: switch (role) { case Qt::DisplayRole: return QHostAddress(arp.ip4()).toString(); default: break; } return QVariant(); case kMacAddress: switch (role) { case Qt::DisplayRole: return QString("%1").arg(arp.mac(), 6*2, 16, QChar('0')) .replace(QRegExp("([0-9a-fA-F]{2}\\B)"), "\\1:") .toUpper(); default: break; } return QVariant(); case kStatus: switch (role) { case Qt::DisplayRole: return arp.mac() ? QString("Resolved") : QString("Failed"); default: break; } return QVariant(); default: Q_ASSERT(false); // unreachable! break; } qWarning("%s: Unsupported field #%d", __FUNCTION__, field); return QVariant(); } void ArpStatusModel::setDeviceIndex(Port *port, int deviceIndex) { port_ = port; deviceIndex_ = deviceIndex; if (port_) neighbors_ = port_->deviceNeighbors(deviceIndex); reset(); } void ArpStatusModel::updateArpStatus() { reset(); } ostinato-0.9/client/arpstatusmodel.h000066400000000000000000000026701321315675200177320ustar00rootroot00000000000000/* Copyright (C) 2016 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _ARP_STATUS_MODEL_H #define _ARP_STATUS_MODEL_H #include class Port; namespace OstEmul { class DeviceNeighborList; } class ArpStatusModel: public QAbstractTableModel { Q_OBJECT public: ArpStatusModel(QObject *parent = 0); int rowCount(const QModelIndex &parent = QModelIndex()) const; int columnCount(const QModelIndex &parent = QModelIndex()) const; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; QVariant data(const QModelIndex &index, int role) const; void setDeviceIndex(Port *port, int deviceIndex); public slots: void updateArpStatus(); private: Port *port_; int deviceIndex_; const OstEmul::DeviceNeighborList *neighbors_; }; #endif ostinato-0.9/client/devicegroupdialog.cpp000066400000000000000000000275651321315675200207240ustar00rootroot00000000000000/* Copyright (C) 2016 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "devicegroupdialog.h" #include "port.h" #include "spinboxdelegate.h" #include "emulproto.pb.h" #include "uint128.h" #include #include enum { kVlanId, kVlanCount, kVlanStep, kVlanCfi, kVlanPrio, kVlanTpid, kVlanColumns }; static QStringList vlanTableColumnHeaders = QStringList() << "Vlan Id" << "Count" << "Step" << "CFI/DE" << "Prio" << "TPID"; enum { kIpNone, kIp4, kIp6, kIpDual }; static QStringList ipStackItems = QStringList() << "None" << "IPv4" << "IPv6" << "Dual"; inline UInt128 UINT128(OstEmul::Ip6Address x) { return UInt128(x.hi(), x.lo()); } inline OstEmul::Ip6Address IP6ADDR(UInt128 x) { OstEmul::Ip6Address ip; ip.set_hi(x.hi64()); ip.set_lo(x.lo64()); return ip; } DeviceGroupDialog::DeviceGroupDialog( Port *port, int deviceGroupIndex, QWidget *parent, Qt::WindowFlags flags) : QDialog(parent, flags), port_(port), index_(deviceGroupIndex) { // Setup the Dialog setupUi(this); vlanTagCount->setRange(0, kMaxVlanTags); // Populate the Vlan Table with placeholders - we do this so that // user entered values are retained during the lifetime of the dialog // even if user is playing around with number of vlan tags vlans->setRowCount(kMaxVlanTags); vlans->setColumnCount(kVlanColumns); vlans->setHorizontalHeaderLabels(vlanTableColumnHeaders); for (int i = 0; i < kMaxVlanTags; i++) { // Use same default values as defined in .proto vlans->setItem(i, kVlanId, new QTableWidgetItem(QString::number(100*(i+1)))); vlans->setItem(i, kVlanCount, new QTableWidgetItem(QString::number(1))); vlans->setItem(i, kVlanStep, new QTableWidgetItem(QString::number(1))); vlans->setItem(i, kVlanCfi, new QTableWidgetItem(QString::number(0))); vlans->setItem(i, kVlanPrio, new QTableWidgetItem(QString::number(0))); vlans->setItem(i, kVlanTpid, new QTableWidgetItem(QString("0x8100"))); } // Set SpinBoxDelegate for all columns except TPID SpinBoxDelegate *spd = new SpinBoxDelegate(this); spd->setColumnRange(kVlanId, 0, 4095); spd->setColumnRange(kVlanStep, 0, 4095); spd->setColumnRange(kVlanCfi, 0, 1); spd->setColumnRange(kVlanPrio, 0, 7); for (int i = 0; i < kVlanColumns; i++) { if (i != kVlanTpid) vlans->setItemDelegateForColumn(i, spd); } vlans->horizontalHeader()->setResizeMode(QHeaderView::Stretch); vlans->resizeRowsToContents(); // Set vlan tag count *after* adding all items, so connected slots // can access the items vlanTagCount->setValue(kMaxVlanTags); devicePerVlanCount->setRange(1, 0x7fffffff); ipStack->insertItems(0, ipStackItems); // TODO: DeviceGroup Traversal; hide buttons for now // NOTE for implementation: Use a QHash // to store modified values while traversing; in accept() // update port->deviceGroups[] from the QHash prev->setHidden(true); next->setHidden(true); // TODO: Preview devices expanded from deviceGroup configuration // for user convenience // setup dialog to auto-resize as widgets are hidden or shown layout()->setSizeConstraint(QLayout::SetFixedSize); connect(devicePerVlanCount, SIGNAL(valueChanged(const QString&)), this, SLOT(updateTotalDeviceCount())); connect(ip4Address, SIGNAL(textEdited(const QString&)), this, SLOT(updateIp4Gateway())); connect(ip4PrefixLength, SIGNAL(valueChanged(const QString&)), this, SLOT(updateIp4Gateway())); connect(ip6Address, SIGNAL(textEdited(const QString&)), this, SLOT(updateIp6Gateway())); connect(ip6PrefixLength, SIGNAL(valueChanged(const QString&)), this, SLOT(updateIp6Gateway())); loadDeviceGroup(); } void DeviceGroupDialog::accept() { storeDeviceGroup(); QDialog::accept(); } // // Private Slots // void DeviceGroupDialog::on_vlanTagCount_valueChanged(int value) { Q_ASSERT((value >= 0) && (value <= kMaxVlanTags)); for (int row = 0; row < kMaxVlanTags; row++) vlans->setRowHidden(row, row >= value); vlans->setVisible(value > 0); updateTotalVlanCount(); } void DeviceGroupDialog::on_vlans_cellChanged(int row, int col) { if (col != kVlanCount) return; if (vlans->isRowHidden(row)) return; updateTotalVlanCount(); } void DeviceGroupDialog::on_ipStack_currentIndexChanged(int index) { switch (index) { case kIpNone: ip4->hide(); ip6->hide(); break; case kIp4: ip4->show(); ip6->hide(); break; case kIp6: ip4->hide(); ip6->show(); break; case kIpDual: ip4->show(); ip6->show(); break; default: Q_ASSERT(false); // Unreachable! break; } } void DeviceGroupDialog::updateTotalVlanCount() { int count = vlanTagCount->value() ? 1 : 0; for (int i = 0; i < vlanTagCount->value(); i++) count *= vlans->item(i, kVlanCount)->text().toUInt(); vlanCount->setValue(count); updateTotalDeviceCount(); } void DeviceGroupDialog::updateTotalDeviceCount() { totalDeviceCount->setValue(qMax(vlanCount->value(), 1) * devicePerVlanCount->value()); } void DeviceGroupDialog::updateIp4Gateway() { quint32 net = ip4Address->value() & (~0 << (32 - ip4PrefixLength->value())); ip4Gateway->setValue(net | 0x01); } void DeviceGroupDialog::updateIp6Gateway() { UInt128 net = ip6Address->value() & (~UInt128(0, 0) << (128 - ip6PrefixLength->value())); ip6Gateway->setValue(net | UInt128(0, 1)); } void DeviceGroupDialog::loadDeviceGroup() { const OstProto::DeviceGroup *devGrp = port_->deviceGroupByIndex(index_); int tagCount = 0; // use 1-indexed id so that it matches the port id used in the // RFC 4814 compliant mac addresses assigned by default to deviceGroups // XXX: use deviceGroupId also as part of the id? quint32 id = (port_->id()+1) & 0xff; Q_ASSERT(devGrp); name->setText(QString::fromStdString(devGrp->core().name())); if (devGrp->has_encap() && devGrp->encap().HasExtension(OstEmul::vlan)) { OstEmul::VlanEmulation vlan = devGrp->encap() .GetExtension(OstEmul::vlan); tagCount = vlan.stack_size(); for (int i = 0; i < tagCount; i++) { OstEmul::VlanEmulation::Vlan v = vlan.stack(i); vlans->item(i, kVlanPrio)->setText( QString::number((v.vlan_tag() >> 13) & 0x7)); vlans->item(i, kVlanCfi)->setText( QString::number((v.vlan_tag() >> 12) & 0x1)); vlans->item(i, kVlanId)->setText( QString::number(v.vlan_tag() & 0x0fff)); vlans->item(i, kVlanCount)->setText(QString::number(v.count())); vlans->item(i, kVlanStep)->setText(QString::number(v.step())); vlans->item(i, kVlanTpid)->setText(QString("0x%1") .arg(v.tpid(), 0, 16)); } } vlanTagCount->setValue(tagCount); updateTotalVlanCount(); devicePerVlanCount->setValue(devGrp->device_count()); OstEmul::MacEmulation mac = devGrp->GetExtension(OstEmul::mac); Q_ASSERT(mac.has_address()); macAddress->setValue(mac.address()); macStep->setValue(mac.step()); OstEmul::Ip4Emulation ip4 = devGrp->GetExtension(OstEmul::ip4); // If address is not set, assign one from RFC 2544 space - 192.18.0.0/15 // Use port Id as the 3rd octet of the address ip4Address->setValue(ip4.has_address() ? ip4.address() : 0xc6120002 | (id << 8)); ip4PrefixLength->setValue(ip4.prefix_length()); ip4Step->setValue(ip4.has_step()? ip4.step() : 1); ip4Gateway->setValue(ip4.has_default_gateway() ? ip4.default_gateway() : 0xc6120001 | (id << 8)); OstEmul::Ip6Emulation ip6 = devGrp->GetExtension(OstEmul::ip6); // If address is not set, assign one from RFC 5180 space 2001:0200::/64 // Use port Id as the 3rd hextet of the address ip6Address->setValue(ip6.has_address() ? UINT128(ip6.address()) : UInt128((0x200102000000ULL | id) << 16, 2)); ip6PrefixLength->setValue(ip6.prefix_length()); ip6Step->setValue(ip6.has_step() ? UINT128(ip6.step()) : UInt128(0, 1)); ip6Gateway->setValue(ip6.has_default_gateway() ? UINT128(ip6.default_gateway()) : UInt128((0x200102000000ULL | id) << 16, 1)); int stk = kIpNone; if (devGrp->HasExtension(OstEmul::ip4)) if (devGrp->HasExtension(OstEmul::ip6)) stk = kIpDual; else stk = kIp4; else if (devGrp->HasExtension(OstEmul::ip6)) stk = kIp6; ipStack->setCurrentIndex(stk); } void DeviceGroupDialog::storeDeviceGroup() { OstProto::DeviceGroup *devGrp = port_->mutableDeviceGroupByIndex(index_); int tagCount = vlanTagCount->value(); Q_ASSERT(devGrp); devGrp->mutable_core()->set_name(name->text().toStdString()); OstEmul::VlanEmulation *vlan = devGrp->mutable_encap() ->MutableExtension(OstEmul::vlan); vlan->clear_stack(); for (int i = 0; i < tagCount; i++) { OstEmul::VlanEmulation::Vlan *v = vlan->add_stack(); v->set_vlan_tag( vlans->item(i, kVlanPrio)->text().toUInt() << 13 | vlans->item(i, kVlanCfi)->text().toUInt() << 12 | vlans->item(i, kVlanId)->text().toUInt()); v->set_count(vlans->item(i, kVlanCount)->text().toUInt()); v->set_step(vlans->item(i, kVlanStep)->text().toUInt()); v->set_tpid(vlans->item(i, kVlanTpid)->text().toUInt(NULL, 16)); } if (!tagCount) devGrp->clear_encap(); devGrp->set_device_count(devicePerVlanCount->value()); OstEmul::MacEmulation *mac = devGrp->MutableExtension(OstEmul::mac); mac->set_address(macAddress->value()); mac->set_step(macStep->value()); if (ipStack->currentIndex() == kIp4 || ipStack->currentIndex() == kIpDual) { OstEmul::Ip4Emulation *ip4 = devGrp->MutableExtension(OstEmul::ip4); ip4->set_address(ip4Address->value()); ip4->set_prefix_length(ip4PrefixLength->value()); ip4->set_default_gateway(ip4Gateway->value()); ip4->set_step(ip4Step->value()); if (ipStack->currentIndex() == kIp4) devGrp->ClearExtension(OstEmul::ip6); } if (ipStack->currentIndex() == kIp6 || ipStack->currentIndex() == kIpDual) { OstEmul::Ip6Emulation *ip6 = devGrp->MutableExtension(OstEmul::ip6); ip6->mutable_address()->CopyFrom(IP6ADDR(ip6Address->value())); ip6->set_prefix_length(ip6PrefixLength->value()); ip6->mutable_step()->CopyFrom(IP6ADDR(ip6Step->value())); ip6->mutable_default_gateway()->CopyFrom(IP6ADDR(ip6Gateway->value())); if (ipStack->currentIndex() == kIp6) devGrp->ClearExtension(OstEmul::ip4); } if (ipStack->currentIndex() == kIpNone) { devGrp->ClearExtension(OstEmul::ip4); devGrp->ClearExtension(OstEmul::ip6); } } ostinato-0.9/client/devicegroupdialog.h000066400000000000000000000027231321315675200203560ustar00rootroot00000000000000/* Copyright (C) 2016 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _DEVICE_GROUP_DIALOG_H #define _DEVICE_GROUP_DIALOG_H #include "ui_devicegroupdialog.h" #include class Port; class DeviceGroupDialog: public QDialog, private Ui::DeviceGroupDialog { Q_OBJECT public: DeviceGroupDialog(Port *port, int deviceGroupIndex, QWidget *parent = NULL, Qt::WindowFlags flags = 0); virtual void accept(); private slots: void on_vlanTagCount_valueChanged(int value); void on_vlans_cellChanged(int row, int col); void on_ipStack_currentIndexChanged(int index); void updateTotalVlanCount(); void updateTotalDeviceCount(); void updateIp4Gateway(); void updateIp6Gateway(); void loadDeviceGroup(); void storeDeviceGroup(); private: static const int kMaxVlanTags = 4; Port *port_; int index_; }; #endif ostinato-0.9/client/devicegroupdialog.ui000066400000000000000000000236641321315675200205530ustar00rootroot00000000000000 DeviceGroupDialog 0 0 504 465 Devices Name Vlan Tags Qt::Horizontal 40 20 Total Vlans false Devices Per Vlan devicePerVlanCount Total Devices false Mac Address Step IP Stack QFrame::Box QFrame::Plain IPv4 Address 0 0 / Step Gateway 1 32 QFrame::Box QFrame::Plain IPv6 Address 0 0 / Step Gateway 1 128 < > Qt::Horizontal 40 20 Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::NoButton|QDialogButtonBox::Ok MacEdit QLineEdit
macedit.h
Ip4Edit QLineEdit
ip4edit.h
Ip6Edit QLineEdit
ip6edit.h
IntEdit QSpinBox
intedit.h
name vlanTagCount vlans devicePerVlanCount macAddress macStep ipStack ip4Address ip4PrefixLength ip4Step ip4Gateway ip6Address ip6PrefixLength ip6Step ip6Gateway prev next buttonBox buttonBox accepted() DeviceGroupDialog accept() 227 289 157 274 buttonBox rejected() DeviceGroupDialog reject() 295 295 286 274
ostinato-0.9/client/devicegroupmodel.cpp000066400000000000000000000162701321315675200205540ustar00rootroot00000000000000/* Copyright (C) 2016 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "devicegroupmodel.h" #include "port.h" #include "emulproto.pb.h" #include "uint128.h" #include enum { kName, kVlanCount, kDeviceCount, // Across all vlans kIp, kIp4Address, kIp6Address, kFieldCount }; static QStringList columns_ = QStringList() << "Name" << "Vlans" << "Devices" << "IP Stack" << "IPv4 Address" << "IPv6 Address"; DeviceGroupModel::DeviceGroupModel(QObject *parent) : QAbstractTableModel(parent) { port_ = NULL; } int DeviceGroupModel::rowCount(const QModelIndex &parent) const { if (!port_ || parent.isValid()) return 0; return port_->numDeviceGroups(); } int DeviceGroupModel::columnCount(const QModelIndex &parent) const { if (parent.isValid()) return 0; return columns_.size(); } QVariant DeviceGroupModel::headerData( int section, Qt::Orientation orientation, int role) const { if (role != Qt::DisplayRole) return QVariant(); switch (orientation) { case Qt::Horizontal: return columns_[section]; case Qt::Vertical: return QString("%1").arg(section + 1); default: Q_ASSERT(false); // Unreachable } return QVariant(); } QVariant DeviceGroupModel::data(const QModelIndex &index, int role) const { if (!port_ || !index.isValid()) return QVariant(); int dgIdx = index.row(); int field = index.column(); Q_ASSERT(dgIdx < port_->numDeviceGroups()); Q_ASSERT(field < kFieldCount); const OstProto::DeviceGroup *devGrp = port_->deviceGroupByIndex(dgIdx); Q_ASSERT(devGrp); switch (field) { case kName: switch (role) { case Qt::DisplayRole: return QString::fromStdString(devGrp->core().name()); default: break; } return QVariant(); case kVlanCount: switch (role) { case Qt::DisplayRole: if (int v = vlanCount(devGrp)) return v; return QString("None"); case Qt::TextAlignmentRole: return Qt::AlignRight; default: break; } return QVariant(); case kDeviceCount: switch (role) { case Qt::DisplayRole: return qMax(vlanCount(devGrp), 1)*devGrp->device_count(); case Qt::TextAlignmentRole: return Qt::AlignRight; default: break; } return QVariant(); case kIp: switch (role) { case Qt::DisplayRole: if (devGrp->HasExtension(OstEmul::ip4)) if (devGrp->HasExtension(OstEmul::ip6)) return QString("Dual Stack"); else return QString("IPv4"); else if (devGrp->HasExtension(OstEmul::ip6)) return QString("IPv6"); else return QString("None"); default: break; } return QVariant(); case kIp4Address: switch (role) { case Qt::DisplayRole: if (devGrp->HasExtension(OstEmul::ip4)) return QHostAddress( devGrp->GetExtension(OstEmul::ip4) .address()).toString(); else return QString("--"); default: break; } return QVariant(); case kIp6Address: switch (role) { case Qt::DisplayRole: if (devGrp->HasExtension(OstEmul::ip6)) { OstEmul::Ip6Address ip = devGrp->GetExtension( OstEmul::ip6).address(); return QHostAddress( UInt128(ip.hi(), ip.lo()).toArray()) .toString(); } else return QString("--"); default: break; } return QVariant(); default: Q_ASSERT(false); // unreachable! break; } qWarning("%s: Unsupported field #%d", __FUNCTION__, field); return QVariant(); } bool DeviceGroupModel::setData( const QModelIndex & /*index*/, const QVariant & /*value*/, int /*role*/) { if (!port_) return false; // TODO; when implementing also implement flags() to // return ItemIsEditable return false; } bool DeviceGroupModel::insertRows( int row, int count, const QModelIndex &parent) { int c = 0; Q_ASSERT(!parent.isValid()); beginInsertRows(parent, row, row+count-1); for (int i = 0; i < count; i++) { if (port_->newDeviceGroupAt(row)) c++; } endInsertRows(); if (c != count) { qWarning("failed to insert rows in DeviceGroupModel at row %d; " "requested = %d, actual = %d", row, count, c); return false; } return true; } bool DeviceGroupModel::removeRows( int row, int count, const QModelIndex &parent) { int c = 0; Q_ASSERT(!parent.isValid()); beginRemoveRows(parent, row, row+count-1); for (int i = 0; i < count; i++) { if (port_->deleteDeviceGroupAt(row)) c++; } endRemoveRows(); if (c != count) { qWarning("failed to delete rows in DeviceGroupModel at row %d; " "requested = %d, actual = %d", row, count, c); return false; } return true; } void DeviceGroupModel::setPort(Port *port) { port_ = port; reset(); } // // ---------------------- Private Methods ----------------------- // int DeviceGroupModel::vlanCount(const OstProto::DeviceGroup *deviceGroup) const { if (!deviceGroup->has_encap() || !deviceGroup->encap().HasExtension(OstEmul::vlan)) return 0; OstEmul::VlanEmulation vlan = deviceGroup->encap() .GetExtension(OstEmul::vlan); int numTags = vlan.stack_size(); int count = 1; for (int i = 0; i < numTags; i++) count *= vlan.stack(i).count(); return count; } ostinato-0.9/client/devicegroupmodel.h000066400000000000000000000032641321315675200202200ustar00rootroot00000000000000/* Copyright (C) 2016 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _DEVICE_GROUP_MODEL_H #define _DEVICE_GROUP_MODEL_H #include #include class Port; namespace OstProto { class DeviceGroup; }; class DeviceGroupModel: public QAbstractTableModel { Q_OBJECT public: DeviceGroupModel(QObject *parent = 0); int rowCount(const QModelIndex &parent = QModelIndex()) const; int columnCount(const QModelIndex &parent = QModelIndex()) const; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; QVariant data(const QModelIndex &index, int role) const; bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole); bool insertRows (int row, int count, const QModelIndex &parent = QModelIndex()); bool removeRows (int row, int count, const QModelIndex &parent = QModelIndex()); void setPort(Port *port); private: int vlanCount(const OstProto::DeviceGroup *deviceGroup) const; Port *port_; }; #endif ostinato-0.9/client/devicemodel.cpp000066400000000000000000000175311321315675200175000ustar00rootroot00000000000000/* Copyright (C) 2016 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "devicemodel.h" #include "arpstatusmodel.h" #include "ndpstatusmodel.h" #include "port.h" #include "emulproto.pb.h" #include "uint128.h" #include #include #include #include enum { kMacAddress, kVlans, kIp4Address, kIp4Gateway, kIp6Address, kIp6Gateway, kArpInfo, kNdpInfo, kFieldCount }; static QStringList columns_ = QStringList() << "Mac" << "Vlans" << "IPv4 Address" << "IPv4 Gateway" << "IPv6 Address" << "IPv6 Gateway" << "ARP" << "NDP"; DeviceModel::DeviceModel(QObject *parent) : QAbstractTableModel(parent) { port_ = NULL; arpStatusModel_ = new ArpStatusModel(this); ndpStatusModel_ = new NdpStatusModel(this); } int DeviceModel::rowCount(const QModelIndex &parent) const { if (!port_ || parent.isValid()) return 0; return port_->numDevices(); } int DeviceModel::columnCount(const QModelIndex &parent) const { if (parent.isValid()) return 0; return columns_.size(); } QVariant DeviceModel::headerData( int section, Qt::Orientation orientation, int role) const { if (role != Qt::DisplayRole) return QVariant(); switch (orientation) { case Qt::Horizontal: return columns_[section]; case Qt::Vertical: return QString("%1").arg(section + 1); default: Q_ASSERT(false); // Unreachable } return QVariant(); } QVariant DeviceModel::data(const QModelIndex &index, int role) const { QString str; if (!port_ || !index.isValid()) return QVariant(); int devIdx = index.row(); int field = index.column(); Q_ASSERT(devIdx < port_->numDevices()); Q_ASSERT(field < kFieldCount); const OstEmul::Device *dev = port_->deviceByIndex(devIdx); Q_ASSERT(dev); switch (field) { case kMacAddress: switch (role) { case Qt::DisplayRole: return QString("%1").arg(dev->mac(), 6*2, 16, QChar('0')) .replace(QRegExp("([0-9a-fA-F]{2}\\B)"), "\\1:") .toUpper(); default: break; } return QVariant(); case kVlans: switch (role) { case Qt::DisplayRole: if (!dev->vlan_size()) return QString("None"); for (int i = 0; i < dev->vlan_size(); i++) str.append(i == 0 ? "" : ", ") .append(QString::number(dev->vlan(i) & 0xfff)); return str; default: break; } return QVariant(); case kIp4Address: switch (role) { case Qt::DisplayRole: if (dev->has_ip4_prefix_length()) return QString("%1/%2") .arg(QHostAddress(dev->ip4()).toString()) .arg(dev->ip4_prefix_length()); else return QString("--"); default: break; } return QVariant(); case kIp4Gateway: switch (role) { case Qt::DisplayRole: if (dev->has_ip4_prefix_length()) return QHostAddress(dev->ip4_default_gateway()) .toString(); else return QString("--"); default: break; } return QVariant(); case kIp6Address: switch (role) { case Qt::DisplayRole: if (dev->has_ip6_prefix_length()) { OstEmul::Ip6Address ip = dev->ip6(); return QHostAddress( UInt128(ip.hi(), ip.lo()).toArray()) .toString(); } else return QString("--"); default: break; } return QVariant(); case kIp6Gateway: switch (role) { case Qt::DisplayRole: if (dev->has_ip6_prefix_length()) { OstEmul::Ip6Address ip = dev->ip6_default_gateway(); return QHostAddress( UInt128(ip.hi(), ip.lo()).toArray()) .toString(); } else return QString("--"); default: break; } return QVariant(); case kArpInfo: switch (role) { case Qt::DisplayRole: if (dev->has_ip4_prefix_length()) return QString("%1/%2") .arg(port_->numArpResolved(devIdx)) .arg(port_->numArp(devIdx)); else return QString("--"); default: if (dev->has_ip4_prefix_length()) return drillableStyle(role); break; } return QVariant(); case kNdpInfo: switch (role) { case Qt::DisplayRole: if (dev->has_ip6_prefix_length()) return QString("%1/%2") .arg(port_->numNdpResolved(devIdx)) .arg(port_->numNdp(devIdx)); else return QString("--"); default: if (dev->has_ip6_prefix_length()) return drillableStyle(role); break; } return QVariant(); default: Q_ASSERT(false); // unreachable! break; } qWarning("%s: Unsupported field #%d", __FUNCTION__, field); return QVariant(); } void DeviceModel::setPort(Port *port) { port_ = port; if (port_) connect(port_, SIGNAL(deviceInfoChanged()), SLOT(updateDeviceList())); reset(); } QAbstractItemModel* DeviceModel::detailModel(const QModelIndex &index) { if (!index.isValid()) return NULL; switch(index.column()) { case kArpInfo: arpStatusModel_->setDeviceIndex(port_, index.row()); return arpStatusModel_; case kNdpInfo: ndpStatusModel_->setDeviceIndex(port_, index.row()); return ndpStatusModel_; default: return NULL; } } void DeviceModel::updateDeviceList() { reset(); } // Style roles for drillable fields QVariant DeviceModel::drillableStyle(int role) const { QFont f; switch (role) { case Qt::ToolTipRole: return QString("Click for details ..."); case Qt::ForegroundRole: return QBrush(QColor(Qt::blue)); case Qt::FontRole: f.setUnderline(true); return f; default: break; } return QVariant(); } ostinato-0.9/client/devicemodel.h000066400000000000000000000027711321315675200171450ustar00rootroot00000000000000/* Copyright (C) 2016 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _DEVICE_MODEL_H #define _DEVICE_MODEL_H #include class ArpStatusModel; class NdpStatusModel; class Port; class DeviceModel: public QAbstractTableModel { Q_OBJECT public: DeviceModel(QObject *parent = 0); int rowCount(const QModelIndex &parent = QModelIndex()) const; int columnCount(const QModelIndex &parent = QModelIndex()) const; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; QVariant data(const QModelIndex &index, int role) const; void setPort(Port *port); QAbstractItemModel* detailModel(const QModelIndex &index); public slots: void updateDeviceList(); private: QVariant drillableStyle(int role) const; Port *port_; ArpStatusModel *arpStatusModel_; NdpStatusModel *ndpStatusModel_; }; #endif ostinato-0.9/client/deviceswidget.cpp000066400000000000000000000176161321315675200200520ustar00rootroot00000000000000/* Copyright (C) 2016 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "deviceswidget.h" #include "devicegroupdialog.h" #include "port.h" #include "portgrouplist.h" #include #include DevicesWidget::DevicesWidget(QWidget *parent) : QWidget(parent), portGroups_(NULL) { setupUi(this); deviceGroupList->setVisible(deviceConfig->isChecked()); deviceList->setVisible(deviceInfo->isChecked()); refresh->setVisible(deviceInfo->isChecked()); deviceDetail->hide(); deviceGroupList->verticalHeader()->setDefaultSectionSize( deviceGroupList->verticalHeader()->minimumSectionSize()); deviceList->verticalHeader()->setDefaultSectionSize( deviceList->verticalHeader()->minimumSectionSize()); deviceDetail->verticalHeader()->setDefaultSectionSize( deviceDetail->verticalHeader()->minimumSectionSize()); // Populate DeviceGroup Context Menu Actions deviceGroupList->addAction(actionNewDeviceGroup); deviceGroupList->addAction(actionEditDeviceGroup); deviceGroupList->addAction(actionDeleteDeviceGroup); // DevicesWidget's actions is an aggegate of all sub-widget's actions addActions(deviceGroupList->actions()); } void DevicesWidget::setPortGroupList(PortGroupList *portGroups) { portGroups_ = portGroups; deviceGroupList->setModel(portGroups_->getDeviceGroupModel()); deviceList->setModel(portGroups_->getDeviceModel()); connect(portGroups_->getDeviceGroupModel(), SIGNAL(rowsInserted(QModelIndex, int, int)), SLOT(updateDeviceViewActions())); connect(portGroups_->getDeviceGroupModel(), SIGNAL(rowsRemoved(QModelIndex, int, int)), SLOT(updateDeviceViewActions())); connect(deviceGroupList->selectionModel(), SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)), SLOT(updateDeviceViewActions())); connect(deviceGroupList->selectionModel(), SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)), SLOT(updateDeviceViewActions())); connect(deviceList->selectionModel(), SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)), SLOT(when_deviceList_currentChanged(const QModelIndex&))); // FIXME: hardcoding deviceGroupList->resizeColumnToContents(1); // Vlan Count deviceGroupList->resizeColumnToContents(2); // Device Count // FIXME: hardcoding deviceList->resizeColumnToContents(1); // Vlan Id(s) deviceList->resizeColumnToContents(6); // ARP Info deviceList->resizeColumnToContents(7); // NDP Info updateDeviceViewActions(); } void DevicesWidget::keyPressEvent(QKeyEvent *event) { if (event->key() == Qt::Key_Escape) { deviceDetail->hide(); event->accept(); } else event->ignore(); } void DevicesWidget::setCurrentPortIndex(const QModelIndex &portIndex) { Port *port = NULL; if (!portGroups_) return; // XXX: We assume portIndex corresponds to sourceModel, not proxyModel if (portGroups_->isPort(portIndex)) port = &portGroups_->port(portIndex); portGroups_->getDeviceGroupModel()->setPort(port); portGroups_->getDeviceModel()->setPort(port); currentPortIndex_ = portIndex; updateDeviceViewActions(); } void DevicesWidget::updateDeviceViewActions() { QItemSelectionModel *devSel = deviceGroupList->selectionModel(); if (!portGroups_) return; // For some reason hasSelection() returns true even if selection size is 0 // so additional check for size introduced if (devSel->hasSelection() && (devSel->selection().size() > 0)) { // If more than one non-contiguous ranges selected, // disable "New" and "Edit" if (devSel->selection().size() > 1) { actionNewDeviceGroup->setDisabled(true); actionEditDeviceGroup->setDisabled(true); } else { actionNewDeviceGroup->setEnabled(true); // Enable "Edit" only if the single range has a single row if (devSel->selection().at(0).height() > 1) actionEditDeviceGroup->setDisabled(true); else actionEditDeviceGroup->setEnabled(true); } // Delete is always enabled as long as we have a selection actionDeleteDeviceGroup->setEnabled(true); } else { qDebug("No device selection"); if (portGroups_->isPort(currentPortIndex_)) actionNewDeviceGroup->setEnabled(true); else actionNewDeviceGroup->setDisabled(true); actionEditDeviceGroup->setDisabled(true); actionDeleteDeviceGroup->setDisabled(true); } } // // DeviceGroup slots // void DevicesWidget::on_deviceInfo_toggled(bool checked) { refresh->setVisible(checked); deviceGroupList->setHidden(checked); deviceList->setVisible(checked); deviceDetail->hide(); } void DevicesWidget::on_actionNewDeviceGroup_triggered() { if (!portGroups_) return; // In case nothing is selected, insert 1 row at the end int row = portGroups_->getDeviceGroupModel()->rowCount(), count = 1; QItemSelection selection = deviceGroupList->selectionModel()->selection(); // In case we have a single range selected; insert as many rows as // in the singe selected range before the top of the selected range if (selection.size() == 1) { row = selection.at(0).top(); count = selection.at(0).height(); } portGroups_->getDeviceGroupModel()->insertRows(row, count); } void DevicesWidget::on_actionDeleteDeviceGroup_triggered() { QModelIndex index; if (!portGroups_) return; if (deviceGroupList->selectionModel()->hasSelection()) { while(deviceGroupList->selectionModel()->selectedRows().size()) { index = deviceGroupList->selectionModel()->selectedRows().at(0); portGroups_->getDeviceGroupModel()->removeRows(index.row(), 1); } } } void DevicesWidget::on_actionEditDeviceGroup_triggered() { QItemSelection selection = deviceGroupList->selectionModel()->selection(); // Ensure we have only one range selected which contains only one row if ((selection.size() == 1) && (selection.at(0).height() == 1)) on_deviceGroupList_activated(selection.at(0).topLeft()); } void DevicesWidget::on_deviceGroupList_activated(const QModelIndex &index) { if (!index.isValid() || !portGroups_) return; DeviceGroupDialog dgd(&portGroups_->port(currentPortIndex_), index.row()); dgd.exec(); } void DevicesWidget::on_refresh_clicked() { if (!portGroups_) return; Q_ASSERT(portGroups_->isPort(currentPortIndex_)); QModelIndex curPortGroup = portGroups_->getPortModel() ->parent(currentPortIndex_); Q_ASSERT(curPortGroup.isValid()); Q_ASSERT(portGroups_->isPortGroup(curPortGroup)); deviceDetail->hide(); portGroups_->portGroup(curPortGroup) .getDeviceInfo(portGroups_->port(currentPortIndex_).id()); } void DevicesWidget::when_deviceList_currentChanged(const QModelIndex &index) { if (!index.isValid() || !portGroups_) return; QAbstractItemModel *detailModel = portGroups_->getDeviceModel() ->detailModel(index); deviceDetail->setModel(detailModel); deviceDetail->setVisible(detailModel != NULL); } ostinato-0.9/client/deviceswidget.h000066400000000000000000000031041321315675200175020ustar00rootroot00000000000000/* Copyright (C) 2016 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _DEVICES_WIDGET_H #define _DEVICES_WIDGET_H #include "ui_deviceswidget.h" #include class PortGroupList; class DevicesWidget: public QWidget, private Ui::DevicesWidget { Q_OBJECT public: DevicesWidget(QWidget *parent = NULL); void setPortGroupList(PortGroupList *portGroups); virtual void keyPressEvent(QKeyEvent *event); public slots: void setCurrentPortIndex(const QModelIndex &portIndex); private slots: void updateDeviceViewActions(); void on_deviceInfo_toggled(bool checked); void on_actionNewDeviceGroup_triggered(); void on_actionDeleteDeviceGroup_triggered(); void on_actionEditDeviceGroup_triggered(); void on_deviceGroupList_activated(const QModelIndex &index); void on_refresh_clicked(); void when_deviceList_currentChanged(const QModelIndex &index); private: PortGroupList *portGroups_; QModelIndex currentPortIndex_; }; #endif ostinato-0.9/client/deviceswidget.ui000066400000000000000000000111131321315675200176670ustar00rootroot00000000000000 DevicesWidget 0 0 675 328 Form 0 Configuration true Information Qt::Horizontal 131 23 Refresh information Refresh device and neighbor information :/icons/refresh.png:/icons/refresh.png Qt::ActionsContextMenu This is the device group list for the selected port A device group is a set of one or more devices/hosts which will be emulated by Ostinato Right-click to create/edit a device group QFrame::StyledPanel 1 QAbstractItemView::ExtendedSelection QAbstractItemView::SelectRows 0 1 No devices being emulated To emulate a device, click on Configuration and create a device group QAbstractItemView::SelectRows IP neighbor cache is empty QAbstractItemView::SingleSelection :/icons/devicegroup_add.png:/icons/devicegroup_add.png New Device Group :/icons/devicegroup_delete.png:/icons/devicegroup_delete.png Delete Device Group :/icons/devicegroup_edit.png:/icons/devicegroup_edit.png Edit Device Group XTableView QTableView
xtableview.h
ostinato-0.9/client/dumpview.cpp000066400000000000000000000272261321315675200170620ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "dumpview.h" //! \todo Enable Scrollbars DumpView::DumpView(QWidget *parent) : QAbstractItemView(parent) { int w, h; // NOTE: Monospaced fonts only !!!!!!!!!!! setFont(QFont("Courier")); w = fontMetrics().width('X'); h = fontMetrics().height(); mLineHeight = h; mCharWidth = w; mSelectedRow = mSelectedCol = -1; // calculate width for offset column and the whitespace that follows it // 0010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ........ ........ mOffsetPaneTopRect = QRect(0, 0, w*4, h); mDumpPaneTopRect = QRect(mOffsetPaneTopRect.right()+w*3, 0, w*((8*3-1)+2+(8*3-1)), h); mAsciiPaneTopRect = QRect(mDumpPaneTopRect.right()+w*3, 0, w*(8+1+8), h); qDebug("DumpView::DumpView"); } QModelIndex DumpView::indexAt(const QPoint &/*point*/) const { #if 0 int x = point.x(); int row, col; if (x > mAsciiPaneTopRect.left()) { col = (x - mAsciiPaneTopRect.left()) / mCharWidth; if (col == 8) // don't select whitespace goto _exit; else if (col > 8) // adjust for whitespace col--; } else if (x > mDumpPaneTopRect.left()) { col = (x - mDumpPaneTopRect.left()) / (mCharWidth*3); } row = point.y()/mLineHeight; if ((col < 16) && (row < ((data.size()+16)/16))) { selrow = row; selcol = col; } else goto _exit; // last row check col if ((row == (((data.size()+16)/16) - 1)) && (col >= (data.size() % 16))) goto _exit; qDebug("dumpview::selection(%d, %d)", selrow, selcol); offset = selrow * 16 + selcol; #if 0 for(int i = 0; i < model()->rowCount(parent); i++) { QModelIndex index = model()->index(i, 0, parent); if (model()->hasChildren(index)) indexAtOffset(offset, index); // Non Leaf else if ( dump.append(model()->data(index, Qt::UserRole).toByteArray()); // Leaf // FIXME: Use RawValueRole instead of UserRole } #endif } _exit: // Clear existing selection selrow = -1; #endif return QModelIndex(); } void DumpView::scrollTo(const QModelIndex &/*index*/, ScrollHint /*hint*/) { // FIXME: implement scrolling } QRect DumpView::visualRect(const QModelIndex &/*index*/) const { // FIXME: calculate actual rect return rect(); } //protected: int DumpView::horizontalOffset() const { return horizontalScrollBar()->value(); } bool DumpView::isIndexHidden(const QModelIndex &/*index*/) const { return false; } QModelIndex DumpView::moveCursor(CursorAction /*cursorAction*/, Qt::KeyboardModifiers /*modifiers*/) { // FIXME(MED): need to implement movement using cursor return currentIndex(); } void DumpView::setSelection(const QRect &/*rect*/, QItemSelectionModel::SelectionFlags flags) { // FIXME(HI): calculate indexes using rect selectionModel()->select(QModelIndex(), flags); } int DumpView::verticalOffset() const { return verticalScrollBar()->value(); } QRegion DumpView::visualRegionForSelection( const QItemSelection &/*selection*/) const { // FIXME(HI) return QRegion(rect()); } //protected slots: void DumpView::dataChanged(const QModelIndex &/*topLeft*/, const QModelIndex &/*bottomRight*/) { // FIXME(HI) update(); } void DumpView::selectionChanged(const QItemSelection &/*selected*/, const QItemSelection &/*deselected*/) { // FIXME(HI) update(); } void DumpView::populateDump(QByteArray &dump, int &selOfs, int &selSize, QModelIndex parent) { // FIXME: Use new enum instead of Qt::UserRole //! \todo (low): generalize this for any model not just our pkt model Q_ASSERT(!parent.isValid()); qDebug("!!!! %d $$$$", dump.size()); for(int i = 0; i < model()->rowCount(parent); i++) { QModelIndex index = model()->index(i, 0, parent); Q_ASSERT(index.isValid()); // Assumption: protocol data is in bytes (not bits) qDebug("%d: %d bytes", i, model()->data(index, Qt::UserRole).toByteArray().size()); dump.append(model()->data(index, Qt::UserRole).toByteArray()); } if (selectionModel()->selectedIndexes().size()) { int j, bits; QModelIndex index; Q_ASSERT(selectionModel()->selectedIndexes().size() == 1); index = selectionModel()->selectedIndexes().at(0); if (index.parent().isValid()) { // Field // SelOfs = SUM(protocol sizes before selected field's protocol) + // SUM(field sizes before selected field) selOfs = 0; j = index.parent().row() - 1; while (j >= 0) { selOfs += model()->data(index.parent().sibling(j,0), Qt::UserRole).toByteArray().size(); j--; } bits = 0; j = index.row() - 1; while (j >= 0) { bits += model()->data(index.sibling(j,0), Qt::UserRole+1). toInt(); j--; } selOfs += bits/8; selSize = model()->data(index, Qt::UserRole).toByteArray().size(); } else { // Protocol selOfs = 0; j = index.row() - 1; while (j >= 0) { selOfs += model()->data(index.sibling(j,0), Qt::UserRole). toByteArray().size(); j--; } selSize = model()->data(index, Qt::UserRole).toByteArray().size(); } } } // TODO(LOW): rewrite this function - it's a mess! void DumpView::paintEvent(QPaintEvent* /*event*/) { QStylePainter painter(viewport()); QRect offsetRect = mOffsetPaneTopRect; QRect dumpRect = mDumpPaneTopRect; QRect asciiRect = mAsciiPaneTopRect; QPalette pal = palette(); static QByteArray data; //QByteArray ba; int selOfs = -1, selSize; int curSelOfs, curSelSize; qDebug("dumpview::paintEvent"); // FIXME(LOW): unable to set the self widget's font in constructor painter.setFont(QFont("Courier")); // set a white background painter.fillRect(rect(), QBrush(QColor(Qt::white))); if (model()) { data.clear(); populateDump(data, selOfs, selSize); } // display the offset, dump and ascii panes 8 + 8 bytes on a line for (int i = 0; i < data.size(); i+=16) { QString dumpStr, asciiStr; //ba = data.mid(i, 16); // display offset painter.drawItemText(offsetRect, Qt::AlignLeft | Qt::AlignTop, pal, true, QString("%1").arg(i, 4, 16, QChar('0')), QPalette::WindowText); // construct the dumpStr and asciiStr for (int j = i; (j < (i+16)) && (j < data.size()); j++) { unsigned char c = data.at(j); // extra space after 8 bytes if (((j+8) % 16) == 0) { dumpStr.append(" "); asciiStr.append(" "); } dumpStr.append(QString("%1").arg((uint)c, 2, 16, QChar('0')). toUpper()).append(" "); if (isPrintable(c)) asciiStr.append(QChar(c)); else asciiStr.append(QChar('.')); } // display dump painter.drawItemText(dumpRect, Qt::AlignLeft | Qt::AlignTop, pal, true, dumpStr, QPalette::WindowText); // display ascii painter.drawItemText(asciiRect, Qt::AlignLeft | Qt::AlignTop, pal, true, asciiStr, QPalette::WindowText); // if no selection, skip selection painting if (selOfs < 0) goto _next; // Check overlap between current row and selection { QRect r1(i, 0, qMin(16, data.size()-i), 8); QRect s1(selOfs, 0, selSize, 8); if (r1.intersects(s1)) { QRect t = r1.intersected(s1); curSelOfs = t.x(); curSelSize = t.width(); } else curSelSize = 0; } // overpaint selection on current row (if any) if (curSelSize > 0) { QRect r; QString selectedAsciiStr, selectedDumpStr; qDebug("dumpview::paintEvent - Highlighted (%d, %d)", curSelOfs, curSelSize); // construct the dumpStr and asciiStr for (int k = curSelOfs; (k < (curSelOfs + curSelSize)); k++) { unsigned char c = data.at(k); // extra space after 8 bytes if (((k+8) % 16) == 0) { // Avoid adding space at the start for fields starting // at second column 8 byte boundary if (k!=curSelOfs) { selectedDumpStr.append(" "); selectedAsciiStr.append(" "); } } selectedDumpStr.append(QString("%1").arg((uint)c, 2, 16, QChar('0')).toUpper()).append(" "); if (isPrintable(c)) selectedAsciiStr.append(QChar(c)); else selectedAsciiStr.append(QChar('.')); } // display dump r = dumpRect; if ((curSelOfs - i) < 8) r.translate(mCharWidth*(curSelOfs-i)*3, 0); else r.translate(mCharWidth*((curSelOfs-i)*3+1), 0); // adjust width taking care of selection stretching between // the two 8byte columns if (( (curSelOfs-i) < 8 ) && ( (curSelOfs-i+curSelSize) > 8 )) r.setWidth((curSelSize * 3 + 1) * mCharWidth); else r.setWidth((curSelSize * 3) * mCharWidth); painter.fillRect(r, pal.highlight()); painter.drawItemText(r, Qt::AlignLeft | Qt::AlignTop, pal, true, selectedDumpStr, QPalette::HighlightedText); // display ascii r = asciiRect; if ((curSelOfs - i) < 8) r.translate(mCharWidth*(curSelOfs-i)*1, 0); else r.translate(mCharWidth*((curSelOfs-i)*1+1), 0); // adjust width taking care of selection stretching between // the two 8byte columns if (( (curSelOfs-i) < 8 ) && ( (curSelOfs-i+curSelSize) > 8 )) r.setWidth((curSelSize * 1 + 1) * mCharWidth); else r.setWidth((curSelSize * 1) * mCharWidth); painter.fillRect(r, pal.highlight()); painter.drawItemText(r, Qt::AlignLeft | Qt::AlignTop, pal, true, selectedAsciiStr, QPalette::HighlightedText); } _next: // move the rects down offsetRect.translate(0, mLineHeight); dumpRect.translate(0, mLineHeight); asciiRect.translate(0, mLineHeight); } } ostinato-0.9/client/dumpview.h000066400000000000000000000040711321315675200165200ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include // FIXME: High class DumpView: public QAbstractItemView { public: DumpView(QWidget *parent=0); QModelIndex indexAt( const QPoint &point ) const; void scrollTo( const QModelIndex &index, ScrollHint hint = EnsureVisible ); QRect visualRect( const QModelIndex &index ) const; protected: int horizontalOffset() const; bool isIndexHidden( const QModelIndex &index ) const; QModelIndex moveCursor( CursorAction cursorAction, Qt::KeyboardModifiers modifiers ); void setSelection( const QRect &rect, QItemSelectionModel::SelectionFlags flags ); int verticalOffset() const; QRegion visualRegionForSelection( const QItemSelection &selection ) const; protected slots: void dataChanged( const QModelIndex &topLeft, const QModelIndex &bottomRight ); void selectionChanged( const QItemSelection &selected, const QItemSelection &deselected ); void paintEvent(QPaintEvent *event); private: void populateDump(QByteArray &dump, int &selOfs, int &selSize, QModelIndex parent = QModelIndex()); bool inline isPrintable(char c) {if ((c >= 32) && (c <= 126)) return true; else return false; } private: QRect mOffsetPaneTopRect; QRect mDumpPaneTopRect; QRect mAsciiPaneTopRect; int mSelectedRow, mSelectedCol; int mLineHeight; int mCharWidth; }; ostinato-0.9/client/hexlineedit.cpp000066400000000000000000000044671321315675200175260ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "hexlineedit.h" #include "qdebug.h" QString & uintToHexStr(quint64 num, QString &hexStr, quint8 octets); HexLineEdit::HexLineEdit( QWidget * parent) : QLineEdit(parent) { //QLineEdit::QLineEdit(parent); } void HexLineEdit::focusOutEvent(QFocusEvent* /*e*/) { #if 0 const QValidator *v = validator(); if ( v ) { int curpos = cursorPosition(); QString str = text(); if ( v->validate( str, curpos ) == QValidator::Acceptable ) { if ( curpos != cursorPosition() ) setCursorPosition( curpos ); if ( str != text() ) setText( str ); } else { if ( curpos != cursorPosition() ) setCursorPosition( curpos ); str = text(); v->fixup( str ); if ( str != text() ) { setText( str ); } } } QLineEdit::focusOutEvent( e ); emit focusOut(); #else #define uintToHexStr(num, bytesize) \ QString("%1").arg((num), (bytesize)*2 , 16, QChar('0')) bool isOk; ulong num; qDebug("before = %s\n", text().toAscii().data()); num = text().remove(QChar(' ')).toULong(&isOk, 16); setText(uintToHexStr(num, 4)); qDebug("after = %s\n", text().toAscii().data()); #undef uintToHexStr #endif } #if 0 void HexLineEdit::focusInEvent( QFocusEvent *e ) { QLineEdit::focusInEvent( e ); emit focusIn(); } void HexLineEdit::keyPressEvent( QKeyEvent *e ) { QLineEdit::keyPressEvent( e ); if ( e->key() == Key_Enter || e->key() == Key_Return ) { setSelection( 0, text().length() ); } } #endif ostinato-0.9/client/hexlineedit.h000066400000000000000000000020441321315675200171600ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _HEXLINEEDIT #define _HEXLINEEDIT #include class HexLineEdit : public QLineEdit { Q_OBJECT public: // Constructors HexLineEdit ( QWidget * parent); protected: void focusOutEvent( QFocusEvent *e ); //void focusInEvent( QFocusEvent *e ); //void keyPressEvent( QKeyEvent *e ); signals: //void focusIn(); void focusOut(); }; #endif ostinato-0.9/client/icons/000077500000000000000000000000001321315675200156205ustar00rootroot00000000000000ostinato-0.9/client/icons/about.png000066400000000000000000000020141321315675200174350ustar00rootroot00000000000000‰PNG  IHDR szzôsRGB®ÎébKGDÿÿÿ ½§“ pHYs  šœtIMEÚ±vâŒIDATXýWÑqã: „Þؘܿ ÌDw Tv¥¦¥¹‚P„© v,An@{?ES²oææaFep ,0u]"‚R ×ë÷lžg(¥ „@p¹\ µFUUðÞƒˆ@D˜çÎ9¤”E‡Ã!‚xÄB¨ë]ׄ "!ཇR DçÜjÿ|>—Ìó )%ˆ///°ÖF§|c¾Äëë+ˆÖZ€1D„aÊà|>G¤}ßßp½^QU†aˆ`‡œs1ìZkÀ8Ž«u@bGÀ²,› ”RÐZcY–xcæÔÖ3Ïó6€D)g©i­¡µÆççg<@k#BDðÞLjð~ ‚JŽ9dBˆÄ9€ôv5ñ]¾nšƘ}0 ÃfÞÒH)὇Ö:²>e~ÀZ )eü–öBÌì-‘ržç³ÇqŒ7[–%F§€A¬ À¶mc^½÷ñ–yxÓ41Oæy†"®S –3G"¾iúpþÙqêœß===ݤM礔BÀJshš&Šñ KG°\3à4ç¹@ñ÷Þ{ô}¿ò[×5~~~85Õ,‡”3er‰ÀUU}j­Wå]²4¯Š{‡ˆbd­]õ†ÒïÈK&ÀleƦûιM… !ÄïB‘/¹°ñïc3*”òt:ÅÜs“ÙkRÜY9r\v9p*©“Ç{¿BÍd,X–˲ÄKpÙæëÜ~QÁ„Ô¶-mÙª*:NB !u]Gïïï«uÉþ›¦‰5çÇâ¾µ–ˆˆú¾'"¢a⺮ë²S)åM¹µm[”^!ÄæhÅÊØ4ͪîMYÄñ|ÇRœæ6„p3fåà꺎╷ã]ιX&ÆH)¡”Â÷÷7¾¾¾¢Š±ÄîµekmŒÄÖ¶9ær™>Æ\.—ÝŽiŒY~O¬ŠeÈ#7 ÑÇÇÇÍ@Âåooo "t]·RÊG[ ÂJrlÍ…±çççÕdôHØWêºÆ4M»ò™‘ y<#x2ʧ۬v0 CœtKç3 “ÓZ[œïÕG7J¨”Šë…nö»®#c̶ȩ*ÓÁåîkš®í›>†£·áíh¯L—LjãöÅÆ³RïwŸ‡UšdÞ:ÿ<$·ÇhݸIEND®B`‚ostinato-0.9/client/icons/arrow_left.png000066400000000000000000000005311321315675200204710ustar00rootroot00000000000000‰PNG  IHDRóÿagAMA¯È7ŠétEXtSoftwareAdobe ImageReadyqÉe<ëIDAT8Ëcüÿÿ?%€‰B0È (=‘ÉÄ»I1€õ2¤À‹ ÍÅÙ$ôþüùÃððýC†_?3üú¿~1˜þŽ`ƒä.ô_edj>)Æ*®§À­Äðçßn90ýûï_ þÍðûÿýÑ¿V¯\‹ðÐÙ¿ÿ0þaøõ÷XÑ ¢ß`Å¿4ÔÜŸÿ@±_P½¹3î¼0›ˆ3'Ã×^¿ƒÕÙ ¯4ÿùý‡áþÂGŒŒÈ)1vUÈM B9 Ç i;N@ͺ@͉Ռ⅜㡻¼zZÓÉIEND®B`‚ostinato-0.9/client/icons/arrow_right.png000066400000000000000000000005351321315675200206600ustar00rootroot00000000000000‰PNG  IHDRóÿagAMA¯È7ŠétEXtSoftwareAdobe ImageReadyqÉe<ïIDAT8Ëcüÿÿ?%€‰B0Ä ˜å±Ûk’ÙüúñËæç÷_W‘ÅA±Pz"ÿüeøóûÃï_~ýü Ô¿~1ˆ–’døõë7ÃÍÛ·.j¿ ÒÃ3ÉJØ–áï? ÿþÀñï¿ø7Ãï@ü&ö‡áË—¯zZYj'¯M»e㤮¨è7Xñoý!÷ë÷o+Ã]röþ‡{±:èg0›‹ƒ‹ARRŠáõ«× ·oܽp{Î=x µ-€ìÚü¨YäX¸ÐõPóA ku1ba„ççÀ7Ýl¬ÐIEND®B`‚ostinato-0.9/client/icons/arrow_up.png000066400000000000000000000005641321315675200201710ustar00rootroot00000000000000‰PNG  IHDRóÿagAMA¯È7ŠétEXtSoftwareAdobe ImageReadyqÉe<IDAT8Ë¥“?/QÅϲ™„hg5²Ñj%ì'‘ˆF,[ ¾’ŠLH–¨ 3•(„ˆ†v‡è¼wÿ=•fcoN{O~9çÞÜVM4Q7Ü¿Ù)v/ûWQ€½ëí»´=»œ&Þàbã¶ÊׯpS»O“ÎâüÌÌ å^Ê×'&^:\˨6Š…eND!& ˜šœ9ê’£Ó_|«?\«ÝÔ Ås·råxó,÷µ«º‘g°*,(Fï#d[çùO¾•aAAŽ*¯P p1…øO+C›$`˜)¼ãˆŽÀ*Íw`AÁž#Ê0˜$àÙÑ „*Š?ÿÂõëºb&NÛRIEND®B`‚ostinato-0.9/client/icons/bullet_error.png000066400000000000000000000007061321315675200210310ustar00rootroot00000000000000‰PNG  IHDRóÿagAMA¯È7ŠétEXtSoftwareAdobe ImageReadyqÉe<XIDAT8Ëcüÿÿ?%€‰B@±,¸$ˆýÿï_ñÿÿý”\–¼"É÷öÄðýûý»‘_ÎßH—‘ì ¦>YoM>IC†??'__`J´·6…h5åóŠ0üý´‰A\/EàïÏ_µ——x°4hÓŸŸ¿*Eµ¢…~œc8µh)Àw†??~ù±A€6¹q ëÅð ~cø÷ó>Ãÿ ¿œfPt(füóógó©I–¼8 ¸¸Ð hK‹€œÿo—€z¿3«3üûõŒƒãƒ¸n„&Ðu…8 Úž*¤âmÌÅû†áÿŸ· ŒÌ g×Ý)ÿ·DÕäj~gjÒÑšþüø ô»?ÐÈ L|¢@‘ÿ fIp{˜9•”\ËÅnmnj „bðóǹÙÁß èüHø?œfÑÿÿK »€qèg&L³¨âØ© IEND®B`‚ostinato-0.9/client/icons/bullet_green.png000066400000000000000000000004471321315675200210020ustar00rootroot00000000000000‰PNG  IHDRóÿagAMA¯È7ŠétEXtSoftwareAdobe ImageReadyqÉe<¹IDAT8Ëcøÿÿ?%˜aÔì8ì²’âûí–Wí7[\µ]kÖ`³ÌDŠf_›þÿУƒÿwßßõê™Iÿ-æ6m€ý6Ë«ø?õÒÔÿg:þ/¹²è¿ù½«D`·ÁüêÖ;[þwŸéúßzªùÿÜ ³þ›tio€õr“†©§'þ_|eáÿÙfþŸp¨ç¿~™*ñ^°œg(e1]¿Á´OçªQ«æUÝ"å­D9©Ñ”H#"⫚¦üIEND®B`‚ostinato-0.9/client/icons/bullet_orange.png000066400000000000000000000004331321315675200211500ustar00rootroot00000000000000‰PNG  IHDRóÿagAMA¯È7ŠétEXtSoftwareAdobe ImageReadyqÉe<­IDAT8ËíÒ1 qÆñ_ɦ ºîÕx¼e:%2\éŸä(¢ãeÈÆtW'9ƒ[ÿ%ÝbRF™¾^ºK6ó~ꩯòÍä|\ÓÀ5Ó’fRÐ89E/c¤áb.º— &¾Ï*²0ð0¡òxÍ,ìQ·Ôã!w0Ta PÙ|FÝnò^¯1Bư‹yó<êÑ€iðÜér §ø…#„»‡[Xj[MàÈU·,ñOÀñ|Þ‹ëds)IEND®B`‚ostinato-0.9/client/icons/bullet_white.png000066400000000000000000000003111321315675200210100ustar00rootroot00000000000000‰PNG  IHDRµú7êgAMA¯È7ŠétEXtSoftwareAdobe ImageReadyqÉe<[IDAT(ÏÍÐ1€ DQÎÁi÷’[€H ’å÷kòÛ—L2÷Ÿ[ _¤è­Y.oéL&Il “NcÕ·N•A°A–Æà¥rØÉG zê.›_öI»;þ“k9ÈIEND®B`‚ostinato-0.9/client/icons/bullet_yellow.png000066400000000000000000000004371321315675200212140ustar00rootroot00000000000000‰PNG  IHDRóÿagAMA¯È7ŠétEXtSoftwareAdobe ImageReadyqÉe<±IDAT8ËíÒ= Aqðó9îçòd1˜ŒÍd°(Yå¥n]q£än¥<‰òΤ«®pÓñ ”VÃYuN$ñKð>,z¨ ]¨oCvUX&€¼VÂÈïðytL„Ó2äkàуF'—÷m†á<Åpßä°ýèãØæm™æU㼬ëìæ €C â…áÁæyÓàb ¬$ *Ì*°FEˆ—‡¶2ÐR’™Œø¢QÞ(ð_θ7iIEND®B`‚ostinato-0.9/client/icons/control_play.png000066400000000000000000000011201321315675200210250ustar00rootroot00000000000000‰PNG  IHDRóÿagAMA¯È7ŠétEXtSoftwareAdobe ImageReadyqÉe<âIDAT8Ë¥“˪â@†ÏCåò8«<ˆ‚"èÆ¢ ®‘ ¢*ÞÚËÂ…÷kÔxAQ±¦ÿæ$"s8Ã0 EBº¿¿ªþê|ÑÇÿÄúý¾Ôëõ´n·«w:ƒ1f´Z-½Ùljõz]úV€Ã2‡Ùz½¦óùLÇCÄét¢ÅbAµZU*ùKOؼ^¯„…çáp Ó4ér¹ˆo-—Ëf©T’ß>Ëf¼Ûíh»ÝR>Ÿ'Ã0h³Ùª²Dt]gÙlV²x¿šuÀ‚:?)—Ë x¹\ŠÀ ”J¥4[€›¥C¥´²:N<£€áü@‚D"¡ÛívÛ€Yûý^ÀȸZ­8ìárý¢jµJ³ÙLÝn7ŠF£†-ÀÇ$P¾ã `Uu‘¢ü{ÓéÔ®"‰¼†ÇцÕ+ªªúO&1™ápHápøÕŸ¯†Íçó)Àù|.ÊUEÀØÇ4è~¿S±X¤`0ø2‘ÏU* `!°"+àÃŒ‡ôv‘2™ŒœN§M¸ƒÇãÑ®— ™!ÂAÓçóÉ_^åd2)ÇãqÆ[-ÀôŒ¹c”dGþögŠÅbR(Ò€î÷û ¯×kpHw»Ý—þú7þküFF?ÔE·E IEND®B`‚ostinato-0.9/client/icons/control_stop.png000066400000000000000000000006231321315675200210540ustar00rootroot00000000000000‰PNG  IHDRµú7êgAMA¯È7ŠétEXtSoftwareAdobe ImageReadyqÉe<%IDAT(Ï}‘MKAÆýPóæ”}…Iï! ÅB]ºuèÐ)ˆ˜C(AHeãËÁÚ–µŽ:Ö’”ôô쬔lÏeØßïÿ²39äþÏ÷¡#Úª¥›Ö؆¾U×"#tdÛDˆ±`^1DÝ\ÉØÍÌ1ƒÃO1.]M.67 ž` ËŒyE›sá…–Š2ø‰º8Q^hê˜m¸ÎäQ$r“1Ž´îìSâˆ0ï…G>Y‘4xx>PE.YfÊ­`È‚ˆåEÉS7âÇßDZ:Dà*råªå¡©sDÌÙ]hTLIfë@ì¨m½e˶¤7UYüzÍ¿ò(Þyv-|úHIEND®B`‚ostinato-0.9/client/icons/deco_exclusive.png000066400000000000000000000014311321315675200213260ustar00rootroot00000000000000‰PNG  IHDRóÿagAMA¯È7ŠétEXtSoftwareAdobe ImageReadyqÉe<«IDAT8Ë¥ÓÛOÒÅñþ¤z¨ÖÖS/=Y.k™[nµ¶Z— ƒ$K,Ì®¢‰šf”P Þr5 /Dä%ÔLù…”—@üú+ýöFk9[ëἜ‡ÏÓ9Û€mÿ“?ŠžÆÛûMW­Ã/K:•Òð«|ÉnÌY°Ôå¼xñ0Ï–€ÍT\9Ú¥X xúù6?C$FC|ù<ʨ½‘ÎG§WŒ¥ç 6zÕe^gá ?Ls·@eËGÊMãèÍ^F¦<:jÏ SŸÊû °èKö:_ʤТ€Í5Gk€7!‰Œ¬à™ ïòÒÖçcÒeÆp=3^[|vG xóTÞ1í2óéó&«€°(2õuž$bPýÊ‹Ýíçµá"ÕWŽéS@wý…ù9ÿË4ñïÄâq´u,Š"ÑÕUZ,ÝØ¦Dî4¸4R«ÌR€õéùd8ô…ÛÏÜ,&$«ë¬— UZÛ‘_SÑå`L”ÈÕ0+ ðXy8‘:kN'CßfPé\ÌÆ$ëÄ(7îÞC~M…Í5Jð;8C4ïñÏôQw9ãÐr?Û73fFcrÓ7-2—„@T¢£÷¾¥ ?@ˆCÛø2ÊÚ>ØÊÑæ¥Ï¦€'êl]¯QÃ=E‰ÉCpÂD×!ºË? ¸ùzÇí™hd‡M) îæ¹íõEG“Îzž™‡¹ešÄ%,KHÄânauÃ'ª›ß2b-¢"'-Y¦<¹ó·!Õ¨N(ž—a¢¿’žA'ź~.•¿ã¢ÆNa‹ý-ÃÖªåû)•gn:eí•ã5ŠôX{U#V5¾ ¾O²(iº{ŠÜ´•ÒKÇŠ¶<“¶àÄ®²¼Œ†¹üU²´D•,-ñ ÷€_#;d,ËÏÞý×7þk~¼ 怡:IEND®B`‚ostinato-0.9/client/icons/delete.png000066400000000000000000000013131321315675200175660ustar00rootroot00000000000000‰PNG  IHDRóÿagAMA¯È7ŠétEXtSoftwareAdobe ImageReadyqÉe<]IDAT8Ë¥“ûKSaÇý[¶¢‰n„„QP˜ó2wÖÚܦγL[,biŽašAÅ\øC¨¥vÕÊ_2Ml”ZFjסå±òÒNMjmž³kÊ·¹`&.#záûËËûù¼<Ï“ 岿bV¯ÎPæ÷T3¥%¹I­†{Gª™qRiv•È…ë ætzâ#E±ß6„ˆ¼EddüÝÌJñª`Ÿ«ÅDRÁ2<]Nñ ·;°4õѾ;ˆ¶Ûm>‡7›°8ÜÉ€Qe6ÿLžI¬Ìèt‚ìæ®·c‰q!zñ |v ü¶j„/Xi ¶ž@øÞ Ì%1|hŸû±l !ˆÁô|­‹®±ø! ïY#‚uºUáN’w]Á˼ H3è„àu„ t]E´³>k%¾I“f¡’o«ÇR…‡D:“0åÚ`ä~¢ |§ øÓñ(rॠáon„3oG0!˜$‹‚¡ÎV„ë ž*[W0_ª‚¿©ýâ-+‚‰ãµÖ d§ÁWÇ&2¾ZfMFô‰ÒVJpËiF&B°³ >­ ÞRɘ•gƒ- Ð~ CâmèÍÚ´ÒÄ×ERÁ ឫРp«5Þ°y•ø¨È+‹Á21ø¶ŒK—aw·h£`Õ ä#Šüôa×Zñ½ž†‡Tâ³ZoüåL¨óÑ“•ÊÇ`"é(?•ï'žÜËŽJváKµÞ†óñ|ª:†G9[—aöw8é2 Jw Äéf'±“y¿ëmæzsÓ˜žìTswæá_·ñ_óÒιIrþIEND®B`‚ostinato-0.9/client/icons/devicegroup_add.png000066400000000000000000000013311321315675200214500ustar00rootroot00000000000000‰PNG  IHDRóÿagAMA¯È7ŠétEXtSoftwareAdobe ImageReadyqÉe<kIDAT8ËuSKkQž•?¡ ·‚¸7®K.\ˆÅ*(¸wÁ…¦¥Õˆ˜4¯&4%Á<ˆ1‚Iò*Yäe“&) ̓“&¥NJ£IúyÏ¥-:©>æÎ™s¾ï»çÜR„Ãá7n·»n2™Ž´§ØY¹#•••1«Õú’E1‹ÅráL‚cÅ2S2Àår!ŸÏc8b0 ›Íò}£Ê=qÄ {ïoµJ¥‡Ãõõu»ÝÎcG=Nàt: Èår\‘‹Åàñx°¼¼ÌA{Š‘›~¿L&ªY\\´L©¿¹¹‰d2‰T*ÅI]ºN% „B!ètº¾Àì‰Ýn;;;ØÞÞF©T;¶¶¶NÑ>‰ P(Àçóq¤ÓihµZQ°Ùlœ`{{{¨V«Ün<?í©R©0 r[F#R9(ŠØÝÝE«ÕB½^çªt´h4Ê É29#'D@jµZØl Óé Ñhðâr¹Œb±ÈGIÉT¨u(ð\uÞÝÄ=ù8æL/¡T*{‚Ùlö{­VãÇ ê9 úöñÓ,^Ùïãˆßx¡ð=ÁmÅÜ™ž¨ lLçØå˜ZZZ*Rw›Í&Úí6o&}Ùì\Ù÷på?ð‰Ì¯NA±úןž?<½’F£qÌ`0¼f³ý¹¶¶ÆIˆŒ89}_7ôÿŒõsFI£?‡^¯¿Ìækf#:¢»á÷ûqãÙEÌy@î•ñbù7Ù¨)X‡Ç*•*:ùâšõÖü%¼õ>äÊô¤wF0ÿ_)Xò C‡l?g(þUÚuO¢IEND®B`‚ostinato-0.9/client/icons/devicegroup_delete.png000066400000000000000000000013511321315675200221640ustar00rootroot00000000000000‰PNG  IHDRóÿagAMA¯È7ŠétEXtSoftwareAdobe ImageReadyqÉe<{IDAT8ËuSMh’aÃÙ=ˆ1¢#:»t‰jÝF‡‚Û ÃNÁZuéÔEß_¸¡µ¹ 2Œ™StsH¨~0S'JNîufY¯úëù?´QÎ^øñ>üßç÷ñüÿÏ+ú±¾¾>»²²Rš››ëhMµA{O–——‡~¡ï¡šÕj=7Pàcž¹uàp8L&ÑívÑét°½½ÍkôöÐÞ£D\€©·ÿvÛÝÝÅÒÒb±‡Ífãµ¾Dm.`·ÛM H$Ü‘ át:1??ÏAkªQI’ÇAƒÁ`˜“”N§‰D°µµÅɽÿ9J‡á÷ûáõz¡Óé$Å›Í&*• ²Ù,r¹Øù°³³sœˆÖH¥Rp»ÝÑhZ­V¹@£Ñ@½^G¡PàqC¡ÐqÈ•šHDŸÏÇAi5H ä¢(bÕj¥R‰»ÒÑ'RdJFIH€ Ôjµ(°ÙþlµZ8<L&ÃGI›9ñí4|.b톟îŽÀÿz …¢-X,7©‹E~ ¡^PàÎŒ›¾†Î7è%WÑúð¡©+x?5þE`c:Í.ǤÙlÎPw÷ööP«Õx3I€â»îÇwFÆ»q`æ 0;ŠÚ«ëpÝÊ_I“É4l4_²Ùlnnr£®Ý”£±ÿ3Öƒ#ì8C½ÿ‚^¯¿Ìæka#êÑÝðx<øxç,¾ŒÔ~" ÎPx$ƒkLVýaÖáÛ*•Ê«T*®É«ÖÏ—¤¯3£¨>“#;q žñ¡Îê˜ìùú¸á)sÌSlö.™ê¿ç›Aáé¶IEND®B`‚ostinato-0.9/client/icons/devicegroup_edit.png000066400000000000000000000014071321315675200216510ustar00rootroot00000000000000‰PNG  IHDRóÿagAMA¯È7ŠétEXtSoftwareAdobe ImageReadyqÉe<™IDAT8Ë…SßkRQ¾O=ôÖËÖKôDýDc½4ˆFía,´ —V+ØCX[!T:lÑÕ¦Ó¡s–™E?v›¶%N&S§S§á66ówjW÷uÎa0G>8|÷|?Î9÷p¸z8Î6›-­Õj·)èœrÖþCLLL4Æ_¨”3 Í vã$­F«ÕŠP(„Z­†jµŠ@ À8ú®¡kÿ4bĽüwZ"‘€Ùl†Ïçcg\]£23°X,ƒA–Hár¹`·Û¡Óéèœr´$Iðûý žç I’Âá0æææàõzY"M¯” ~çÖ_DÔ*Ã<¯‡oV9R/W,±¶¶†X,†ååeý!‰ì6¢sï×GȽÈùßÅøé7ã›üü6g2™˜A>ŸG6›E2™du=Ïî¦Ø\èG%õ™©ÇØ XPMLç¹Æär9lll`}}ét𥲭M©]¸‡ÚÖ4¶b×±9ÛƒE]'wá•J^àÈÝVDQD¡P@&“aâx<Žh4аG‹làj%Ä¥”VÚQõaée+f…7*sz½þ³ H¥RlÔ„ž…wrOïŽø JÉKÈ/ÜEôy fì£ ÁP*•Ž\Ó>òstŒŒDVWWq!ôñ©íD)Þ‰òÊeäH“ˆê¬c<&z ÅþÝ_R£Ñ4©Õj?Ð-ß÷Á÷á Òî6ˆñvlÎ߯¢ªjå€H„r"<¸ç[0 ž „_\€Ð׌RY„îYÿ'">þßÇt¿ë¨Tq)Qq?…[v Æ«‡ª£ƒÝ7½Ä†m'”v‘&Z¤/òÓ[ï±Ã{‰)~¶ çµÛã“IEND®B`‚ostinato-0.9/client/icons/exit.png000066400000000000000000000012601321315675200172760ustar00rootroot00000000000000‰PNG  IHDRóÿagAMA¯È7ŠétEXtSoftwareAdobe ImageReadyqÉe<BIDAT8Ë•“»kÔA…¿ùíf³I ˜à›D¢†h AP+-´Ó?ÁN±A›”‚–‘ˆˆµXY±ÄW4>Œ…(Ñ ‘dgîc,V³YltàrªóÍÜÙsæÏÔõÓ—Üí¬™.–ÿÇøúÆ™wî6XY¿‰™Ù)z‹Š®ÆÆÆ²»ógÌlEOŒd œÊºM¬Ù6ˆ›“g^ Y˜}}}„Z4ç—ôì;âÏyL"9;"J±ÉLÖï­˜ÿŒ™áfÔkŸ‘¥\ÙíïÜ_¾ËBª:›fÕ„›`špM@ »“R½ ˜î}Ê–›é±µÜú8©¡b˜Wû÷UqI˜&È €ÊoÀ‘+‡žô0:4Šº²kp'⊘pûöä÷Í&‘¬u6®ÿÊû¬¤)¾|ðæÀ–ÑÑ¡jõK²„˜\è*¯AÕI˜*–"Ý]?è];O[i™E©SÜ;÷àØ›o§={LJ´—ªTËTKDK¨"u\#&‘Ú÷³³ëˆ©­âäŇ#ÏïÏËõeT•f0uL 7'¥ˆ«à1æcÜIÍ·ÝÉLxK‘KœÛ{¡¥ úêZ#L#!”qwTS³ªÊñáSdË-!$6ÌqIä ¼P'vŸjé@‘Æ =ý¸ — ÁL ãããyzzš¹¹¹–þ¯Ö£ÛgéêÚE¥ÚI[¥ÅŸ5>½yò-üËwž8¹uƒ™î0“a3r·¾¢\9j~ÜM£÷w{ͽIEND®B`‚ostinato-0.9/client/icons/gaps.png000066400000000000000000000066131321315675200172660ustar00rootroot00000000000000‰PNG  IHDR,zë¡ sBIT|dˆ pHYs``ÓŸŸtEXtSoftwarewww.inkscape.org›î< IDATxœíœyxMçÚ‡ïµ÷NöÎÎ1Ï[G |%†olålÇqNQ¶òg+^ò¹xN!˜#Ÿ·ŽêdÜ ;­7Ëÿëæ‹gX2[+‘øÿBJXõ)aIHHÔ¤„%!!Qo–„„D½AJXõEm:ë z´:-.N.˜L&nk hàãoÕ×l6s[S€ŸwÛ(­Ä ×¡×•#JGärEe<å¥Å¨\Á²mg¯Ä ×YùQ©,ûVñ­-ÃÑÅý™ušÍð Ô„‹£½AD«7#Õ2ä5œ& 4F<Ýä(äÂ3Ç~”ò²bÌ&r¹¥ƒ#‚Pá_¯+Çl2¡R;`ÐkÑë´5Η ¨\­| zvö6Õ[WÊK`6›Q(ìP:8Zì:mJ•Ú²m4èk§L.ÇAí\ÅfÐk™eœ¢ÙŒ^W^%Fm0™L””–áìäˆÞ`@§Ó`gg‡ÚAeÕ_§×SRRŠ§Ç³¯ÉGù£u!šÍ–ñéuåô:ì•*ô:m[¥Å÷±³Wb¯t°©f¨åVÚÎ 4ïÀ?2vá×Á•ð„u mçK¿5›Wâ×Á•8BâüY±i©ÍïÞº’ÄnscH;fh‡æv>N"±›;÷5·0™Œ,Ÿþ2“_§ ÿ ‰ÝÜ­~§í©âW4›ù`J¾=È&:Ï^Òâ{‹9:æ­½{ì\;þŠ¢U6/¾~»…&2”Ò î"~]/âñ†ϼŽÙ†/ ÍûK»¹3âEg^ë àÓ¥ؼr‹§ôàVÞe’‚ùë;C9œ±Ãj®ÆÆW=!]=œ©ƒ#y½« ˦ Àd4ØNp™40œÄnî ïâÄŽö|µq!kæ#õÝD B÷˜^ H[6‰ïw¤ZsÆh+¿Ÿ¼?Ž ‹ÇY¶OgýÀ–ÕoÕYç™ì_iÙžk¹ù¤,YIÃÈö4ŒlOxkÂÚvåèÉ3è FN˜†oD‚£;ÑîúràPVãVgÎÈX˺ÒÁŽÏW¼ @ÚòÉ,™Ú€9HJbí¼áü¸7Ýj¾Æ÷ ´ò{#çI}9ôýßl¦õQê\.ùä=† IáI-“FNeë?¶`6›ùúû¼½d*Kß^É­¬û¼“4Ÿ¾XÍõ‚|›‰öò $-³•;²¹{+—ýÿüÌÒ&šÍ¤¾›È…Ó‡˜›šA@“¤eòé> IóÓHË,$ºÓŸ,ûi˜=²#Ïüd3Õ‰ŠPqï§Nî áD¶–íß?àj¾žÞãsy¥— ·„³g} Y¥ìù±Ä¦±û §û4Œ™ý1{·¯åVÞeK›æv> “zÚ’éË¿¦s¯!¤e2vΙŒ´ÌB>Þ{£Š¿=ÛÖÐ4º3ë÷\'ûÄŽì·Ù»¡ÏÄÐäÅlú÷]Ia熔߷´]ÿí<ïMŒ§M§Þ$¥¤?8™´ÌB^KZ„»WÒ2 Yš~ÊÊgî¥3”Ý£ðNÅ\>w„°l¦¹M«ܽ|м³Y°qK:SßY@Ö±|³e#yg³hÝ*’ek?F´á{Þ}‡OçÓ}F¿½–Ý[WqçÆ5KÛÝ[¹,LêIH³¦.ÙÉ‹½‡‘–YȳÖ!WØ‘–YȺݹUüøq7óßèŠ^Wn3Õ©UIø(½ãú²dý"®æ]áãóUê·Èd2öø–VM£9è/Œ<–уÇÚL0€(Šèuå”—T9þâMgìàꦅ Gg7ÄÊË¥ƒ#ŽÎnUüéuåôñW²rõüq›j}ˆÙ,R®)-7#`0Šd.Åd‚ÉÞ¸9ËñõTps¸Íc›ŒF´e%+K £¡¢ ))ºÇ¢¤ž”<(ä­å»°³¯(IÝ,%Tõ¹˜òׯ¢-+±*£ž&£mi1F£³Ù„(VüÍ5y,œÐ¥JMòÂ-ÈdrdöòßËA¨qœåeÅÈärâú%²gÛ†&/æJöQb{¶™æ; ›¶l£¸´”«×r˜2~{2ö3tPâ:wà“•Ø,æC̦ÊuQ¹Œ•ÇQñý»,Lꉶ¬˜iK¿ÂÎ^ T¬{•á1ó%Š" 6$eLœÍµ>¤ÎWX³ÆÍeÛš]¨”*ÞZ<™.¯¶£L[ÆmÍm|<}X•¶u¤€:R`õgÚL´¦ ±ñþÌÝ¿€0â'[ÚÎÉ (<Šo¿ø³ÙôTþ<}Ûã›é«‰³—t4ì~‘î£rˆr q€w M¨UNjY§ËZd#´Èfàä<›ÆÞ»}-I A|¹v&ýGÌ QHsò®ü‚£‹;FƒŽ}ýq­|jnç³p|ÚvíOô ñOì/B3Aö ‚àôˆMUikY­oº ýj%ض~ú5æ»ôUüyâœ\<¸xæ0~BÑäñÓÛžÚßÕìc„4!¦Kgd`Ðk¹‘sÿ ˆÚJ{,%¥e9qŠc'OcE®åäc0¹_ôo/ú S@3œšqî׋6‹½{ë*’‚ø[ꎚMƒÀ0r.ÁÅÍ ¶ŒïÚð/¿Ó%µ;á ‚0K„9Õl¯ ‚°©š­ƒ ûê”°L&£gG!W°í¯»Ø÷Å!.]»È/ÎÐ!:–ÿ;ºŸ%3$‰ßÜĵ†lü,x7âËŸ´¤géY¸éG\ܽ-m3>ü;ÉïnæJö1¾ýb…Mã> ÑMUhO4Cw²?lÂÅIFl”E%f+#&RÅÍýáôêäôdgµ¤ïðé¤géÙ|°˜?OZb±û„275ƒ~#f°mÝ\næ>ÝÁP\¤aÑ„—hÜ´5SÞOZ€ý€ñ›©ÒVT­opƒZ2|ÊrÒ6–YH¿¿ßg kѹë2èÞ4iË&Qt¯à©ü]>w„Ðí‘Éätê5„ïÒWãåh¹Am šñyê v~¶ž7ÇfÁòÕ(ärÚDµd÷ûؼn%ÿr£Íb>dÀÈ™¤ÿlàóƒ’ôžÅîÁÜuô6ôfSÅæ±á|åïQò€£Õl÷€ýuJXr¹/oÞ˜=’)‹&0vÎ(¼<¼iÖ‚äa“‰iFd|¦/žÌà‰(-+!¨ap]BÕŒ X.S«ãJPX+â_MfÛúyÜȹ`»¸Ï€LJûª ½[{GFôs¥OR.¯Ï¾Á˜”›üëp ¡¶}ò&—++ì¬ìîÞþ(Uj&ÎÂÕÓ—Ô”DKéüGlx׫Xckæ çøÁ>qQ¯‹¢˜"Š¢ö›¡Ò–[­ïjQ=ydU‘ËVO~¼!—+:±¢¬úäýqV}jâò¹£„VÞ¯êÞ4{·dÓûW9ù×™<+…±Sg±jÝ&:Æ´F–½;›Ó¿œ§m÷æ°‚äsñpwÃÕÅÅf±7_> ±W:ðòè98»y‘º`”Mï=Š(Šßˆ¢øU5Û!Q×W³]Eqü ))Oë\ü}Û¦qºÕ,šÜ9´ŽŒaÕœðr÷F©T1èO¯Ò¸QšÂ»´êÈ’™+éÜöÅZ ä’7EÞ~A„µìhewtv%2¦ ;{"¢^@&W W(hÜ´R?DÆt«rEVßFM¯ùÜC[Ë•Â}õÓèpTˈkçˆÚAF“{b"¬úôs&:BEQ‰‰ vÌOòáAµ{„}ÝÜóf‘nýn Mš·Ã·aˆU›`8ÁÑÈv„F¶*h<,¥Ü<|iÓÕj¿[ùWhÖº3®¾¸¸yÚ/߀šÂo‹ôç×Z æȾÉ8À¯¦6ˆˆê„§O#«¶ÀÐ4 iŽR¥&8¢5&£F›¡r¨¸šõò ¼U¬Õ~%EZÇÆ#vö*<¼ý‰lÛ Wß*ýš8jîÈÑçiÆ NŽjºÄ¶ÇA¥ÂÏÛ ''G<=ÜHèÕƒ9Ó&booŸ¯7/'Äãêẫâú÷~‰Õ‹Sð­,Ÿ†«ež&QVã|„F¶ÇÇ¿qU}€pÁáQ(v4iÞDoÿ`Ý7/?š·ùãã9"êÜ<ºÎëB>/óôHŸ—©5Òçež#Òçe$$$$ž#RÂ’¨7H KBB¢Þ %, ‰zƒ”°$$$ê RÂ’¨7üçÍ0#y‘IEND®B`‚ostinato-0.9/client/icons/help.png000066400000000000000000000014221321315675200172550ustar00rootroot00000000000000‰PNG  IHDRóÿagAMA¯È7ŠétEXtSoftwareAdobe ImageReadyqÉe<¤IDAT8Ë¥“ÝO’aÆý[àOh­æjÕjÕÖ8è §æ'ÐÔrµ –³t˜Ó,)53 j꜊o"ˆ&Ê— ðb"_ÊÇDxáU³íê5œËÙZ¿“{ÏõÛ®g÷ çøc #ÙZ÷@ãÜ'Uë4õÅž¢6Šœ±$S¦=ö™µ“æÌ:Ò!Oø;4ÜA*ƒg; G ‘Åhhh!Ì9U@¬¥8rk2µÙ‡ùÛ.¤s~ˆÆœ¨YG±ƒ# WˆÂ'•?ÕElqNdækr5ÜÓXpÆPË„: ü1¾Xc‹ÔH­P®†`÷%Ð:I›Ç]¬¬`t)*\ó§°â‰C8hÇã3ÒG ?~s„ÚA júM˜·G ]‹ Njf}s!ÒÅtí&6QÝcBU÷ üÑ4¦–PÛ¶A3µ-n»­SX½»xÒm$³‚…—v“J,¨üh¯ÓÀ<ÖcÚèÃ.u€]½³.”¶iQùA‡o<±žÎ ZÆ]´+ÄÓ ¸z”¿×¡ìIú;¶"øb-ŠZT¸ÏÀk`÷Ǒߤ>¼ÚHÛVb™ ¼vÊÄó(y;‡â×ê …¯6*PÐ(G½tFOwëäǘ>B…1½#‚ÊC6Kì#²G#O4“¡P$±êƒ„©s«fâøùâVù›ùà’3åŠÜ6 š”h7eøÎo˜À°Æ %€›ÕcÁëGY')O4˹×@¤töm¨­A¼”,¡¤YŽ¢Æi<ïÕCiôBeöãjÅçÔåCœSWùŽPƹýl2ÔG¬ã«; ‹7–aѹƒ.™ ¹áÿý#%%&Ñÿÿ÷¯9$1ýûÿüÿ-ìø(¡ÿüÿ}Î,!AM=%*"öý‚ÿÂCÿŸ ñýÿÿ³ÿû¾Y$&6]£ÿDãÿýÿ^”ÿ?kÿýÿÿ¼ÿüÿÖØ€ÿüÿRêþÿÝñºÕÿÿþÿÐÕýüüûÿ;(ÿüÿÿýÿ^iÿ8 Zÿý€ÿ1ÿúÿÒØ€ÿþÝXÿü€ÿÕï‘æþÿ iÿþÏØÿüûÿTÉ€ÿ üÿ=‡ä‡ÿþÿsTí×ÙÿÿôZ yÿýÿýÿ|8ÿ*"Kÿüƒÿ†Pœ¢gÿýÿþÿ¾î@"%óýƒÿûÿÜe$% &f×ÿýÿþÿ×ÎK%åþ…ÿûýÿмìþÿþýÿüÿÑ#­;)çþ…ÿ_ÿþüÏØÿüýÿüýÿ¨6€&Lõý†ÿ)¸ÿÓÖýûþÿÿäU4I‘ÿü‡ÿ$)?‚ ×ÿÿï½^#(Vâÿüˆÿ?$#/(&i×ÿýýŠÿã­l $%8@BNe‘Ëûÿþü‹ÿüþÿÚ * ߃ÿüýÿ üÿq#wÿúüüýþÿ-Ë„ÿþÿ•›ÿüÿe-¥ AÿŒ tÿýÿÿñ‚èýƒÿýÿm éü9Söëê÷ýÿÿûÿX/øüƒÿüót½ $Q ºÿH¬ÿþÿÿûÿ7>ÿü‚ÿýÿuò&!½ ˆÿ iÿý€ÿüõ.QÿüƒÿÆÚÁ ë»U#1?1ÿúÿýð(qÿü‚ÿæÁº++mD³ÿýÿþãÿþ€ÿýóY¡éô=ÿð&–‚ÿþÿÌÆ‚ÿäœñùü!6ÿ÷ƒÉÿÿýþÿþÿ¦íý€ÿaÅ¿ÿ/³ÿÿ&/ÿø½p@ÿýÿè¯ÃûÿtEÿüýÿuôÿ¤5ÿü#9ÿð¼¬ýûj2êÿ>ƒÿúòZÿúÿ(‡ÿKÿÝÕx)á8#!"\ÿåÒÿj ΀ÿæ Ufÿ¾‡‘ € TÿÿžHqÿý€ÿ è3,iRsÿ#"€ "ÅÿùÿAKÿü€ÿüÿkI;:áÿý!!"Ñÿÿö®6 -ýûÿüÿ*ìø$ ÿüÿ{Í(>J9!&öý‚ÿÁ@ÿžñýÿÿ²ÿû½V "3Z¡ÿAãÿýÿ[’ÿ;hÿýÿÿ»ÿüÿÕØ€ÿüÿOéþÿÝñ¸ÕÿŽÿþÿÏÕýüüûÿ8$ÿüÿÿýÿ[fÿ5Wÿý€ÿ-ÿúÿÑØ€ÿþÜUÿü€ÿÔïåþÿ gÿþÏ×ÿüûÿP È€ÿ üÿ:„ä…ÿþÿqQíÖØÿÿôWvÿýÿýÿz4ÿ&Hÿüƒÿ„ Mš d |ÿýÿþÿ½î=!óüƒÿûÿÛb ""c×ÿýÿýÿÖÍH!äþ…ÿûýÿÏ»ëþÿþýÿüÿЫ8%çþ…ÿ\ÿþüÎØÿüýÿüýÿ¦2~"Iõý†ÿ%·ÿÒÕýûþÿÿäR1Eÿü‡ÿ %<€Öÿÿï¼\$ Sáÿüˆÿ< ,$"g×ÿýýŠÿâ¬j !4=?KbÊûÿþü‹ÿüþÿÙ&Þƒÿüýÿ üÿntÿúüüýþÿ.Ì„ÿþÿ–œÿüÿe.¥BÿŒ tÿýÿÿòƒèýƒÿýÿm éü:Töëê ÷ýÿÿûÿY0øüƒÿüóu½ %‘R»ÿH­ÿþÿÿûÿ8?ÿü‚ÿýÿuò'"¾‘ ˆÿjÿý€ÿüõ/RÿüƒÿÆÚ ë»V$2@2ÿúÿýð)qÿü‚ÿç», ,mE ´ÿýÿþãžÿþ€ÿýóZ¢éô>ÿð'—‚ÿþÿÌÆ‚ÿäœñùü"6ÿ÷ „Éÿÿýþÿþÿ§íý€ÿaÅ¿ÿ0³ÿÿ'0ÿø ¾q@ÿýÿè¯ÃûÿuFÿüýÿvôÿ¤6ÿü$:ÿð¼¬ýûk3êÿ?„ÿúò[ÿúÿ)ˆÿLÿÝÕy*á9$"#]ÿæÒÿk ΀ÿæ Vgÿ¾‡’!€ TÿÿžIrÿý€ÿ è4-jRsÿ$#€ #ÅÿùÿBLÿü€ÿüÿlJ< ;áÿý ""#Ñÿÿ÷®7!.ýûÿüÿ+ìø% ÿüÿ|Í)?K:"'öý‚ÿÁAÿžñýÿÿ²ÿû½W!#4Z¢ÿBãÿýÿ\’ÿ<iÿýÿÿ¼ÿüÿÕØ€ÿüÿOéþÿÝñ¹ÕÿŽÿþÿÏÕýüüûÿ9%ÿüÿÿýÿ\gÿ6Xÿý€ÿ.ÿúÿÑØ€ÿþÜVÿü€ÿÔïåþÿ gÿþÏØÿüûÿQ È€ÿ üÿ;…ä†ÿþÿrRíÖØÿÿôWwÿýÿýÿz5ÿ'Hÿüƒÿ„ N› e }ÿýÿþÿ½î="óüƒÿûÿÛc!# #d×ÿýÿýÿÖÍH"åþ…ÿûýÿÏ»ëþÿþýÿüÿÑ ¬9&çþ…ÿ]ÿþüÎØÿüýÿüýÿ¦3~#Jõý†ÿ&·ÿÓÕýûþÿÿäS2Fÿü‡ÿ!&<Öÿÿï¼\ % Tâÿüˆÿ=! -%#g×ÿýýŠÿã¬j!"5>?KcÊûÿþü‹ÿüþÿÙ' Þƒÿüýÿ üÿo uÿúüüýþÿl8mkÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿit327ƒˆ#kþ¤ÿ©'ƒ#wŽÿÄ#)ÿÄ…#Jƒÿm„#Iÿ‰#Š¥ÿ¿%ƒ#›ŒÿÄ/#Cÿÿ>…#æ‚ÿÑ…#ÊŽÿ##:+†#¬¥ÿžƒ#$½Šÿ©&‚#bÿÿˆ…#¦ƒÿZ„#qŽÿ#'Ϲ†#(É¥ÿyƒ#.è‡ÿú~„#­ÿÿÒ…#gƒÿ¼„#(ìÿ#ÿÿ—†#3à¤ÿúWƒ#w†ÿæQ„#.ö€ÿ@„#-ú‚ÿü3„#–ÿiÿq†#Fñ¤ÿì>‚#%Û„ÿÆ4…#{ÿŠ…#ăÿz„#>ýŒÿñÿøT†#]ü¤ÿ×)‚#gƒÿâ-†#ã€ÿÔ…#ƒƒÿÆ…#½’ÿì=†#|¥ÿ†ƒ#Ì‚ÿY‰#”€ÿA„#Rƒÿý7„#c“ÿÖ-†#¤ÿî/‚#UÿÛ‹#£ÿÿˆ„#(úƒÿ€„#$ä“ÿ¼$†#±¤ÿ—ƒ#»€ÿ™‹#3øÿ¾…#ÕƒÿÊ…#¤”ÿ™†#'£ÿö9‚#O€ÿŒ#µÿñ$„#©ƒÿþ4„#m•ÿu†#-Ñ£ÿ¦ƒ#èÿÿ”Œ#¦ÿÿM„#|„ÿg„#5þ”ÿúV†#4Þ¢ÿü@‚#¦ÿÿÝŒ#¶ÿÿ‚„#P„ÿš…#Ù•ÿí>†#>ê¢ÿ‡‚#e€ÿmŠ#5öÿÿ·„#(úƒÿÎ…# –ÿÚ.†#Lò¡ÿÉ‚#*öÿÿöS‰#ª€ÿì…#Ôƒÿú)„#h—ÿÂ(†#Zø ÿü3‚#½€ÿöƒ$„#'S¹‚ÿF„#©„ÿW„#3þ—ÿ¹&†#ký ÿq‚#|‚ÿ¦‚#gÂõ„ÿ{„#GxŒ—»ãÿŠ…#Õ˜ÿ±$†#~ ÿµ‚#;‚ÿ¹‚#¹„ÿÚ O‹#1@…#™ÿª‡#‘Ÿÿò'‚#íÿ¹‚#Ô€ÿè¬q8˜#dšÿ¡‡#©ŸÿG‚#Úÿ¹‚#îõ­Zœ#/üšÿ™†#'Éžÿ\‚#Æÿ¹#%w.Ÿ#Ñ›ÿ†#4ãÿr‚#°ÿ¹¦#~þ›ÿ†#Iõœÿ†‚#ÿ¹•#oŽtfT.ˆ#aêšÿýl†#gþ›ÿš‚#Š€ÿ×WŠ#5m¤}„#¬‚ÿþÞw‡#6šÿøY†#Œ›ÿ¯‚#uÿøŠ&‡#4k£Ú€ÿ¦„#¦„ÿ©ˆ#%°šÿñI†#±šÿÄ‚#aÈC†#7†Ôþƒÿ·„# „ÿ·‰#$©šÿè;…#)Ñ™ÿÄ‚#)†#%¾ü†ÿÆ„#™„ÿÆ‹#¡šÿÚ1…#9è˜ÿ°#î‡ÿÖ„#“„ÿÕŒ#»šÿÊ(…#Pø—ÿ›ˆ#+#чÿè„#„ÿã†#Z‚#1äšÿ¯†#o—ÿ††#-ª™#³‡ÿø„#‡„ÿò†#­“‚#TüšÿІ#––ÿr…#SàÿÖ#”ˆÿ+ƒ#€„ÿö†#£ÿ‡‚#Žšÿþg†#»•ÿ\ƒ#%üÿÿþ6€#bˆÿ;ƒ#z„ÿò†#šÿüI#-îšÿõK…#-×”ÿG‚#>΂ÿq€#*ø‡ÿCƒ#t„ÿï†#ÿÿÏ$#›ÿå6…#Cõ’ÿò'‚#ʃÿ­#À‡ÿAƒ#m„ÿì†#Š€ÿm#3ö›ÿÎ(…#s’ÿµ‚#(øƒÿê#‡ÿ=ƒ#g„ÿê†#‘€ÿ»‚#œÿ°†#¬‘ÿq‚#Q…ÿJ€#3õ†ÿ;ƒ#c„ÿæ†#”€ÿú0#ŠÿŠ…#+Ýÿü3‚#„…ÿ¦#†ÿ7ƒ#t„ÿà†#™ÿT#QÿüZ…#LøŽÿɃ#·…ÿú=€#;ú…ÿ4ƒ#Š„ÿΆ#žÿf#;Œÿ꬞“аî‡ÿé5…#}Žÿ‡ƒ#é†ÿ©#£…ÿ1ƒ# „ÿ¹†#£ÿu#6‰ÿú·e&ƒ#%V—ì…ÿÄ$…#·Œÿü@‚#G‡ÿú>€#+ß„ÿ.ƒ#·„ÿ£†#©ÿg#3‡ÿöŠ3‰#7È…ÿ‘…#0ã‹ÿ¦ƒ# ˆÿª#Tüƒÿ+ƒ#΄ÿކ#³ÿF#F†ÿÌCŒ#%–þƒÿý[…#Tü‰ÿö8‚#+ñˆÿüA#Š‚ÿú$ƒ#ã„ÿz†#Ì€ÿã&#q„ÿö„$#l„ÿê7…#Љÿ”ƒ#zŠÿÂ%#ŠÿÞ„#ú„ÿd†#æ€ÿ~‚#ƒÿàC’#£„ÿÆ$…#‡ÿí.ƒ#Õ‹ÿ™‚#†€ÿ½ƒ#4…ÿG…#'ýÿÿÕ)‚#Ú‚ÿÚ8“#*Ü„ÿ“…#<ö†ÿ„ƒ#Sÿm‚#hìÿžƒ#J„ÿú&…#>ÿÿê;‚#[ÿñ€ÿÒ3•#{„ÿý_…#Š…ÿÔ(ƒ#»ÿöJ‚#5»~ƒ#a„ÿÖ†#ZÿÚFƒ#¹ÿqÿÿÊ.–#C…ÿì6„#'Úƒÿé;ƒ#gÿã3ƒ#-ƒ#x„ÿ¯†#t©*ƒ#Zýÿ#°Î+˜#ó…ÿµ…#W‚ÿøRƒ#-åÿÈ/‰#„ÿ‡†#.„#4éÿÿ#/3˜#&ý†ÿf…#¬ÿq„#’ÿßJˆ#”üƒÿ`#(€ÿ›#;‡ÿè.„#4îÿÿ”„#Mü“ÿöqˆ#&@_|š·À6Œ#0Æÿ›#Vˆÿ¤…#zÿ·„#&Ñ–ÿ¡'œ#>Ú‚ÿ›#ˆÿþV…#(„#ª˜ÿÌ$™#(öƒÿš#$é‰ÿÎ#‘™ÿÖ˜#'mâ…ÿš#‹ÿc‹#ušÿ—–#D™ê‡ÿ™#bü‹ÿÔŠ#_úšÿV„#RF+‹#=Šÿ—#$ióÿkˆ#Lòšÿï%ƒ#'óÿÿíÑ·e†#„Šÿ•#$~Þÿ¦‡#Lí›ÿ³„#V„ÿ†#ÌŠÿ•#/ÿÆW‡#Mîœÿs„#‘ƒÿü;…#9þŠÿ•#/Œÿõ³V‰#¬œÿý4„#΃ÿ½†#~‹ÿ##$N‘#/‰ÿñ³i*‹#|œÿÌ„#/üƒÿg†#Æ‹ÿ##Tý£0# -ÎÛèóïãÔȶg=#Lœÿx„#gƒÿé&…#=ü‹ÿ##”ÿÿêq­#%õšÿó+„#£ƒÿ”†#—Œÿ##ÖÿÆF¬#Æšÿ§…#Þ‚ÿþ>…#)îŒÿ#;ƒÿú–* #C§[„#”šÿR„#Iƒÿ†#wÿ#z…ÿå–Iœ#Q¹ýÿ‚„#q™ÿÖ…#›ƒÿl†#Ôÿ#›ˆÿÚC–#B‡Ïÿ£„#w™ÿ€„#&ê‚ÿé'…#TŽÿ#£‹ÿÔˆ=#.W”Ú„ÿÆ„#|˜ÿø1„#cƒÿ†#±Žÿ#ªÿñƒ# 40+-9FS`n¹ãþ‡ÿè„#„˜ÿ¦…#µ‚ÿè)…#7úŽÿ#°ÿñ‚#/•ÿ)ƒ#Š—ÿø;„#1ø‚ÿ€†#‘ÿ#·ÿñ‚#/”ÿý$ƒ#‘—ÿ …#|‚ÿè(…#'éÿ#½ÿñ‚#/”ÿî„#––ÿö6…#Ï‚ÿ}†#nÿ#ÿñ‚#/”ÿß„# –ÿ–…#W‚ÿå(†#Îÿ#qÿñ‚#/”ÿÏ„#È•ÿñ3…#Ä‚ÿ|†#g‘ÿ#Cÿñ‚#/”ÿ·ƒ#&ö•ÿ…#S‚ÿÜ(…#(à‘ÿ##ìŒÿñ‚#/”ÿ}ƒ#P•ÿî.…#¾ÿþW†#Š’ÿ##Œÿñ‚#/”ÿAƒ#„•ÿ‡…#N‚ÿ©†#7ó’ÿ##3ö‹ÿñ‚#/“ÿà„#µ”ÿé*…#»ÿì1†#¬“ÿ€#¡‹ÿñ‚#/“ÿ „#è”ÿ~…#Jþÿo†#Pþ“ÿ€#;ñŠÿñ‚#/’ÿø<ƒ#O”ÿã(…#ÀÿÁ‡#Δÿ#iŠÿñ‚#/’ÿ—„#»”ÿh…#lÿö>†#r•ÿ‚#£‰ÿñ‚#/‘ÿî/ƒ#Jþ“ÿ©…#.èÿ†#+è•ÿ‚#)Ôˆÿñ‚#/‘ÿw„#µ“ÿÞ+…#žÿµ‡#“–ÿƒ#1̇ÿñ‚#/ÿ“„#Gý’ÿúP…#Mü€ÿÛ-†#Mø–ÿ„#+À†ÿñ‚#/ÿ§…#°“ÿŒ…#$Ï€ÿóF†#0â—ÿF„#'­…ÿñ‚#/Žÿµ%„#n“ÿÉ$…#~ÿj‡#»˜ÿå<…#míƒÿñ‚#/Œÿü‡…#Rø’ÿñ<…#Có€ÿ›‡#ŠšÿÝ4…#5¹‚ÿñ‚#/‹ÿäT…#;ê“ÿn…#+Ù€ÿÉ&†#Yü›ÿÒ.†#dÉ€ÿñ‚#/‰ÿî/…#+Õ“ÿ¯†#¬€ÿÚ3†#7êÿÉ4‡#M­úñ‚#/‡ÿå†+†#'»“ÿã.…#w€ÿà8†#%ÉŸÿæTˆ#4r‚#/ƒÿöÁƒGˆ#=Ï“ÿüT…#Køÿÿè>‡#™¡ÿúƒŽ#)––ŒqT9‹#fï”ÿ|…#.ßÿÿìF‡#gþ£ÿ¹/Ÿ#'•ÿ–…#%»ÿÿñM‡#Nñ¥ÿê„*›#$kÖ•ÿ¯†#ªÿÿõT‡#Rñ¨ÿê„+—#(yÞ–ÿÄ'…#›ÿÿíT‡#Tò«ÿê‡5“#<‡è—ÿÕ.…#ÿÿÞA‡#Wõ®ÿýÌŠFŒ#3j¤Þ™ÿÒ3…#}ÿÿÉ3‡#Zõ³ÿÔƒ#5H]r†š¯Ôý›ÿÊ.…#qþÿ±(‡#]ö´ÿñ‚#/£ÿÂ+…#küÿ”ˆ#`øµÿñ‚#/¢ÿ»(…#ýøwˆ#cø¤ÿéŽÿñ‚#/¡ÿ£&…#›ÿÖGˆ#wú¥ÿ8Ìÿñ‚#/Ÿÿøt…#(µþ(‡#(¦§ÿ#'¤Œÿñ‚#/žÿäN…#1Éé^ˆ#;Ϩÿ€#xøŠÿñ‚#/ÿÆ4…#=Û»4ˆ#Zì©ÿ#F̉ÿñ‚#/›ÿõ‚$…#bßz‰#†üªÿ‚#$€õ‡ÿñ‚#/šÿÄ>…#'š©5ˆ#7½¬ÿ„#>†ÿñ‚#/˜ÿñx†#A O‰#jî­ÿ†#iÛ„ÿñ‚#/–ÿøž5†#Yg‰#0¯¯ÿ1†#%tã‚ÿñ‚#/”ÿú¦;†#&C.‰#]æ°ÿ‰#(€ã€ÿñ‚#/’ÿú­@•#9­þ±ÿŒ#]³øñ‚#/ÿÏ~3•#)õ³ÿŽ#1w‚#/ŒÿþÚ–D—#mãµÿ•#.õ†ÿüê¿_/˜#[Ê·ÿ—#(9J[gcTB1›#%mѹÿ¼#*}â»ÿ;¹#W£í½ÿþχ@³#QèÄÿÔG¬#(T™ãÊÿñÄ—j>£#>nžÎøÒÿíÁ“<˜#4Kcz“»ìÚÿN‰#3𠣤§ª¬¯°¶Ìãúàÿý*‰#:îÿý+‰#FïÿU‰#rïÿ‡‰#£ïÿí4‡#Føðÿµ†#$Îòÿ¹7ƒ#AÈôÿî‘a5;f–õãÿˆgþ¤ÿ§#ƒuŽÿÂ%ÿÃ…Gƒÿk„Gÿ‰ˆ¥ÿ¾!ƒ™ŒÿÂ,@ÿÿ;…å‚ÿÐ…ÊŽÿ6(†«¥ÿƒ »Šÿ§"‚_ÿÿ‡…¤ƒÿW„mŽÿ#η†$È¥ÿvƒ*æ‡ÿú{„¬ÿÿÑ…cƒÿ»„$ìÿÿÿ•†/ߤÿúUƒu†ÿåO„+ö€ÿ<„)ú‚ÿû/„–ÿfÿo†Bñ¤ÿì;‚!Ú„ÿÅ1…xÿ‡…ƒÿy„;ýŒÿñÿøP†Zû¤ÿÖ%‚cƒÿà)†€ã€ÿÓ…‚ƒÿÄ…½’ÿì:†y¥ÿƒƒÊ‚ÿV‰’€ÿ>„Mƒÿý3„`“ÿÖ)†œ¤ÿî,‚RÿÚ‹ ÿÿ‡„%úƒÿ}„ ä“ÿ» †±¤ÿ•ƒ¹€ÿ–‹/øÿ½…ÕƒÿÊ…¤”ÿ˜†#£ÿö5‚L€ÿŠŒ³ÿñ „§ƒÿþ1„k•ÿr†)Уÿ¤ƒæÿÿ’Œ¤ÿÿI„y„ÿe„2þ”ÿúS†1Þ¢ÿû<‚¤ÿÿÝŒ´ÿÿ„N„ÿ™…וÿí;†;ê¢ÿ…‚a€ÿjŠ2öÿÿ¶„%úƒÿÎ…ž–ÿÙ*†Hò¡ÿÈ‚&öÿÿöP‰¨€ÿì…Óƒÿú%„f—ÿÂ$†Wø ÿü/‚½€ÿö‚ „#P¹‚ÿB„§„ÿT„.þ—ÿ¹"†gý ÿo‚y‚ÿ¤‚eÂô„ÿx„Cu€Š•¹ãÿˆ…Õ˜ÿ± †{ ÿ³‚7‚ÿ¹‚·„ÿÙL‹-<…œ™ÿ¨‡Ÿÿò#‚íÿ¹‚Ó€ÿæ«o4˜cšÿ ‡§ŸÿD‚Ùÿ¹‚îô¬Xœ,üšÿ–†#ÈžÿY‚Äÿ¹!t*ŸЛÿކ0ãÿn‚¯ÿ¹¦|þ›ÿ€†Góœÿƒ‚œÿ¹•mŒ€rdP*ˆ^êšÿýj†cþ›ÿ™‚ˆ€ÿÖUŠ2k¤|„«‚ÿþÞu‡3šÿøV†Š›ÿ­‚rÿøˆ"‡0g Ù€ÿ¤„¤„ÿ§ˆ!¯šÿñG†±šÿ‚^Ç@†3ƒÓþƒÿ¶„„ÿ¶‰ §šÿæ7…%ЙÿÂ%†!½ü†ÿÅ„˜„ÿÅ‹ šÿÙ-…5ç˜ÿ¯î‡ÿÖ„’„ÿÕŒ¹šÿÊ%…Nø—ÿ™ˆ'Їÿ愊„ÿã†X‚-äšÿ­†m—ÿƒ†)ª˜²‡ÿø„„„ÿò†¬’‚Pûšÿˆ†“–ÿn…PßÿÖ’ˆÿ'ƒ~„ÿö†£ÿ„‚Œšÿþe†¹•ÿYƒ!Žûÿÿþ3€_ˆÿ7ƒy„ÿò†™ÿûG)îšÿôI…)Ö”ÿD‚;Ì‚ÿo€&ø‡ÿ@ƒr„ÿÿÿÎ Ž›ÿå3…@ó’ÿò#‚ʃÿ¬À‡ÿ=ƒj„ÿ솈€ÿj.ö›ÿÎ%…p’ÿ³‚$øƒÿꀇÿ:ƒe„ÿꆀÿ¹‚œÿ¯†«‘ÿo‚O…ÿG€.ô†ÿ7ƒ`„ÿ冒€ÿú,‡ÿ‡…(Ýÿü/‚ƒ…ÿ¤†ÿ3ƒr„ÿ߆˜ÿQOÿüW…HøŽÿȃ¶…ÿú:€7ú…ÿ1ƒ‡„ÿ̆ÿd7Œÿê«’ˆ€¯î‡ÿé2…|Žÿ…ƒé†ÿ§ …ÿ-ƒž„ÿ·†¡ÿr3‰ÿú¶a"ƒ!S•ì…ÿ …¶Œÿû<‚D‡ÿú;€(Þ„ÿ+ƒ¶„ÿ£†§ÿc.‡ÿöˆ/‰3Ç…ÿ…,ã‹ÿ¤ƒžˆÿ¨Qüƒÿ'ƒÌ„ÿŒ†²ÿBB†ÿÊ@Œ!“þƒÿýX…Pû‰ÿö4‚(ñˆÿû=‡‚ÿú ƒã„ÿw†Ê€ÿã"o„ÿö€ j„ÿê3…ˆ‰ÿ’ƒwŠÿÂ!ˆÿÞ„ú„ÿc†å€ÿ|‚œƒÿß@’¡„ÿÅ …‡ÿí+ƒÕ‹ÿ˜‚ƒ€ÿ½ƒ0…ÿD…#ýÿÿÕ%‚Ù‚ÿÙ4“&Ü„ÿ‘…9ö†ÿ€ƒPÿj‚fìÿƒG„ÿú"…;ÿÿê7‚Xÿñ€ÿÑ/•x„ÿý\…ˆ…ÿÓ$ƒ¹ÿöG‚2¹|ƒ^„ÿÖ†WÿÙBƒ¹ÿmÿÿÊ+–@…ÿì3„#Ùƒÿé7ƒcÿá.ƒ)ƒu„ÿ­†q§&ƒWýÿ¯Ì'˜ó…ÿ³…T‚ÿøMƒ)åÿÇ,‰‹„ÿ…†*„1éÿÿ,.˜"ý†ÿd…«ÿm„œ’ÿÞGˆ’ûƒÿ\$€ÿ›7‡ÿæ+„1îÿÿ’„Iü“ÿöoˆ"<\y™¶À3Œ,Åÿ›Sˆÿ¤…yÿ¶„"Жÿ #œ;Ù‚ÿ›šˆÿþS…Ž$„ª˜ÿÌ ™%‹öƒÿš é‰ÿÌ™ÿÖ˜#kà…ÿš€‹ÿ`‹ršÿ•–A˜ê‡ÿ™_ü‹ÿÓŠ\úšÿS„MB'‹:Šÿ— fóÿgˆHòšÿï!ƒ#óÿÿíжša†ƒŠÿ• {Þÿ¤‡Hí›ÿ²„S„ÿ€†ÌŠÿ•,ÿÄT‡Iîœÿp„ƒÿü7…5þŠÿ•,Œÿó²S‰«œÿý1„̃ÿ½†|‹ÿ K‘,‰ÿñ²f&‹yœÿÊ„,üƒÿc†Å‹ÿPý£, )ÎÚæóïáÓÇ´Žc:Hœÿu„eƒÿé"…:ü‹ÿ’ÿÿêo­!óšÿó(„¡ƒÿ’†•ŒÿÖÿÅB¬Äšÿ¥…Þ‚ÿþ;…%îŒÿ7ƒÿú“& @¥X„’šÿM„Gƒÿ†tÿy…ÿå“GœO·ýÿ„o™ÿÖ…™ƒÿj†Óÿ™ˆÿÙŽ@–@…Îÿ£„t™ÿ~„"ê‚ÿé#…QŽÿ ‹ÿÓ‡:+U’Ù„ÿÄ„y˜ÿø-„`ƒÿ€†±Žÿ¨ÿñƒ 1,()5BP\l޹áþ‡ÿæ„€˜ÿ¤…³‚ÿç%…3úŽÿ¯ÿñ‚,•ÿ%ƒˆ—ÿø7„-ø‚ÿ}†ÿ¶ÿñ‚,”ÿý ƒ—ÿ…y‚ÿæ%…#éÿ»ÿñ‚,”ÿî„––ÿö3…Ï‚ÿ|†lÿÿñ‚,”ÿÞ„–ÿ–…U‚ÿå$†Ìÿmÿñ‚,”ÿ΄Ç•ÿñ.…‚ÿy†c‘ÿ@ÿñ‚,”ÿ¶ƒ"ö•ÿŽ…P‚ÿÜ$…$ß‘ÿìŒÿñ‚,”ÿ|ƒN•ÿî*…½ÿþU†ˆ’ÿŽŒÿñ‚,”ÿ=ƒ€•ÿ……K‚ÿ§†3ó’ÿ/ö‹ÿñ‚,“ÿß„³”ÿé&…¹ÿì-†«“ÿ€ ‹ÿñ‚,“ÿ„ç”ÿ|…Gþÿm†Nþ“ÿ€7ñŠÿñ‚,’ÿø9ƒL”ÿã$…ÀÿÀ‡Ì”ÿfŠÿñ‚,’ÿ•„¹”ÿf…jÿö;†n•ÿ‚¡‰ÿñ‚,‘ÿî,ƒGþ“ÿ§…*çÿ€†(æ•ÿ‚%Óˆÿñ‚,‘ÿt„³“ÿÞ(…ÿ³‡’–ÿƒ-̇ÿñ‚,ÿ‘„Cý’ÿúN…Iü€ÿÚ)†Iø–ÿ„'À†ÿñ‚,ÿ¥…¯“ÿŠ… Ï€ÿóB†,à—ÿB„#¬…ÿñ‚,Žÿ³!„l“ÿÈ …{ÿh‡¹˜ÿå9…kíƒÿñ‚,Œÿû……Mø’ÿñ9…@ó€ÿ™‡ˆšÿÝ1…2·‚ÿñ‚,‹ÿäP…7ê“ÿl…'×€ÿÈ"†Vü›ÿÑ*†cÈ€ÿñ‚,‰ÿî‹,…(Õ“ÿ­†«€ÿÙ/†3êÿÈ0‡I¬úñ‚,‡ÿåƒ(†#¹“ÿá+…u€ÿß4†!ÈŸÿåPˆ0n‚,ƒÿöÀ‚Dˆ:ΓÿüQ…Iøÿÿæ;‡–¡ÿú‚Ž%––ŠmQ5‹dï”ÿy…+ÞÿÿìB‡cþ£ÿ·,Ÿ#œ•ÿ“…!¹ÿÿñI‡Kñ¥ÿê€&› gÖ•ÿ¬†¨ÿÿôQ‡Mñ¨ÿêƒ'—$vÞ–ÿÂ#…™ÿÿíQ‡Pò«ÿê„2“9…ç—ÿÕ*…ŠÿÿÞ>‡Uó®ÿý̈BŒ.h¤Þ™ÿÑ/…|ÿÿÈ.‡Wô³ÿÓƒ2EZnƒ™¬Óý›ÿÊ+…mþÿ±%‡Zö´ÿñ‚,£ÿÂ'…gûÿ’ˆ\øµÿñ‚,¢ÿ¹%…€ýøtˆ`ø¤ÿéŽÿñ‚,¡ÿ "…™ÿÖDˆuú¥ÿ4Ìÿñ‚,Ÿÿøq…%³þœ%‡$¤§ÿ#¤Œÿñ‚,žÿäK…-Èé[ˆ7Ϩÿ€uøŠÿñ‚,ÿÄ0…:Ú¹0ˆWì©ÿB̉ÿñ‚,›ÿô …_Þy‰ƒüªÿ‚ ~ô‡ÿñ‚,šÿÂ;…#™§2ˆ3½¬ÿ„;†ÿñ‚,˜ÿñu†=žL‰hî­ÿ†fÚ„ÿñ‚,–ÿø2†Ve‰,­¯ÿ-†!qã‚ÿñ‚,”ÿú¤7†"@*‰Zå°ÿ‰$}ã€ÿñ‚,’ÿú¬<•5¬þ±ÿŒZ²øñ‚,ÿÏ|/•%‹ô³ÿŽ-u‚,ŒÿþÙ“A—kãµÿ•+ó†ÿü꾎\,˜XÊ·ÿ—%5GXc`P@-›!kйÿ¼&|à»ÿ7¹U í½ÿþÎ…<³OœæÄÿÓŠC¬$P–ãÊÿñÕh;£;lÎøÒÿíÀ’9˜1I`y‘¹ìÚÿJ‰/Š™ ¤¥¨«¬¯´Êãúàÿý&‰6îÿý(‰BïÿR‰nïÿ„‰ ïÿí1‡Bøðÿ³† Ìòÿ·3ƒ=Çôÿî^27d–ôãÿˆ iþ¤ÿ§$ƒ wŽÿÄ &ÿÃ… Hƒÿk„ Gÿ‰ ‰¥ÿ¾"ƒ ™ŒÿÄ- Aÿÿ<… æ‚ÿÑ… ÊŽÿ 7)† «¥ÿƒ !½Šÿ§"‚ `ÿÿ‡… ¦ƒÿW„ nŽÿ $η† %É¥ÿvƒ +æ‡ÿú{„ ­ÿÿÑ… eƒÿ»„ %ìÿ ÿÿ•† 0ߤÿúUƒ w†ÿæP„ ,ö€ÿ=„ )ú‚ÿü0„ –ÿgÿo† Bñ¤ÿì;‚ "Ú„ÿÅ2… zÿˆ… ăÿy„ ;ýŒÿñÿøQ† Zü¤ÿ×&‚ eƒÿà)† €ã€ÿÓ… ‚ƒÿÄ… ½’ÿì;† z¥ÿ…ƒ Ê‚ÿW‰ ’€ÿ>„ Oƒÿý4„ b“ÿÖ)† œ¤ÿî-‚ TÿÚ‹  ÿÿ‡„ &úƒÿ}„ !ä“ÿ»!† ±¤ÿ•ƒ ¹€ÿ—‹ 0øÿ½… ÕƒÿÊ… ¤”ÿ™† $£ÿö6‚ L€ÿŒŒ ´ÿñ!„ §ƒÿþ2„ k•ÿs† )Ñ£ÿ¦ƒ æÿÿ’Œ ¦ÿÿJ„ z„ÿe„ 3þ”ÿúS† 2Þ¢ÿü=‚ ¦ÿÿÝŒ µÿÿ„ N„ÿ™… Ù•ÿí<† <ê¢ÿ…‚ a€ÿlŠ 3öÿÿ¶„ &úƒÿÎ…  –ÿÙ+† Iò¡ÿÉ‚ 'öÿÿöP‰ ¨€ÿì… Óƒÿú&„ f—ÿÂ%† Wø ÿü0‚ ½€ÿö‚!„ $P¹‚ÿC„ §„ÿV„ /þ—ÿ¹"† iý ÿo‚ z‚ÿ¦‚ eÂô„ÿz„ Du€‹–¹ãÿ‰… Õ˜ÿ±!† { ÿ´‚ 7‚ÿ¹‚ ·„ÿÙžL‹ .=… œ™ÿ¨‡ Ÿÿò$‚ íÿ¹‚ Ó€ÿæ«o5˜ cšÿ ‡ §ŸÿE‚ Ùÿ¹‚ îô­Xœ -üšÿ—† $Èžÿ[‚ Äÿ¹ "t+Ÿ Ñ›ÿކ 0ãÿp‚ ¯ÿ¹¦ ~þ›ÿ€† Gõœÿ…‚ œÿ¹• mŒ€rdQ+ˆ ^êšÿýj† eþ›ÿ™‚ ˆ€ÿ×UŠ 3k¤}„ «‚ÿþÞw‡ 4šÿøW† ‹›ÿ®‚ sÿø‰"‡ 0i Ù€ÿ¦„ ¦„ÿ§ˆ "¯šÿñG† ±šÿÄ‚ ^ÇA† 4…Óþƒÿ¶„ ž„ÿ¶‰ !§šÿæ9… &Ñ™ÿ &† "½ü†ÿÅ„ ™„ÿÅ‹  šÿÙ.… 6ç˜ÿ¯ î‡ÿÖ„ ’„ÿÕŒ ¹šÿÊ&… Nø—ÿ™ˆ ( чÿæ„ Œ„ÿㆠX‚ .äšÿ®† m—ÿ…† )ª™ ²‡ÿø„ „„ÿò† ­’‚ Qüšÿ‰† “–ÿp… PßÿÖ ’ˆÿ(ƒ ~„ÿö† £ÿ„‚ Œšÿþe† ¹•ÿ[ƒ "Žüÿÿþ4€ `ˆÿ7ƒ y„ÿò† ™ÿüG )îšÿôI… )×”ÿE‚ <Ì‚ÿo€ 'ø‡ÿAƒ r„ÿï† ÿÿÎ! ›ÿå4… Aõ’ÿò$‚ ʃÿ­ À‡ÿ>ƒ l„ÿì† ‰€ÿl /ö›ÿÎ&… p’ÿ´‚ %øƒÿê €‡ÿ;ƒ e„ÿê† €ÿ»‚ œÿ¯† «‘ÿo‚ P…ÿH€ /ô†ÿ7ƒ b„ÿæ† ’€ÿú- ˆÿˆ… )Ýÿü0‚ „…ÿ¤ †ÿ4ƒ r„ÿ߆ ™ÿR PÿüW… IøŽÿɃ ¶…ÿú;€ 9ú…ÿ2ƒ ˆ„ÿ̆ ÿd 7Œÿê«’ˆ€¯î‡ÿé3… }Žÿ…ƒ é†ÿ§  …ÿ.ƒ  „ÿ·† ¡ÿs 4‰ÿú¶a"ƒ "S–ì…ÿÄ!… ¶Œÿü=‚ E‡ÿú<€ )ß„ÿ,ƒ ¶„ÿ£† §ÿe /‡ÿö‰0‰ 4Ç…ÿ… -ã‹ÿ¤ƒ  ˆÿ¨ Rüƒÿ(ƒ Ì„ÿŒ† ²ÿB C†ÿÊAŒ "“þƒÿýY… Qü‰ÿö5‚ )ñˆÿü> ˆ‚ÿú!ƒ ã„ÿw† Ê€ÿã" o„ÿö! j„ÿê4… ˆ‰ÿ’ƒ wŠÿÂ" ‰ÿÞ„ ú„ÿc† æ€ÿ~‚ œƒÿßA’ ¡„ÿÅ!… ‡ÿí,ƒ Õ‹ÿ™‚ …€ÿ½ƒ 0…ÿE… $ýÿÿÕ&‚ Ù‚ÿÙ5“ 'Ü„ÿ’… :ö†ÿƒ Pÿl‚ fìÿƒ H„ÿú"… <ÿÿê9‚ Yÿñ€ÿÑ0• z„ÿý\… ˆ…ÿÓ%ƒ »ÿöH‚ 3»~ƒ ^„ÿÖ† WÿÙCƒ ¹ÿnÿÿÊ,– A…ÿì4„ $Ùƒÿé7ƒ eÿá/ƒ )ƒ u„ÿ®† s§'ƒ Wýÿ ¯Ì(˜ ó…ÿ´… V‚ÿøOƒ )åÿÇ-‰ ‹„ÿ…† +„ 2éÿÿ -/˜ "ý†ÿd… «ÿn„ œ’ÿßHˆ ’üƒÿ^ %€ÿ› 7‡ÿæ,„ 2îÿÿ’„ Jü“ÿöoˆ "=\z™¶À4Œ -Åÿ› Sˆÿ¤… yÿ¶„ "Ñ–ÿ $œ <Ù‚ÿ› šˆÿþS… %„ ª˜ÿÌ!™ &‹öƒÿš !é‰ÿÌ ™ÿÖ˜ $kà…ÿ𠀋ÿb‹ sšÿ•– B™ê‡ÿ™ `ü‹ÿÓŠ \úšÿS„ OC(‹ ;Šÿ— !góÿiˆ Iòšÿï"ƒ $óÿÿíѶša† „Šÿ• !{Þÿ¦‡ Ií›ÿ²„ S„ÿ€† ÌŠÿ• -ÿÄV‡ Jîœÿp„ ƒÿü7… 6þŠÿ• -Œÿõ²S‰ «œÿý2„ ̃ÿ½† ~‹ÿ !M‘ -‰ÿñ²g'‹ zœÿÊ„ -üƒÿe† Å‹ÿ Qý£- )ÎÚæóïáÓǵŽe; Iœÿu„ eƒÿé"… ;ü‹ÿ ’ÿÿêo­ "õšÿó)„ ¡ƒÿ’† –Œÿ ÖÿÅB¬ Äšÿ§… Þ‚ÿþ<… &îŒÿ 7ƒÿú“'  A§Y„ ’šÿO„ Gƒÿ† tÿ y…ÿå“Gœ P·ýÿ„ o™ÿÖ… ™ƒÿj† Óÿ ™ˆÿÙŽA– @…Îÿ£„ t™ÿ~„ "ê‚ÿé$… RŽÿ  ‹ÿÓ‡; ,U’Ù„ÿÄ„ z˜ÿø.„ bƒÿ€† ±Žÿ ¨ÿñƒ 2-))6CP^l¹áþ‡ÿæ„ ˜ÿ¦… ´‚ÿç&… 4úŽÿ ¯ÿñ‚ -•ÿ&ƒ ˆ—ÿø9„ .ø‚ÿ}† ÿ ¶ÿñ‚ -”ÿý!ƒ —ÿž… z‚ÿæ&… $éÿ ½ÿñ‚ -”ÿî„ ––ÿö4… Ï‚ÿ}† lÿ ÿñ‚ -”ÿß„ ž–ÿ–… U‚ÿå%† Ìÿ nÿñ‚ -”ÿ΄ Ç•ÿñ/… Ä‚ÿz† e‘ÿ Aÿñ‚ -”ÿ¶ƒ "ö•ÿŽ… P‚ÿÜ%… %ß‘ÿ ìŒÿñ‚ -”ÿ}ƒ N•ÿî+… ½ÿþU† ˆ’ÿ ŽŒÿñ‚ -”ÿ>ƒ •ÿ…… M‚ÿ§† 4ó’ÿ 0ö‹ÿñ‚ -“ÿß„ ´”ÿé'… ¹ÿì.† «“ÿ€  ‹ÿñ‚ -“ÿž„ ç”ÿ~… Hþÿm† Nþ“ÿ€ 7ñŠÿñ‚ -’ÿø:ƒ L”ÿã%… ÀÿÀ‡ Ì”ÿ gŠÿñ‚ -’ÿ–„ ¹”ÿf… jÿö<† p•ÿ‚ ¡‰ÿñ‚ -‘ÿî-ƒ Hþ“ÿ§… +çÿ€† )æ•ÿ‚ &Óˆÿñ‚ -‘ÿt„ ´“ÿÞ)… ÿ´‡ ’–ÿƒ .̇ÿñ‚ -ÿ’„ Dý’ÿúN… Jü€ÿÚ)† Jø–ÿ„ (À†ÿñ‚ -ÿ§… ¯“ÿ‹… !Ï€ÿóB† -à—ÿB„ $­…ÿñ‚ -Žÿ´"„ l“ÿÈ!… {ÿh‡ »˜ÿå:… kíƒÿñ‚ -Œÿü…… Oø’ÿñ:… Aó€ÿ™‡ ˆšÿÝ2… 3·‚ÿñ‚ -‹ÿäQ… 9ê“ÿl… (Ù€ÿÈ"† Wü›ÿÑ+† cÈ€ÿñ‚ -‰ÿî‹-… )Õ“ÿ®† «€ÿÙ0† 4êÿÉ0‡ J­úñ‚ -‡ÿå…)† $¹“ÿá,… w€ÿß5† "ÈŸÿæQˆ 0p‚ -ƒÿöÀ‚Eˆ ;ΓÿüR… Iøÿÿæ;‡ —¡ÿú‚Ž &––‹nR6‹ dï”ÿz… ,ßÿÿìB‡ eþ£ÿ·-Ÿ $œ•ÿ“… "»ÿÿñJ‡ Mñ¥ÿê'› !iÖ•ÿ­† ¨ÿÿôR‡ Oñ¨ÿê„(— %vÞ–ÿÄ$… ™ÿÿíR‡ Qò«ÿê„3“ :…ç—ÿÕ+… ŒÿÿÞ>‡ Uõ®ÿý̈BŒ /h¤Þ™ÿÑ0… }ÿÿÉ/‡ Wô³ÿÓƒ 3EZp…™­Óý›ÿÊ,… nþÿ±&‡ Zö´ÿñ‚ -£ÿÂ(… iüÿ’ˆ ^øµÿñ‚ -¢ÿ»&… €ýøtˆ bø¤ÿéŽÿñ‚ -¡ÿ "… ™ÿÖEˆ wú¥ÿ5Ìÿñ‚ -Ÿÿøs… &´þœ&‡ %¦§ÿ $¤Œÿñ‚ -žÿäM… .Éé]ˆ 7Ϩÿ€ uøŠÿñ‚ -ÿÄ0… ;Ú¹0ˆ Wì©ÿ B̉ÿñ‚ -›ÿô!… `ßy‰ …üªÿ‚ !~ô‡ÿñ‚ -šÿÄ;… $™§3ˆ 4½¬ÿ„ ;†ÿñ‚ -˜ÿñu† > L‰ hî­ÿ† gÚ„ÿñ‚ -–ÿø3† We‰ -®¯ÿ.† "sã‚ÿñ‚ -”ÿú¦7† "A+‰ Zæ°ÿ‰ %}ã€ÿñ‚ -’ÿú­=• 6­þ±ÿŒ Z²øñ‚ -ÿÏ~0• &‹ô³ÿŽ .w‚ -ŒÿþÙ“B— kãµÿ• ,õ†ÿü꾎\-˜ YÊ·ÿ— &6HYebQ@.› "kѹÿ¼ '}à»ÿ9¹ U í½ÿþÎ…=³ PœæÄÿÓŒD¬ %Q—ãÊÿñÖh;£ Çôÿî^37d–ôãÿt8mk@ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿicnV ¿€ostinato-0.9/client/icons/logo.ico000066400000000000000000000042761321315675200172650ustar00rootroot00000000000000 ¨( @ !!!///000???@@@AAAPPPQQQ___```ooopppqqq€€€‘‘‘   ¡¡¡¯¯¯°°°±±±¿¿¿ÀÀÀÁÁÁÏÏÏÐÐÐÑÑÑàààðððñññþþþÿÿÿ&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&%%&&&&&&&&&&&&&&&&&&% &&&&&&&&&&&&&&&&&&&&&&&&&& #&&&&&&&&&&&&&&&&&&  #&&&&&&&&&&&&&&& &&&& #&&&&%%&&&&&&&&& &&&&&&  &%&&%%&&&&&&"%&& &&&%&&&& &&&&&&&&&&&&&&##&&&%%& &&&&&&&&&&&#!&&&&&&&&&&&&&&&&&&& &&&&&&&&"& &&%"&&&&#&&&&&&&#&& &&&&#&&&&&& &&&&&&&&&& &&&&&&&&&&#&&&&&&&&&& &&&&&&&&&&##&&&&&&&&&  &&&&&&%%&&&&&&&#&&&&&&&&%&&&%&&&&&""# &&%&&&&&&&&&&& &&&&#&& &&&&&& &&&$&&&&&&## &#&&&& &&& &&&& &# & &#&&&&&&&&&&&&#&&% &&&&&&&&& &&&&&& &&&&&&&&&&#&&&&&& &&&&&&&&&&  &&&&&&&   #&&&&& &%&&&&&& % #&&&&& &&&&&&&&$ & &&&&&#&&&&&&&&& &&&&&&&&&&&&&&&" && &&&& $&&&&&&&&#"&! #&&&&&ostinato-0.9/client/icons/logo.png000066400000000000000000000440431321315675200172730ustar00rootroot00000000000000‰PNG  IHDRÈÄÚšnåsBIT|dˆ pHYs]]IÅX¢tEXtSoftwarewww.inkscape.org›î< IDATxœìi€UÕ†ßs«g&™° 8™éª^’€APÂŽJAÙE@dSDEÅPAQDAAE¶°ˆ(Ù ë!¡—ªžIBXY&Ó]÷ý~tO˜™îZz ÉóÒuëÞ;Ýuênç¼GHb='ÞßFîG@Ih…¸qÝ4€Õ VC¡dŽP/ˆðÿ„|¡cƒ æÏŸ?`”»¿ž²Þ@ìY³ÚÞxí=”èÏòfU½ b¡®Ïöfç’tØÍõTÉz©“m§L™°rüøãI|@Wƒ«ëµÂEÙlöÕ×½ž¬7ÙjêÔI…¶¶S@œB`Óf¶E`…@.¡!e2™eÍlk=ÃYo 5°¬£AüÀ&­lW€7ù±+üµmÛý­l{]e½TÁ Ó´ À€ì=Ê]Y,ï§söÕë×(Íe½„$µNÁ:G»/ƒPð ¼~4ië $˲ÆEˆ?8¼†Û] ,È„~”‚ˆlFp2€É6+ýWÕØÅJ[d¿T*õv÷¯Ç‡õâƒiš[¶Qî `ÇðwqžˆÌðxçªUÿ~þ•WVÝÇ7†ëî%"ŸñqT¿EüŒ´EöI¥R¯UyßzXo Ä{â³Dé;jë–Ë u!üý˶ý\½m[–5#B|‹ÀQŒ·-DÄøX:î«·ýõ¼Ëz©À4ËÚI"x½A€×€3Çù_Ãûaš[iÈù>ò–Þ<õv¹\îÍF÷e]e½Œ ‹MWZ? Èd¿r,€à˜”m?Ñì>%¢ÑÝ ê6[&~—ÎÙ'6»Oë ë d¦in<†À5ïwE>oÛö[­èPÚb¹D2 (E°k+ w] Ö“÷Édr£6ÈßdÄo»sÖ'[i°Àqlc }W!ž (*š¸\D®]ÖãÃz)Á|á&Ûú\šÎÙ'ÍåÜB‹º5Œ…‹.í\½jOÏø•`»˜ižÒ¢n½§Y?ÅŒÆ>OáÍÅþžÉ9ûŽ…“k˲Ìñ´Ÿ˜oLÍ™[Œ–1¿WXçG®®®N‚{QÚ"‡ŒãÛ¶(9 ÅØ’ŠØt‘éìÖÂn½'Yç d|[Çw èö)2 ÀƒÆÚIu*›½ÄŸüÊhÊ~­êÏ{•uÚ@’==q€gø•!qñËŽóR«úT Ôê|ÝY*"ÂÏ´°;ïI"£Ýфʸ@‡O‘\aà¼F¶™L&7ÂÀÀT(µÉNãU'ñTìT¥Ï5ÐZDVè×@?€~V¡ôÿ*‚~’²{å?I˲fض½ ‘úÄ:k ±X,ª€ý} ¾¾xñâ•ÕÔ;½kú休ßJ”îQ"=Ôè ‚n=6†(€Å¥¸Q")ÕD‚Á=)k©TEÅ+ï‘’ô\¯¬Ç›uv+aÆÎø¯ë,H9öV~ul;eÊ„wÚ;?d(½);B°€XÃ;[?ïx‚§H>%‘ÈSë}¶Â±NȬY³ÚÞZúzü\7ˆSÓ9ûÒ¡%“Ét¡ðiPöᎠ¶Fxg±Æ+<ò)w笇×o —³NH2;˜Â›|Ь¤¡Þ—Éd–%“ÉÍw÷'x€½´·¨›­æu€·kª›í^ûÁ±²¥=Ú¬›bÆn%x O‘{@Ü ÁvÇÚ;JÔÊk æ@xs&—ûçºl,ë¤$Lk/ÑS\Àd9ÁåBYA‘å"\!À*’JDÚ5ÑH»@w@dc!¦˜`| :ù*!·Á•ßfú2ÿׂöÆëœLïîîrÈ¢7ë|‰ÀsÖ¬úË‘‘ò“loö¯|?@ëÜ9ˆ;5±ú~üUCÏQmmw·Ê=…Äû|®É¢8%kT‹»‰ð/ñht~²ÎK۶߆ÇZÍ:3‚ˆˆŠõ˜§ˆð<@6hpõïˆà7®È/š-*"ëŽÍRRX™yÃuÝ·:ŒÈRx‡?“É9;$z¬(úL@f5ºO$†V_}/®QÖ ‰ÅbÛ*òJ;4¸ê×ù¥+üU+¨¦¹«@~·2B.Ë8Ù¯þ;ï-ÂoÜ«ÁÝs!¸ŒJ}ï½$úž6îîîñã"‘sI~N'¹T€wö÷ÿ.Œ¬O#HöôÄ©Œy&VsÁ#2ŽsÃÈÏã=ñY"úH€»Mõ,àÌŒã\ó^XŸ¼g ¤ø¦Ô¿ohÅÄÛÜüõì@ÕBÂŒÝ_Ë[?Æ8Žíu=ï+¢/ÐSWËyÜ»¶;J¾çÜݧwMŸœ°¬kDô?Ð@ã`öHçìcZmÓ»¦O¸G-÷æ­ý®gr™»Ç­œð~!.ë| ìbO'ÍØq ¬³å¼§ $}Ám[ýˆ£Xíj@¾×±Á„mS¶ýÏÖ¶ÄQãi¾ß*3Éü婜}¨wðl-íxÐIðʸiÞF[ª„ß(ÞS¬®®®Îñ‘ö« 8¤ÁU;rœF>4U“L&7b¾PóÂWÀ]SŽóx¨²"FÜ4/ñ­ZÛó Wk92Û›}¤Áõ6•µ~I$ÝãÛÚm‚qü}µ[Ø~´R©ÔÛü»Öû L ]–tÓ¶}&‡£œÕ(z”âܤe}m’$Z« $;ÀX-ž—É9Ÿîëë{£õÖ…vå5ß«T¦Ú{2¶}£€»è­µÝ $ÎGͶš:uRëmk­$ÍØqõ €ÍXí*¡ìŸvœï޵¼loö¡py•·ê W®¬Ú@ å8Ï bìA£§EÍG"OÄb±é ®·á¬u2[fG¦y)Á+ÑøØ Ùx³MÿÞà:F*—½™À¡ÕÝÅËê9«I§ÓK&Nš´—eg)õ! ¥ùxÂ4÷ll½e­Z¤wwwoÚaDnм/•zV:—{ºiõ×A2™ì@¾ð!oy12®cç ¼SoÛ"¢bÑè59²ÞºFÈÉ)'ûû×ÛÖš$¾¿ÃˆüÍ4TªŠd9-¦PøAÆñ¬´Eöh„qIÍå¾Áµ¨om¯L˜æE"2æžÇ1סJÄ£ñ})êq´@At5Ù¤ZÇ4ËÚŽ„¯†W‰…Ý3wltÆ)’:ã8Ç€¸¦‘õ‘3â=æœîîîV…fÌ»»ÇMóÓ¥”fm-i°ªtk­ƒÄñð?,|A(ç§{í[›¹Á@R‹È±‰¨© ÓÐÊûw¨Èí3gÎÜoþüù ­»FÆô$aš³¹À¸6«|ûÍ pª1âQ󿨼c—ÊY%ÃhÙ)"*nš·ƒh†¼éÝ9óócAeeÌN±’–µ3€»Ðã ßïtûÀßfíÄzb³QÉ8ˆ3'Nž´U*—½¥Õž³$u¤£ãHÍeý좨sÍXX“Œz*Qšoÿ-D`S yy¨~è[‚<¶úz›‡RºLZTˆŸ¤sö…óæÍËFŸ`Á‚ïÐUû¨6¦?è‡Ç£Ñ߉ˆ¿td“sbYÖ M܇€¸‚ר¸7“Ë}Û(´ÝÀçÁ’™±Xl̬EX®^rOº×9{T:3‚L_&%‚CQ'ð8@.#p>#ÇÅMóõô¯^Æ”X–eÄý6ó-(¸KDnðþðµ3í*JR/\¼p)ˆ{üJ+òKáën.¢ß=%°B+9v,ô§lû^@½†‡ÃÙ ú,Šâ>ÅpjÂŒ5T@¼ÆŒ$“ÉEÜŽ`½ª¹.pˆoVQýJ!÷+ôߪ$ëêê JÝ(\³{%`ÓãÞk!íd¢ñ†] ƒCàèó“ÑØÁõô¯VÆŒ0Ÿ¿H€íJÍ‹ŒëØ_\ÙÀ®¡ë&¾“Êå^úÙ¸ 6ø+¿À§ Ç··>lMEä±âÿp©´µ]4ºñ&}€ÿ†-¯5ÏÊ8Î ¾ŒA±z(¼Â²,³®ÖÀ˜0d4ö@‚’N¾ÔV(ì³`Á‚w”âYaë&ñp¶×¹täçóçÏ äÏ·‰iV^ë{D䂱–éj(¹\îM‚ÇWqË>IÓü`ƶ¯A·òÆqÃl™ÝÒ³»Q?))>ÀÇý™K¥ÐöÁÔ¢Ô¢i–µf¸¨7+”v·MõöVôf÷Äw¥}Ó*k%;e³Ù ÔË5ÓÕÕÕ9~üøIZëM•Ö“t(‘7èºK †ñºã8ËH2aY7K$ò…T*µºY}i‰¨õG¾ª°à–´m qÓú¹§Üp~ÚÉžSw'C2ª""*5ðQŸb$ø™ŒãüQëÏუ䔴“ýµ_‰¤i½äçßDà¹lΙU¯€srjr* w-ÜE€]PIØÁç<.Š»uor? ‹/Ìå²õô§™X–5Ñ æ#œþ±¦¡¶Êd2/Ïš5«mÙk¯?Jo-‚½R¶ýPczëϨH·,n+ä·m…xƨH<ß]D?ÿ‡ý™qLØeÐ/'aY¿CÑ')ˆUðý~r7ƒX–5Î ^`z•!°†Ú:“ÉF×§mî—ÖG/ð—÷䬻Ƃ‹$¢ÖÕ¢è€"ñÔ¢Ô"HFcQx‹ï‚»Ò¶ÝhM¯òfFÃ@Šo×ÕÿñO¿Ìåt ¾·š:uR>Ò¶þI7‹w?ÎälÏôj#‰›æ¹Í¯Œ€w§§bÖX˲Æ)àÇïÄ5ÿŠà÷ÈG®|àF Ó4·Œ@Òð–E]ƒ~²íïþ;µ.‡àD¿{(8)cÛ¿­¿§ÞŒÊ.V~Õê³r“T_:mÈí‡#„qxõÓjú“qœ9ôíd߸i–%ÝIöÄö3ˆÿñ›1`ð>ßc¤àÄÍØ¯ãñ¸ßT¥©8Žó?@. S–Ä1C}¯\…Ó¼àw"η,«*¥Éji¹Äb±¨ˆÿNÁëÒ¹ìðÀáÑaê§à¼Z´a…úkE%tŸ2K“ÉäFǧ%LëoT¼c3q§!àWÄÕ –Ue˜nã !Âÿ¼i«ÇZ£iÛv¿+8ÄÏ…À¦ŠòÝFôÓ‹–O±¦u€Ã|Š,¡¡¦ }ÈãÝñmÄоo `ß`ÂŒZc ¦yiðy /"¯“8 µÅÄÀBƒði¥å5 ÷M%òFAäÍH$ò†ëºãL1È)¦PcŠ(l `K»Ö²àÁ?taø¨Ü*’–u‰Ÿ$nJçìaÆœ4ÍÓ ñÛ 0¨·jÖ®^K dšeí¤‰Çá“Ü›à—2ŽsÕÐÏâfìbOªßK¨9,Ñht“6‘—™\k•û[ 7úÑ<ùD.—{³žúb±Ø¶Jë}ÙÀŽ?XMâÜLήj Z/ÉžØ~Tú²†½zµ[xßP¹¥RFâùðÉoBðÖŒã4Åë¡¥’0­ÁÇEDˆ§Ò½ÎÎC·fgËìH_ÔY¿”ÍEžÉÏ+êúƒ–uˆ,üX¨;¡pEÚ¶ÿѬxd2¹ …Oò…ÐâÖ‚k'Nšt\«\哦ùB6„ÿyWÊ×ҹ쯆~7ÍO änÿûôîé\î_uu´-3©—©•ì<òÔ:}†Â»‚ê-û§z³åë)^Ö,FG`ˆ µÂ¶m¿Roª¡¤hÿS Q|®+8 ÙyMŠ#ÀÒ7YŽâôrK¿òžË8vÙwŸˆZ‡à>÷=™Í9»4úEÔ’Ez2™ì ÐwXàêJ. ·8Ϥûlÿ7LHHjR} ÎsÜCmÉÙ?lµq@&—¹?“sf•dCƒæä³ â±f;.{ýõÝJçASäÚ@€í’¦YfàJxšß&Š;ÅM³Ñò³­1N…ÏA€eŒeˆ%yÊ}ë'~ÕÈøˆbW†>9'`Ü7íØŸ s˜ØLH2cÛ7NœM _†·`ÅGß\úÆåh/ï]aÛ¹?N/Ö¿ ºŸ‚вÀqlüæpR]õ¥©B]&˜ãÌOÖ/ò‡ÚzVN2™Ü†{€šS…<0Öc–½5ÇE‘hî·°€_J˜f͹H€5ÛÍ·V¸Ô ™\îAžÖHNMVìW†®º°Òç""~{Þ%ÞqýçÕ¡Å|áF[ù[,SSœ',+\4Ý(‘vœ¿‘~!®ra<Ü=¬DÒŒ'âþ •ã9ÖŒö"ðwg@í8Šdz3ó<äw¯ÝQ¤iBJÐ"÷‰ÒZFÒ4·EpÚ°»mÛs#ˆõ˜ç•‡ôy%8PÀ/ ÌùqE¬'æûrm29û—½R(}Ãôîî0€™3g¶'Më7ż-STÊ»ÅZß\kåuÅ×·‹ÀAÉdÒ_>*M1˲ÆA2Í7z_ ž^5!1MsKœéÛ’à[/Ûö“iǹ]¶¬K´+ÅÛ“==ÍÑÞ`&Nž|²Oö¨ F›o´ç ɩɩ«–¯x˜þsÿ·WV¯™v•r°øf ÷š9sfÅmþ©½Ñ¿ÁßK¸] u‹n4Å@y`Àâ\» žo­± \îŠü­Æî #B9þ=s2¶½f÷'eÛ?.©:1 ʸ»Ùñ õ0oÞ¼¼D"p*]ðèi¦é7íDÂ4g3RxF€|#n\¼xñ°3 ¡÷3P*±ÁªwV}¤Ò•¹œ[\¬'Ô«ïÛœ)Vðôê!/WŒd2¹‘4¯¤!Ó«x<> â« ˜¡Q~ª«ÚÚŽ˜V™À ƒ¼¥ÕR5ÕJ¥^£«öCQ/w$†¦üØëÞéÑh EñੌÂêX,ö™0xš%Ú{êK_ß>0cÑhˆÙˆ7 7˲fˆ ¢ÕRtý®ŒÎç÷Úfeƒ¦WpÝó}ÚÊ xp¥à«T*µã³rÁÈÞ}=NCÏjM¦/󀇇`ÿD4º[¥K®¨«BÇÝ_SšNÂ4×LQ‹)¶é»M¯ˆŠm@&—{€¯Ï›@ÕµXo¸(Ê—Šäèz"ä½zÀuëž^Åb±r×uB®M9Î3^×Óéô­d¿PYg'&-+@ïitYíæ/P9SŒ2GÓÒÔ+Ø}½¼²ó’¦ù•wÿ)süJS°­×áeÑÿŽ•¶“‡–úd<奄ŸEš0Åâçü¯Ë}~CÔþqÝ$žìëë«;Á½Òü‰Ïå|tàyF6›}A¨G°ëH\·¬`×™Q¢¯¯ï xx\s·¤e SÑ”Ša äƒN…$ÿP¼ãí×_÷)' i–‚ö™¦ÞÜ@b±Øt,¿2Þïw]Ûú_üB™‹}Àl¯ë„\F2R¹ì_B*›‹×Ä¢±…íg«Y90ðKxhë’®fX…3g&aÀ-f¦2Œ!àCr{¯kÙÞì¿øæbŸß:ˆ†ˆ "ÑÚ3)})6ÁW…#Ä'M–©“ !Ôè1”´“½ÄCmS¢çÄ{⳪©¿U,^¼x%([»2kø¹ˆ®kgÉc¡´Æ{Þ·,Åóû*HùG{ÔêÔØ)V°{È;™ÞÞç¼.Fà?zÈ÷ »GÐ;ƤšÑc(ã6œpÀGƒKÊ¢ô=ñîx²Ú6ZAwoô*xœO¸ªíÓƒÿ¯• ˜ûû#C“ ö-¬à9‚”* µÝ<Þ¯"—ÌЦDWWWgÐî‰Çý4nuÀúÀ¼‘{éÕRZ\zÊôháÏj©wþüùF¾ãs!|Œ`3ú^˲‚¼ZNQ•‘7QDôšuG6›]Xe>¡,é×ù5½$ý „ø€Ÿ—±Hˆ“蚦Y 3qmm³ Ä,"žÓ«âõ€õQÿô ÊÏÇ(cÛö‚Zë^¸xáREý„ˆšÀŠÔÙKh­þRésBöšÇ|µ.à‰*«'Á³†n´¨¶6ßç@Dz¥Kgz^œ0áT>Çyáèˆ èôPÊ(e2¡ªúÇ(Gè¹ûBȽõVŸÊåþ#JBˆøkÛ2_¸³Ña¢õb÷Ù¡²ÇøvÃØsð}}}«V»…= üÁ;yyO@pÀHY§R­µ!¢|å°âåÞÞªÏCi7Œ}à„5йz¥¯>o5¤mûޏe%Äu© ˜ˆš¿pr£Ú¯Eu…å‘yÂ}QÉ)QqIe?g^›ÍÙÿðj'nM¨½!ÜÞGO¶&°u± B E"aZoŠøŠS{RÜîm¾tuuuvF:ö…à`vòS(OSp{lOâ,äF>¿^Í#ÙÉÇYýQß7\ dlûƸiŽÈïðë8)µ^Éälßî-£Ý¸ùBÀˆÅ±LÞ5}òÂÅ —†©FF8BNïîîr#‘½Iî-½ jË5%kg„tÓE©*œº*‰÷ÄwÑß×Ö¾/Á ÕÜ‚€oHæ×Ý‚ÆÞ­Èܺë¯@Æq®J˜±N€þX"øAÒ4§§a‘’µ’J¥ÞN˜Ö˨j[Gú·Ê@ú]7·í”)VttáÑ0"{€ÖG& ñ1ªÉj‘žH$º“fì:QúI ÑÆ Y×ú ÞAX.®»~ÒNö× ÌÉš~@./ô¤¢# E‚d`qÇ‘Ÿ­7þ¥´Û³QçPQ'ǺcU)fúÈŒ36L˜±óQp<£øÇ‰Ôo â§ïK–ér5’tξPa¦OmZëÛš­xñð”ep´ç c4L†§n®f7ËsŠ•´¬=HüÁ¢Ñ¡â'nʆl„â—üA'±,B9XDªg§‡~‹6šj ²ís–5Ä×üKÊd¸kææ3w¿d~°·p“ ‡¨ð#H£¹`"˜`"ˆÍÌDH#ð+ñ¨ÅbGg³ÙWƒÊW4D4v<¿FÙ­Rƒx# zV¤sv™´è¬Y³Ú–-]ºÙ©¤Ùä­¢(8!Nš9sæµäÿ(…nz.äiî2HÆqN‹›æ– üåú‰múǯ¸ND>×,eøxÅZ 3xw|‰ 0=…K xˆÀ\P¶Ð{'OpoÚv®)ûXDbSc Jóùd,vT*›½Ï¯`Ù+aš•ö³ýŒ#Á-÷¢ °Câ;8í8 C%› ±ãÁ°Ùšö,ôÌM$›‡,_¬}€>Âåˆ1 K*•ZMCíAˆu¿3Zq$Úc‘Á”¤e}Ô žÁY!C_äb?”Éå6O9ÙƒRŽsY*—{qh!ŠHÕFß鸆~Ãïz ü)išž/ù5ë‰}XÅ;.¸wÆÉ~cx<¹|PEüsÔMÕ¨r{ õR¼¨ïÇ IDAT؆r}|ž¤å'Ø™Lf™Q(|D_PY!®jª»'¯©Ý$æÂ'ëS —à§39gjÚÉž‘rœgý¦‹Ã<|+¨<‚ ¹¿–Ì]Š+fì«/ÅüJéà=­º=O~ í8åg”À€}ÒI$AzezT2«ÅŠŠÂsû  ?ëª1¶F±°¯o±@ïƒàvœ(}G˲E¼v«Æ!ÜŽ¦1àºO„=wÐJ%ûô5%Rkj;ø«‘Q“Àà’Ï ÞùãnÏ䜃<Ãd%Ø@úþá†a<œ¼àqi$Ä© Ëúlpû@D ¾o§ÂÛo74/aXR¹Ü‹¤ÚÀꀢï+Æ–e…Ûõk~;w²P„vi­ ֖ψïß^o …I\=2~]Íœ9³ô<"m‘Ãü¶V5l "¾1²|¹¯ˆÈŠŒã\åa$W‡93èxKD"u«ðÕJ&—yT(Ç$v0œ_£q(ßtiïÂå È÷G^C‡6H$À@´ïbF8¡ü¨˜>zqõŸ‡Ê4©•ï¬ü(Ý ˆë“J¥|ßl"!4‘HßÉU‘ˆ¯²(ºm2òDzbD㦠ÙþÞÞÞeð‘ וdT.{ O ª!–4Íï4½C=‚ÅÛ F¤ª<á‰ht·#ò\16ÂkîîË÷'Mó;~/’´ãü@v×D¥yc-iߺºº:Aœ[éŒÈµÕÔד³®EÑ{(»&zÌŠÏUi⹎r•ò_ƒ„u®ÔzXˆA,‹,Ù>­`ü„ ×C*/€cјÿî+'€IÁ0|;OˆHYç“ÉdGܲ¾­4SürᛕF{%òŒJÚ¦®:EýÔµñ·ÉhÌ3~ZT˜<}€ñ݆Ÿ„‚5""F²Žf¾° ´…ë~{G&ç<2†=•Ëý.Ï¥¾LèˆD|ez’==ñ#ò_G#ÓHŸOš¦g\vQh€Y­H|+‹}äæoÄ£ñŠŽœÉhtk*ãÔ?jx4-?³,k†×õbàZ¹kù„šWvuuù¥¸^ƒŽ œ ]DO¼lÛUIv¥”`¨ÂÈÈo$LkN2™,îr²|¦ð.þ;XÅ€7žX4¾aYÖ­q*OëLår/{àÓŽóS®P¸ à…ñ¨ùÀÈ€’‘Ò’^dkÿë¾ °)ñ¨ù‚7 Š‡R„ðº–Ée%+¸g¿‹!¢¯‹ÇãÃÖ?‰ht{§T!c%jb¼¡ñu¿™œ}¾<5oKôt¶·û¿$MóCo]`ÑÞ© Ã²ª0p,Fœ°—øó…ùÉžØ~ñK*äk +W®Ü*Lض”Îg’Ñèû âAP@ð+`ÄÔ€¤Îäœ#ñš£ï!®žŸ0Ío úd¨)FWüÖ *H`†hÔ×Û•FÅ ™¡D•«×L-âÑøîõ j[ˆW‡øçÕ#©iG"À©‘ÄéÉhÔó»3MsKwÂKr‰¸3Õ›½+D}Y¼xñÊøY¸óÍ©x§@{ºÌ¼@a¸þ¿õˆ\ÒŒGQÿ†÷3ùPÆqî*ÌIêlÎþ2—zܼ! ?{kéÒlÂ4¤‹ÃS˜˜ã鵿‰ Á<‚_&&‰ÊoÙlö)ž9Û€ÀáqÓ<"iš»ˆè{á= i4Ó‚äÒéô¡œâW@›†ª¸~°,k\åïpk.G›Th8Ž ÁðŒËñιN0à D{Ά7J˜^‚ë¯Àça—$™¶íS)8 ž™{d* çhÈ‹ðsQ~—ÎéѨ)"F2}Ü4O˜æ…I˺/aZ¯ J R‘·EpzwÎÜdÅ@# ¾o–¶Bþ¼…r w5Ÿo ,,\¸0Pm>•ËÞï^©äÐ!þ@ÁŽÞ÷É÷Òét êJ5¤lû!¸Æ,TžnùÑï»uM g þ›:šrÄÐÃP í‹ÅbÓ•æÕv Ù?2ºVnÔW€k ‚ï JÅãñiâê2 ÎÊØ¶¯Ã_Ò²ö'ƒ#ùZ ¥ÇsôÊôîî.׈¼ÿÑíµÕnaÆ {KÂ4Ïðr~6“sv¨F ½ººº:ǵµý®R’oX¤Wjd ’!tVDV–² ×#‰úÀF*÷ˆˆŠG­Óþµåip“Vòý‘þR–eM4X.Ì Àó¤þJ:—óõB›æŸªû±š M;NHµB nY' q¹o!ÁiÛ>!nš äx9\jc×LoæßÕô¸ŠzTü9BÈÜ6 ¶¢Þw¤Ò@‰Åb[(òt'a»I|=“³ËÄí’S“SÉŸÈ9•î!ž…¼¶+“ÉäFÌC-›M@Ào¤§’‡Cåò"FÄCc@?ÁwࣃLÁ±Û¾ºšþÖCÂ4wä4:-y0«^´*Ÿÿ±Wöäª:”Íf_MÛö™yê(в)½75%2L]0‹í˜0­9Œ?ãŠÂ.ñ¨—ØA*•zÛì àµÆõ¸ft>`ó`$$)@£¥ˆàûð1ü •ÆZ©åh­qä!¸Ù Þ:í8ßõK-^ÕRv³ˆJ˜æGâøøL…dÅ¡Ök·ëõLÎÙ‚¤ï‰Ï‚ÒU›Ì‡ÀŠˆ[˜¾°¯¯bœÒÛìA„‰µo‚KÓ¶}j-·&ÌØÝ?\²ÄÓ9û˜šî­ƒ¤eE¢Ìõ¤ ,äZáÚ0©€:“x–¶ÂðPÓ•\Ã8Àñ•®§ç±Ò¶Ò¶q+¸'ã8µ¦€@—OÁû%Sù>Á?6ž<©âwÒlH~ʯ»BõàB3 žWùö½S©”Ÿ¸wÓhSêãð=Gã“C¿“…‹.Íôfæeç¶”ãüœ"Ù0íôèH&pIÜöϰmܸ‡‹ûÝÛèˆÎ£ŽW¼xWx2ÝhÞI9öÉ™¾LY¸Aµ$LsÏxÔúg)Ržò{…MéÜèÿ2"ÅÓßLD a"(ÿ›Éå*êxÑPQÀœPIO¡æâÛBž hg¶5ï´h°âÛ¤$–J-~¬0só™$,ëЄi>È"Ÿ!WpéK‹)ª7¢w¸ìãWFI™RãbÑè>Þn2ïBÈíµ¦±k¨¼ì8/!x »O3MçEúO³(jCŽÚ¶™Wöçó¯tAçóÁû T‡œB6˲&º­¿sÅ7œTFc§&t/4ñžž]_Aeé\Îóe)”PRBµN¯€F.Ò×À?¸0\¨ãT m‘HøœkpÏ6WoÚì¹)|~<‚_{î{S¾è³²y2“³w™æ RÜ’œºM_h$-kg!—‰ë¾-&¼½jÕªÈx‘‰.0"CËd(nCm!ØÎ€ˆqXDâ>TGûêØ€×ú\¯ Ó4·Œ>b)šËär•…IBÐðÃik»!„üb2™¬|Ö‰<À/g €cÇù_·cî)à®"ÁYVr[ÆqÊ¢Û`†iZ"~†Æ¿’ÔÛ¾\+™Aðº öÂ!x\C^tÈ¢Bÿê·ÛD½Q€d y†Ä\ o!ñ=öGƒŒÌl–¶o3fÌØP‡ø•‘r!ì5D€cÃĸ¼ž]Á†H*•z R)z¬ŒI,>çQÇjø„Ä‘cDĘ˹…”ã<-‹Ú{¼ <Ò+PAäãðyi¥îüÿl6ûjÆq¾pO´;–1ú;—ly7‡ÂªCƒyµaÜ_ésB¤{îow󿯩ƒ%šr¼OWý*\AxÏ!~Ê#ÐU\¤Ñà}êJùöýü4eÉrÙ—!We³Ù2Aï´ãÌíØ`¶ž ÿo4x”㬂2*Ó,÷FM©Àÿe2™—+]²z¬½°‚ÛÀõnB4Å@JbnarVÏö[](Ü Ÿ| CÞ""ð2×Q0>´•)ð1‘{¼.ÍŸ? å8 bLðìp©›'É9[¥sÙ+ƒúC êÓåz‰Åbð‹C×x]RŠ!u~ùëª:V©­z+ð‚`˜QD`N¨t¡¯¯o•·´ò™!Is*ýÐZËçÂä:ŸßdCI§Ó})ǹ mÛê÷‹à‡ïk‚À‹”`çŒcïœvœÛ§’ ØUT–Ÿž+­ò1² •ª¸Æ3MsK0ÔVüc)Çy¦†î £i²ÉäÉ· X¿ „œäy².*“ˆr9)W¶1@â{ÙÞì#A}(¥€6=[ñŒª¬L*—{1eÛç¦{º€‚àBŒÌeRoCðÀs„zfÚ¶§¥ç[/Ûö“# ék d(ĕaYÖ8ÆÝ¨{½œ #"ßFˆØ ê=€¦ló™7o^>µ~[r¯öD€ nÛêo¡‚«vÚqŽGÍüvn'Íœ9ó!–qÄ›†TRh)g ÚÑéíî ëX_¤çYÏZ–õãJ](z,ÐF M@ò6ŠqÛË@,ƒàM DáùùüÂ\Î {ðe¸îB×ðËISñ׋¡å(}ÓaQqz5£§ç}PF‡ÊW6™4)`öަh…ßÄÙ°xBNN$?K§ÓK†}N2aÆ®Cqì…¹zùòã dÙPçÏe³ÙPoíùKæ/O˜Ö;ðkæCôvÚ±CÉö×¾¾Å Óê‡W˜³jÝRÜÖ§oÜ€7ÑnTŒ±/(uB„k‹àwr¼ljŠmÛ¯Êó¿D€ (èŠÁ>4‚õ` 9›ÜÖDÂ;ì•ð“jé[¶‘”Äþü"µìoã@áOª5…pS¥ÔãEAñP'çùÉ€XX90àZД֧J'ñD&—)s-±,kdžhf@èV•×% -S’ Rg Ô|WŽ«¤ªžvœ»P!Ö6ù*€'Nœx3”•…Õw噇J$ƒÒ¤]|2÷Š<ë§èѶš:u‰Ó‚Ê,Ëž%ÅTTW Äè!‚_¤z{G&ë©›–H6›ÍIñÀ,ÑW”ïÖ@’”àµ!'Ã5N¨v›oñâÅ+â%á)âºu'Ç ü„ç5à”Îu’/æY ÐP“§3ŽSæÎ“ˆZ_ ÊðŠÑÑf†Q5-êZ90ðSa´^·ê_¾²lÁžuœ›úº0ATm³¦òÔ¢"ñ•°¹6Æ Éžž8€¸w ÌX[¦in ÈWƒÊ‰”‰Dbs†Mã-8«±ü•h©,^¼x%T¸;À³ãñø°\ $ÝP£ˆà¤äÔd`(æH6Ùl“{ЧÝ0i|[G˜¹ð˜bøÆ{,4u1 ÎF°Tí éRªaÜKP!ól‚gÇÓ±±^Z-õˆ´mÿ¹èGH‡¸ú·#?Ì:Î5ƒò·wÐ(Ø–QÊÌê³™ Ï´,«A‘„ÍŲ¬qÊ›E8oAooµ[ᡉÅbQA¯[Á™#Ýf’–õ ‡…h†BžZk¼yZn ä©ȱWbv²ŽúIMmœx¿ðK‰htûjûæG(ß+™jA @ÇJãT=^×E‚âëkŸ¿@ÐâZpKÚ¶‡étww×A"Ükn—½­;’Q1”ã< A¸H/âg#§K™ÞÌ¿A\á£D êúj× ¶m;$üÒ%à—ds,°ÕÔ©“J)©½¨<­iI3v€ŠÑ¢Cx'âºeiæ:"‘Ÿ„ †"°B¹yßD«`T $9E Ò &éHáÖ‘»Zyèï XhzúøH{h…ôA´Â…l¯ë„\œ4ÍU[o«(DÚ.‚N²PÎkÖ´$ÞOj0Pa“ÄwGNñ’ÑØÁ ¾¦üÄK_¹‘Œš¤R©×„òËÀN«–¯üÅÐÏr¹Ü› ~Á Éhì3ÕôͶí~¥Å/‰æ8Bî/ ]-fì»ü¨_L÷Ú5Ëàø1kÖ¬6‰èëCŠ?›íu†ÅkL3Í­(ùë×Ü/mÆEµõ²:FÍ@ •Ëþ D(ÏKONFcÃmŠ»¸Cá*9Búö­˜¸Ò3ÔÀ&€ü#aY¾Âg­$}¹”äÈ‚ç{ WÔË[Kßø>‚Ýr´VrâP¥‘3flHÈøä(ÚŒA}P%ßf0ªÒ95Ä®€ÂßÅb±5¹èHR+œ èPp³R¹ª0òí_ P-éqW";ªÚºM2û<„A‹Û?gç†f´?-ûÀo•àw#óýýW…Œ§hùâÂ\.”o#uI¥R«µRy¤I§Òú¶¡¹Ë³Ùì 3Õ>YL÷ž…‹.eÄø8üÛ ¼&aY7ù‹r7‡Ù2;’0Í )¼ >"ÐB<åŠïÔ«fâñøÆZóO~ž^6Æu [X'Ló9(L;"¸°)©«aÔ (º¡<¡©%!Z_;4¥tƶ/B8›%£Ñ­«é[:îÓJ>Ž  â`·mà? Ó Ú½iñx¼§/ê< È7á'1Hôö÷“=ª‡Òy•ç–r‰•ÐÆ0õúâ¨ã»c8”¦Ç/²´)Œ €´ãü `¸½yb¿¸i[´ÓPÇzÇiQ×Ü "›Í.õ'¹3lÈœ¸eÝUúñ›ÂVS§NJ˜æÄÕ/ @׊À ¥°ß`6àFSZXPprº7½ÆÙ4¾_kÎ £ŽàWph³2îúQW ¶FSJéöw W?LÙö¹ƒÿNšæ‡Xô/ ˆ<ãÏÒŽŸ0’XOìÃJé9€„J= Ê%ã6ìüóŠ+:=D–¥;Ôé|"‘èFAŸJðÄ©çò"ø|ʶï Ùߪ˜ašVòüÓOƒ?dœìš€§dOOœÊxÀ–!šq•’=_ÎfkÖ×­‡1e @1ƒÛ¶úÙ0²öExFÚq.üWܲNâ7A7Ü;í8Aªƒe3ênFUŠ„\*P÷<¢ÂEOéêêêìloÿ°&>!ÀÇQ]ÞœRI ¨ˆˆšðÝê&ðœì28½+åt>2K#*83³Ã9-61g ´¬I<Œ°¹³)ǧsÙ+ÿ™0­<ì¿ר5¤¨Ü0fÍšÕöæÒ×*ŽsA??¡l( ÉâÃc¢ø†­>“–஼ÖGçr9/Ç˺ID­Ë!81 ØÛpí¿ãd2¹ò…‡C«9wfzÏ5Ó×*ˆ1i ˆÆŽ‚ðj„['iŽHÛöŸbR™þÎÿtÚ‘¶È.©Tª¦ÔÏŹ\ú³û6мÎLÙö/‚‹ÖNÒ4¿CHPü…+ZÜuŠÇã‹«çž±#§iÈ^™LfY}½­1³HI:—½–àQÂ,ÌÈ? ž˜Ï_2¹´Eö ¡¼Ç@áîZã<ÒŽs»+˜!Ä%]ñj-Û´’šnÑØB?hÛN™2A\}BGqKš{¶qû2FGA’ÑØç)ú†»«)8&cÛ7kÖ ÿBÀ¤€w§s¹ÏÖ³K‹Å¶o x"jOU\-¯¥a\䥄ÞHâÑøÞ"úN}ß]×”w"`­2„Ç¥-²Ïh%ɘ7HZÖþ$nFe €ž“rœ €¢óœúQ1å¼2í8uç Åb[(­Ïä¨à6k¦àM­/ifLÇP¦YÖvº¸. Ÿ=?ídÏ¿{÷@ÂÉ& ·b§æ/™ï¥O…ãwˆIDAT ÐrÖ €¸i~Jй®¥'@€«¦æÌærn¡ôã>„€µ‚—§sÎW±(Fw‘ý@ì_OšÞÐàƒyõ@#2âVC"Ý¢îà+!DÈo2Nö+@qKÜP¼#„šÉ Mè_µï󯼲¢Þþ6’µÆ@ }L ïDpg ÞOÃ8(“É,KD£»AÔ}šþ®Í8α>”Jšæ ñ’ã_p €WêUUñ_âUˆþO:—{®Y†AÄ£ñÝEô_pÖ!À éœs$IÆ-ë0!®F8%D¼µëî×××7Ö’­]IËÚCw‡<(€ÿÐPŸÊd2½Å9´{{ר@nëØ óðùóç7LrÔ²¬‰õ¶šÒ é¼TrÛÔ\ôйœ[H˜æÙ€üa·§‰{]…Ï6Ë ¦^Æì.–)Û~H¨?€ìSCx¿¸úɤeíœÉeîà£È[BðÀþå+îèîî9R½÷HöÄöS¿ xĽ4³vL'’4­«PÌpòìFþ*푦ùˆ5‚µÎ@ ËýKÀÙŸ'} ÉGfìœt.÷¼[L”Ïý“Fä¢tͺEÒ4¿B¥oƒÿ‰ÏHÛö©–eÍ4ˆyZ#n–?ŒÛ ó€VÅuÔÊZ7ÅJ2™ÜŒùÂMf‡¿‹j¥ŽìXž´Ý…àíÇÿ<(í8uiH­ S,˲Æ¿…à‹EW å¨t¯}KÂ4¿NâǽÞÀ(§¤sÙM±ÁZ9‚ ’J¥^ëΙ'PÅá˜ì®4ŸˆD>±Ú-ì`7ù-y(nYAnk5ñx¼Ç <Â8ÞÔZ>–ýH¬Çü;‰Ÿ#üb|‘VòáµÅ8€µÜ@`.ç2Ž}zÉ0¬ÎìÆ¹~œÑvežúK„\P¾MˆË“¦õ‡b–¤÷ ÓÜS\÷i€A2I9îfÜ4¼ ‚WÑÌCˆD¶¯”Þ`,³ÖÈ ǹA+ÙÕOd$hõŒÞJÁ±|÷à ‹|á¹±(ÖP ÛN™2!aY—ò.ü÷À5öÖ"_'qG.ÿäâLÎÙ{dнµ÷Œ@6›}†ú À?Uq›Ib®û(%þù2Š~òHÂ4/ÝvÊ”°[ÍcŽx4¾÷Šqãçƒ8þÏAo ävî¿@Tãmð Á}ÓNöŒÑvjkõ"ݸi(Ào«{Óa% —8…•“Â%'¤²ÙÀ|ˆce‘nYÖÄqqÈ¿/Ê/!ú(@ªÊ‘BðÖöBáÄ—-z½Æ®Ž Þ³–eM1(¿è+â\à#€ì‹0ÊÀžó²ã¼äÓ—Q5d2Ù¡ 'J1O`±¢(ä½?ª‹IyK(§¤rÙëjéçXã=m ƒ$¢±/CôÏCê. åe›!œ‘¸ ®s¾oÛ¶3òâhȬY³ÚÞzíc ú»á¢4Y$‹b°V€cbÙ½÷K¡í˜Ô¢Ô¢Zú:Y' Œçv/Dúx…+rñPCiµ” ã0Ïktý#ø/(gezí?fô_3Xg d¢Ó¢\R휺ðˆüA"‘Û …ÂøVH,ÛQ4ŽðPxß6€~€?›Ðßÿ“±æ…Û(Ö9йïâ¦ùE œ²F½¼IÈ¿TáZ]""’èéÙ"û³ƒ2½önVÓ0nÖ"ßÊf³¹–´7J¬“2ÈÌÍgn°zŠ3@œREÜB£y{ÜÊ ]a‚„DÄ0Ms3ƒü ˆì `;¢µ1ñ’êÜL.óh Û5ÖidÛ)S&¬èè<–ÂÓÂä¦h«¼`)€¥ ÞHà¦L1ÀDÔ¢rR?.·ÑUez3¡t”ß+¬7!ˆˆ7̓@ùf·‹uU„\¡{q+£Çë ăD4º”ú<4€ {´ûÓBÁ£BÞ¤ò7-\¼péhwh4Yo ˆˆ$MsGMøÿíÝÁJ[AÅñÿ™F-¤ ‘î$@Aº­J÷}õ |ߣk7.|fÕ·Ð,lé®b[SðêÆBáÖ™ÏűMÒf’ù~û ³9pïÌó¶È¿e:½ Þ…ëëî$&7Í Ȉª»åa lxÉt¾ Æ¡Ž0ÞÇ@÷¡ÃMçù/Ng1¤´ a Ùšª³•ö´×õƒSAÏŒÃèÑh|¬ûm¾:ð€ŒÙÊòÊó4_®šijNø ¹ãbÀ¥A!T +0)ñÉæÃá,þj^Œ^´;G î¾ïhø‰Y)YLRTR4¥()))&¥,”F¼ˆ!Ífó|œ-,®âɤ*Žkÿbè¯[Å—þé£\nêÂTÝüKÙ[-:i]Å’‰™%UÓ—F¢?·/º)ð€d•&óŒËÅ’Ñ“ß o4÷Ágææö3.ÉÈ’Ñqq|)Ó°³ÙضŸMÔ‹$³“ïߺ`›ü½ø«Ä›“~ß_¯jÆ·y'¤Õj-=m4vÌx ¼+‘><»ºÚ}¬7òfÝ Oe¼ö³‡¢IEND®B`‚ostinato-0.9/client/icons/magnifier.png000066400000000000000000000011471321315675200202720ustar00rootroot00000000000000‰PNG  IHDRóÿagAMA¯È7ŠétEXtSoftwareAdobe ImageReadyqÉe<ùIDAT8Ë¥’¿k“aÇ¿ï$F¬©š¤ƒ?èPõ*B EpÈÔ­ ‚8Twݺަƒ‹‹ÒM;TE!"ˆšAÐÆVÐD 1ÑØ &oÞçyîžs°V$y•âg¹›>÷½ãÁÿàÿjרµÖ–ˆ¥ÈÌ9b CÜ âcxfz‰ ̶´3a ÙBÃhw­#Ð2IdS±‚ÒT”À".îN:ø®0 ºZ°ÖcìÝ“„ÒTŒâœç°°ÅO‰t•E<æAkÊEQ‚•È ¬X~W±   !2ÖÔ…ï:“±Yc¾ƒ 4 ­?G -Ô[†.âžÞH’ð '=¼~ׯH÷åðÃËű¥i¦Öì”—>|C̲C>²)qWð¼ÚDçk“¹Ö6¥‚'w.ëû¸z÷½kˆJÆpQçÈ0 ñ§toIO¤^Ì>ŠÆÊ ¼­Üo?uíÍÊ‚(gOĵçÓûMí?2ŽÕåg¨VîµÏ\¯g6Wø'gh¥‚ézõéíåÊ"²ùQh­Ò}+ü‹çFãDjÞ‹%¦‚Îú•ó7×.mIs§33\¸µÞØr‚(~J¸7¡ª2Ë¡IEND®B`‚ostinato-0.9/client/icons/name.png000066400000000000000000000053751321315675200172600ustar00rootroot00000000000000‰PNG  IHDR¯Ìï®sBIT|dˆ pHYs  Þ©dètEXtSoftwarewww.inkscape.org›î< zIDATxœÕœ{ðUUÇ?ëò~òB!%ˆÅ@"Í`¤‚)iÌ2EQ(’O|MŽö0² R¥Ç€Q‰:ˆâƒBÀP‡³@Tð…"¦‚ °úcíÃÙ÷pιç>ÎëšÙs÷ÞwíïÚwŸuö^{í½®¨*¥HDÚ£€ã\*¯¸´X¡ªÛJ‚¥Ë)g æÀf`°DUßOé_ç ÅnWÕwN#ÐÃÕïPÕ7cdµÚTC"r<öw«ê‹UbµTÒ/é´­Pôsªz° Y'gêѱÀnL^Êô;T51ýùÀ{€fH€Ñ€¤á&È:X“‚ý pjBÛ)û—Æy8'{õ×$ȃ½¬­Êý1XÛ¬Gk€u/0¿Â¶÷T1~í2à€Àrà`Ì}ÀR *n‚°Fà— À{ÿÿJQê'~e ^gà寗üº€Iu ¼ê·Y=(/Ð×)Å^ k=)/Ð{ÙãÚ¾l¶¸gýþ °øhv‘žÀlÖ h 0ÇÕoSo™‘NÀÀTàWÝX-"TõQ1tÐÅå7Ó€ÇÖÀPà|l©iî‘mªºÂkÿwà’ÜÁ˜²,Ä^ª(mÈп8 Ì.ª°}-é*@€Ø8\Qfû_ÇÔOú¸üÕ@œÙ¶' TD†¿ŽöªÅž÷*U}Ããm†™cocæ›`ϯŸˆŒTÕg‹DÞ’^ÀNBÍÓ5.d|ƒûÿ øÍ™\¢M7`¿ãßtˆáàÇîs@C†þø3ò¸ üå̼AºüHμ@Ooüø 3U•3rIó Òv<Å+gæÕ33Æoxíßúñy Za³^À¼8¶‚ܘëá| KááñÞX{fÐwÉØ—¦PÞƒÀØ#¨¼ó"ýQàÚ#©¼Ølí›”óÆ äø›‡óÐ9ø¾@H·.k€!ZÁXU÷ªêTàWÕ,‘%4éäå_-0TU_)·_9’ EäóM.X¤ 0ÑßÀvíÓE䨦îëS[̼låªfªêšà)J#U} 8›°ÀÌŠÅÁ÷'ð à®n6“쫬û‡èFàA—o\ŸÀ·ÙËLTÕ}ªz Ê~Õ’žrŸ-¥"òÉ&–™“ ðsÌvèˆÙ«G‚®ÃÌO€UÀµÕ€©ê~ÌuLXCDdTð%ÀŸ§æµXrîј­˜=cx:``÷yb åçm6\ üÄ+o:•Ñ¿ŠÍ7nï¸ö»€vØÌ´ÏÕ=O†}A-ÍìEÚAh{g‹ ØÃ½¾l¤ "]1»l#t9oF©9ù¸böEyv·ºb`‘ˆ´ŠòÕ1Í~ïò=ûš¨ÿÓ°ñ˜£ªo«™yw»ºÄŒwÎ48ÆåïRÕ×k¬ªËg\ñSÀÀ0¹Ìæ©SóÒí^~dÏ­.ßX+"½xëŠÜxMÄ\@ƒ€»Ýia.$"­1å³sgy_ÏÄf'€""yõ#†|Se^øs½üÈæàèϵ–¦ªÿÿÜ ¸YIUw§Á±°^DÆÖº?yªîÎÅ–3€¯`>à¼h ¡ïôNUÝáõe¶Üƒã99ö#J.ýWU7怿ÔËŸVÀΗÁ\>UÝMH¡çݧ±^UÝŠ*¬sUm1b–ˆv˜Ro¤ªo_"ô˜\$"—ÕZŽˆ´À6j`öíOcØnòòWÕºq$"mM†r³ûÍ] „Q^UÛÙåA[½|§$&UÝ|øW=X."òéZíHU_Àfºw]ÕÍ"2&¥I%4èêò ܘEû±X銧4‘¯‡—Ïet&Z°:w*`»V°k^´ÛË·LäâŸxp1vr0 X'"ŸÎ©5#UÝœ‡.>àSÒ[e#w„z¥+ÀìÛ$jêÙ×?n ]jYÀœÛQᵦî^>ÓTUoÅ®G×þ*"¯qßjNªº øŽ+6R;ðìª(À"UݜĨª+õ®x¶ˆôMâ­íðòM¡K¯€×ΞÊS ž¼’¤ª«0ßëWÕ¸ßÙWuMª:ó¢€=ÌD¤c•°W{ùÓEäÙ´„¹îÀV€UÊ.EþsíšÈU‰H;ÌŸ ðZxÚ€ÓrØèíŠ[ÜÆ&3©êóÀ 7BŸÅNqêžTõ:ìV„>àŠŽmEäà3^Uì$+-ù3ày"Ò£ÙYÈùô[wpN¾îA^þ‰v‰9 ssø5ìj#À}•¨êà˘G`ê‡açh2ðËŽÊ}À×xù‡°£÷,i­kÓ\^Ür(x¾ÀY9àO‰ÊjMx̸èV«#=wÞñá±Þ*±–xXYŽ{s?ÎØïvߨû…–q<Œy`‚¶«Ë³ØŒ<ߎe´-÷xx˜Ç¿ž "jR°»^X h,¨ê{ÀÍN›"´Óª&™Š-ó`qIU ù/ß'‘«ÎHU߯.ӿ쪦‰È¥e@ø³nYÏGí‚•ÿ|§—Ó¾LY+Õ®8øj áçžÏTÕ÷ƒåëg„6å©::@DNÆn:½-‡mD¤™ˆ\("YÏà›{ù¼|Ò¹Úõ¾³±Uìp¡äND.ÁOªêˆŸOè ¸0"͉ü(Ž;Dä¤jEär,lµš îJ¤›}Ç*Ä,™P…°ØQsླྀAUŸˆð4ÃÂwnæŠH–èßá^~}"W’ª>]^Ùyš§·Š= ­Šªº›p"iO±íXSRÕǸbkl“Ú¯R<9ŸÐgý0^U÷Â’lDÅœà-Ê´M¦b€ÆH°}°Ùø-´IÁŒÅK×+KÚnԉ̓112α6/æ1Biž!c8VÖG°ÃÅf¯’Ï•Ê#)Ä=÷ í»”m‚™8¿ŽŒÓ”"ž˜FSɹ˜TB±Z9žh”èïH ÿša»á€ÿ1฾³(+ÉæR¯Êëp®Ï ¼þÃûz¥²<¼ï{xßÌKy]ÛFìzfÑKŠë&F]c>âïaÿá´ÛL?Œ7`(á¥â íV`ËÏ•.ÍÃ6Q»"¼ÊP°îØ=â íà.Sû²„Ö­òF”ó0åÅâ·‚‹å[¨òb¹Ã<›Õyê8V£¼Æ•ˆ*vjº ªý®{‘bfd4~'pf,vŠÐöÀÈþ‡#Aºè]æ숅º§áî,g?ÊÛ€ùbã”w¶'gJ5r"¸·x¸£óV^‡Ó‹b3"KÚƒyI‹&’8ðDrÿË0»î7Ä)šÁù]ì”np¯ªþ30YN»{ 0€ð`ãE‡‹ª>\&æI„Q"KJõÍE•|ËWjŒkODzcÿ-𠪮)§O1xmQªº0R61×ÐÞjäx¸±ÿEؤª‹RxÇaQ 7UÛw±j$¦K(þ‹©`–]M¨K©¹ÿ'óŽÂîâFIEND®B`‚ostinato-0.9/client/icons/neighbor_clear.png000066400000000000000000000014071321315675200212730ustar00rootroot00000000000000‰PNG  IHDRóÿagAMA¯È7ŠétEXtSoftwareAdobe ImageReadyqÉe<™IDAT8Ë¥SMHTQþÞ¼Ñ7?©ù36 S¢ùYµ"Qj!‚m‚ ]`®„–-ƒ¶2jQBDÐv‚¢(0œHÆšJ ç9ÎäŒoœû×¹ÏúZôàp/÷Üï»ß9ç{†R ÿóy·;L$QŒJ)»hí  ­LÒú‘Ö;‰ááaaü©€À=tö  õ†ÃaƒAè{…BkkkXXX@6›}Agçþ"ˆÇãÏ:;;û£Ñ( ÃØV6çÉd©Tê^•À~{rP 5Yæ û}–ß„”œÁðÑrø&Lß®*ÁêêªV›õþ›ñ7ëÞQÛê‚Nî|{΂£ÙLÆ-Cƒc±¨›M”BF‰àBMp/xá <¾>zÅF9ŸÂ?±…Xž…mÛðx<Õž¸˯OÔ’ÔKþ¦Cu)w24› äã0ÂÑÜt^¯Œ1w-•Jn,„€—À}—­ú(îGtJYqâûmÅ!€4PÊrUT(.'ý¡£-Ž=G¥(ÔÚ‰Þ‹‘Aª’UP´g¡bWP‘–«Ä4M—ÀU@õ?*~{ù\qÁh_Ãø»±–Ø)(çÁÖWPò´Á=P^¾êØïQÌ/…FÁûá÷ùI «¾ºÕƒJ¥ò»•%—O|þ°Qâh¸…啸ãTóÚX‘HÚdétzSÁVòóãžÓTBÛb¾ùzZ ŒuÉõ¬-Ërúu ÖVÖ.œ™™É¡†ªNüô°«W !ÛÏÏ'§§§÷‘Äk#Ôñ=wí¾\.‡r¹¬÷©”ËÆ¿~ç©©©:´SPØÜøøøÆ¯ùæ[³Lh-OEIEND®B`‚ostinato-0.9/client/icons/neighbor_resolve.png000066400000000000000000000014021321315675200216570ustar00rootroot00000000000000‰PNG  IHDRóÿagAMA¯È7ŠétEXtSoftwareAdobe ImageReadyqÉe<”IDAT8Ë…S]H“Q~¾ùéÒMÍÑ—Ö4+4\¡!Ò…DDE]”±‹2Ê«í¢.ì./ºŠ.½ ¡ Ø &A™†w2”Ôùï7²6 ·Üêû[ï9ñ‰+¥žóÞsÞçý;GÈd2VÒW -†aÔ®&ÐÖøDz‰ô B ¹¹YÇ_"øýþãtÉ#IR]YYl6 Àˆ766L&‡‹Å>’í¦Ûí^Ïbðù|ïeYfá2»‰ªª™`0˜yìíXk÷¶æ0rZN”––âât:‘ÖRNUÑÛí"EæLÓÓÓ°Ûí%%%XI.âükÉehšMÕá,,íšýì-Ïuo_Ë`SÃá`u"‰ ‘÷“Ê9qáàeèA7t¾O¥Ò—®=oZnóV‰f-,²(ŠMC >ƒ}¹$ë~LEƒXù¶ UQQ^TÁÏCáÿÈ}ßE–ÅÌ€P³8!î8²0„D|­{Û𴱠럿jsrh\ù©4v:?«]×9 ýC9Q4EòG’ÛØ<Ū“ó9_ǘA¾é¬ Ìè&É¡ªáí—!~§AhL³þÏLG“PÑ~Qã GR6¹]Q4v6ŒQ?ÎÐ8¹,²HñxôùÒéôfK.ä>äå@Póqçå j¤&­8,ª¯Çìü<ÆÆ'¼‚Çã¹Bìý.—ËVYY «Õ Ap÷Õmãä±Ú­ñ™ã\Z]…ß?:©kú#¥ÛÛÛ{„H: n‹ÅRÌþ+gP~=’ÈÒLhO›õFÿ@ÎOV{£œ`»tww’sÁ ¦†<Ñ/¥î˜êš­Ûñoÿ;¡öAMŽ«½º·óß#@#*£ìIEND®B`‚ostinato-0.9/client/icons/portgroup_add.png000066400000000000000000000014151321315675200212000ustar00rootroot00000000000000‰PNG  IHDRóÿagAMA¯È7ŠétEXtSoftwareAdobe ImageReadyqÉe<ŸIDAT8Ë}SMhQþv³I´qÍQ¨–@±ŠbÕŠ E¢Iôâ!/ÚÒƒz°ÏR<”¤­-žÔÖ‚¶©H[±”\Œ‰öEi/ ‘Ú¦?Úd»›]gÞ&_˜7»›™ïûfæ=ɲ,üo%‰.Ã0.麮yakkkìIÿH¥R^Jèðù|Ýî2h”Ó„ˆ¦¿ß‡ÁÁPþ–œL&#œ wx<LÏÌ‚ÙMÓ‚i™d¡@`š¦A™ššÒ …‚‹L•d²Ïf³¿KÆÂB–€L„ÏÔ‰o 'WT„°¸¸$Ø-þY¬ÒÆ/ĉ€ß‹þ¾gØW}H°óB£ròŽ…I$ÚíMx¶•åeÜn<*ß%â`µåç.Ø X*³r ?°²,C"“‹ÆÏ `™6cõ#âÙ*ö@fôN2Ç/&=/n3ΩÓè\Œ‰²„½8I–àP”uvá¹òöälÆÎ÷ðcÅ*oÜ.+·•‰úí%!½dØjæ æ¼“¨š"—Ïah¼WLEÈÅ:Ù¾|úó\löN`&7‹ðÉã¨ìÂðÄSÄÇ»‘×6€Ó© ¥¾FŒG× ¬®jB²Ëå¤2,´?@Íž rËÃx5ÙƒÚýÇÐýù9ª«uddèDé ¨ªZÉÓÉd2©sË}…SÚ„º½õBQÓé»èÿÐNìƒF›JRÛÚÚ®ÈùU»+UÕ‡Þ'Mšùýì÷ù+é1)•Es¸ -//cƒÃM’-¹”‹Å¶[,9{* AU=hhhܘÏ磆¥ßŠ¿ƒ‹®3»$£É×tzÌÖõËDÒÈÞöô<>\:ÿÅ{1¿“¾Y{m»Ò==pP)|…¬¾7ÿò/¥Oˆ6ôèIEND®B`‚ostinato-0.9/client/icons/portgroup_connect.png000066400000000000000000000013541321315675200221030ustar00rootroot00000000000000‰PNG  IHDRóÿagAMA¯È7ŠétEXtSoftwareAdobe ImageReadyqÉe<~IDAT•ÁKHaðÿÎ|³Î´k("¦Ò)ôС <äŃT`‡Ñ5:EPÑ%¼FêPdtJ‚"è"Ý-²Béhø~츻£ûšùž³_{؃„Bþ~1­5ãчå>JÙD…+Rq%¦µÆAÞ¤v‰”²ƒ‰j'å¢5äâb ú–cip©ðk%O 02å9\¨ ©¢^)T#‰D<†¤m‚ ‰l#à!•·mK÷¶4™àBƒI Ê%¶óâ&°¼U€(Ó ûx5¾Ô]ªæ%ÇQð%vJ!(—¹D£mb9]‚—-~y5E°ÇÈ”çH¡Þ ±zIµL+^šqNÜ@¶ÀA™€³qËe:†‚º×“9Â…úiÝe9'᯻‘èL™Ó³2jmh3“6A.Ï7 @„ÌCº ULÈ®¤cÀ"U8 çÌòï32’rèÏÆr´áÑt„ `a!„¬Ð4j Ô…œu !A¹Ä¦WA<ƒ“è¶l{Øsþ«™õàºE¬-d–üE 5ê*o GÈ$B*°¶íñbhk>K2?šÐ30wqIeWs£*â§ç'ï+ÔÔ•Bv£½ÉBH*”ƒ2 ¯(a[& û13úIEUqjnòÁ<ö ¨¹ùbæ^£c\Ž™B€„mÂ25¬„XߨE.Ý>4ûqpÿ ƒÏ¾ßMØÆ“DÜe–©±ž.agÇõ˜OÁ|þEqöû >åOÛš“Ð:ÂfÖǦ[DÁ †•cç^ÄUZ éÎßQØ)ùÛLh¤·Š(îúýÓo¯Má?Ðc+ùRF…Š*ƾ½¿žÂ!üЇâ‰ë®QIEND®B`‚ostinato-0.9/client/icons/portgroup_delete.png000066400000000000000000000014071321315675200217130ustar00rootroot00000000000000‰PNG  IHDRóÿagAMA¯È7ŠétEXtSoftwareAdobe ImageReadyqÉe<™IDAT8Ë}SKHTaþîc8]¼£‰¦Í¢•EVFXô† ,h- j)¸q„‹ˆ©qS Åv¥Y‹Š…dˆ„6£‘R$Zø˜4œÑÉÇõ¾:ç¿3öú/ç?ÿ½÷œïûÎþ_r]ÿñxüeYLÓTɃ¼°µµ5ö]Ò¿††† )¡C×õ³@ J‚ã@DSN8¬£·÷Ô¿%'‰cœ\RRR …0>1fwŽë ›À À:::jضí'A9™ìS©Ôï’1?Ÿ" ‡ßTN./¯@:½ Ø]~\Vé ãâDQ¸/ž?CUõNÁÎÿ„FåäæŽ8…I$š½Ix¶Ìâ"n_Ù-ãïÀjKOžö°TfåÀpQ1dY†D&g× à:cõö±v³{ ³z'Š¢¬39ëyðæ1ãœ6ŽÎtT”%˜ÙöH²EUóìÂs ä½ÎyŒŸÚ±”±„ Ào—ÀŒ‘M¢~oH˜^°Äju 5ƒí¨íŸ…¥cÉì ®H€œ­“mìóŸç¢6ƒÿËGÔ]jB R…•á>Œ¼}²TØðùTܺ¼C´Ç4-,/B²ßï£2\,´6cÛ™FÇbÀ›((Ô±µ¢“c ¨T×X¬¿.wPÈišáî$“IƒÔjÌ ¸9œhÊ«R[J¡?±î.´µµ5È©={÷Ô4OŸávU™Å„@Ê vàŠ- WÓðcm禺_÷«çª¨ƒðÕ¤#E"rÖ¬GÀvxC&åµýåVx4‘>*âÏzcZoggGçÝì`~«²ÜŒGUØL ©ðl pÑöŸúße,iT?³×/‰nE ÀÙÙÙ¾®®®Ž3ÝÏOqLFkcÅVa‘!ý`07‡ÀEuy€ìªËŽ Ci„PÀf|–7”}~~¾c|EVéLæd3 ‚ÑÊšËMûL¤¾ær´9ïh5&¼±ÀД‡›ô·+&]6N?”â¡©ÆðÅ7梳+Ÿh+±õ”‹ eq¼›ç=ð­ÂFAaTëPÉ–>¿ª¯¯7b± Ž×é#mK~Ûq)Lüö,p1®Ðn‡@r‡AiµÇŒÉ¹7ÐâÅ2=44”‘Ï3›žØ.n*²TZpÙ] ¶2¢ºVí ~?͸h¹¶×wkÛ±‹¦ÌôXE™©9~¦Eê@W9‰•mùçaÏÇázÖÅ^Ij™sXÛ°ÁÝñ{28Øñ§…÷ö œžõИçR.¬¯góƒAù²è6§tCäøÏ/¯oð}ÈE—${"ƒÒÎ1-ú0ÐAá?Î/d«ªoîÈ^—IEND®B`‚ostinato-0.9/client/icons/portstats_clear.png000066400000000000000000000005571321315675200215460ustar00rootroot00000000000000‰PNG  IHDRóÿa6IDATxÚcüÿÿ?%€‰"ÝÔ0€›àߟO½¿ûïï_†ÿ¿ÿ1üÿ÷áßï¿`ZÚå #A|}ØË/ÍÀÅÅÁÀ´‚O*hÀŸ¿èšqÀ! Ïð÷Û  íXE~}yÂðïï?u¢ÂàÕ ›/ÿÿþføóñ"Ð+?¾¾:ÇðéɾR9¯ w±Àø)šÍ™ÙDN°süfø÷ë7ÃßÏŸ>úöÿÿß¿@atPüÏGÕ˜;âX]T´ó×çç??>{ùóÓË7?YEý¸9deCØÿÿafÿ÷óÏRdÍ`r.üérËÿ{«ôþß^¢2—¬Ñ6èœgçVdøû뛄Jôí—$¥ƒûkµ®ý+"°™‘Àæ¬;+ÔÍñy £Ä9`à3÷èi÷Õ‹IEND®B`‚ostinato-0.9/client/icons/portstats_clear_all.png000066400000000000000000000013401321315675200223650ustar00rootroot00000000000000‰PNG  IHDRóÿagAMA¯È7ŠétEXtSoftwareAdobe ImageReadyqÉe<rIDAT¥ÁMlÓuÀáOÿýwݺ5d[ëmѰd‘pÐj€èNœ8 á1f<c49qÇ‹ÅáÀe'g”ñe[3Èëf۵ݠ]ßYÒ&¼ž'á˜–o¯T>wç#u23Ô@ÌP5ÔQCÔ5DGÍ~i1÷S{²9žÃß/îŠiénæŸa­QÆÔ01Le]#~ìÚKÕSˆ ¢Â§û¾&MÇìî’XýÄë”ïÌÐ÷êÇL¾r˜ çf—˜š ­XkD‰MuÌÅŽ¥³£¸Ô!ù2„€H’¿ óXá+"Y'ïÝ\ºú’ ÃöÁ Í Ä.vº³¨!bÄ&zöñýŸú½~hz÷pš•ÂErÃo" Í'EÊ6jÛ?£šØÂ…k«Ä‰U ر¥ÇÀÝqw¾<{ËË+e_<7æfÞ÷¹ïöyeáo›¹¾ìµ'ÁÛæÿ©ø‘S³Ó"f¬]ÿ„tgË<¥ÑSÜ«gáf‘(ÁàÊÜ m[ó¨1-›íBíËýÇ™Ø?Åå?ŠLîÌÑv~ö_>˜ÈÓ¶p·J2‚˜–ªô’?M¶g„‡å:[º¸õh UCÍym°ƒ…»Užeæ–pw6L3wU‚înªuQ‚(*FPCTø?•ÕÆÅ„»ó"þë”zÀßW5IEND®B`‚ostinato-0.9/client/icons/portstats_filter.png000066400000000000000000000006601321315675200217400ustar00rootroot00000000000000‰PNG  IHDRµú7êgAMA¯È7ŠétEXtSoftwareAdobe ImageReadyqÉe<BIDAT(Ï}‘ÍJ‚A†½¨¹ŒˆÖÝÀl Z´‰æ ñ£Ÿ ÊMQ‹m "fIRÙø³paþ–Nú™IŠo¯£„ÅáÀpÎ3ïyçL‘ÿãçY•ÑiklJ?©1ädÖÔ¢Ïè †¤¹—ÛA@møä)Ä]c€âfØn"Ë8DÝ!Ú\ dTݵX£øæ°WVò8WHë²oPÈ`™¹H°ƒwœj<Û>ZjXg`>*ÔøÂ‘u@Š@“%³/beÆP#>u›– –Å (ñ=ìF$U Té~»TXe¾ ‡[ø#“ qcZt]FŒ7‹¸àÝßøb¼¨+y4Xüàä*WÕ#âžœXõ™<1I 9; ÏDåÔg‹=µ£·mÌFõ¦Š‰_¿ùW|*Iu¤uRƒIEND®B`‚ostinato-0.9/client/icons/preferences.png000066400000000000000000000011101321315675200206200ustar00rootroot00000000000000‰PNG  IHDRóÿagAMA¯È7ŠétEXtSoftwareAdobe ImageReadyqÉe<ÚIDAT8Ë•“»kSQÇó8H)N]uA,¥ƒˆ“w“ÒjÍûazcbb6ÖêÅ&­ Qcn^b4b2e l„ x¥W„ }$çÅçûûžï9ÇX¦µt:­$“Én"‘0âñ¸ñÿÞT8•J)N†Ã!ãñ˜h4ʉîF£ù|žp8|2X,f˜Õ[­ªª–͵`0ˆßïÇçóSÄòï~¿OµZ% } º¦iôz=Ün÷ñ‘Hä‚XÞët:4 ŠÅ"…Br¹L¥RÁétv‡_ÝT'™-±lÔëušÍ&f_«ÕÐuR©„Ãá˜Øl6å ¼u]e× Ù5~lÞ¢Ýn“Ëåðz½{¦e©jØíö®ÕjU†¸iÂwáÛ|}šÂøÉç§Ëåš?Ìíþ$³¬²³&°€ÀûûÐx ËürŸû|TNÿà%•ì*4ŸÃ‡ l‹Ð;'T^ëÙ—G <»¦²½"pFªà­Œwì Ç!yÖÏhÇÝ”…Ôeø²E±üæ6äî ø ܙצ½ ‘‹P—³îJÕì:”C\DWç^ÏòÑ,<<ß4ƒú –A쬜ž Þѹð]‚ËsŸš6ÛÞ‘øéî×—IEND®B`‚ostinato-0.9/client/icons/qt.png000066400000000000000000000037651321315675200167650ustar00rootroot00000000000000‰PNG  IHDR szzô¼IDATx^Å—kŒ\eÇ眹ìîÌîÎÎtwg)½A[zã"¶Ö†´\„FK"-ˆH$øE%"1^HP>C0|P”˜&^0¬¦[JÄ(-….m—ÝÒîýÒÙí\/;—sÎ{Þ÷xvr’Æ&"ÆÿäÉ›ÉdòûÍó<™¼Gs]—ÿgøÑ4ía`70 À 'XýŸ øð»ºW¿þz¶ÎŽˆ›§Efö¼(®g<±a|_(ÃÇ è „´ß¯Û¾ý§‘ÖP€ºíà‡BÊ)¦'âÄi‘ý𨙙µÓø]ò…&þ[‡¯ÜúvWŸ±é¾ŸÄ¹vU’hK€šeã(IÕ´)V-*¦ƒ#¢îŠÔy;35 2玛™Ñ~«—„€‰26 øóµ·¶~ À`ÛUëñºšš&Þi Ѫ,’òÆ–YÛÈ»ffâ}»8_þÌß=™YÌ]À6 œI_â ÒÕ5¡`›î /# ùp0pÉ<³’7Óßfgär`ëì°ÈzÐúð Kž?f ¯`Nùð²¿P–_>ð£Gó?œžõg–.`Þš€ð.CšÏ}ݨ¦–~Œù7HI“N^«*IEND®B`‚ostinato-0.9/client/icons/refresh.png000066400000000000000000000012551321315675200177670ustar00rootroot00000000000000‰PNG  IHDRóÿagAMA¯È7ŠétEXtSoftwareAdobe ImageReadyqÉe<?IDAT8Ë“ÛKÓaÆû3bvÐ’Bº°BÖ9Ëd-Od­6_×tä¡eÎÅšþÜÜ¡ÜIͱaIF%üÀh„eµ ×aù#èÚ;¼ ïž¾]8ýa»xoÞÃó<ïóá+ ùߺñ¼q°eZ!;“”"Ð4U/(Æ/Dvèû¬•Ý]ÔtoUqÍ|ÛúÍ͸U¢!\ýËvt®¸êP¡ÝØñõëÖ¸#i)8:S.¹Î¬žåjÜ[/Uv– åloL´Äë3 kÔò&µü§Ö~jã´å·ýÒ~M™©LµGJfŒ2BÊ”‹ŒÌ˜„Zf—=çEf™QdÖŸî’wLÛ…ÈŒ#³f›d¶&ÊÆ©°uºÉlÕ“¶;îåû‰ïxLHÖŒ ɇ È È Èè~£…+É:Ò#h›hÎ#5=&¤N.eÄÜïhiL˜(B:’* ÕÎÞÆ‘îC/¯€ã78þLP(䃲,=ÿX ª' KŠ"$iï “ ,LhZÉ¥iÅ1Å¢ê¢áûfSG§s€“±×”ù©‚RIE­VEµZF¥RF«µ‡v»ólO>Ÿ%0g® ªyôûgèõzèv»0 §¢ˆ!ÇáÖï·s3tW˜æ>E,Ë8„ñä»`Û‰8lK´ŠÒõ:!0<2 .½^lƈ¢`/Èå²h4ê8âùñä+–ÇíF(´ŠXŒ·¤Ó[8Œ„hx7tïÔF>zŽÐ;B"ö‚Çéo,ûñ²ƒõµ%BÂɤˆL&…h4<[@^¤¼R.¾Ú¦SQ>,SF¶‚‰Äóý©&¢Êðÿ>Ó4>pGÁš}±ùêIEND®B`‚ostinato-0.9/client/icons/sound_none.png000066400000000000000000000006411321315675200204760ustar00rootroot00000000000000‰PNG  IHDRóÿagAMA¯È7ŠétEXtSoftwareAdobe ImageReadyqÉe<3IDAT8ËÅÓ±jÂ@pÁ·Ê’9[%S)%¦&ÆÔ¨5Z°"åzDÆMEAE!Cq™êc´kû®_ÏLÝ®¶”ÜòýîÇ]@î7ÉýàûýKJÉûÙ@øyV&žG„<‚ „áPÃA–Á Xùu±˜c¿F¯÷À†Ã“I„(a<a³Y!Žc¬×+¸Î=‚>‡¤iŠ$I°\.Aa`»Yçì¬Øí¶ð<”Rt»]ضÍÖO¨ÕL>pº¨ù| ]ס( dY†$I0Mƒ¥Ì:³Ù®ë Ñ¨£P¸€(Š »†a”ø@»ÝD«Õ`åj6²e™¢ª ›JåÍ;K¨ÞV„JEÊåA׋o'ÈqlhZg?$¶k^+)ËñGÀèŠåãÿ>Ówó ºÞÉÇjIEND®B`‚ostinato-0.9/client/icons/stream_add.png000066400000000000000000000014561321315675200204370ustar00rootroot00000000000000‰PNG  IHDRóÿagAMA¯È7ŠétEXtSoftwareAdobe ImageReadyqÉe<ÀIDAT8Ëu“ËK[Aƃ€û,º¯‹.ºjéR²pQh5‹J¡`šôAR»P4($bHjJM Ñ Š¯`L(‘ÞFã3>¯Æ4ÅÇÂG/ -–®îýzÎPÓìÀ0 w¾ßùÎ7sut7͵µ5óÒÒRjaa!EÃü¿sE›­­­²R—øüôô''';O$%ñx¼t||¼ìF‰{H¬®®®*étZ?77§âàà$Tb±˜ž@ÊÈȈ:00ÐSÈd2öýý}\\\`oo$¾šžžÖòùÂSg¹öøuùw£Ñ(B$‹. RÅ+=$ŒŽŽ‚îˆލ ŸrAd¾Ið±â‰ÿîÙô>!ŽF£-ÃÃÃÚöö¶óuÑÙ0¨ÆSØ×÷ˆå?€‡/ùþä+ü€ÁÁA;'Ï/@ t¯\.—Æ¡qßíw‘È…ñïˆgƒ øû¦GO]]j±X”††=Av¼¹¥y¥çpK5Bìþ\Sìàz Ìjµ–677—ð|jjŠÿTXîÿ4únãd•yå}!ƒ›fmm­¹ºº:UYY™ªªª2Óáš?ØöŸµƒÏýeyÛ1çB0_IEND®B`‚ostinato-0.9/client/icons/stream_delete.png000066400000000000000000000015171321315675200211470ustar00rootroot00000000000000‰PNG  IHDRóÿagAMA¯È7ŠétEXtSoftwareAdobe ImageReadyqÉe<áIDAT8ËuSKHQ½of’˜L$Q“T0f¡%´ÅÒº1³h©t(n„Bµtg© JC (.ÄîÚEèÊ,µ»n´`‰%ŠUÁ`«bCIBlbb¢Ñü43é½ 6pgÂ{÷œsÏyoXµZ…ë~ëëë•J¥_–e¸¸¸ð»\®×õ±Ë¡PÈ€8Ö‚­­­M´¿ººš2 \3"Y‹ÛíÞù‡á.}ÜÆ¦=l2cÉår¨´Z­Lkççç{XÛ~¿ßw…`kkkLÅA›ÍÆI’DJûXæR©ÅbE!Â}Ü· ®±±qpzzzŒ°=•K$ä…‘¢Ùl9Ž#慠Ãá u‘ö"‘*’sW2ƒ&“i”T)¸““ˆF£ ÑhÀn·ƒ *a¡P »“###ê ÚÀq›±D‹Å"åóyÈf³D¸‚ÿ=:Ж·«««›ÄÐ*,--e‘,˜C÷ÚÛÛ¹££#U•“É$ ÏÔÔÔRñù|´¶ˆ@}N§SB›ÒÌÌÌMŽÆ"oH¤ŽN*F£ÐNíx©‡ö1Lµ3š”ì°ÙÙÙt:ÝŒcŠ¥ŽÇF>Wè!0–÷‹w+kŸ ’ˆÁ™¶NÙÓ¶®i:µççç'¬Vë()Q`t ±XLUlIn‚)„—töÛP -ÀÏåE9»»óZ%˜››{ƒ*ã8;>>V' ¢¡±óïúÀùìèÃ_âßL7 -Ø`saù·z0bz½ž.„Ãá÷6'zGº5Ý|icÅ %dÈ>,Èþ¡oB~Ö!G}”ñ~Ú„Ù?d­5_šÕIJ1t9µÆæÔ5÷vïýÝÛ9%/œ1~¿ó<çyžs/§ª*\ôÄb±AY–c IR°··wú¢>î,ÁÚÚš {XÇÎØl¶º_ZZÊ566ZñÌ€dýýý'þ ØÀulJ`“‹Õj5 ÒëõŒÎDQL`­ƒAÿ9‚x<îaÈn·óF£‘&%±,Õj*• (ŠB„I¼·¢Þl6MNNú«£då3™ yáh¢Åbxž'ïÚ—ËEçÝmooC8V‘œ?—A4ojj¥©\¡P€t: uuuàp8@§Ói„år™ìNŒŒŒh 8 hå¶b V«ÕX*•àððñÿËúúz@[cn·û. C«°°°pˆd%Äd¹H$œN'ŸÏç¡X,j766 •Jõù|¾yšâ÷ûühœy[’ ð¨ã5 ² SSS O²È²iÒi 5 ÓõROU¡Óì‚é-/e¤”ìp@`u¿e ÝÝÝFJ×ÄSL_d¢"ƒ$3¾Ýäänuz`å×;ˆï,Õ•Þ]$Ξ†8777ÞÖÖ6JJ(°™­gÐ×õ˜ªS( B¦° F¡·æa5ýAFUzm¡Pè²=onn†££#MI&ðv> ª $&ÁßZn_½ÇRY·œŠT5Ì@Á p ôRÁææf©j­ 2“ÁrÙ†`¦‘ý)¤Ád°Â—O°œøœ+ØO-Œùž`8Y“ÉÔ“t„~×0,Q¡‚j®›oÂk!–ŽBxýãA‰¯´ÿžPkç>¦ááaZÙC+ǸÿŒÇãi¡´gggsßnÌpnÛ}søçûïøªõ Xüïk<ûx½ÞÁ\.7@+æ8.øµëÍ+¦\2WV—P哾ÏCæÂ÷—1;IEND®B`‚ostinato-0.9/client/icons/stream_edit.png000066400000000000000000000015411321315675200206270ustar00rootroot00000000000000‰PNG  IHDRóÿagAMA¯È7ŠétEXtSoftwareAdobe ImageReadyqÉe<óIDAT8Ëu“ÝO’aÆ™€[‡vZ9ÿN:0Íb6·üXLMW,õ 5!i~®šffµ4Óðc 4`Ò"ñ­( ~„¸7] ™Jf/WÏËÒÆ¦ïöìݳ=×ï¾îë~ÖQkjjŠk6›' Ãù¸ÇKØ8ÎÔ™™™d"N"âõÕÕU„B!H¥Òu•J•¤T*“e2Yê‘"î&bÚf³Q&“)E¯×S‹‹‹XXXR …"…€¨ááaZ,w'‡0 Ãï÷ƒˆ·µZmÌëõÂívC.—Ljx›8EQP«Õ‰DÂC©ZGz1F0??˜½Ëå°X,1W—ЂÑhleOOOÃjµb||===èïïg´Ûǧð ràW´Âñ: ή´!ÚIõNñù|°ÛíÐh4hjj2ñùüÌúúúÌöövÓÌÄ#PZ>6]£ÀÏUl¸¤Ð‹.ÿf1½¶¶·ËT&Abpp---™î¬Òì¦ïî{Ø[Q‚Ò‰°;÷– ÄEXdÆt0Œ÷ÈØdÄÑÙÙ„Ô§/EÜwcô®» ·¶UⳘ ×›2ð*Ji‰X\éëë‹ÌÎÎÆ!L½½½¦É¡+µ‘9è¨;RDƒEØòÖÂÓ•AßææËË˳†HÆÔJ‚Äää$˜¿^þÔÿŸ¸ѯ…øábþùE´ÕpzîD"©Šy<ž¸Ø0ÚïX è]=¢K\ü ^Ã&qò…ˆíŸÆPUU+//ÿ?Æ!“[¶A«¶\9qœºpˆ<ÑRˆÒx âU’=LÐ(SД—ð•©ØÖÕóÜdŸhÝÛµ³ûìœßuνÝêDÑ‘ý~sxk:TÃT  Ýû*&¼e¨Ý»sî`x~Š×E Ù610³ŽÇ–/è£óñ6ÀÞ«ÖOà_tϸzÖ‚ßIÒ’Õô¢ÁÔÒ84©1¨*Œ0r`0ˆ\.‡µµ5x<d2( Èd²Ë”Ü^ÀAýó‰ Þ~¼(W8…t: «Õ ©T ¥RIÖˆÇãP«Õ2Áàìöp§šÎ¾¥*‰­ÂR©Ä9µ§Òju(‹p¹\Ëå ú_®€—môý!‹Å·ÛB¡½^g²B¤ÕéÍfYB¡TßóOØctÓ9¸ÒŒFÉd ÃÀï÷ˆŽmv:ˆD"‰Dzp…ºoZEíè •ƒSìÔNP¬Ôñu÷Ÿ·a4 €›†[‡k4™LˆF£b±ø%1ä¢bíJýöÄ2n>M¡g$ëJWå1HdÓèííEÔ³Ù •J…ki‚Ÿ<ïyÄn¿­D"ŸÏÇßù_(Y8ÅF*SIEND®B`‚ostinato-0.9/client/jumpurl.h000066400000000000000000000020631321315675200163550ustar00rootroot00000000000000/* Copyright (C) 2017 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _JUMP_URL_H #define _JUMP_URL_H #include inline QString jumpUrl( QString keyword, QString source="app", QString medium="hint", QString name="help") { return QString("http://jump.ostinato.org/" + keyword + "?" + "utm_source=" + source + "&" + "utm_medium=" + medium + "&" + "utm_campaign=" + name); } #endif ostinato-0.9/client/main.cpp000066400000000000000000000053411321315675200161400ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "mainwindow.h" #include "../common/ostprotolib.h" #include "../common/protocolmanager.h" #include "../common/protocolwidgetfactory.h" #include "params.h" #include "preferences.h" #include "settings.h" #include #include #include #include #include #include extern const char* version; extern const char* revision; extern ProtocolManager *OstProtocolManager; extern ProtocolWidgetFactory *OstProtocolWidgetFactory; Params appParams; QSettings *appSettings; QMainWindow *mainWindow; int main(int argc, char* argv[]) { QApplication app(argc, argv); int exitCode; app.setApplicationName("Ostinato"); app.setOrganizationName("Ostinato"); app.setProperty("version", version); app.setProperty("revision", revision); appParams.parseCommandLine(argc, argv); OstProtocolManager = new ProtocolManager(); OstProtocolWidgetFactory = new ProtocolWidgetFactory(); /* (Portable Mode) If we have a .ini file in the same directory as the executable, we use that instead of the platform specific location and format for the settings */ QString portableIni = QCoreApplication::applicationDirPath() + "/ostinato.ini"; if (QFile::exists(portableIni)) appSettings = new QSettings(portableIni, QSettings::IniFormat); else appSettings = new QSettings(); OstProtoLib::setExternalApplicationPaths( appSettings->value(kTsharkPathKey, kTsharkPathDefaultValue).toString(), appSettings->value(kGzipPathKey, kGzipPathDefaultValue).toString(), appSettings->value(kDiffPathKey, kDiffPathDefaultValue).toString(), appSettings->value(kAwkPathKey, kAwkPathDefaultValue).toString()); Preferences::initDefaults(); qsrand(QDateTime::currentDateTime().toTime_t()); mainWindow = new MainWindow; mainWindow->show(); exitCode = app.exec(); delete mainWindow; delete appSettings; delete OstProtocolManager; google::protobuf::ShutdownProtobufLibrary(); return exitCode; } ostinato-0.9/client/mainwindow.cpp000066400000000000000000000413331321315675200173710ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "mainwindow.h" #if 0 #include "dbgthread.h" #endif #include "jumpurl.h" #include "params.h" #include "portgrouplist.h" #include "portstatswindow.h" #include "portswindow.h" #include "preferences.h" #include "sessionfileformat.h" #include "settings.h" #include "ui_about.h" #include "updater.h" #include "fileformat.pb.h" #include #include #include #include #include #include #include #include #ifdef Q_OS_WIN32 #define WIN32_NO_STATUS #include #undef WIN32_NO_STATUS #include #endif extern const char* version; extern const char* revision; PortGroupList *pgl; MainWindow::MainWindow(QWidget *parent) : QMainWindow (parent) { Updater *updater = new Updater(); if (appParams.optLocalDrone()) { QString serverApp = QCoreApplication::applicationDirPath(); #ifdef Q_OS_MAC // applicationDirPath() does not return bundle, // but executable inside bundle serverApp.replace("Ostinato.app", "drone.app"); #endif #ifdef Q_OS_WIN32 serverApp.append("/drone.exe"); #else serverApp.append("/drone"); #endif qDebug("staring local server - %s", qPrintable(serverApp)); localServer_ = new QProcess(this); connect(localServer_, SIGNAL(finished(int, QProcess::ExitStatus)), SLOT(onLocalServerFinished(int, QProcess::ExitStatus))); connect(localServer_, SIGNAL(error(QProcess::ProcessError)), SLOT(onLocalServerError(QProcess::ProcessError))); localServer_->setProcessChannelMode(QProcess::ForwardedChannels); localServer_->start(serverApp, QStringList()); QTimer::singleShot(5000, this, SLOT(stopLocalServerMonitor())); } else localServer_ = NULL; pgl = new PortGroupList; portsWindow = new PortsWindow(pgl, this); statsWindow = new PortStatsWindow(pgl, this); portsDock = new QDockWidget(tr("Ports and Streams"), this); portsDock->setObjectName("portsDock"); portsDock->setFeatures( portsDock->features() & ~QDockWidget::DockWidgetClosable); statsDock = new QDockWidget(tr("Port Statistics"), this); statsDock->setObjectName("statsDock"); statsDock->setFeatures( statsDock->features() & ~QDockWidget::DockWidgetClosable); setupUi(this); menuFile->insertActions(menuFile->actions().at(3), portsWindow->actions()); statsDock->setWidget(statsWindow); addDockWidget(Qt::BottomDockWidgetArea, statsDock); portsDock->setWidget(portsWindow); addDockWidget(Qt::TopDockWidgetArea, portsDock); // Save the default window geometry and layout ... defaultGeometry_ = geometry(); defaultLayout_ = saveState(0); // ... before restoring the last used settings QRect geom = appSettings->value(kApplicationWindowGeometryKey).toRect(); if (!geom.isNull()) setGeometry(geom); QByteArray layout = appSettings->value(kApplicationWindowLayout) .toByteArray(); if (layout.size()) restoreState(layout, 0); connect(actionFileExit, SIGNAL(triggered()), this, SLOT(close())); connect(actionAboutQt, SIGNAL(triggered()), qApp, SLOT(aboutQt())); connect(actionViewShowMyReservedPortsOnly, SIGNAL(toggled(bool)), portsWindow, SLOT(showMyReservedPortsOnly(bool))); connect(actionViewShowMyReservedPortsOnly, SIGNAL(toggled(bool)), statsWindow, SLOT(showMyReservedPortsOnly(bool))); connect(updater, SIGNAL(newVersionAvailable(QString)), this, SLOT(onNewVersion(QString))); updater->checkForNewVersion(); if (appParams.argumentCount()) { QString fileName = appParams.argument(0); if (QFile::exists(fileName)) on_actionOpenSession_triggered(fileName); else QMessageBox::information(NULL, qApp->applicationName(), QString("File not found: " + fileName)); } #if 0 { DbgThread *dbg = new DbgThread(pgl); dbg->start(); } #endif } MainWindow::~MainWindow() { stopLocalServerMonitor(); if (localServer_) { #ifdef Q_OS_WIN32 //! \todo - find a way to terminate cleanly localServer_->kill(); #else localServer_->terminate(); #endif } delete pgl; // We don't want to save state for Stream Stats Docks - so delete them QList streamStatsDocks = findChildren("streamStatsDock"); foreach(QDockWidget *dock, streamStatsDocks) delete dock; Q_ASSERT(findChildren("streamStatsDock").size() == 0); QByteArray layout = saveState(0); appSettings->setValue(kApplicationWindowLayout, layout); appSettings->setValue(kApplicationWindowGeometryKey, geometry()); if (localServer_) { localServer_->waitForFinished(); delete localServer_; } } void MainWindow::on_actionOpenSession_triggered(QString fileName) { qDebug("Open Session Action (%s)", qPrintable(fileName)); static QString dirName; QStringList fileTypes = SessionFileFormat::supportedFileTypes( SessionFileFormat::kOpenFile); QString fileType; QString errorStr; bool ret; if (!fileName.isEmpty()) goto _skip_prompt; if (portsWindow->portGroupCount()) { if (QMessageBox::question(this, tr("Open Session"), tr("Existing session will be lost. Proceed?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::No) == QMessageBox::No) goto _exit; } if (fileTypes.size()) fileType = fileTypes.at(0); fileName = QFileDialog::getOpenFileName(this, tr("Open Session"), dirName, fileTypes.join(";;"), &fileType); if (fileName.isEmpty()) goto _exit; _skip_prompt: ret = openSession(fileName, errorStr); if (!ret || !errorStr.isEmpty()) { QMessageBox msgBox(this); QStringList str = errorStr.split("\n\n\n\n"); msgBox.setIcon(ret ? QMessageBox::Warning : QMessageBox::Critical); msgBox.setWindowTitle(qApp->applicationName()); msgBox.setText(str.at(0)); if (str.size() > 1) msgBox.setDetailedText(str.at(1)); msgBox.setStandardButtons(QMessageBox::Ok); msgBox.exec(); } dirName = QFileInfo(fileName).absolutePath(); _exit: return; } void MainWindow::on_actionSaveSession_triggered() { qDebug("Save Session Action"); static QString fileName; QStringList fileTypes = SessionFileFormat::supportedFileTypes( SessionFileFormat::kSaveFile); QString fileType; QString errorStr; QFileDialog::Options options; if (portsWindow->reservedPortCount()) { QString myself = appSettings->value(kUserKey, kUserDefaultValue) .toString(); if (QMessageBox::question(this, tr("Save Session"), QString("Some ports are reserved!\n\nOnly ports reserved by %1 will be saved. Proceed?").arg(myself), QMessageBox::Yes | QMessageBox::No, QMessageBox::No) == QMessageBox::No) goto _exit; } // On Mac OS with Native Dialog, getSaveFileName() ignores fileType. // Although currently there's only one supported file type, we may // have more in the future #if defined(Q_OS_MAC) options |= QFileDialog::DontUseNativeDialog; #endif if (fileTypes.size()) fileType = fileTypes.at(0); _retry: fileName = QFileDialog::getSaveFileName(this, tr("Save Session"), fileName, fileTypes.join(";;"), &fileType, options); if (fileName.isEmpty()) goto _exit; if (QFileInfo(fileName).suffix().isEmpty()) { QString fileExt = fileType.section(QRegExp("[\\*\\)]"), 1, 1); qDebug("Adding extension '%s' to '%s'", qPrintable(fileExt), qPrintable(fileName)); fileName.append(fileExt); if (QFileInfo(fileName).exists()) { if (QMessageBox::warning(this, tr("Overwrite File?"), QString("The file \"%1\" already exists.\n\n" "Do you wish to overwrite it?") .arg(QFileInfo(fileName).fileName()), QMessageBox::Yes|QMessageBox::No, QMessageBox::No) != QMessageBox::Yes) goto _retry; } } if (!saveSession(fileName, fileType, errorStr)) QMessageBox::critical(this, qApp->applicationName(), errorStr); else if (!errorStr.isEmpty()) QMessageBox::warning(this, qApp->applicationName(), errorStr); fileName = QFileInfo(fileName).absolutePath(); _exit: return; } void MainWindow::on_actionPreferences_triggered() { Preferences *preferences = new Preferences(); preferences->exec(); delete preferences; } void MainWindow::on_actionViewRestoreDefaults_triggered() { // Use the saved default geometry/layout, however keep the // window location same defaultGeometry_.moveTo(geometry().topLeft()); setGeometry(defaultGeometry_); restoreState(defaultLayout_, 0); // Add streamStats as tabs QList streamStatsDocks = findChildren("streamStatsDock"); foreach(QDockWidget *dock, streamStatsDocks) { dock->setFloating(false); tabifyDockWidget(statsDock, dock); } statsDock->show(); statsDock->raise(); actionViewShowMyReservedPortsOnly->setChecked(false); } void MainWindow::on_actionHelpOnline_triggered() { QDesktopServices::openUrl(QUrl(jumpUrl("help", "app", "menu"))); } void MainWindow::on_actionHelpAbout_triggered() { QDialog *aboutDialog = new QDialog; Ui::About about; about.setupUi(aboutDialog); about.versionLabel->setText( QString("Version: %1 Revision: %2").arg(version).arg(revision)); aboutDialog->exec(); delete aboutDialog; } void MainWindow::stopLocalServerMonitor() { // We are only interested in startup errors disconnect(localServer_, SIGNAL(error(QProcess::ProcessError)), this, SLOT(onLocalServerError(QProcess::ProcessError))); disconnect(localServer_, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(onLocalServerFinished(int, QProcess::ExitStatus))); } void MainWindow::onLocalServerFinished(int exitCode, QProcess::ExitStatus /*exitStatus*/) { if (exitCode) reportLocalServerError(); } void MainWindow::onLocalServerError(QProcess::ProcessError /*error*/) { reportLocalServerError(); } void MainWindow::reportLocalServerError() { QMessageBox msgBox(this); msgBox.setIcon(QMessageBox::Warning); msgBox.setTextFormat(Qt::RichText); msgBox.setStyleSheet("messagebox-text-interaction-flags: 5"); // mouse copy QString errorStr = tr("

Failed to start the local drone agent - " "error 0x%1, exit status 0x%2 exit code 0x%3.

") .arg(localServer_->error(), 0, 16) .arg(localServer_->exitStatus(), 0, 16) .arg(localServer_->exitCode(), 0, 16); if (localServer_->error() == QProcess::FailedToStart) errorStr.append(tr("

The drone program does not exist at %1 or you " "don't have sufficient permissions to execute it." "

") .arg(QCoreApplication::applicationDirPath())); if (localServer_->exitCode() == 1) errorStr.append(tr("

The drone program was not able to bind to " "TCP port 7878 - maybe a drone process is already " "running?

")); #ifdef Q_OS_WIN32 if (localServer_->exitCode() == STATUS_DLL_NOT_FOUND) errorStr.append(tr("

This is most likely because Packet.dll " "was not found - make sure you have " "WinPcap" " installed.

") .arg(jumpUrl("winpcap"))); #endif msgBox.setText(errorStr); msgBox.setInformativeText(tr("Try running drone directly.")); msgBox.exec(); QMessageBox::information(this, QString(), tr("

If you have remote drone agents running, you can still add " "and connect to them.

" "

If you don't want to start the local drone agent at startup, " "provide the -s option to Ostinato on the command line.

" "

Learn about Ostinato's Controller-Agent " "architecture

").arg(jumpUrl("arch"))); } void MainWindow::onNewVersion(QString newVersion) { QLabel *msg = new QLabel(tr("New Ostinato version %1 available. Visit " "ostinato.org to download") .arg(newVersion) .arg(jumpUrl("download", "app", "status", "update"))); msg->setOpenExternalLinks(true); statusBar()->addPermanentWidget(msg); } //! Returns true on success (or user cancel) and false on failure bool MainWindow::openSession(QString fileName, QString &error) { bool ret = false; QDialog *optDialog; QProgressDialog progress("Opening Session", "Cancel", 0, 0, this); OstProto::SessionContent session; SessionFileFormat *fmt = SessionFileFormat::fileFormatFromFile(fileName); if (fmt == NULL) { error = tr("Unknown session file format"); goto _fail; } if ((optDialog = fmt->openOptionsDialog())) { int ret; optDialog->setParent(this, Qt::Dialog); ret = optDialog->exec(); optDialog->setParent(0, Qt::Dialog); if (ret == QDialog::Rejected) goto _user_opt_cancel; } progress.setAutoReset(false); progress.setAutoClose(false); progress.setMinimumDuration(0); progress.show(); setDisabled(true); progress.setEnabled(true); // to override the mainWindow disable connect(fmt, SIGNAL(status(QString)),&progress,SLOT(setLabelText(QString))); connect(fmt, SIGNAL(target(int)), &progress, SLOT(setMaximum(int))); connect(fmt, SIGNAL(progress(int)), &progress, SLOT(setValue(int))); connect(&progress, SIGNAL(canceled()), fmt, SLOT(cancel())); fmt->openAsync(fileName, session, error); qDebug("after open async"); while (!fmt->isFinished()) qApp->processEvents(); qDebug("wait over for async operation"); if (!fmt->result()) goto _fail; // process any remaining events posted from the thread for (int i = 0; i < 10; i++) qApp->processEvents(); // XXX: user can't cancel operation from here on! progress.close(); portsWindow->openSession(&session, error); _user_opt_cancel: ret = true; _fail: progress.close(); setEnabled(true); return ret; } bool MainWindow::saveSession(QString fileName, QString fileType, QString &error) { bool ret = false; QProgressDialog progress("Saving Session", "Cancel", 0, 0, this); SessionFileFormat *fmt = SessionFileFormat::fileFormatFromType(fileType); OstProto::SessionContent session; if (fmt == NULL) goto _fail; progress.setAutoReset(false); progress.setAutoClose(false); progress.setMinimumDuration(0); progress.show(); setDisabled(true); progress.setEnabled(true); // to override the mainWindow disable // Fill in session ret = portsWindow->saveSession(&session, error, &progress); if (!ret) goto _user_cancel; connect(fmt, SIGNAL(status(QString)),&progress,SLOT(setLabelText(QString))); connect(fmt, SIGNAL(target(int)), &progress, SLOT(setMaximum(int))); connect(fmt, SIGNAL(progress(int)), &progress, SLOT(setValue(int))); connect(&progress, SIGNAL(canceled()), fmt, SLOT(cancel())); fmt->saveAsync(session, fileName, error); qDebug("after save async"); while (!fmt->isFinished()) qApp->processEvents(); qDebug("wait over for async operation"); ret = fmt->result(); goto _exit; _user_cancel: goto _exit; _fail: error = QString("Unsupported File Type - %1").arg(fileType); goto _exit; _exit: progress.close(); setEnabled(true); return ret; } ostinato-0.9/client/mainwindow.h000066400000000000000000000036641321315675200170430ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _MAIN_WINDOW_H #define _MAIN_WINDOW_H #include "ui_mainwindow.h" #include #include class PortsWindow; class PortStatsWindow; class QDockWidget; class QProcess; class MainWindow : public QMainWindow, private Ui::MainWindow { Q_OBJECT private: bool openSession(QString fileName, QString &error); bool saveSession(QString fileName, QString fileType, QString &error); QProcess *localServer_; PortsWindow *portsWindow; PortStatsWindow *statsWindow; QDockWidget *portsDock; QDockWidget *statsDock; QRect defaultGeometry_; QByteArray defaultLayout_; public: MainWindow(QWidget *parent = 0); ~MainWindow(); public slots: void on_actionOpenSession_triggered(QString fileName = QString()); void on_actionSaveSession_triggered(); void on_actionPreferences_triggered(); void on_actionViewRestoreDefaults_triggered(); void on_actionHelpOnline_triggered(); void on_actionHelpAbout_triggered(); private slots: void stopLocalServerMonitor(); void onLocalServerFinished(int exitCode, QProcess::ExitStatus exitStatus); void onLocalServerError(QProcess::ProcessError error); void reportLocalServerError(); void onNewVersion(QString version); }; #endif ostinato-0.9/client/mainwindow.ui000066400000000000000000000065431321315675200172300ustar00rootroot00000000000000 MainWindow 0 0 1024 600 Ostinato :/icons/about.png &File &Help &View :/icons/exit.png E&xit :/icons/about.png &About :/icons/preferences.png Preferences :/icons/qt.png About &Qt true Show &My Reserved Ports Only Restore &Defaults Open Session ... Save Session ... :/icons/help.png Help (Online) ostinato-0.9/client/modeltest.cpp000066400000000000000000000471701321315675200172220ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2007 Trolltech ASA. All rights reserved. ** ** This file is part of the Qt Concurrent project on Trolltech Labs. ** ** This file may be used under the terms of the GNU General Public ** License version 2.0 as published by the Free Software Foundation ** and appearing in the file LICENSE.GPL included in the packaging of ** this file. Please review the following information to ensure GNU ** General Public Licensing requirements will be met: ** http://www.trolltech.com/products/qt/opensource.html ** ** If you are unsure which license is appropriate for your use, please ** review the following information: ** http://www.trolltech.com/products/qt/licensing.html or contact the ** sales department at sales@trolltech.com. ** ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ** ****************************************************************************/ #include #include "modeltest.h" Q_DECLARE_METATYPE(QModelIndex) /*! Connect to all of the models signals. Whenever anything happens recheck everything. */ ModelTest::ModelTest(QAbstractItemModel *_model, QObject *parent) : QObject(parent), model(_model), fetchingMore(false) { Q_ASSERT(model); connect(model, SIGNAL(columnsAboutToBeInserted(const QModelIndex &, int, int)), this, SLOT(runAllTests())); connect(model, SIGNAL(columnsAboutToBeRemoved(const QModelIndex &, int, int)), this, SLOT(runAllTests())); connect(model, SIGNAL(columnsInserted(const QModelIndex &, int, int)), this, SLOT(runAllTests())); connect(model, SIGNAL(columnsRemoved(const QModelIndex &, int, int)), this, SLOT(runAllTests())); connect(model, SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &)), this, SLOT(runAllTests())); connect(model, SIGNAL(headerDataChanged(Qt::Orientation, int, int)), this, SLOT(runAllTests())); connect(model, SIGNAL(layoutAboutToBeChanged ()), this, SLOT(runAllTests())); connect(model, SIGNAL(layoutChanged ()), this, SLOT(runAllTests())); connect(model, SIGNAL(modelReset ()), this, SLOT(runAllTests())); connect(model, SIGNAL(rowsAboutToBeInserted(const QModelIndex &, int, int)), this, SLOT(runAllTests())); connect(model, SIGNAL(rowsAboutToBeRemoved(const QModelIndex &, int, int)), this, SLOT(runAllTests())); connect(model, SIGNAL(rowsInserted(const QModelIndex &, int, int)), this, SLOT(runAllTests())); connect(model, SIGNAL(rowsRemoved(const QModelIndex &, int, int)), this, SLOT(runAllTests())); // Special checks for inserting/removing connect(model, SIGNAL(layoutAboutToBeChanged()), this, SLOT(layoutAboutToBeChanged())); connect(model, SIGNAL(layoutChanged()), this, SLOT(layoutChanged())); connect(model, SIGNAL(rowsAboutToBeInserted(const QModelIndex &, int, int)), this, SLOT(rowsAboutToBeInserted(const QModelIndex &, int, int))); connect(model, SIGNAL(rowsAboutToBeRemoved(const QModelIndex &, int, int)), this, SLOT(rowsAboutToBeRemoved(const QModelIndex &, int, int))); connect(model, SIGNAL(rowsInserted(const QModelIndex &, int, int)), this, SLOT(rowsInserted(const QModelIndex &, int, int))); connect(model, SIGNAL(rowsRemoved(const QModelIndex &, int, int)), this, SLOT(rowsRemoved(const QModelIndex &, int, int))); runAllTests(); } void ModelTest::runAllTests() { if (fetchingMore) return; nonDestructiveBasicTest(); rowCount(); columnCount(); hasIndex(); index(); parent(); data(); } /*! nonDestructiveBasicTest tries to call a number of the basic functions (not all) to make sure the model doesn't outright segfault, testing the functions that makes sense. */ void ModelTest::nonDestructiveBasicTest() { Q_ASSERT(model->buddy(QModelIndex()) == QModelIndex()); model->canFetchMore(QModelIndex()); Q_ASSERT(model->columnCount(QModelIndex()) >= 0); Q_ASSERT(model->data(QModelIndex()) == QVariant()); fetchingMore = true; model->fetchMore(QModelIndex()); fetchingMore = false; Qt::ItemFlags flags = model->flags(QModelIndex()); Q_ASSERT(flags == Qt::ItemIsDropEnabled || flags == 0); model->hasChildren(QModelIndex()); model->hasIndex(0, 0); model->headerData(0, Qt::Horizontal); model->index(0, 0); Q_ASSERT(model->index(-1, -1) == QModelIndex()); model->itemData(QModelIndex()); QVariant cache; model->match(QModelIndex(), -1, cache); model->mimeTypes(); Q_ASSERT(model->parent(QModelIndex()) == QModelIndex()); Q_ASSERT(model->rowCount() >= 0); QVariant variant; model->setData(QModelIndex(), variant, -1); model->setHeaderData(-1, Qt::Horizontal, QVariant()); model->setHeaderData(0, Qt::Horizontal, QVariant()); model->setHeaderData(999999, Qt::Horizontal, QVariant()); QMap roles; model->sibling(0, 0, QModelIndex()); model->span(QModelIndex()); model->supportedDropActions(); } /*! Tests model's implementation of QAbstractItemModel::rowCount() and hasChildren() Models that are dynamically populated are not as fully tested here. */ void ModelTest::rowCount() { // check top row QModelIndex topIndex = model->index(0, 0, QModelIndex()); int rows = model->rowCount(topIndex); Q_ASSERT(rows >= 0); if (rows > 0) Q_ASSERT(model->hasChildren(topIndex) == true); QModelIndex secondLevelIndex = model->index(0, 0, topIndex); if (secondLevelIndex.isValid()) { // not the top level // check a row count where parent is valid rows = model->rowCount(secondLevelIndex); Q_ASSERT(rows >= 0); if (rows > 0) Q_ASSERT(model->hasChildren(secondLevelIndex) == true); } // The models rowCount() is tested more extensively in checkChildren(), // but this catches the big mistakes } /*! Tests model's implementation of QAbstractItemModel::columnCount() and hasChildren() */ void ModelTest::columnCount() { // check top row QModelIndex topIndex = model->index(0, 0, QModelIndex()); Q_ASSERT(model->columnCount(topIndex) >= 0); // check a column count where parent is valid QModelIndex childIndex = model->index(0, 0, topIndex); if (childIndex.isValid()) Q_ASSERT(model->columnCount(childIndex) >= 0); // columnCount() is tested more extensively in checkChildren(), // but this catches the big mistakes } /*! Tests model's implementation of QAbstractItemModel::hasIndex() */ void ModelTest::hasIndex() { // Make sure that invalid values returns an invalid index Q_ASSERT(model->hasIndex(-2, -2) == false); Q_ASSERT(model->hasIndex(-2, 0) == false); Q_ASSERT(model->hasIndex(0, -2) == false); int rows = model->rowCount(); int columns = model->columnCount(); // check out of bounds Q_ASSERT(model->hasIndex(rows, columns) == false); Q_ASSERT(model->hasIndex(rows + 1, columns + 1) == false); if (rows > 0) Q_ASSERT(model->hasIndex(0, 0) == true); // hasIndex() is tested more extensively in checkChildren(), // but this catches the big mistakes } /*! Tests model's implementation of QAbstractItemModel::index() */ void ModelTest::index() { // Make sure that invalid values returns an invalid index Q_ASSERT(model->index(-2, -2) == QModelIndex()); Q_ASSERT(model->index(-2, 0) == QModelIndex()); Q_ASSERT(model->index(0, -2) == QModelIndex()); int rows = model->rowCount(); int columns = model->columnCount(); if (rows == 0) return; // Catch off by one errors Q_ASSERT(model->index(rows, columns) == QModelIndex()); Q_ASSERT(model->index(0, 0).isValid() == true); // Make sure that the same index is *always* returned QModelIndex a = model->index(0, 0); QModelIndex b = model->index(0, 0); Q_ASSERT(a == b); // index() is tested more extensively in checkChildren(), // but this catches the big mistakes } /*! Tests model's implementation of QAbstractItemModel::parent() */ void ModelTest::parent() { // Make sure the model wont crash and will return an invalid QModelIndex // when asked for the parent of an invalid index. Q_ASSERT(model->parent(QModelIndex()) == QModelIndex()); if (model->rowCount() == 0) return; // Column 0 | Column 1 | // QModelIndex() | | // \- topIndex | topIndex1 | // \- childIndex | childIndex1 | // Common error test #1, make sure that a top level index has a parent // that is a invalid QModelIndex. QModelIndex topIndex = model->index(0, 0, QModelIndex()); Q_ASSERT(model->parent(topIndex) == QModelIndex()); // Common error test #2, make sure that a second level index has a parent // that is the first level index. if (model->rowCount(topIndex) > 0) { QModelIndex childIndex = model->index(0, 0, topIndex); qDebug("topIndex RCI %x %x %llx", topIndex.row(), topIndex.column(), topIndex.internalId()); qDebug("topIndex I %llx", topIndex.internalId()); qDebug("childIndex RCI %x %x %llx", childIndex.row(), childIndex.column(), childIndex.internalId()); Q_ASSERT(model->parent(childIndex) == topIndex); } // Common error test #3, the second column should NOT have the same children // as the first column in a row. // Usually the second column shouldn't have children. QModelIndex topIndex1 = model->index(0, 1, QModelIndex()); if (model->rowCount(topIndex1) > 0) { QModelIndex childIndex = model->index(0, 0, topIndex); QModelIndex childIndex1 = model->index(0, 0, topIndex1); Q_ASSERT(childIndex != childIndex1); } // Full test, walk n levels deep through the model making sure that all // parent's children correctly specify their parent. checkChildren(QModelIndex()); } /*! Called from the parent() test. A model that returns an index of parent X should also return X when asking for the parent of the index. This recursive function does pretty extensive testing on the whole model in an effort to catch edge cases. This function assumes that rowCount(), columnCount() and index() already work. If they have a bug it will point it out, but the above tests should have already found the basic bugs because it is easier to figure out the problem in those tests then this one. */ void ModelTest::checkChildren(const QModelIndex &parent, int currentDepth) { // First just try walking back up the tree. QModelIndex p = parent; while (p.isValid()) p = p.parent(); // For models that are dynamically populated if (model->canFetchMore(parent)) { fetchingMore = true; model->fetchMore(parent); fetchingMore = false; } int rows = model->rowCount(parent); int columns = model->columnCount(parent); if (rows > 0) Q_ASSERT(model->hasChildren(parent)); // Some further testing against rows(), columns(), and hasChildren() Q_ASSERT(rows >= 0); Q_ASSERT(columns >= 0); if (rows > 0) Q_ASSERT(model->hasChildren(parent) == true); //qDebug() << "parent:" << model->data(parent).toString() << "rows:" << rows // << "columns:" << columns << "parent column:" << parent.column(); Q_ASSERT(model->hasIndex(rows + 1, 0, parent) == false); for (int r = 0; r < rows; ++r) { if (model->canFetchMore(parent)) { fetchingMore = true; model->fetchMore(parent); fetchingMore = false; } Q_ASSERT(model->hasIndex(r, columns + 1, parent) == false); for (int c = 0; c < columns; ++c) { Q_ASSERT(model->hasIndex(r, c, parent) == true); QModelIndex index = model->index(r, c, parent); // rowCount() and columnCount() said that it existed... Q_ASSERT(index.isValid() == true); // index() should always return the same index when called twice in a row QModelIndex modifiedIndex = model->index(r, c, parent); Q_ASSERT(index == modifiedIndex); // Make sure we get the same index if we request it twice in a row QModelIndex a = model->index(r, c, parent); QModelIndex b = model->index(r, c, parent); Q_ASSERT(a == b); // Some basic checking on the index that is returned Q_ASSERT(index.model() == model); Q_ASSERT(index.row() == r); Q_ASSERT(index.column() == c); // While you can technically return a QVariant usually this is a sign // of an bug in data() Disable if this really is ok in your model. //Q_ASSERT(model->data(index, Qt::DisplayRole).isValid() == true); // If the next test fails here is some somewhat useful debug you play with. /* if (model->parent(index) != parent) { qDebug() << r << c << currentDepth << model->data(index).toString() << model->data(parent).toString(); qDebug() << index << parent << model->parent(index); // And a view that you can even use to show the model. //QTreeView view; //view.setModel(model); //view.show(); }*/ // Check that we can get back our real parent. QModelIndex p = model->parent(index); //qDebug() << "child:" << index; //qDebug() << p; //qDebug() << parent; Q_ASSERT(model->parent(index) == parent); // recursively go down the children if (model->hasChildren(index) && currentDepth < 10 ) { //qDebug() << r << c << "has children" << model->rowCount(index); checkChildren(index, ++currentDepth); }/* else { if (currentDepth >= 10) qDebug() << "checked 10 deep"; };*/ // make sure that after testing the children that the index doesn't change. QModelIndex newerIndex = model->index(r, c, parent); Q_ASSERT(index == newerIndex); } } } /*! Tests model's implementation of QAbstractItemModel::data() */ void ModelTest::data() { // Invalid index should return an invalid qvariant Q_ASSERT(!model->data(QModelIndex()).isValid()); if (model->rowCount() == 0) return; // A valid index should have a valid QVariant data Q_ASSERT(model->index(0, 0).isValid()); // shouldn't be able to set data on an invalid index Q_ASSERT(model->setData(QModelIndex(), QLatin1String("foo"), Qt::DisplayRole) == false); // General Purpose roles that should return a QString QVariant variant = model->data(model->index(0, 0), Qt::ToolTipRole); if (variant.isValid()) { Q_ASSERT(qVariantCanConvert(variant)); } variant = model->data(model->index(0, 0), Qt::StatusTipRole); if (variant.isValid()) { Q_ASSERT(qVariantCanConvert(variant)); } variant = model->data(model->index(0, 0), Qt::WhatsThisRole); if (variant.isValid()) { Q_ASSERT(qVariantCanConvert(variant)); } // General Purpose roles that should return a QSize variant = model->data(model->index(0, 0), Qt::SizeHintRole); if (variant.isValid()) { Q_ASSERT(qVariantCanConvert(variant)); } // General Purpose roles that should return a QFont QVariant fontVariant = model->data(model->index(0, 0), Qt::FontRole); if (fontVariant.isValid()) { Q_ASSERT(qVariantCanConvert(fontVariant)); } // Check that the alignment is one we know about QVariant textAlignmentVariant = model->data(model->index(0, 0), Qt::TextAlignmentRole); if (textAlignmentVariant.isValid()) { int alignment = textAlignmentVariant.toInt(); Q_ASSERT(alignment == Qt::AlignLeft || alignment == Qt::AlignRight || alignment == Qt::AlignHCenter || alignment == Qt::AlignJustify || alignment == Qt::AlignTop || alignment == Qt::AlignBottom || alignment == Qt::AlignVCenter || alignment == Qt::AlignCenter || alignment == Qt::AlignAbsolute || alignment == Qt::AlignLeading || alignment == Qt::AlignTrailing); } // General Purpose roles that should return a QColor QVariant colorVariant = model->data(model->index(0, 0), Qt::BackgroundColorRole); if (colorVariant.isValid()) { Q_ASSERT(qVariantCanConvert(colorVariant)); } colorVariant = model->data(model->index(0, 0), Qt::TextColorRole); if (colorVariant.isValid()) { Q_ASSERT(qVariantCanConvert(colorVariant)); } // Check that the "check state" is one we know about. QVariant checkStateVariant = model->data(model->index(0, 0), Qt::CheckStateRole); if (checkStateVariant.isValid()) { int state = checkStateVariant.toInt(); Q_ASSERT(state == Qt::Unchecked || state == Qt::PartiallyChecked || state == Qt::Checked); } } /*! Store what is about to be inserted to make sure it actually happens \sa rowsInserted() */ void ModelTest::rowsAboutToBeInserted(const QModelIndex &parent, int start, int end) { Q_UNUSED(end); Changing c; c.parent = parent; c.oldSize = model->rowCount(parent); c.last = model->data(model->index(start - 1, 0, parent)); c.next = model->data(model->index(start, 0, parent)); insert.push(c); } /*! Confirm that what was said was going to happen actually did \sa rowsAboutToBeInserted() */ void ModelTest::rowsInserted(const QModelIndex & parent, int start, int end) { Changing c = insert.pop(); Q_ASSERT(c.parent == parent); Q_ASSERT(c.oldSize + (end - start + 1) == model->rowCount(parent)); Q_ASSERT(c.last == model->data(model->index(start - 1, 0, c.parent))); /* if (c.next != model->data(model->index(end + 1, 0, c.parent))) { qDebug() << start << end; for (int i=0; i < model->rowCount(); ++i) qDebug() << model->index(i, 0).data().toString(); qDebug() << c.next << model->data(model->index(end + 1, 0, c.parent)); } */ Q_ASSERT(c.next == model->data(model->index(end + 1, 0, c.parent))); } void ModelTest::layoutAboutToBeChanged() { for (int i = 0; i < qBound(0, model->rowCount(), 100); ++i) changing.append(QPersistentModelIndex(model->index(i, 0))); } void ModelTest::layoutChanged() { for (int i = 0; i < changing.count(); ++i) { QPersistentModelIndex p = changing[i]; Q_ASSERT(p == model->index(p.row(), p.column(), p.parent())); } changing.clear(); } /*! Store what is about to be inserted to make sure it actually happens \sa rowsRemoved() */ void ModelTest::rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end) { Changing c; c.parent = parent; c.oldSize = model->rowCount(parent); c.last = model->data(model->index(start - 1, 0, parent)); c.next = model->data(model->index(end + 1, 0, parent)); remove.push(c); } /*! Confirm that what was said was going to happen actually did \sa rowsAboutToBeRemoved() */ void ModelTest::rowsRemoved(const QModelIndex & parent, int start, int end) { Changing c = remove.pop(); Q_ASSERT(c.parent == parent); Q_ASSERT(c.oldSize - (end - start + 1) == model->rowCount(parent)); Q_ASSERT(c.last == model->data(model->index(start - 1, 0, c.parent))); Q_ASSERT(c.next == model->data(model->index(start, 0, c.parent))); } ostinato-0.9/client/modeltest.h000066400000000000000000000043341321315675200166620ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2007 Trolltech ASA. All rights reserved. ** ** This file is part of the Qt Concurrent project on Trolltech Labs. ** ** This file may be used under the terms of the GNU General Public ** License version 2.0 as published by the Free Software Foundation ** and appearing in the file LICENSE.GPL included in the packaging of ** this file. Please review the following information to ensure GNU ** General Public Licensing requirements will be met: ** http://www.trolltech.com/products/qt/opensource.html ** ** If you are unsure which license is appropriate for your use, please ** review the following information: ** http://www.trolltech.com/products/qt/licensing.html or contact the ** sales department at sales@trolltech.com. ** ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ** ****************************************************************************/ #ifndef MODELTEST_H #define MODELTEST_H #include #include #include class ModelTest : public QObject { Q_OBJECT public: ModelTest(QAbstractItemModel *model, QObject *parent = 0); private Q_SLOTS: void nonDestructiveBasicTest(); void rowCount(); void columnCount(); void hasIndex(); void index(); void parent(); void data(); protected Q_SLOTS: void runAllTests(); void layoutAboutToBeChanged(); void layoutChanged(); void rowsAboutToBeInserted(const QModelIndex &parent, int start, int end); void rowsInserted(const QModelIndex & parent, int start, int end); void rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end); void rowsRemoved(const QModelIndex & parent, int start, int end); private: void checkChildren(const QModelIndex &parent, int currentDepth = 0); QAbstractItemModel *model; struct Changing { QModelIndex parent; int oldSize; QVariant last; QVariant next; }; QStack insert; QStack remove; bool fetchingMore; QList changing; }; #endif ostinato-0.9/client/modeltest.pri000066400000000000000000000001451321315675200172210ustar00rootroot00000000000000INCLUDEPATH += $$PWD DEPENDPATH += $$PWD SOURCES += $$PWD/modeltest.cpp HEADERS += $$PWD/modeltest.h ostinato-0.9/client/ndpstatusmodel.cpp000066400000000000000000000073501321315675200202640ustar00rootroot00000000000000/* Copyright (C) 2016 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "ndpstatusmodel.h" #include "port.h" #include "emulproto.pb.h" #include "uint128.h" #include enum { kIp4Address, kMacAddress, kStatus, kFieldCount }; static QStringList columns_ = QStringList() << "IPv6 Address" << "Mac Address" << "Status"; NdpStatusModel::NdpStatusModel(QObject *parent) : QAbstractTableModel(parent) { port_ = NULL; deviceIndex_ = -1; neighbors_ = NULL; } int NdpStatusModel::rowCount(const QModelIndex &parent) const { if (!port_ || deviceIndex_ < 0 || parent.isValid()) return 0; return port_->numNdp(deviceIndex_); } int NdpStatusModel::columnCount(const QModelIndex &parent) const { if (parent.isValid()) return 0; return columns_.size(); } QVariant NdpStatusModel::headerData( int section, Qt::Orientation orientation, int role) const { if (role != Qt::DisplayRole) return QVariant(); switch (orientation) { case Qt::Horizontal: return columns_[section]; case Qt::Vertical: return QString("%1").arg(section + 1); default: Q_ASSERT(false); // Unreachable } return QVariant(); } QVariant NdpStatusModel::data(const QModelIndex &index, int role) const { QString str; if (!port_ || deviceIndex_ < 0 || !index.isValid()) return QVariant(); int ndpIdx = index.row(); int field = index.column(); Q_ASSERT(ndpIdx < port_->numNdp(deviceIndex_)); Q_ASSERT(field < kFieldCount); const OstEmul::NdpEntry &ndp = neighbors_->ndp(ndpIdx); switch (field) { case kIp4Address: switch (role) { case Qt::DisplayRole: return QHostAddress( UInt128(ndp.ip6().hi(), ndp.ip6().lo()).toArray()) .toString(); default: break; } return QVariant(); case kMacAddress: switch (role) { case Qt::DisplayRole: return QString("%1").arg(ndp.mac(), 6*2, 16, QChar('0')) .replace(QRegExp("([0-9a-fA-F]{2}\\B)"), "\\1:") .toUpper(); default: break; } return QVariant(); case kStatus: switch (role) { case Qt::DisplayRole: return ndp.mac() ? QString("Resolved") : QString("Failed"); default: break; } return QVariant(); default: Q_ASSERT(false); // unreachable! break; } qWarning("%s: Unsupported field #%d", __FUNCTION__, field); return QVariant(); } void NdpStatusModel::setDeviceIndex(Port *port, int deviceIndex) { port_ = port; deviceIndex_ = deviceIndex; if (port_) neighbors_ = port_->deviceNeighbors(deviceIndex); reset(); } void NdpStatusModel::updateNdpStatus() { reset(); } ostinato-0.9/client/ndpstatusmodel.h000066400000000000000000000026701321315675200177310ustar00rootroot00000000000000/* Copyright (C) 2016 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _NDP_STATUS_MODEL_H #define _NDP_STATUS_MODEL_H #include class Port; namespace OstEmul { class DeviceNeighborList; } class NdpStatusModel: public QAbstractTableModel { Q_OBJECT public: NdpStatusModel(QObject *parent = 0); int rowCount(const QModelIndex &parent = QModelIndex()) const; int columnCount(const QModelIndex &parent = QModelIndex()) const; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; QVariant data(const QModelIndex &index, int role) const; void setDeviceIndex(Port *port, int deviceIndex); public slots: void updateNdpStatus(); private: Port *port_; int deviceIndex_; const OstEmul::DeviceNeighborList *neighbors_; }; #endif ostinato-0.9/client/ostinato.pro000066400000000000000000000056161321315675200170770ustar00rootroot00000000000000TEMPLATE = app CONFIG += qt ver_info macx: TARGET = Ostinato win32:RC_FILE = ostinato.rc macx:ICON = icons/logo.icns QT += network script xml INCLUDEPATH += "../rpc/" "../common/" win32 { QMAKE_LFLAGS += -static CONFIG(debug, debug|release) { LIBS += -L"../common/debug" -lostprotogui -lostproto LIBS += -L"../rpc/debug" -lpbrpc POST_TARGETDEPS += \ "../common/debug/libostprotogui.a" \ "../common/debug/libostproto.a" \ "../rpc/debug/libpbrpc.a" } else { LIBS += -L"../common/release" -lostprotogui -lostproto LIBS += -L"../rpc/release" -lpbrpc POST_TARGETDEPS += \ "../common/release/libostprotogui.a" \ "../common/release/libostproto.a" \ "../rpc/release/libpbrpc.a" } } else { LIBS += -L"../common" -lostprotogui -lostproto LIBS += -L"../rpc" -lpbrpc POST_TARGETDEPS += \ "../common/libostprotogui.a" \ "../common/libostproto.a" \ "../rpc/libpbrpc.a" } LIBS += -lprotobuf LIBS += -L"../extra/qhexedit2/$(OBJECTS_DIR)/" -lqhexedit2 RESOURCES += ostinato.qrc HEADERS += \ arpstatusmodel.h \ devicegroupdialog.h \ devicegroupmodel.h \ devicemodel.h \ deviceswidget.h \ dumpview.h \ hexlineedit.h \ mainwindow.h \ ndpstatusmodel.h \ packetmodel.h \ port.h \ portconfigdialog.h \ portgroup.h \ portgrouplist.h \ portmodel.h \ portstatsfilterdialog.h \ portstatsmodel.h \ portstatsproxymodel.h \ portstatswindow.h \ portswindow.h \ preferences.h \ settings.h \ streamconfigdialog.h \ streamlistdelegate.h \ streammodel.h \ streamstatsfiltermodel.h \ streamstatsmodel.h \ streamstatswindow.h \ variablefieldswidget.h FORMS += \ about.ui \ devicegroupdialog.ui \ deviceswidget.ui \ mainwindow.ui \ portconfigdialog.ui \ portstatsfilter.ui \ portstatswindow.ui \ portswindow.ui \ preferences.ui \ streamconfigdialog.ui \ streamstatswindow.ui \ variablefieldswidget.ui SOURCES += \ arpstatusmodel.cpp \ devicegroupdialog.cpp \ devicegroupmodel.cpp \ devicemodel.cpp \ deviceswidget.cpp \ dumpview.cpp \ stream.cpp \ hexlineedit.cpp \ main.cpp \ mainwindow.cpp \ ndpstatusmodel.cpp \ packetmodel.cpp \ params.cpp \ port.cpp \ portconfigdialog.cpp \ portgroup.cpp \ portgrouplist.cpp \ portmodel.cpp \ portstatsmodel.cpp \ portstatsfilterdialog.cpp \ portstatswindow.cpp \ portswindow.cpp \ preferences.cpp \ streamconfigdialog.cpp \ streamlistdelegate.cpp \ streammodel.cpp \ streamstatsmodel.cpp \ streamstatswindow.cpp \ variablefieldswidget.cpp QMAKE_DISTCLEAN += object_script.* include(../install.pri) include(../version.pri) # TODO(LOW): Test only CONFIG(debug, debug|release):include(modeltest.pri) ostinato-0.9/client/ostinato.qrc000066400000000000000000000036001321315675200170530ustar00rootroot00000000000000 icons/about.png icons/arrow_down.png icons/arrow_left.png icons/arrow_right.png icons/arrow_up.png icons/bullet_error.png icons/bullet_green.png icons/bullet_orange.png icons/bullet_red.png icons/bullet_white.png icons/bullet_yellow.png icons/control_play.png icons/control_stop.png icons/deco_exclusive.png icons/delete.png icons/devicegroup_add.png icons/devicegroup_delete.png icons/devicegroup_edit.png icons/exit.png icons/gaps.png icons/help.png icons/logo.png icons/magnifier.png icons/name.png icons/neighbor_clear.png icons/neighbor_resolve.png icons/portgroup_add.png icons/portgroup_connect.png icons/portgroup_delete.png icons/portgroup_disconnect.png icons/portstats_clear.png icons/portstats_clear_all.png icons/portstats_filter.png icons/preferences.png icons/qt.png icons/refresh.png icons/sound_mute.png icons/sound_none.png icons/stream_add.png icons/stream_delete.png icons/stream_duplicate.png icons/stream_edit.png icons/stream_stats.png ostinato-0.9/client/ostinato.rc000066400000000000000000000001021321315675200166640ustar00rootroot00000000000000IDI_ICON1 ICON DISCARDABLE "icons/logo.ico" ostinato-0.9/client/packetmodel.cpp000066400000000000000000000141611321315675200175040ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include #include "packetmodel.h" #include "../common/protocollistiterator.h" #include "../common/abstractprotocol.h" PacketModel::PacketModel(QObject *parent) : QAbstractItemModel(parent) { } void PacketModel::setSelectedProtocols(ProtocolListIterator &iter) { QList currentProtocols; iter.toFront(); while (iter.hasNext()) currentProtocols.append(iter.next()); if (mSelectedProtocols != currentProtocols) { mSelectedProtocols = currentProtocols; reset(); } else { emit layoutAboutToBeChanged(); emit layoutChanged(); } } int PacketModel::rowCount(const QModelIndex &parent) const { IndexId parentId; // qDebug("in %s", __FUNCTION__); // Parent == Invalid i.e. Invisible Root. // ==> Children are Protocol (Top Level) Items if (!parent.isValid()) return mSelectedProtocols.size(); // Parent - Valid Item parentId.w = parent.internalId(); switch(parentId.ws.type) { case ITYP_PROTOCOL: return mSelectedProtocols.at(parentId.ws.protocol)->frameFieldCount(); case ITYP_FIELD: return 0; default: qWarning("%s: Unhandled ItemType", __FUNCTION__); } Q_ASSERT(1 == 0); // Unreachable code qWarning("%s: Catch all - need to investigate", __FUNCTION__); return 0; // catch all } int PacketModel::columnCount(const QModelIndex &/*parent*/) const { return 1; } QModelIndex PacketModel::index(int row, int col, const QModelIndex &parent) const { QModelIndex index; IndexId id, parentId; if (!hasIndex(row, col, parent)) goto _exit; // Parent is Invisible Root // Request for a Protocol Item if (!parent.isValid()) { id.w = 0; id.ws.type = ITYP_PROTOCOL; id.ws.protocol = row; index = createIndex(row, col, id.w); goto _exit; } // Parent is a Valid Item parentId.w = parent.internalId(); id.w = parentId.w; switch(parentId.ws.type) { case ITYP_PROTOCOL: id.ws.type = ITYP_FIELD; index = createIndex(row, col, id.w); goto _exit; case ITYP_FIELD: Q_ASSERT(1 == 0); // Unreachable code goto _exit; default: qWarning("%s: Unhandled ItemType", __FUNCTION__); } Q_ASSERT(1 == 0); // Unreachable code _exit: return index; } QModelIndex PacketModel::parent(const QModelIndex &index) const { QModelIndex parentIndex; IndexId id, parentId; if (!index.isValid()) return QModelIndex(); id.w = index.internalId(); parentId.w = id.w; switch(id.ws.type) { case ITYP_PROTOCOL: // return invalid index for invisible root goto _exit; case ITYP_FIELD: parentId.ws.type = ITYP_PROTOCOL; parentIndex = createIndex(id.ws.protocol, 0, parentId.w); goto _exit; default: qWarning("%s: Unhandled ItemType", __FUNCTION__); } Q_ASSERT(1 == 1); // Unreachable code _exit: return parentIndex; } QVariant PacketModel::data(const QModelIndex &index, int role) const { IndexId id; int fieldIdx = 0; if (!index.isValid()) return QVariant(); id.w = index.internalId(); if (id.ws.type == ITYP_FIELD) { const AbstractProtocol *p = mSelectedProtocols.at(id.ws.protocol); int n = index.row() + 1; while (n) { if (p->fieldFlags(fieldIdx).testFlag(AbstractProtocol::FrameField)) n--; fieldIdx++; } fieldIdx--; } // FIXME(HI): Relook at this completely if (role == Qt::UserRole) { switch(id.ws.type) { case ITYP_PROTOCOL: qDebug("*** %d/%d", id.ws.protocol, mSelectedProtocols.size()); return mSelectedProtocols.at(id.ws.protocol)-> protocolFrameValue(); case ITYP_FIELD: return mSelectedProtocols.at(id.ws.protocol)->fieldData( fieldIdx, AbstractProtocol::FieldFrameValue); default: qWarning("%s: Unhandled ItemType", __FUNCTION__); } return QByteArray(); } // FIXME: Use a new enum here instead of UserRole if (role == (Qt::UserRole+1)) { switch(id.ws.type) { case ITYP_PROTOCOL: return mSelectedProtocols.at(id.ws.protocol)-> protocolFrameValue().size(); case ITYP_FIELD: return mSelectedProtocols.at(id.ws.protocol)->fieldData( fieldIdx, AbstractProtocol::FieldBitSize); default: qWarning("%s: Unhandled ItemType", __FUNCTION__); } return QVariant(); } if (role != Qt::DisplayRole) return QVariant(); switch(id.ws.type) { case ITYP_PROTOCOL: return QString("%1 (%2)") .arg(mSelectedProtocols.at(id.ws.protocol)->shortName()) .arg(mSelectedProtocols.at(id.ws.protocol)->name()); case ITYP_FIELD: return mSelectedProtocols.at(id.ws.protocol)->fieldData(fieldIdx, AbstractProtocol::FieldName).toString() + QString(" : ") + mSelectedProtocols.at(id.ws.protocol)->fieldData(fieldIdx, AbstractProtocol::FieldTextValue).toString(); default: qWarning("%s: Unhandled ItemType", __FUNCTION__); } Q_ASSERT(1 == 1); // Unreachable code return QVariant(); } ostinato-0.9/client/packetmodel.h000066400000000000000000000034241321315675200171510ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _PACKET_MODEL_H #define _PACKET_MODEL_H #include class ProtocolListIterator; class AbstractProtocol; class PacketModel: public QAbstractItemModel { public: PacketModel(QObject *parent = 0); void setSelectedProtocols(ProtocolListIterator &iter); int rowCount(const QModelIndex &parent = QModelIndex()) const; int columnCount(const QModelIndex &parent = QModelIndex()) const; QVariant data(const QModelIndex &index, int role) const; QVariant headerData(int /*section*/, Qt::Orientation /*orientation*/, int /*role= Qt::DisplayRole*/) const { return QVariant(); } QModelIndex index (int row, int col, const QModelIndex & parent = QModelIndex() ) const; QModelIndex parent(const QModelIndex &index) const; private: typedef union _IndexId { quint32 w; struct { quint16 type; #define ITYP_PROTOCOL 1 #define ITYP_FIELD 2 quint16 protocol; // protocol is valid for both ITYPs } ws; } IndexId; QList mSelectedProtocols; }; #endif ostinato-0.9/client/params.cpp000066400000000000000000000026111321315675200164740ustar00rootroot00000000000000/* Copyright (C) 2016 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "params.h" #include Params::Params() { localDrone_ = true; } int Params::parseCommandLine(int argc, char* argv[]) { int c, n = 0; opterr = 0; while ((c = getopt (argc, argv, "c")) != -1) { switch (c) { case 'c': localDrone_ = false; break; default: qDebug("ignoring unrecognized option (%c)", c); } n++; } for (int i = optind; i < argc; i++, n++) args_ << argv[i]; return n; } bool Params::optLocalDrone() { return localDrone_; } int Params::argumentCount() { return args_.size(); } QString Params::argument(int index) { return index < args_.size() ? args_.at(index) : QString(); } ostinato-0.9/client/params.h000066400000000000000000000017511321315675200161450ustar00rootroot00000000000000/* Copyright (C) 2016 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _PARAMS_H #define _PARAMS_H #include class Params { public: Params(); int parseCommandLine(int argc, char* argv[]); bool optLocalDrone(); int argumentCount(); QString argument(int index); private: bool localDrone_; QStringList args_; }; extern Params appParams; #endif ostinato-0.9/client/port.cpp000066400000000000000000000625541321315675200162110ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "port.h" #include "emulation.h" #include "streamfileformat.h" #include #include #include #include #include #include extern QMainWindow *mainWindow; uint Port::mAllocStreamId = 0; uint Port::allocDeviceGroupId_ = 1; static const int kEthOverhead = 20; uint Port::newStreamId() { // We use a monotonically increasing class variable to generate // unique id. To ensure that we take into account the already // existing ids at drone, we update this variable to be greater // than the last id that we fetched // FIXME: In some cases e.g. wrap around or more likely multiple // clients, we will still run into trouble - to fix this we should // check here that the same id does not already exist; but currently // we can only do a linear search - fix this when the lookup/search // is optimized return mAllocStreamId++; } Port::Port(quint32 id, quint32 portGroupId) { mPortId = id; d.mutable_port_id()->set_id(id); stats.mutable_port_id()->set_id(id); mPortGroupId = portGroupId; capFile_ = NULL; dirty_ = false; } Port::~Port() { qDebug("%s", __FUNCTION__); while (!mStreams.isEmpty()) delete mStreams.takeFirst(); } void Port::protoDataCopyInto(OstProto::Port *data) { data->CopyFrom(d); } void Port::updatePortConfig(OstProto::Port *port) { bool recalc = false; if (port->has_transmit_mode() && port->transmit_mode() != d.transmit_mode()) recalc = true; d.MergeFrom(*port); // Setup a user-friendly alias for Win32 ports if (name().startsWith("\\Device\\NPF_")) setAlias(QString("if%1").arg(id())); if (recalc) recalculateAverageRates(); } void Port::updateStreamOrdinalsFromIndex() { for (int i=0; i < mStreams.size(); i++) mStreams[i]->setOrdinal(i); } void Port::reorderStreamsByOrdinals() { qSort(mStreams.begin(), mStreams.end(), StreamBase::StreamLessThan); } void Port::setDirty(bool dirty) { if (dirty == dirty_) return; dirty_ = dirty; emit localConfigChanged(mPortGroupId, mPortId, dirty_); } void Port::recalculateAverageRates() { double pps = 0; double bps = 0; int n = 0; foreach (Stream* s, mStreams) { if (!s->isEnabled()) continue; double r = s->averagePacketRate(); pps += r; bps += r * (s->frameLenAvg() + kEthOverhead) * 8; n++; if ((transmitMode() == OstProto::kSequentialTransmit) && (s->nextWhat() == Stream::e_nw_stop)) break; } if (n) { switch (transmitMode()) { case OstProto::kSequentialTransmit: avgPacketsPerSec_ = pps/n; avgBitsPerSec_ = bps/n; break; case OstProto::kInterleavedTransmit: avgPacketsPerSec_ = pps; avgBitsPerSec_ = bps; break; default: Q_ASSERT(false); // Unreachable!! } numActiveStreams_ = n; } else avgPacketsPerSec_ = avgBitsPerSec_ = numActiveStreams_ = 0; qDebug("%s: avgPps = %g avgBps = %g numActive = %d", __FUNCTION__, avgPacketsPerSec_, avgBitsPerSec_, numActiveStreams_); emit portRateChanged(mPortGroupId, mPortId); } void Port::setAveragePacketRate(double packetsPerSec) { double rate = 0; double pps = 0; double bps = 0; int n = 0; qDebug("@%s: packetsPerSec = %g", __FUNCTION__, packetsPerSec); qDebug("@%s: avgPps = %g avgBps = %g numActive = %d", __FUNCTION__, avgPacketsPerSec_, avgBitsPerSec_, numActiveStreams_); foreach (Stream* s, mStreams) { if (!s->isEnabled()) continue; switch (transmitMode()) { case OstProto::kSequentialTransmit: rate = s->averagePacketRate() * (packetsPerSec/avgPacketsPerSec_); break; case OstProto::kInterleavedTransmit: rate = s->averagePacketRate() + ((s->averagePacketRate()/avgPacketsPerSec_) * (packetsPerSec - avgPacketsPerSec_)); break; default: Q_ASSERT(false); // Unreachable!! } qDebug("cur stream pps = %g", s->averagePacketRate()); s->setAveragePacketRate(rate); qDebug("new stream pps = %g", s->averagePacketRate()); double r = s->averagePacketRate(); pps += r; bps += r * (s->frameLenAvg() + kEthOverhead) * 8; n++; if ((transmitMode() == OstProto::kSequentialTransmit) && (s->nextWhat() == Stream::e_nw_stop)) break; } if (n) { switch (transmitMode()) { case OstProto::kSequentialTransmit: avgPacketsPerSec_ = pps/n; avgBitsPerSec_ = bps/n; break; case OstProto::kInterleavedTransmit: avgPacketsPerSec_ = pps; avgBitsPerSec_ = bps; break; default: Q_ASSERT(false); // Unreachable!! } numActiveStreams_ = n; setDirty(true); } else avgPacketsPerSec_ = avgBitsPerSec_ = numActiveStreams_ = 0; qDebug("%s: avgPps = %g avgBps = %g numActive = %d", __FUNCTION__, avgPacketsPerSec_, avgBitsPerSec_, numActiveStreams_); emit portRateChanged(mPortGroupId, mPortId); } void Port::setAverageBitRate(double bitsPerSec) { double rate = 0; double pps = 0; double bps = 0; int n = 0; qDebug("@%s: bitsPerSec = %g", __FUNCTION__, bitsPerSec); qDebug("@%s: avgPps = %g avgBps = %g numActive = %d", __FUNCTION__, avgPacketsPerSec_, avgBitsPerSec_, numActiveStreams_); foreach (Stream* s, mStreams) { if (!s->isEnabled()) continue; switch (transmitMode()) { case OstProto::kSequentialTransmit: rate = s->averagePacketRate() * (bitsPerSec/avgBitsPerSec_); qDebug("rate = %g", rate); break; case OstProto::kInterleavedTransmit: rate = s->averagePacketRate() + ((s->averagePacketRate()/avgPacketsPerSec_) * ((bitsPerSec - avgBitsPerSec_) / ((s->frameLenAvg()+kEthOverhead)*8))); break; default: Q_ASSERT(false); // Unreachable!! } qDebug("cur stream pps = %g", s->averagePacketRate()); s->setAveragePacketRate(rate); qDebug("new stream pps = %g", s->averagePacketRate()); double r = s->averagePacketRate(); pps += r; bps += r * (s->frameLenAvg() + kEthOverhead) * 8; n++; if ((transmitMode() == OstProto::kSequentialTransmit) && (s->nextWhat() == Stream::e_nw_stop)) break; } if (n) { switch (transmitMode()) { case OstProto::kSequentialTransmit: avgPacketsPerSec_ = pps/n; avgBitsPerSec_ = bps/n; break; case OstProto::kInterleavedTransmit: avgPacketsPerSec_ = pps; avgBitsPerSec_ = bps; break; default: Q_ASSERT(false); // Unreachable!! } numActiveStreams_ = n; setDirty(true); } else avgPacketsPerSec_ = avgBitsPerSec_ = numActiveStreams_ = 0; qDebug("%s: avgPps = %g avgBps = %g numActive = %d", __FUNCTION__, avgPacketsPerSec_, avgBitsPerSec_, numActiveStreams_); emit portRateChanged(mPortGroupId, mPortId); } bool Port::newStreamAt(int index, OstProto::Stream const *stream) { Stream *s = new Stream; if (index > mStreams.size()) return false; if (stream) s->protoDataCopyFrom(*stream); s->setId(newStreamId()); mStreams.insert(index, s); updateStreamOrdinalsFromIndex(); recalculateAverageRates(); setDirty(true); return true; } bool Port::deleteStreamAt(int index) { if (index >= mStreams.size()) return false; delete mStreams.takeAt(index); updateStreamOrdinalsFromIndex(); recalculateAverageRates(); setDirty(true); return true; } bool Port::insertStream(uint streamId) { Stream *s = new Stream; s->setId(streamId); // FIXME(MED): If a stream with id already exists, what do we do? mStreams.append(s); // Update mAllocStreamId to take into account the stream id received // from server if (mAllocStreamId <= streamId) mAllocStreamId = streamId + 1; return true; } bool Port::updateStream(uint streamId, OstProto::Stream *stream) { int i, streamIndex; for (i = 0; i < mStreams.size(); i++) { if (streamId == mStreams[i]->id()) goto _found; } qDebug("%s: Invalid stream id %d", __FUNCTION__, streamId); return false; _found: streamIndex = i; mStreams[streamIndex]->protoDataCopyFrom(*stream); reorderStreamsByOrdinals(); return true; } void Port::getDeletedStreamsSinceLastSync( OstProto::StreamIdList &streamIdList) { streamIdList.clear_stream_id(); for (int i = 0; i < mLastSyncStreamList.size(); i++) { int j; for (j = 0; j < mStreams.size(); j++) { if (mLastSyncStreamList[i] == mStreams[j]->id()) break; } if (j < mStreams.size()) { // stream still exists! continue; } else { // stream has been deleted since last sync OstProto::StreamId *s; s = streamIdList.add_stream_id(); s->set_id(mLastSyncStreamList.at(i)); } } } void Port::getNewStreamsSinceLastSync( OstProto::StreamIdList &streamIdList) { streamIdList.clear_stream_id(); for (int i = 0; i < mStreams.size(); i++) { if (mLastSyncStreamList.contains(mStreams[i]->id())) { // existing stream! continue; } else { // new stream! OstProto::StreamId *s; s = streamIdList.add_stream_id(); s->set_id(mStreams[i]->id()); } } } void Port::getModifiedStreamsSinceLastSync( OstProto::StreamConfigList &streamConfigList) { qDebug("In %s", __FUNCTION__); //streamConfigList.mutable_port_id()->set_id(mPortId); for (int i = 0; i < mStreams.size(); i++) { OstProto::Stream *s; s = streamConfigList.add_stream(); mStreams[i]->protoDataCopyInto(*s); } qDebug("Done %s", __FUNCTION__); } void Port::getDeletedDeviceGroupsSinceLastSync( OstProto::DeviceGroupIdList &deviceGroupIdList) { deviceGroupIdList.clear_device_group_id(); foreach(int id, lastSyncDeviceGroupList_) { if (!deviceGroupById(id)) deviceGroupIdList.add_device_group_id()->set_id(id); } } void Port::getNewDeviceGroupsSinceLastSync( OstProto::DeviceGroupIdList &deviceGroupIdList) { deviceGroupIdList.clear_device_group_id(); foreach(OstProto::DeviceGroup *dg, deviceGroups_) { quint32 dgid = dg->device_group_id().id(); if (!lastSyncDeviceGroupList_.contains(dgid)) deviceGroupIdList.add_device_group_id()->set_id(dgid); } } void Port::getModifiedDeviceGroupsSinceLastSync( OstProto::DeviceGroupConfigList &deviceGroupConfigList) { deviceGroupConfigList.clear_device_group(); foreach(quint32 id, modifiedDeviceGroupList_) deviceGroupConfigList.add_device_group() ->CopyFrom(*deviceGroupById(id)); } /*! * Finds the user modifiable fields in 'config' that are different from * the current configuration on the port and modifes 'config' such that * only those fields are set and returns true. If 'config' is same as * current port config, 'config' is unmodified and false is returned */ bool Port::modifiablePortConfig(OstProto::Port &config) const { bool change = false; OstProto::Port modCfg; if (config.is_exclusive_control() != d.is_exclusive_control()) { modCfg.set_is_exclusive_control(config.is_exclusive_control()); change = true; } if (config.transmit_mode() != d.transmit_mode()) { modCfg.set_transmit_mode(config.transmit_mode()); change = true; } if (config.user_name() != d.user_name()) { modCfg.set_user_name(config.user_name()); change = true; } if (change) { modCfg.mutable_port_id()->set_id(id()); config.CopyFrom(modCfg); return true; } return false; } void Port::when_syncComplete() { //reorderStreamsByOrdinals(); mLastSyncStreamList.clear(); for (int i=0; iid()); lastSyncDeviceGroupList_.clear(); for (int i = 0; i < deviceGroups_.size(); i++) { lastSyncDeviceGroupList_.append( deviceGroups_.at(i)->device_group_id().id()); } modifiedDeviceGroupList_.clear(); setDirty(false); } void Port::updateStats(OstProto::PortStats *portStats) { OstProto::PortState oldState; oldState = stats.state(); stats.MergeFrom(*portStats); if (oldState.link_state() != stats.state().link_state()) { qDebug("portstate changed"); emit portDataChanged(mPortGroupId, mPortId); } } void Port::duplicateStreams(const QList &list, int count) { QList sources; foreach(int index, list) { OstProto::Stream stream; Q_ASSERT(index < mStreams.size()); mStreams.at(index)->protoDataCopyInto(stream); sources.append(stream); } int insertAt = mStreams.size(); for (int i=0; i < count; i++) { for (int j=0; j < sources.size(); j++) { newStreamAt(insertAt, &sources.at(j)); // Rename stream by appending the copy count mStreams.at(insertAt)->setName(QString("%1 (%2)") .arg(mStreams.at(insertAt)->name()) .arg(i+1)); insertAt++; } } setDirty(true); emit streamListChanged(mPortGroupId, mPortId); } bool Port::openStreams(QString fileName, bool append, QString &error) { bool ret = false; QDialog *optDialog; QProgressDialog progress("Opening Streams", "Cancel", 0, 0, mainWindow); OstProto::StreamConfigList streams; StreamFileFormat *fmt = StreamFileFormat::fileFormatFromFile(fileName); if (fmt == NULL) { error = tr("Unknown streams file format"); goto _fail; } if ((optDialog = fmt->openOptionsDialog())) { int ret; optDialog->setParent(mainWindow, Qt::Dialog); ret = optDialog->exec(); optDialog->setParent(0, Qt::Dialog); if (ret == QDialog::Rejected) goto _user_opt_cancel; } progress.setAutoReset(false); progress.setAutoClose(false); progress.setMinimumDuration(0); progress.show(); mainWindow->setDisabled(true); progress.setEnabled(true); // to override the mainWindow disable connect(fmt, SIGNAL(status(QString)),&progress,SLOT(setLabelText(QString))); connect(fmt, SIGNAL(target(int)), &progress, SLOT(setMaximum(int))); connect(fmt, SIGNAL(progress(int)), &progress, SLOT(setValue(int))); connect(&progress, SIGNAL(canceled()), fmt, SLOT(cancel())); fmt->openAsync(fileName, streams, error); qDebug("after open async"); while (!fmt->isFinished()) qApp->processEvents(); qDebug("wait over for async operation"); if (!fmt->result()) goto _fail; // process any remaining events posted from the thread for (int i = 0; i < 10; i++) qApp->processEvents(); if (!append) { int n = numStreams(); progress.setLabelText("Deleting existing streams..."); progress.setRange(0, n); for (int i = 0; i < n; i++) { if (progress.wasCanceled()) goto _user_cancel; deleteStreamAt(0); progress.setValue(i); if (i % 32 == 0) qApp->processEvents(); } } progress.setLabelText("Constructing new streams..."); progress.setRange(0, streams.stream_size()); for (int i = 0; i < streams.stream_size(); i++) { if (progress.wasCanceled()) goto _user_cancel; newStreamAt(mStreams.size(), &streams.stream(i)); progress.setValue(i); if (i % 32 == 0) qApp->processEvents(); } setDirty(true); _user_cancel: emit streamListChanged(mPortGroupId, mPortId); _user_opt_cancel: ret = true; _fail: progress.close(); mainWindow->setEnabled(true); recalculateAverageRates(); return ret; } bool Port::saveStreams(QString fileName, QString fileType, QString &error) { bool ret = false; QProgressDialog progress("Saving Streams", "Cancel", 0, 0, mainWindow); StreamFileFormat *fmt = StreamFileFormat::fileFormatFromType(fileType); OstProto::StreamConfigList streams; if (fmt == NULL) goto _fail; progress.setAutoReset(false); progress.setAutoClose(false); progress.setMinimumDuration(0); progress.show(); mainWindow->setDisabled(true); progress.setEnabled(true); // to override the mainWindow disable connect(fmt, SIGNAL(status(QString)),&progress,SLOT(setLabelText(QString))); connect(fmt, SIGNAL(target(int)), &progress, SLOT(setMaximum(int))); connect(fmt, SIGNAL(progress(int)), &progress, SLOT(setValue(int))); connect(&progress, SIGNAL(canceled()), fmt, SLOT(cancel())); progress.setLabelText("Preparing Streams..."); progress.setRange(0, mStreams.size()); streams.mutable_port_id()->set_id(0); for (int i = 0; i < mStreams.size(); i++) { OstProto::Stream *s = streams.add_stream(); mStreams[i]->protoDataCopyInto(*s); if (progress.wasCanceled()) goto _user_cancel; progress.setValue(i); if (i % 32 == 0) qApp->processEvents(); } fmt->saveAsync(streams, fileName, error); qDebug("after save async"); while (!fmt->isFinished()) qApp->processEvents(); qDebug("wait over for async operation"); ret = fmt->result(); goto _exit; _user_cancel: goto _exit; _fail: error = QString("Unsupported File Type - %1").arg(fileType); goto _exit; _exit: progress.close(); mainWindow->setEnabled(true); return ret; } // ------------ Device Group ----------- // uint Port::newDeviceGroupId() { // We use a monotonically increasing class variable to generate // unique id. To ensure that we take into account the already // existing ids at drone, we update this variable to be greater // than the last id that we fetched // FIXME: In some cases e.g. wrap around or more likely multiple // clients, we will still run into trouble - to fix this we should // check here that the same id does not already exist; but currently // we can only do a linear search - fix this when the lookup/search // is optimized return allocDeviceGroupId_++; } int Port::numDeviceGroups() const { return deviceGroups_.size(); } const OstProto::DeviceGroup* Port::deviceGroupByIndex(int index) const { if ((index < 0) || (index >= numDeviceGroups())) { qWarning("%s: index %d out of range (0 - %d)", __FUNCTION__, index, numDeviceGroups() - 1); return NULL; } return deviceGroups_.at(index); } OstProto::DeviceGroup* Port::mutableDeviceGroupByIndex(int index) { if ((index < 0) || (index >= numDeviceGroups())) { qWarning("%s: index %d out of range (0 - %d)", __FUNCTION__, index, numDeviceGroups() - 1); return NULL; } OstProto::DeviceGroup *devGrp = deviceGroups_.at(index); // Caller can modify DeviceGroup - assume she will modifiedDeviceGroupList_.insert(devGrp->device_group_id().id()); setDirty(true); return devGrp; } const OstProto::DeviceGroup* Port::deviceGroupById(uint deviceGroupId) const { for (int i = 0; i < deviceGroups_.size(); i++) { OstProto::DeviceGroup *devGrp = deviceGroups_.at(i); if (devGrp->device_group_id().id() == deviceGroupId) return devGrp; } return NULL; } bool Port::newDeviceGroupAt(int index, const OstProto::DeviceGroup *deviceGroup) { if (index < 0 || index > numDeviceGroups()) return false; OstProto::DeviceGroup *devGrp = newDeviceGroup(id()); if (!devGrp) { qWarning("failed allocating a new device group"); return false; } if (deviceGroup) devGrp->CopyFrom(*deviceGroup); devGrp->mutable_device_group_id()->set_id(newDeviceGroupId()); deviceGroups_.insert(index, devGrp); modifiedDeviceGroupList_.insert(devGrp->device_group_id().id()); setDirty(true); return true; } bool Port::deleteDeviceGroupAt(int index) { if (index < 0 || index >= deviceGroups_.size()) return false; OstProto::DeviceGroup *devGrp = deviceGroups_.takeAt(index); modifiedDeviceGroupList_.remove(devGrp->device_group_id().id()); delete devGrp; setDirty(true); return true; } bool Port::insertDeviceGroup(uint deviceGroupId) { OstProto::DeviceGroup *devGrp; if (deviceGroupById(deviceGroupId)) { qDebug("%s: deviceGroup id %u already exists", __FUNCTION__, deviceGroupId); return false; } devGrp = newDeviceGroup(id()); devGrp->mutable_device_group_id()->set_id(deviceGroupId); deviceGroups_.append(devGrp); // Update allocDeviceGroupId_ to take into account the deviceGroup id // received from server - this is required to make sure newly allocated // deviceGroup ids are unique if (allocDeviceGroupId_ <= deviceGroupId) allocDeviceGroupId_ = deviceGroupId + 1; return true; } bool Port::updateDeviceGroup( uint deviceGroupId, OstProto::DeviceGroup *deviceGroup) { using OstProto::DeviceGroup; // XXX: We should not call mutableDeviceGroupById() because that will // implicitly set the port as dirty, so we use a const_cast hack instead DeviceGroup *devGrp = const_cast( deviceGroupById(deviceGroupId)); if (!devGrp) { qDebug("%s: deviceGroup id %u does not exist", __FUNCTION__, deviceGroupId); return false; } devGrp->CopyFrom(*deviceGroup); return true; } // ------------ Devices ----------- // int Port::numDevices() { return devices_.size(); } const OstEmul::Device* Port::deviceByIndex(int index) { if ((index < 0) || (index >= numDevices())) { qWarning("%s: index %d out of range (0 - %d)", __FUNCTION__, index, numDevices() - 1); return NULL; } return devices_.at(index); } void Port::clearDeviceList() { while (devices_.size()) delete devices_.takeFirst(); } void Port::insertDevice(const OstEmul::Device &device) { OstEmul::Device *dev = new OstEmul::Device(device); devices_.append(dev); } // ------------- Device Neighbors (ARP/NDP) ------------- // const OstEmul::DeviceNeighborList* Port::deviceNeighbors(int deviceIndex) { if ((deviceIndex < 0) || (deviceIndex >= numDevices())) { qWarning("%s: index %d out of range (0 - %d)", __FUNCTION__, deviceIndex, numDevices() - 1); return NULL; } return deviceNeighbors_.value(deviceIndex); } int Port::numArp(int deviceIndex) { if (deviceNeighbors_.contains(deviceIndex)) return deviceNeighbors_.value(deviceIndex)->arp_size(); return 0; } int Port::numArpResolved(int deviceIndex) { if (arpResolvedCount_.contains(deviceIndex)) return arpResolvedCount_.value(deviceIndex); return 0; } int Port::numNdp(int deviceIndex) { if (deviceNeighbors_.contains(deviceIndex)) return deviceNeighbors_.value(deviceIndex)->ndp_size(); return 0; } int Port::numNdpResolved(int deviceIndex) { if (ndpResolvedCount_.contains(deviceIndex)) return ndpResolvedCount_.value(deviceIndex); return 0; } void Port::clearDeviceNeighbors() { arpResolvedCount_.clear(); ndpResolvedCount_.clear(); qDeleteAll(deviceNeighbors_); deviceNeighbors_.clear(); } void Port::insertDeviceNeighbors(const OstEmul::DeviceNeighborList &neighList) { int count; OstEmul::DeviceNeighborList *neighbors = new OstEmul::DeviceNeighborList(neighList); deviceNeighbors_.insert(neighList.device_index(), neighbors); count = 0; for (int i = 0; i < neighbors->arp_size(); i++) if (neighbors->arp(i).mac()) count++; arpResolvedCount_.insert(neighbors->device_index(), count); count = 0; for (int i = 0; i < neighbors->ndp_size(); i++) if (neighbors->ndp(i).mac()) count++; ndpResolvedCount_.insert(neighbors->device_index(), count); } void Port::deviceInfoRefreshed() { emit deviceInfoChanged(); } ostinato-0.9/client/port.h000066400000000000000000000201731321315675200156450ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _PORT_H #define _PORT_H #include #include #include #include #include #include #include "stream.h" //class StreamModel; namespace OstEmul { class Device; class DeviceNeighborList; } class Port : public QObject { Q_OBJECT static uint mAllocStreamId; static uint allocDeviceGroupId_; OstProto::Port d; OstProto::PortStats stats; QTemporaryFile *capFile_; // FIXME(HI): consider removing mPortId as it is duplicated inside 'd' quint32 mPortId; quint32 mPortGroupId; QString mUserAlias; // user defined bool dirty_; double avgPacketsPerSec_; double avgBitsPerSec_; int numActiveStreams_; QList mLastSyncStreamList; QList mStreams; // sorted by stream's ordinal value QList lastSyncDeviceGroupList_; QSet modifiedDeviceGroupList_; QList deviceGroups_; QList devices_; QHash deviceNeighbors_; QHash arpResolvedCount_; QHash ndpResolvedCount_; uint newStreamId(); void updateStreamOrdinalsFromIndex(); void reorderStreamsByOrdinals(); void setDirty(bool dirty); public: enum AdminStatus { AdminDisable, AdminEnable }; // FIXME(HIGH): default args is a hack for QList operations on Port Port(quint32 id = 0xFFFFFFFF, quint32 pgId = 0xFFFFFFFF); ~Port(); quint32 portGroupId() const { return mPortGroupId; } const QString userAlias() const { return mUserAlias.isEmpty() ? name() : mUserAlias; } quint32 id() const { return d.port_id().id(); } const QString name() const { return QString().fromStdString(d.name()); } const QString description() const { return QString().fromStdString(d.description()); } const QString notes() const { return QString().fromStdString(d.notes()); } const QString userName() const { return QString().fromStdString(d.user_name()); } AdminStatus adminStatus() const { return (d.is_enabled()?AdminEnable:AdminDisable); } bool hasExclusiveControl() const { return d.is_exclusive_control(); } OstProto::TransmitMode transmitMode() const { return d.transmit_mode(); } bool trackStreamStats() const { return d.is_tracking_stream_stats(); } double averagePacketRate() const { return avgPacketsPerSec_; } double averageBitRate() const { return avgBitsPerSec_; } //void setAdminEnable(AdminStatus status) { mAdminStatus = status; } void setAlias(QString alias) { mUserAlias = alias; } //void setExclusive(bool flag); int numStreams() { return mStreams.size(); } const Stream* streamByIndex(int index) const { Q_ASSERT(index < mStreams.size()); return mStreams[index]; } Stream* mutableStreamByIndex(int index, bool assumeChange = true) { Q_ASSERT(index < mStreams.size()); if (assumeChange) setDirty(true); return mStreams[index]; } void setLocalConfigChanged(bool changed) { setDirty(changed); } OstProto::LinkState linkState() { return stats.state().link_state(); } OstProto::PortStats getStats() { return stats; } QTemporaryFile* getCaptureFile() { delete capFile_; capFile_ = new QTemporaryFile(QString(QDir::tempPath()) .append("/") .append(userAlias()) .append(".XXXXXX")); return capFile_; } void protoDataCopyInto(OstProto::Port *data); //! Used when config received from server // FIXME(MED): naming inconsistency - PortConfig/Stream; also retVal void updatePortConfig(OstProto::Port *port); //! Used by StreamModel //@{ bool newStreamAt(int index, OstProto::Stream const *stream = NULL); bool deleteStreamAt(int index); //@} //! Used by MyService::Stub to update from config received from server //@{ bool insertStream(uint streamId); bool updateStream(uint streamId, OstProto::Stream *stream); //@} bool isDirty() { return dirty_; } void getDeletedStreamsSinceLastSync(OstProto::StreamIdList &streamIdList); void getNewStreamsSinceLastSync(OstProto::StreamIdList &streamIdList); void getModifiedStreamsSinceLastSync( OstProto::StreamConfigList &streamConfigList); void getDeletedDeviceGroupsSinceLastSync( OstProto::DeviceGroupIdList &streamIdList); void getNewDeviceGroupsSinceLastSync( OstProto::DeviceGroupIdList &streamIdList); void getModifiedDeviceGroupsSinceLastSync( OstProto::DeviceGroupConfigList &streamConfigList); bool modifiablePortConfig(OstProto::Port &config) const; void when_syncComplete(); void setAveragePacketRate(double packetsPerSec); void setAverageBitRate(double bitsPerSec); // FIXME(MED): Bad Hack! port should not need an external trigger to // recalculate - refactor client side domain objects and model objects void recalculateAverageRates(); void updateStats(OstProto::PortStats *portStats); void duplicateStreams(const QList &list, int count); bool openStreams(QString fileName, bool append, QString &error); bool saveStreams(QString fileName, QString fileType, QString &error); // ------------ Device Group ----------- // uint newDeviceGroupId(); int numDeviceGroups() const; const OstProto::DeviceGroup* deviceGroupByIndex(int index) const; OstProto::DeviceGroup* mutableDeviceGroupByIndex(int index); const OstProto::DeviceGroup* deviceGroupById(uint deviceGroupId) const; //! Used by StreamModel //@{ bool newDeviceGroupAt(int index, const OstProto::DeviceGroup *deviceGroup = NULL); bool deleteDeviceGroupAt(int index); //@} //! Used by MyService::Stub to update from config received from server //@{ bool insertDeviceGroup(uint deviceGroupId); bool updateDeviceGroup(uint deviceGroupId, OstProto::DeviceGroup *deviceGroup); //@} // ------------ Device ----------- // int numDevices(); const OstEmul::Device* deviceByIndex(int index); //! Used by MyService::Stub to update from config received from server void clearDeviceList(); void insertDevice(const OstEmul::Device &device); const OstEmul::DeviceNeighborList* deviceNeighbors(int deviceIndex); int numArp(int deviceIndex); int numArpResolved(int deviceIndex); int numNdp(int deviceIndex); int numNdpResolved(int deviceIndex); //! Used by MyService::Stub to update from config received from server void clearDeviceNeighbors(); void insertDeviceNeighbors(const OstEmul::DeviceNeighborList &neighList); void deviceInfoRefreshed(); signals: //! Used when local config changed and when config received from server void portRateChanged(int portGroupId, int portId); //! Used by MyService::Stub to update from config received from server //@{ void portDataChanged(int portGroupId, int portId); void deviceInfoChanged(); //@} //! Used when local config changed //@{ void streamListChanged(int portGroupId, int portId); void localConfigChanged(int portGroupId, int portId, bool changed); //@} }; #endif ostinato-0.9/client/portconfigdialog.cpp000066400000000000000000000101341321315675200205420ustar00rootroot00000000000000/* Copyright (C) 2011 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "portconfigdialog.h" #include "settings.h" PortConfigDialog::PortConfigDialog( OstProto::Port &portConfig, const OstProto::PortState &portState, QWidget *parent) : QDialog(parent), portConfig_(portConfig) { QString currentUser(portConfig_.user_name().c_str()); qDebug("In %s", __FUNCTION__); setupUi(this); switch(portConfig_.transmit_mode()) { case OstProto::kSequentialTransmit: sequentialStreamsButton->setChecked(true); break; case OstProto::kInterleavedTransmit: interleavedStreamsButton->setChecked(true); break; default: Q_ASSERT(false); // Unreachable!!! break; } // Port Reservation myself_ = appSettings->value(kUserKey, kUserDefaultValue).toString(); // XXX: what if myself_ is empty? if (currentUser.isEmpty()) { reservedBy_ = kNone; reservedBy->setText("Unreserved"); reserveButton->setText("Reserve"); } else if (currentUser == myself_) { reservedBy_ = kSelf; reservedBy->setText("Reserved by: me ("+currentUser+")"); reserveButton->setText("Reserve (uncheck to unreserve)"); reserveButton->setChecked(true); } else { reservedBy_ = kOther; reservedBy->setText("Reserved by: "+currentUser+""); reserveButton->setText("Force reserve"); } qDebug("reservedBy_ = %d", reservedBy_); exclusiveControlButton->setChecked(portConfig_.is_exclusive_control()); streamStatsButton->setChecked(portConfig_.is_tracking_stream_stats()); // Disable UI elements based on portState if (portState.is_transmit_on()) { transmitModeBox->setDisabled(true); streamStatsButton->setDisabled(true); } } void PortConfigDialog::accept() { OstProto::Port pc; if (sequentialStreamsButton->isChecked()) pc.set_transmit_mode(OstProto::kSequentialTransmit); else if (interleavedStreamsButton->isChecked()) pc.set_transmit_mode(OstProto::kInterleavedTransmit); else Q_ASSERT(false); // Unreachable!!! switch (reservedBy_) { case kSelf: if (!reserveButton->isChecked()) pc.set_user_name(""); // unreserve break; case kOther: case kNone: if (reserveButton->isChecked()) pc.set_user_name(myself_.toStdString()); // (force) reserve break; default: qWarning("Unreachable code"); break; } pc.set_is_exclusive_control(exclusiveControlButton->isChecked()); pc.set_is_tracking_stream_stats(streamStatsButton->isChecked()); // Update fields that have changed, clear the rest if (pc.transmit_mode() != portConfig_.transmit_mode()) portConfig_.set_transmit_mode(pc.transmit_mode()); else portConfig_.clear_transmit_mode(); if (pc.user_name() != portConfig_.user_name()) portConfig_.set_user_name(pc.user_name()); else portConfig_.clear_user_name(); if (pc.is_exclusive_control() != portConfig_.is_exclusive_control()) portConfig_.set_is_exclusive_control(pc.is_exclusive_control()); else portConfig_.clear_is_exclusive_control(); if (pc.is_tracking_stream_stats() != portConfig_.is_tracking_stream_stats()) portConfig_.set_is_tracking_stream_stats(pc.is_tracking_stream_stats()); else portConfig_.clear_is_tracking_stream_stats(); QDialog::accept(); } ostinato-0.9/client/portconfigdialog.h000066400000000000000000000022461321315675200202140ustar00rootroot00000000000000/* Copyright (C) 2011 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _PORT_CONFIG_DIALOG_H #define _PORT_CONFIG_DIALOG_H #include "ui_portconfigdialog.h" #include "protocol.pb.h" #include class PortConfigDialog : public QDialog, public Ui::PortConfigDialog { public: PortConfigDialog(OstProto::Port &portConfig, const OstProto::PortState& portState, QWidget *parent); private: virtual void accept(); OstProto::Port &portConfig_; enum { kNone, kSelf, kOther } reservedBy_; QString myself_; }; #endif ostinato-0.9/client/portconfigdialog.ui000066400000000000000000000064461321315675200204100ustar00rootroot00000000000000 PortConfigDialog 0 0 244 257 Port Config Transmit Mode Sequential Streams true Interleaved Streams Reservation Reserved by: Reserve Exclusive Control Stream Statistics Qt::Vertical 226 71 Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::NoButton|QDialogButtonBox::Ok buttonBox accepted() PortConfigDialog accept() 234 205 157 214 buttonBox rejected() PortConfigDialog reject() 234 205 243 214 ostinato-0.9/client/portgroup.cpp000066400000000000000000001503421321315675200172570ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "portgroup.h" #include "jumpurl.h" #include "settings.h" #include "emulproto.pb.h" #include "fileformat.pb.h" #include #include #include #include #include #include #include #include #include #include #include using ::google::protobuf::NewCallback; extern QMainWindow *mainWindow; extern char *version; quint32 PortGroup::mPortGroupAllocId = 0; PortGroup::PortGroup(QString serverName, quint16 port) { // Allocate an id for self mPortGroupId = PortGroup::mPortGroupAllocId++; portIdList_ = new OstProto::PortIdList; portStatsList_ = new OstProto::PortStatsList; statsController = new PbRpcController(portIdList_, portStatsList_); isGetStatsPending_ = false; atConnectConfig_ = NULL; compat = kUnknown; reconnect = false; reconnectAfter = kMinReconnectWaitTime; reconnectTimer = new QTimer(this); reconnectTimer->setSingleShot(true); connect(reconnectTimer, SIGNAL(timeout()), this, SLOT(on_reconnectTimer_timeout())); rpcChannel = new PbRpcChannel(serverName, port, OstProto::Notification::default_instance()); serviceStub = new OstProto::OstService::Stub(rpcChannel); // FIXME(LOW):Can't for my life figure out why this ain't working! //QMetaObject::connectSlotsByName(this); connect(rpcChannel, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(on_rpcChannel_stateChanged(QAbstractSocket::SocketState))); connect(rpcChannel, SIGNAL(connected()), this, SLOT(on_rpcChannel_connected())); connect(rpcChannel, SIGNAL(disconnected()), this, SLOT(on_rpcChannel_disconnected())); connect(rpcChannel, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(on_rpcChannel_error(QAbstractSocket::SocketError))); connect(rpcChannel, SIGNAL(notification(int, ::google::protobuf::Message*)), this, SLOT(on_rpcChannel_notification(int, ::google::protobuf::Message*))); connect(this, SIGNAL(portListChanged(quint32)), this, SLOT(when_portListChanged(quint32)), Qt::QueuedConnection); } PortGroup::~PortGroup() { qDebug("PortGroup Destructor"); // Disconnect and free rpc channel etc. PortGroup::disconnectFromHost(); delete serviceStub; delete rpcChannel; delete statsController; delete atConnectConfig_; } void PortGroup::setConfigAtConnect(const OstProto::PortGroupContent *config) { if (!config) { delete atConnectConfig_; atConnectConfig_ = NULL; return; } if (!atConnectConfig_) atConnectConfig_ = new OstProto::PortGroupContent; atConnectConfig_->CopyFrom(*config); } int PortGroup::numReservedPorts() const { int count = 0; for (int i = 0; i < mPorts.size(); i++) { if (!mPorts[i]->userName().isEmpty()) count++; } return count; } const QString PortGroup::serverFullName() const { return serverPort() == DEFAULT_SERVER_PORT ? serverName() : QString("%1:%2").arg(serverName()).arg(serverPort()); } // ------------------------------------------------ // Slots // ------------------------------------------------ void PortGroup::on_reconnectTimer_timeout() { reconnectAfter *= 2; if (reconnectAfter > kMaxReconnectWaitTime) reconnectAfter = kMaxReconnectWaitTime; connectToHost(); } void PortGroup::on_rpcChannel_stateChanged(QAbstractSocket::SocketState state) { qDebug("state changed %d", state); switch (state) { case QAbstractSocket::UnconnectedState: case QAbstractSocket::ClosingState: break; default: emit portGroupDataChanged(mPortGroupId); } } void PortGroup::on_rpcChannel_connected() { OstProto::VersionInfo *verInfo = new OstProto::VersionInfo; OstProto::VersionCompatibility *verCompat = new OstProto::VersionCompatibility; qDebug("connected\n"); emit portGroupDataChanged(mPortGroupId); reconnectAfter = kMinReconnectWaitTime; qDebug("requesting version check ..."); verInfo->set_client_name("ostinato"); verInfo->set_version(version); PbRpcController *controller = new PbRpcController(verInfo, verCompat); serviceStub->checkVersion(controller, verInfo, verCompat, NewCallback(this, &PortGroup::processVersionCompatibility, controller)); } void PortGroup::processVersionCompatibility(PbRpcController *controller) { OstProto::VersionCompatibility *verCompat = static_cast(controller->response()); Q_ASSERT(verCompat != NULL); qDebug("got version result ..."); if (controller->Failed()) { qDebug("%s: rpc failed(%s)", __FUNCTION__, qPrintable(controller->ErrorString())); goto _error_exit; } if (verCompat->result() == OstProto::VersionCompatibility::kIncompatible) { qWarning("incompatible version %s (%s)", version, qPrintable(QString::fromStdString(verCompat->notes()))); compat = kIncompatible; emit portGroupDataChanged(mPortGroupId); QMessageBox msgBox; msgBox.setIcon(QMessageBox::Warning); msgBox.setTextFormat(Qt::RichText); msgBox.setStyleSheet("messagebox-text-interaction-flags: 5"); msgBox.setText(tr("The Drone agent at %1:%2 is incompatible with this " "Ostinato version - %3") .arg(serverName()) .arg(int(serverPort())) .arg(version)); msgBox.setInformativeText(QString::fromStdString(verCompat->notes())); msgBox.exec(); goto _error_exit; } compat = kCompatible; { OstProto::Void *void_ = new OstProto::Void; OstProto::PortIdList *portIdList = new OstProto::PortIdList; qDebug("requesting portlist ..."); PbRpcController *controller = new PbRpcController(void_, portIdList); serviceStub->getPortIdList(controller, void_, portIdList, NewCallback(this, &PortGroup::processPortIdList, controller)); } _error_exit: delete controller; } void PortGroup::on_rpcChannel_disconnected() { qDebug("disconnected\n"); emit portListAboutToBeChanged(mPortGroupId); while (!mPorts.isEmpty()) delete mPorts.takeFirst(); atConnectPortConfig_.clear(); emit portListChanged(mPortGroupId); emit portGroupDataChanged(mPortGroupId); isGetStatsPending_ = false; if (reconnect) { qDebug("starting reconnect timer for %d ms ...", reconnectAfter); reconnectTimer->start(reconnectAfter); } } void PortGroup::on_rpcChannel_error(QAbstractSocket::SocketError socketError) { qDebug("%s: error %d", __FUNCTION__, socketError); emit portGroupDataChanged(mPortGroupId); if (socketError == QAbstractSocket::RemoteHostClosedError) reconnect = false; qDebug("%s: state %d", __FUNCTION__, rpcChannel->state()); if ((rpcChannel->state() == QAbstractSocket::UnconnectedState) && reconnect) { qDebug("starting reconnect timer for %d ms...", reconnectAfter); reconnectTimer->start(reconnectAfter); } } void PortGroup::on_rpcChannel_notification(int notifType, ::google::protobuf::Message *notification) { OstProto::Notification *notif = dynamic_cast(notification); if (!notif) { qWarning("unable to dynamic cast notif"); return; } if (notifType != notif->notif_type()) { qWarning("notif type mismatch %d/%d msg = %s", notifType, notif->notif_type(), notification->DebugString().c_str()); return; } switch (notifType) { case OstProto::portConfigChanged: { if (!notif->port_id_list().port_id_size()) { qWarning("notif(portConfigChanged) has an empty port_id_list"); return; } OstProto::PortIdList *portIdList = new OstProto::PortIdList; OstProto::PortConfigList *portConfigList = new OstProto::PortConfigList; PbRpcController *controller = new PbRpcController(portIdList, portConfigList); portIdList->CopyFrom(notif->port_id_list()); serviceStub->getPortConfig(controller, portIdList, portConfigList, NewCallback(this, &PortGroup::processUpdatedPortConfig, controller)); break; } default: break; } } void PortGroup::when_portListChanged(quint32 /*portGroupId*/) { if (state() == QAbstractSocket::ConnectedState && numPorts() <= 0) { QMessageBox msgBox; msgBox.setIcon(QMessageBox::Warning); msgBox.setTextFormat(Qt::RichText); msgBox.setStyleSheet("messagebox-text-interaction-flags: 5"); QString msg = tr("

The portgroup %1:%2 does not contain any ports!

" "

Packet Transmit/Capture requires special privileges. " "Please ensure that you are running 'drone' - the agent " "component of Ostinato with required privileges.

") .arg(serverName()) .arg(int(serverPort())); msgBox.setText(msg); msgBox.setInformativeText(tr("See the Ostinato FAQ " "for instructions to fix this problem").arg(jumpUrl("noports"))); msgBox.exec(); } } void PortGroup::processPortIdList(PbRpcController *controller) { OstProto::PortIdList *portIdList = static_cast(controller->response()); Q_ASSERT(portIdList != NULL); qDebug("got a portlist ..."); if (controller->Failed()) { qDebug("%s: rpc failed(%s)", __FUNCTION__, qPrintable(controller->ErrorString())); goto _error_exit; } emit portListAboutToBeChanged(mPortGroupId); for(int i = 0; i < portIdList->port_id_size(); i++) { Port *p; p = new Port(portIdList->port_id(i).id(), mPortGroupId); connect(p, SIGNAL(portDataChanged(int, int)), this, SIGNAL(portGroupDataChanged(int, int))); connect(p, SIGNAL(localConfigChanged(int, int, bool)), this, SIGNAL(portGroupDataChanged(int, int))); qDebug("before port append\n"); mPorts.append(p); atConnectPortConfig_.append(NULL); // will be filled later } emit portListChanged(mPortGroupId); portIdList_->CopyFrom(*portIdList); // Request PortConfigList { qDebug("requesting port config list ..."); OstProto::PortIdList *portIdList2 = new OstProto::PortIdList(); OstProto::PortConfigList *portConfigList = new OstProto::PortConfigList(); PbRpcController *controller2 = new PbRpcController(portIdList2, portConfigList); portIdList2->CopyFrom(*portIdList); serviceStub->getPortConfig(controller, portIdList2, portConfigList, NewCallback(this, &PortGroup::processPortConfigList, controller2)); goto _exit; } _error_exit: _exit: delete controller; } void PortGroup::processPortConfigList(PbRpcController *controller) { OstProto::PortConfigList *portConfigList = static_cast(controller->response()); qDebug("In %s", __FUNCTION__); if (controller->Failed()) { qDebug("%s: rpc failed(%s)", __FUNCTION__, qPrintable(controller->ErrorString())); goto _error_exit; } //emit portListAboutToBeChanged(mPortGroupId); for(int i = 0; i < portConfigList->port_size(); i++) { uint id; id = portConfigList->port(i).port_id().id(); // FIXME: don't mix port id & index into mPorts[] mPorts[id]->updatePortConfig(portConfigList->mutable_port(i)); } // FIXME: Ideally we should emit portGroupDataChanged since only // port data is changing; but writing the corresponding slot in // PortStatsModel for that signal turned out to be very bug prone // causing assert failures when portgroups are added/deleted or // connected/disconnected in different orders // TODO: Revisit this when we refactor the domain-objects/model/view // design emit portListChanged(mPortGroupId); if (numPorts() > 0) { // XXX: The open session code (atConnectConfig_ related) assumes // the following two RPCs are invoked in the below order // Any change here without coressponding change in that code // will break stuff getDeviceGroupIdList(); getStreamIdList(); } // Now that we have the port details, let's identify which ports // need to be re-configured based on atConnectConfig_ if (atConnectConfig_ && numPorts() > 0) { QString myself = appSettings->value(kUserKey, kUserDefaultValue) .toString(); for (int i = 0; i < atConnectConfig_->ports_size(); i++) { const OstProto::PortContent *pc = &atConnectConfig_->ports(i); for (int j = 0; j < mPorts.size(); j++) { Port *port = mPorts[j]; if (port->name() == pc->port_config().name().c_str()) { if (!port->userName().isEmpty() // rsvd? && port->userName() != myself) // by someone else? { QString warning = QString("%1 - %2: %3 is reserved by %4.\n\n" "Port will not be reconfigured.") .arg(serverFullName()) .arg(j) .arg(port->userAlias()) .arg(port->userName()); QMessageBox::warning(NULL, tr("Open Session"), warning); qWarning("%s", qPrintable(warning)); continue; } atConnectPortConfig_[j] = pc; qDebug("port %d will be reconfigured", j); break; } } } } _error_exit: delete controller; } void PortGroup::when_configApply(int portIndex) { OstProto::StreamIdList *streamIdList; OstProto::StreamConfigList *streamConfigList; OstProto::Ack *ack; PbRpcController *controller; Q_ASSERT(portIndex < mPorts.size()); if (state() != QAbstractSocket::ConnectedState) return; QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); mainWindow->setDisabled(true); // FIXME: as currently written this code will make unnecessary RPCs // even if the request contains no data; the fix will need to take // care to identify when sync is complete // NOTE: DeviceGroup RPCs are no longer called unnecessarily; // Stream RPCs need to be fixed similarly // Also, drone currently updates its packet list at the end of // modifyStream() implicitly assuming that will be the last API // called - this will also need to be fixed // // Update/Sync DeviceGroups // OstProto::DeviceGroupIdList *deviceGroupIdList; OstProto::DeviceGroupConfigList *deviceGroupConfigList; bool refreshReqd = false; qDebug("applying 'deleted deviceGroups' ..."); deviceGroupIdList = new OstProto::DeviceGroupIdList; deviceGroupIdList->mutable_port_id()->set_id(mPorts[portIndex]->id()); mPorts[portIndex]->getDeletedDeviceGroupsSinceLastSync(*deviceGroupIdList); if (deviceGroupIdList->device_group_id_size()) { ack = new OstProto::Ack; controller = new PbRpcController(deviceGroupIdList, ack); serviceStub->deleteDeviceGroup(controller, deviceGroupIdList, ack, NewCallback(this, &PortGroup::processDeleteDeviceGroupAck, controller)); refreshReqd = true; } else delete deviceGroupIdList; qDebug("applying 'new deviceGroups' ..."); deviceGroupIdList = new OstProto::DeviceGroupIdList; deviceGroupIdList->mutable_port_id()->set_id(mPorts[portIndex]->id()); mPorts[portIndex]->getNewDeviceGroupsSinceLastSync(*deviceGroupIdList); if (deviceGroupIdList->device_group_id_size()) { ack = new OstProto::Ack; controller = new PbRpcController(deviceGroupIdList, ack); serviceStub->addDeviceGroup(controller, deviceGroupIdList, ack, NewCallback(this, &PortGroup::processAddDeviceGroupAck, controller)); refreshReqd = true; } else delete deviceGroupIdList; qDebug("applying 'modified deviceGroups' ..."); deviceGroupConfigList = new OstProto::DeviceGroupConfigList; deviceGroupConfigList->mutable_port_id()->set_id(mPorts[portIndex]->id()); mPorts[portIndex]->getModifiedDeviceGroupsSinceLastSync( *deviceGroupConfigList); if (deviceGroupConfigList->device_group_size()) { ack = new OstProto::Ack; controller = new PbRpcController(deviceGroupConfigList, ack); serviceStub->modifyDeviceGroup(controller, deviceGroupConfigList, ack, NewCallback(this, &PortGroup::processModifyDeviceGroupAck, portIndex, controller)); refreshReqd = true; } else delete deviceGroupConfigList; if (refreshReqd) getDeviceInfo(portIndex); // // Update/Sync Streams // qDebug("applying 'deleted streams' ..."); streamIdList = new OstProto::StreamIdList; ack = new OstProto::Ack; controller = new PbRpcController(streamIdList, ack); streamIdList->mutable_port_id()->set_id(mPorts[portIndex]->id()); mPorts[portIndex]->getDeletedStreamsSinceLastSync(*streamIdList); serviceStub->deleteStream(controller, streamIdList, ack, NewCallback(this, &PortGroup::processDeleteStreamAck, controller)); qDebug("applying 'new streams' ..."); streamIdList = new OstProto::StreamIdList; ack = new OstProto::Ack; controller = new PbRpcController(streamIdList, ack); streamIdList->mutable_port_id()->set_id(mPorts[portIndex]->id()); mPorts[portIndex]->getNewStreamsSinceLastSync(*streamIdList); serviceStub->addStream(controller, streamIdList, ack, NewCallback(this, &PortGroup::processAddStreamAck, controller)); qDebug("applying 'modified streams' ..."); streamConfigList = new OstProto::StreamConfigList; ack = new OstProto::Ack; controller = new PbRpcController(streamConfigList, ack); streamConfigList->mutable_port_id()->set_id(mPorts[portIndex]->id()); mPorts[portIndex]->getModifiedStreamsSinceLastSync(*streamConfigList); serviceStub->modifyStream(controller, streamConfigList, ack, NewCallback(this, &PortGroup::processModifyStreamAck, portIndex, controller)); } void PortGroup::processAddDeviceGroupAck(PbRpcController *controller) { qDebug("In %s", __FUNCTION__); delete controller; } void PortGroup::processDeleteDeviceGroupAck(PbRpcController *controller) { qDebug("In %s", __FUNCTION__); delete controller; } void PortGroup::processModifyDeviceGroupAck(int /*portIndex*/, PbRpcController *controller) { qDebug("In %s", __FUNCTION__); delete controller; } void PortGroup::processAddStreamAck(PbRpcController *controller) { qDebug("In %s", __FUNCTION__); delete controller; } void PortGroup::processDeleteStreamAck(PbRpcController *controller) { qDebug("In %s", __FUNCTION__); delete controller; } void PortGroup::processModifyStreamAck(int portIndex, PbRpcController *controller) { qDebug("In %s", __FUNCTION__); qDebug("apply completed"); mPorts[portIndex]->when_syncComplete(); mainWindow->setEnabled(true); QApplication::restoreOverrideCursor(); delete controller; } void PortGroup::getDeviceInfo(int portIndex) { OstProto::PortId *portId; OstProto::PortDeviceList *deviceList; OstProto::PortNeighborList *neighList; PbRpcController *controller; Q_ASSERT(portIndex < mPorts.size()); if (state() != QAbstractSocket::ConnectedState) return; portId = new OstProto::PortId; portId->set_id(mPorts[portIndex]->id()); deviceList = new OstProto::PortDeviceList; controller = new PbRpcController(portId, deviceList); serviceStub->getDeviceList(controller, portId, deviceList, NewCallback(this, &PortGroup::processDeviceList, portIndex, controller)); portId = new OstProto::PortId; portId->set_id(mPorts[portIndex]->id()); neighList = new OstProto::PortNeighborList; controller = new PbRpcController(portId, neighList); serviceStub->getDeviceNeighbors(controller, portId, neighList, NewCallback(this, &PortGroup::processDeviceNeighbors, portIndex, controller)); } void PortGroup::processDeviceList(int portIndex, PbRpcController *controller) { OstProto::PortDeviceList *deviceList = static_cast(controller->response()); qDebug("In %s (portIndex = %d)", __FUNCTION__, portIndex); if (controller->Failed()) { qDebug("%s: rpc failed(%s)", __FUNCTION__, qPrintable(controller->ErrorString())); goto _exit; } Q_ASSERT(portIndex < numPorts()); if (deviceList->port_id().id() != mPorts[portIndex]->id()) { qDebug("Invalid portId %d (expected %d) received for portIndex %d", deviceList->port_id().id(), mPorts[portIndex]->id(), portIndex); goto _exit; } mPorts[portIndex]->clearDeviceList(); for(int i = 0; i < deviceList->ExtensionSize(OstEmul::device); i++) { mPorts[portIndex]->insertDevice( deviceList->GetExtension(OstEmul::device, i)); } _exit: delete controller; } void PortGroup::processDeviceNeighbors( int portIndex, PbRpcController *controller) { OstProto::PortNeighborList *neighList = static_cast(controller->response()); qDebug("In %s (portIndex = %d)", __FUNCTION__, portIndex); if (controller->Failed()) { qDebug("%s: rpc failed(%s)", __FUNCTION__, qPrintable(controller->ErrorString())); goto _exit; } Q_ASSERT(portIndex < numPorts()); if (neighList->port_id().id() != mPorts[portIndex]->id()) { qDebug("Invalid portId %d (expected %d) received for portIndex %d", neighList->port_id().id(), mPorts[portIndex]->id(), portIndex); goto _exit; } mPorts[portIndex]->clearDeviceNeighbors(); for(int i=0; i < neighList->ExtensionSize(OstEmul::device_neighbor); i++) { mPorts[portIndex]->insertDeviceNeighbors( neighList->GetExtension(OstEmul::device_neighbor, i)); } mPorts[portIndex]->deviceInfoRefreshed(); _exit: delete controller; } void PortGroup::modifyPort(int portIndex, OstProto::Port portConfig) { OstProto::PortConfigList *portConfigList = new OstProto::PortConfigList; OstProto::Ack *ack = new OstProto::Ack; qDebug("%s: portIndex = %d", __FUNCTION__, portIndex); Q_ASSERT(portIndex < mPorts.size()); QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); mainWindow->setDisabled(true); OstProto::Port *port = portConfigList->add_port(); port->CopyFrom(portConfig); port->mutable_port_id()->set_id(mPorts[portIndex]->id()); PbRpcController *controller = new PbRpcController(portConfigList, ack); serviceStub->modifyPort(controller, portConfigList, ack, NewCallback(this, &PortGroup::processModifyPortAck, true, controller)); } void PortGroup::processModifyPortAck(bool restoreUi,PbRpcController *controller) { qDebug("In %s", __FUNCTION__); if (controller->Failed()) { qDebug("%s: rpc failed(%s)", __FUNCTION__, qPrintable(controller->ErrorString())); } if (restoreUi) { mainWindow->setEnabled(true); QApplication::restoreOverrideCursor(); } delete controller; } void PortGroup::processUpdatedPortConfig(PbRpcController *controller) { OstProto::PortConfigList *portConfigList = static_cast(controller->response()); qDebug("In %s", __FUNCTION__); if (controller->Failed()) { qDebug("%s: rpc failed(%s)", __FUNCTION__, qPrintable(controller->ErrorString())); goto _exit; } for(int i = 0; i < portConfigList->port_size(); i++) { uint id; id = portConfigList->port(i).port_id().id(); // FIXME: don't mix port id & index into mPorts[] mPorts[id]->updatePortConfig(portConfigList->mutable_port(i)); emit portGroupDataChanged(mPortGroupId, id); } _exit: delete controller; } void PortGroup::getStreamIdList() { for (int portIndex = 0; portIndex < numPorts(); portIndex++) { OstProto::PortId *portId = new OstProto::PortId; OstProto::StreamIdList *streamIdList = new OstProto::StreamIdList; PbRpcController *controller = new PbRpcController(portId, streamIdList); portId->set_id(mPorts[portIndex]->id()); serviceStub->getStreamIdList(controller, portId, streamIdList, NewCallback(this, &PortGroup::processStreamIdList, portIndex, controller)); } } void PortGroup::processStreamIdList(int portIndex, PbRpcController *controller) { OstProto::StreamIdList *streamIdList = static_cast(controller->response()); const OstProto::PortContent *newPortContent = atConnectPortConfig_.at(portIndex); qDebug("In %s (portIndex = %d)", __FUNCTION__, portIndex); if (controller->Failed()) { qDebug("%s: rpc failed(%s)", __FUNCTION__, qPrintable(controller->ErrorString())); goto _exit; } Q_ASSERT(portIndex < numPorts()); if (streamIdList->port_id().id() != mPorts[portIndex]->id()) { qDebug("Invalid portId %d (expected %d) received for portIndex %d", streamIdList->port_id().id(), mPorts[portIndex]->id(), portIndex); goto _exit; } if (newPortContent) { // This port needs to configured with new content - to do this // we'll insert the following RPC sequence at this point and once // this sequence is over, return to the regular RPC sequence by // re-requesting getStreamId() // * delete (existing) deviceGroups // (already done by processDeviceIdList) // * delete (existing) streams // * modify port // * add (new) deviceGroup ids // * modify (new) deviceGroups // * add (new) stream ids // * modify (new) streams // XXX: This assumes getDeviceGroupIdList() was invoked before // getStreamIdList() - if the order changes this code will break! // XXX: same name as input param, but shouldn't cause any problem PbRpcController *controller; quint32 portId = mPorts[portIndex]->id(); QString myself = appSettings->value(kUserKey, kUserDefaultValue) .toString(); // delete all existing streams if (streamIdList->stream_id_size()) { OstProto::StreamIdList *streamIdList2 = new OstProto::StreamIdList; streamIdList2->CopyFrom(*streamIdList); OstProto::Ack *ack = new OstProto::Ack; controller = new PbRpcController(streamIdList2, ack); serviceStub->deleteStream(controller, streamIdList2, ack, NewCallback(this, &PortGroup::processDeleteStreamAck, controller)); } OstProto::Port portCfg = newPortContent->port_config(); if (mPorts[portIndex]->modifiablePortConfig(portCfg)) { OstProto::PortConfigList *portConfigList = new OstProto::PortConfigList; OstProto::Port *port = portConfigList->add_port(); port->CopyFrom(portCfg); if (port->has_user_name()) port->set_user_name(qPrintable(myself)); // overwrite OstProto::Ack *ack = new OstProto::Ack; controller = new PbRpcController(portConfigList, ack); serviceStub->modifyPort(controller, portConfigList, ack, NewCallback(this, &PortGroup::processModifyPortAck, false, controller)); } // add/modify deviceGroups if (newPortContent->device_groups_size()) { OstProto::DeviceGroupIdList *deviceGroupIdList = new OstProto::DeviceGroupIdList; OstProto::DeviceGroupConfigList *deviceGroupConfigList = new OstProto::DeviceGroupConfigList; deviceGroupIdList->mutable_port_id()->set_id(portId); deviceGroupConfigList->mutable_port_id()->set_id(portId); for (int i = 0; i < newPortContent->device_groups_size(); i++) { const OstProto::DeviceGroup &dg = newPortContent->device_groups(i); deviceGroupIdList->add_device_group_id()->set_id( dg.device_group_id().id()); deviceGroupConfigList->add_device_group()->CopyFrom(dg); } OstProto::Ack *ack = new OstProto::Ack; controller = new PbRpcController(deviceGroupIdList, ack); serviceStub->addDeviceGroup(controller, deviceGroupIdList, ack, NewCallback(this, &PortGroup::processAddDeviceGroupAck, controller)); ack = new OstProto::Ack; controller = new PbRpcController(deviceGroupConfigList, ack); serviceStub->modifyDeviceGroup(controller, deviceGroupConfigList, ack, NewCallback(this, &PortGroup::processModifyDeviceGroupAck, portIndex, controller)); } // add/modify streams if (newPortContent->streams_size()) { OstProto::StreamIdList *streamIdList = new OstProto::StreamIdList; OstProto::StreamConfigList *streamConfigList = new OstProto::StreamConfigList; streamIdList->mutable_port_id()->set_id(portId); streamConfigList->mutable_port_id()->set_id(portId); for (int i = 0; i < newPortContent->streams_size(); i++) { const OstProto::Stream &s = newPortContent->streams(i); streamIdList->add_stream_id()->set_id(s.stream_id().id()); streamConfigList->add_stream()->CopyFrom(s); } OstProto::Ack *ack = new OstProto::Ack; controller = new PbRpcController(streamIdList, ack); serviceStub->addStream(controller, streamIdList, ack, NewCallback(this, &PortGroup::processAddStreamAck, controller)); ack = new OstProto::Ack; controller = new PbRpcController(streamConfigList, ack); serviceStub->modifyStream(controller, streamConfigList, ack, NewCallback(this, &PortGroup::processModifyStreamAck, portIndex, controller)); } // delete newPortConfig atConnectPortConfig_[portIndex] = NULL; // return to normal sequence re-starting from // getDeviceGroupIdList() and getStreamIdList() OstProto::PortId *portId2 = new OstProto::PortId; portId2->set_id(portId); OstProto::DeviceGroupIdList *devGrpIdList = new OstProto::DeviceGroupIdList; controller = new PbRpcController(portId2, devGrpIdList); serviceStub->getDeviceGroupIdList(controller, portId2, devGrpIdList, NewCallback(this, &PortGroup::processDeviceGroupIdList, portIndex, controller)); portId2 = new OstProto::PortId; portId2->set_id(portId); OstProto::StreamIdList *streamIdList = new OstProto::StreamIdList; controller = new PbRpcController(portId2, streamIdList); serviceStub->getStreamIdList(controller, portId2, streamIdList, NewCallback(this, &PortGroup::processStreamIdList, portIndex, controller)); } else { for(int i = 0; i < streamIdList->stream_id_size(); i++) { uint streamId; streamId = streamIdList->stream_id(i).id(); mPorts[portIndex]->insertStream(streamId); } mPorts[portIndex]->when_syncComplete(); getStreamConfigList(portIndex); } _exit: delete controller; } void PortGroup::getStreamConfigList(int portIndex) { if (mPorts[portIndex]->numStreams() == 0) return; qDebug("requesting stream config list (port %d)...", portIndex); OstProto::StreamIdList *streamIdList = new OstProto::StreamIdList; OstProto::StreamConfigList *streamConfigList = new OstProto::StreamConfigList; PbRpcController *controller = new PbRpcController( streamIdList, streamConfigList); streamIdList->mutable_port_id()->set_id(mPorts[portIndex]->id()); for (int j = 0; j < mPorts[portIndex]->numStreams(); j++) { OstProto::StreamId *s = streamIdList->add_stream_id(); s->set_id(mPorts[portIndex]->streamByIndex(j)->id()); } serviceStub->getStreamConfig(controller, streamIdList, streamConfigList, NewCallback(this, &PortGroup::processStreamConfigList, portIndex, controller)); } void PortGroup::processStreamConfigList(int portIndex, PbRpcController *controller) { OstProto::StreamConfigList *streamConfigList = static_cast(controller->response()); qDebug("In %s", __PRETTY_FUNCTION__); Q_ASSERT(portIndex < numPorts()); if (controller->Failed()) { qDebug("%s: rpc failed(%s)", __FUNCTION__, qPrintable(controller->ErrorString())); goto _exit; } Q_ASSERT(portIndex < numPorts()); if (streamConfigList->port_id().id() != mPorts[portIndex]->id()) { qDebug("Invalid portId %d (expected %d) received for portIndex %d", streamConfigList->port_id().id(), mPorts[portIndex]->id(), portIndex); goto _exit; } for(int i = 0; i < streamConfigList->stream_size(); i++) { uint streamId; streamId = streamConfigList->stream(i).stream_id().id(); mPorts[portIndex]->updateStream(streamId, streamConfigList->mutable_stream(i)); } #if 0 // FIXME: incorrect check - will never be true if last port does not have any streams configured // Are we done for all ports? if (portIndex >= (numPorts()-1)) { // FIXME(HI): some way to reset streammodel } #endif _exit: delete controller; } void PortGroup::getDeviceGroupIdList() { using OstProto::PortId; using OstProto::DeviceGroupIdList; for (int portIndex = 0; portIndex < numPorts(); portIndex++) { PortId *portId = new PortId; DeviceGroupIdList *devGrpIdList = new DeviceGroupIdList; PbRpcController *controller = new PbRpcController(portId, devGrpIdList); portId->set_id(mPorts[portIndex]->id()); serviceStub->getDeviceGroupIdList(controller, portId, devGrpIdList, NewCallback(this, &PortGroup::processDeviceGroupIdList, portIndex, controller)); } } void PortGroup::processDeviceGroupIdList( int portIndex, PbRpcController *controller) { using OstProto::DeviceGroupIdList; DeviceGroupIdList *devGrpIdList = static_cast( controller->response()); const OstProto::PortContent *newPortContent = atConnectPortConfig_.at( portIndex); qDebug("In %s (portIndex = %d)", __FUNCTION__, portIndex); if (controller->Failed()) { qDebug("%s: rpc failed(%s)", __FUNCTION__, qPrintable(controller->ErrorString())); goto _exit; } Q_ASSERT(portIndex < numPorts()); if (devGrpIdList->port_id().id() != mPorts[portIndex]->id()) { qDebug("Invalid portId %d (expected %d) received for portIndex %d", devGrpIdList->port_id().id(), mPorts[portIndex]->id(), portIndex); goto _exit; } if (newPortContent) { // We delete all existing deviceGroups // Remaining stuff is done in processStreamIdList() - see notes there if (devGrpIdList->device_group_id_size()) { OstProto::DeviceGroupIdList *devGrpIdList2 = new OstProto::DeviceGroupIdList; devGrpIdList2->CopyFrom(*devGrpIdList); OstProto::Ack *ack = new OstProto::Ack; PbRpcController *controller = new PbRpcController(devGrpIdList2, ack); serviceStub->deleteDeviceGroup(controller, devGrpIdList2, ack, NewCallback(this, &PortGroup::processDeleteDeviceGroupAck, controller)); } } else { for(int i = 0; i < devGrpIdList->device_group_id_size(); i++) { uint devGrpId; devGrpId = devGrpIdList->device_group_id(i).id(); mPorts[portIndex]->insertDeviceGroup(devGrpId); } getDeviceGroupConfigList(portIndex); } _exit: delete controller; } void PortGroup::getDeviceGroupConfigList(int portIndex) { using OstProto::DeviceGroupId; using OstProto::DeviceGroupIdList; using OstProto::DeviceGroupConfigList; if (mPorts[portIndex]->numDeviceGroups() == 0) return; qDebug("requesting device group config list (port %d) ...", portIndex); DeviceGroupIdList *devGrpIdList = new DeviceGroupIdList; DeviceGroupConfigList *devGrpCfgList = new DeviceGroupConfigList; PbRpcController *controller = new PbRpcController( devGrpIdList, devGrpCfgList); devGrpIdList->mutable_port_id()->set_id(mPorts[portIndex]->id()); for (int j = 0; j < mPorts[portIndex]->numDeviceGroups(); j++) { DeviceGroupId *dgid = devGrpIdList->add_device_group_id(); dgid->set_id(mPorts[portIndex]->deviceGroupByIndex(j) ->device_group_id().id()); } serviceStub->getDeviceGroupConfig(controller, devGrpIdList, devGrpCfgList, NewCallback(this, &PortGroup::processDeviceGroupConfigList, portIndex, controller)); } void PortGroup::processDeviceGroupConfigList(int portIndex, PbRpcController *controller) { using OstProto::DeviceGroupConfigList; DeviceGroupConfigList *devGrpCfgList = static_cast(controller->response()); qDebug("In %s", __PRETTY_FUNCTION__); Q_ASSERT(portIndex < numPorts()); if (controller->Failed()) { qDebug("%s: rpc failed(%s)", __FUNCTION__, qPrintable(controller->ErrorString())); goto _exit; } Q_ASSERT(portIndex < numPorts()); if (devGrpCfgList->port_id().id() != mPorts[portIndex]->id()) { qDebug("Invalid portId %d (expected %d) received for portIndex %d", devGrpCfgList->port_id().id(), mPorts[portIndex]->id(), portIndex); goto _exit; } for(int i = 0; i < devGrpCfgList->device_group_size(); i++) { uint dgid = devGrpCfgList->device_group(i).device_group_id().id(); mPorts[portIndex]->updateDeviceGroup(dgid, devGrpCfgList->mutable_device_group(i)); } if (devGrpCfgList->device_group_size()) getDeviceInfo(portIndex); #if 0 // FIXME: incorrect check - will never be true if last port does not have any deviceGroups configured // Are we done for all ports? if (portIndex >= (numPorts()-1)) { // FIXME: reset deviceGroupModel? } #endif _exit: delete controller; } void PortGroup::startTx(QList *portList) { qDebug("In %s", __FUNCTION__); if (state() != QAbstractSocket::ConnectedState) goto _exit; if (portList == NULL) goto _exit; { OstProto::PortIdList *portIdList = new OstProto::PortIdList; OstProto::Ack *ack = new OstProto::Ack; PbRpcController *controller = new PbRpcController(portIdList, ack); for (int i = 0; i < portList->size(); i++) { OstProto::PortId *portId = portIdList->add_port_id(); portId->set_id(portList->at(i)); } serviceStub->startTransmit(controller, portIdList, ack, NewCallback(this, &PortGroup::processStartTxAck, controller)); } _exit: return; } void PortGroup::processStartTxAck(PbRpcController *controller) { qDebug("In %s", __FUNCTION__); delete controller; } void PortGroup::stopTx(QList *portList) { qDebug("In %s", __FUNCTION__); if (state() != QAbstractSocket::ConnectedState) goto _exit; if ((portList == NULL) || (portList->size() == 0)) goto _exit; { OstProto::PortIdList *portIdList = new OstProto::PortIdList; OstProto::Ack *ack = new OstProto::Ack; PbRpcController *controller = new PbRpcController(portIdList, ack); for (int i = 0; i < portList->size(); i++) { OstProto::PortId *portId = portIdList->add_port_id(); portId->set_id(portList->at(i)); } serviceStub->stopTransmit(controller, portIdList, ack, NewCallback(this, &PortGroup::processStopTxAck, controller)); } _exit: return; } void PortGroup::processStopTxAck(PbRpcController *controller) { qDebug("In %s", __FUNCTION__); delete controller; } void PortGroup::startCapture(QList *portList) { qDebug("In %s", __FUNCTION__); if (state() != QAbstractSocket::ConnectedState) return; if ((portList == NULL) || (portList->size() == 0)) goto _exit; { OstProto::PortIdList *portIdList = new OstProto::PortIdList; OstProto::Ack *ack = new OstProto::Ack; PbRpcController *controller = new PbRpcController(portIdList, ack); for (int i = 0; i < portList->size(); i++) { OstProto::PortId *portId = portIdList->add_port_id(); portId->set_id(portList->at(i)); } serviceStub->startCapture(controller, portIdList, ack, NewCallback(this, &PortGroup::processStartCaptureAck, controller)); } _exit: return; } void PortGroup::processStartCaptureAck(PbRpcController *controller) { qDebug("In %s", __FUNCTION__); delete controller; } void PortGroup::stopCapture(QList *portList) { qDebug("In %s", __FUNCTION__); if (state() != QAbstractSocket::ConnectedState) return; if ((portList == NULL) || (portList->size() == 0)) goto _exit; { OstProto::PortIdList *portIdList = new OstProto::PortIdList; OstProto::Ack *ack = new OstProto::Ack; PbRpcController *controller = new PbRpcController(portIdList, ack); for (int i = 0; i < portList->size(); i++) { OstProto::PortId *portId = portIdList->add_port_id(); portId->set_id(portList->at(i)); } serviceStub->stopCapture(controller, portIdList, ack, NewCallback(this, &PortGroup::processStopCaptureAck, controller)); } _exit: return; } void PortGroup::processStopCaptureAck(PbRpcController *controller) { qDebug("In %s", __FUNCTION__); delete controller; } void PortGroup::viewCapture(QList *portList) { qDebug("In %s", __FUNCTION__); if (state() != QAbstractSocket::ConnectedState) goto _exit; if ((portList == NULL) || (portList->size() != 1)) goto _exit; for (int i = 0; i < portList->size(); i++) { OstProto::PortId *portId = new OstProto::PortId; OstProto::CaptureBuffer *buf = new OstProto::CaptureBuffer; PbRpcController *controller = new PbRpcController(portId, buf); QFile *capFile = mPorts[portList->at(i)]->getCaptureFile(); portId->set_id(portList->at(i)); capFile->open(QIODevice::ReadWrite|QIODevice::Truncate); qDebug("Temp CapFile = %s", capFile->fileName().toAscii().constData()); controller->setBinaryBlob(capFile); serviceStub->getCaptureBuffer(controller, portId, buf, NewCallback(this, &PortGroup::processViewCaptureAck, controller)); } _exit: return; } void PortGroup::processViewCaptureAck(PbRpcController *controller) { QFile *capFile = static_cast(controller->binaryBlob()); QString viewer = appSettings->value(kWiresharkPathKey, kWiresharkPathDefaultValue).toString(); qDebug("In %s", __FUNCTION__); capFile->flush(); capFile->close(); if (!QFile::exists(viewer)) { QMessageBox::warning(NULL, "Can't find Wireshark", viewer + QString(" does not exist!\n\nPlease correct the path" " to Wireshark in the Preferences.")); goto _exit; } if (!QProcess::startDetached(viewer, QStringList() << capFile->fileName())) qDebug("Failed starting Wireshark"); _exit: delete controller; } void PortGroup::resolveDeviceNeighbors(QList *portList) { qDebug("In %s", __FUNCTION__); if (state() != QAbstractSocket::ConnectedState) return; if ((portList == NULL) || (portList->size() == 0)) goto _exit; { OstProto::PortIdList *portIdList = new OstProto::PortIdList; OstProto::Ack *ack = new OstProto::Ack; PbRpcController *controller = new PbRpcController(portIdList, ack); for (int i = 0; i < portList->size(); i++) { OstProto::PortId *portId = portIdList->add_port_id(); portId->set_id(portList->at(i)); } serviceStub->resolveDeviceNeighbors(controller, portIdList, ack, NewCallback(this, &PortGroup::processResolveDeviceNeighborsAck, controller)); } _exit: return; } void PortGroup::processResolveDeviceNeighborsAck(PbRpcController *controller) { qDebug("In %s", __FUNCTION__); delete controller; } void PortGroup::clearDeviceNeighbors(QList *portList) { qDebug("In %s", __FUNCTION__); if (state() != QAbstractSocket::ConnectedState) return; if ((portList == NULL) || (portList->size() == 0)) goto _exit; { OstProto::PortIdList *portIdList = new OstProto::PortIdList; OstProto::Ack *ack = new OstProto::Ack; PbRpcController *controller = new PbRpcController(portIdList, ack); for (int i = 0; i < portList->size(); i++) { OstProto::PortId *portId = portIdList->add_port_id(); portId->set_id(portList->at(i)); } serviceStub->clearDeviceNeighbors(controller, portIdList, ack, NewCallback(this, &PortGroup::processClearDeviceNeighborsAck, controller)); } _exit: return; } void PortGroup::processClearDeviceNeighborsAck(PbRpcController *controller) { qDebug("In %s", __FUNCTION__); delete controller; } void PortGroup::getPortStats() { //qDebug("In %s", __FUNCTION__); if (state() != QAbstractSocket::ConnectedState) goto _exit; if (numPorts() <= 0) goto _exit; if (isGetStatsPending_) goto _exit; statsController->Reset(); isGetStatsPending_ = true; serviceStub->getStats(statsController, static_cast(statsController->request()), static_cast(statsController->response()), NewCallback(this, &PortGroup::processPortStatsList)); _exit: return; } void PortGroup::processPortStatsList() { //qDebug("In %s", __FUNCTION__); if (statsController->Failed()) { qDebug("%s: rpc failed(%s)", __FUNCTION__, qPrintable(statsController->ErrorString())); goto _error_exit; } for(int i = 0; i < portStatsList_->port_stats_size(); i++) { uint id = portStatsList_->port_stats(i).port_id().id(); // FIXME: don't mix port id & index into mPorts[] mPorts[id]->updateStats(portStatsList_->mutable_port_stats(i)); } emit statsChanged(mPortGroupId); _error_exit: isGetStatsPending_ = false; } void PortGroup::clearPortStats(QList *portList) { qDebug("In %s", __FUNCTION__); if (state() != QAbstractSocket::ConnectedState) goto _exit; { OstProto::PortIdList *portIdList = new OstProto::PortIdList; OstProto::Ack *ack = new OstProto::Ack; PbRpcController *controller = new PbRpcController(portIdList, ack); if (portList == NULL) portIdList->CopyFrom(*portIdList_); else { for (int i = 0; i < portList->size(); i++) { OstProto::PortId *portId = portIdList->add_port_id(); portId->set_id(portList->at(i)); } } serviceStub->clearStats(controller, portIdList, ack, NewCallback(this, &PortGroup::processClearPortStatsAck, controller)); } _exit: return; } void PortGroup::processClearPortStatsAck(PbRpcController *controller) { qDebug("In %s", __FUNCTION__); // Refresh stats immediately after a stats clear/reset getPortStats(); delete controller; } bool PortGroup::clearStreamStats(QList *portList) { qDebug("In %s", __FUNCTION__); if (state() != QAbstractSocket::ConnectedState) return false; OstProto::StreamGuidList *guidList = new OstProto::StreamGuidList; OstProto::Ack *ack = new OstProto::Ack; PbRpcController *controller = new PbRpcController(guidList, ack); if (portList == NULL) guidList->mutable_port_id_list()->CopyFrom(*portIdList_); else for (int i = 0; i < portList->size(); i++) guidList->mutable_port_id_list()->add_port_id() ->set_id(portList->at(i)); serviceStub->clearStreamStats(controller, guidList, ack, NewCallback(this, &PortGroup::processClearStreamStatsAck, controller)); return true; } void PortGroup::processClearStreamStatsAck(PbRpcController *controller) { qDebug("In %s", __FUNCTION__); delete controller; } bool PortGroup::getStreamStats(QList *portList) { qDebug("In %s", __FUNCTION__); if (state() != QAbstractSocket::ConnectedState) return false; OstProto::StreamGuidList *guidList = new OstProto::StreamGuidList; OstProto::StreamStatsList *statsList = new OstProto::StreamStatsList; PbRpcController *controller = new PbRpcController(guidList, statsList); if (portList == NULL) guidList->mutable_port_id_list()->CopyFrom(*portIdList_); else for (int i = 0; i < portList->size(); i++) guidList->mutable_port_id_list()->add_port_id() ->set_id(portList->at(i)); serviceStub->getStreamStats(controller, guidList, statsList, NewCallback(this, &PortGroup::processStreamStatsList, controller)); return true; } void PortGroup::processStreamStatsList(PbRpcController *controller) { using OstProto::StreamStatsList; qDebug("In %s", __FUNCTION__); StreamStatsList *streamStatsList = static_cast(controller->response()); // XXX: It is required to emit the signal even if the returned // streamStatsList contains no records since the recipient // StreamStatsModel slot needs to disconnect this signal-slot // connection to prevent future stream stats for this portgroup // to be sent to it emit streamStatsReceived(mPortGroupId, streamStatsList); delete controller; } ostinato-0.9/client/portgroup.h000066400000000000000000000153241321315675200167240ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _PORT_GROUP_H #define _PORT_GROUP_H #include "port.h" #include #include #include "../common/protocol.pb.h" #include "pbrpcchannel.h" /* TODO HIGH MED LOW - Allow hostnames in addition to IP Address as "server address" */ #define DEFAULT_SERVER_PORT 7878 namespace OstProto { class PortContent; class PortGroupContent; class StreamStatsList; } class QFile; class QTimer; class PortGroup : public QObject { Q_OBJECT private: enum { kIncompatible, kCompatible, kUnknown } compat; static quint32 mPortGroupAllocId; quint32 mPortGroupId; QString mUserAlias; // user defined bool reconnect; int reconnectAfter; // time in milliseconds static const int kMinReconnectWaitTime = 2000; // ms static const int kMaxReconnectWaitTime = 60000; // ms QTimer *reconnectTimer; PbRpcChannel *rpcChannel; PbRpcController *statsController; bool isGetStatsPending_; OstProto::OstService::Stub *serviceStub; OstProto::PortIdList *portIdList_; OstProto::PortStatsList *portStatsList_; OstProto::PortGroupContent *atConnectConfig_; QList atConnectPortConfig_; public: // FIXME(HIGH): member access QList mPorts; public: PortGroup(QString serverName = "127.0.0.1", quint16 port = DEFAULT_SERVER_PORT); ~PortGroup(); void connectToHost() { reconnect = true; compat = kUnknown; rpcChannel->establish(); } void connectToHost(QString serverName, quint16 port) { reconnect = true; compat = kUnknown; rpcChannel->establish(serverName, port); } void disconnectFromHost() { reconnect = false; rpcChannel->tearDown(); } void setConfigAtConnect(const OstProto::PortGroupContent *config); int numPorts() const { return mPorts.size(); } int numReservedPorts() const; quint32 id() const { return mPortGroupId; } const QString& userAlias() const { return mUserAlias; } void setUserAlias(QString alias) { mUserAlias = alias; }; const QString serverName() const { return rpcChannel->serverName(); } quint16 serverPort() const { return rpcChannel->serverPort(); } const QString serverFullName() const; QAbstractSocket::SocketState state() const { if (compat == kIncompatible) return QAbstractSocket::SocketState(-1); return rpcChannel->state(); } void processVersionCompatibility(PbRpcController *controller); void processPortIdList(PbRpcController *controller); void processPortConfigList(PbRpcController *controller); void processAddStreamAck(PbRpcController *controller); void processDeleteStreamAck(PbRpcController *controller); void processModifyStreamAck(int portIndex, PbRpcController *controller); void processAddDeviceGroupAck(PbRpcController *controller); void processDeleteDeviceGroupAck(PbRpcController *controller); void processModifyDeviceGroupAck(int portIndex, PbRpcController *controller); void processDeviceList(int portIndex, PbRpcController *controller); void processDeviceNeighbors(int portIndex, PbRpcController *controller); void modifyPort(int portId, OstProto::Port portConfig); void processModifyPortAck(bool restoreUi, PbRpcController *controller); void processUpdatedPortConfig(PbRpcController *controller); void getStreamIdList(); void processStreamIdList(int portIndex, PbRpcController *controller); void getStreamConfigList(int portIndex); void processStreamConfigList(int portIndex, PbRpcController *controller); void processModifyStreamAck(OstProto::Ack *ack); void getDeviceGroupIdList(); void processDeviceGroupIdList(int portIndex, PbRpcController *controller); void getDeviceGroupConfigList(int portIndex); void processDeviceGroupConfigList( int portIndex, PbRpcController *controller); void startTx(QList *portList = NULL); void processStartTxAck(PbRpcController *controller); void stopTx(QList *portList = NULL); void processStopTxAck(PbRpcController *controller); void startCapture(QList *portList = NULL); void processStartCaptureAck(PbRpcController *controller); void stopCapture(QList *portList = NULL); void processStopCaptureAck(PbRpcController *controller); void viewCapture(QList *portList = NULL); void processViewCaptureAck(PbRpcController *controller); void resolveDeviceNeighbors(QList *portList = NULL); void processResolveDeviceNeighborsAck(PbRpcController *controller); void clearDeviceNeighbors(QList *portList = NULL); void processClearDeviceNeighborsAck(PbRpcController *controller); void getPortStats(); void processPortStatsList(); void clearPortStats(QList *portList = NULL); void processClearPortStatsAck(PbRpcController *controller); bool clearStreamStats(QList *portList = NULL); void processClearStreamStatsAck(PbRpcController *controller); bool getStreamStats(QList *portList = NULL); void processStreamStatsList(PbRpcController *controller); signals: void portGroupDataChanged(int portGroupId, int portId = 0xFFFF); void portListAboutToBeChanged(quint32 portGroupId); void portListChanged(quint32 portGroupId); void statsChanged(quint32 portGroupId); void streamStatsReceived(quint32 portGroupId, const OstProto::StreamStatsList *stats); private slots: void on_reconnectTimer_timeout(); void on_rpcChannel_stateChanged(QAbstractSocket::SocketState state); void on_rpcChannel_connected(); void on_rpcChannel_disconnected(); void on_rpcChannel_error(QAbstractSocket::SocketError socketError); void on_rpcChannel_notification(int notifType, ::google::protobuf::Message *notification); void when_portListChanged(quint32 portGroupId); public slots: void when_configApply(int portIndex); void getDeviceInfo(int portIndex); }; #endif ostinato-0.9/client/portgrouplist.cpp000066400000000000000000000106121321315675200201460ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "portgrouplist.h" #include "params.h" // TODO(LOW): Remove #include PortGroupList::PortGroupList() : mPortGroupListModel(this), mStreamListModel(this), mPortStatsModel(this, this), mDeviceGroupModel(this), mDeviceModel(this) { #ifdef QT_NO_DEBUG streamModelTester_ = NULL; portModelTester_ = NULL; portStatsModelTester_ = NULL; deviceGroupModelTester_ = NULL; deviceModelTester_ = NULL; #else streamModelTester_ = new ModelTest(getStreamModel()); portModelTester_ = new ModelTest(getPortModel()); portStatsModelTester_ = new ModelTest(getPortStatsModel()); deviceGroupModelTester_ = new ModelTest(getDeviceGroupModel()); deviceModelTester_ = new ModelTest(getDeviceModel()); #endif // Add the "Local" Port Group if (appParams.optLocalDrone()) { PortGroup *pg = new PortGroup; addPortGroup(*pg); } } PortGroupList::~PortGroupList() { delete portStatsModelTester_; delete portModelTester_; delete streamModelTester_; delete deviceGroupModelTester_; while (!mPortGroups.isEmpty()) delete mPortGroups.takeFirst(); } bool PortGroupList::isPortGroup(const QModelIndex& index) { return mPortGroupListModel.isPortGroup(index); } bool PortGroupList::isPort(const QModelIndex& index) { return mPortGroupListModel.isPort(index); } PortGroup& PortGroupList::portGroup(const QModelIndex& index) { Q_ASSERT(mPortGroupListModel.isPortGroup(index)); return *(mPortGroups[index.row()]); } Port& PortGroupList::port(const QModelIndex& index) { Q_ASSERT(mPortGroupListModel.isPort(index)); return (*mPortGroups.at(index.parent().row())->mPorts[index.row()]); } void PortGroupList::addPortGroup(PortGroup &portGroup) { mPortGroupListModel.portGroupAboutToBeAppended(); connect(&portGroup, SIGNAL(portGroupDataChanged(int, int)), &mPortGroupListModel, SLOT(when_portGroupDataChanged(int, int))); #if 0 connect(&portGroup, SIGNAL(portListAboutToBeChanged(quint32)), &mPortGroupListModel, SLOT(triggerLayoutAboutToBeChanged())); connect(&portGroup, SIGNAL(portListChanged(quint32)), &mPortGroupListModel, SLOT(triggerLayoutChanged())); #endif connect(&portGroup, SIGNAL(portListChanged(quint32)), &mPortGroupListModel, SLOT(when_portListChanged())); connect(&portGroup, SIGNAL(portListChanged(quint32)), &mPortStatsModel, SLOT(when_portListChanged())); connect(&portGroup, SIGNAL(statsChanged(quint32)), &mPortStatsModel, SLOT(when_portGroup_stats_update(quint32))); mPortGroups.append(&portGroup); portGroup.connectToHost(); mPortGroupListModel.portGroupAppended(); mPortStatsModel.when_portListChanged(); } void PortGroupList::removePortGroup(PortGroup &portGroup) { mPortGroupListModel.portGroupAboutToBeRemoved(&portGroup); PortGroup* pg = mPortGroups.takeAt(mPortGroups.indexOf(&portGroup)); qDebug("after takeAt()"); mPortGroupListModel.portGroupRemoved(); delete pg; mPortStatsModel.when_portListChanged(); } void PortGroupList::removeAllPortGroups() { if (mPortGroups.isEmpty()) return; do { PortGroup *pg = mPortGroups.at(0); mPortGroupListModel.portGroupAboutToBeRemoved(pg); mPortGroups.removeFirst(); delete pg; } while (!mPortGroups.isEmpty()); mPortGroupListModel.portGroupRemoved(); mPortGroupListModel.when_portListChanged(); mPortStatsModel.when_portListChanged(); } //.................... // Private Methods //.................... int PortGroupList::indexOfPortGroup(quint32 portGroupId) { for (int i = 0; i < mPortGroups.size(); i++) { if (mPortGroups.value(i)->id() == portGroupId) return i; } return -1; } ostinato-0.9/client/portgrouplist.h000066400000000000000000000045441321315675200176220ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _PORT_GROUP_LIST_H #define _PORT_GROUP_LIST_H #include "devicegroupmodel.h" #include "devicemodel.h" #include "portgroup.h" #include "portmodel.h" #include "portstatsmodel.h" #include "streammodel.h" class PortModel; class StreamModel; class PortGroupList : public QObject { Q_OBJECT friend class PortModel; friend class StreamModel; friend class PortStatsModel; QList mPortGroups; PortModel mPortGroupListModel; StreamModel mStreamListModel; PortStatsModel mPortStatsModel; DeviceGroupModel mDeviceGroupModel; DeviceModel mDeviceModel; QObject *streamModelTester_; QObject *portModelTester_; QObject *portStatsModelTester_; QObject *deviceGroupModelTester_; QObject *deviceModelTester_; // Methods public: PortGroupList(); ~PortGroupList(); PortModel* getPortModel() { return &mPortGroupListModel; } PortStatsModel* getPortStatsModel() { return &mPortStatsModel; } StreamModel* getStreamModel() { return &mStreamListModel; } DeviceGroupModel* getDeviceGroupModel() { return &mDeviceGroupModel; } DeviceModel* getDeviceModel() { return &mDeviceModel; } bool isPortGroup(const QModelIndex& index); bool isPort(const QModelIndex& index); PortGroup& portGroup(const QModelIndex& index); Port& port(const QModelIndex& index); int numPortGroups() { return mPortGroups.size(); } PortGroup& portGroupByIndex(int index) { return *(mPortGroups[index]); } void addPortGroup(PortGroup &portGroup); void removePortGroup(PortGroup &portGroup); void removeAllPortGroups(); private: int indexOfPortGroup(quint32 portGroupId); }; #endif ostinato-0.9/client/portmodel.cpp000066400000000000000000000223001321315675200172130ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "portmodel.h" #include "portgrouplist.h" #include #include #if 0 #define DBG0(x) qDebug(x) #define DBG1(x, p1) qDebug(x, (p1)) #else #define DBG0(x) {} #define DBG1(x, p1) {} #endif PortModel::PortModel(PortGroupList *p, QObject *parent) : QAbstractItemModel(parent) { pgl = p; portIconFactory[OstProto::LinkStateUnknown][false] = QIcon(":/icons/bullet_white.png"); portIconFactory[OstProto::LinkStateDown][false] = QIcon(":/icons/bullet_red.png"); portIconFactory[OstProto::LinkStateUp][false] = QIcon(":/icons/bullet_green.png"); for (int linkState = 0; linkState < kLinkStatesCount; linkState++) { QPixmap pixmap(":/icons/deco_exclusive.png"); QPainter painter(&pixmap); QIcon icon = portIconFactory[linkState][false]; painter.drawPixmap(0, 0, icon.pixmap(QSize(32,32))); portIconFactory[linkState][true] = QIcon(pixmap); } } int PortModel::rowCount(const QModelIndex &parent) const { // qDebug("RowCount Enter\n"); if (!parent.isValid()) { // Top Level Item //qDebug("RowCount (Top) Exit: %d\n", pgl->mPortGroups.size()); return pgl->mPortGroups.size(); } // qDebug("RowCount non top %d, %d, %llx\n", // parent.row(), parent.column(), parent.internalId()); quint16 pg = (parent.internalId() >> 16) & 0xFFFF; quint16 p = parent.internalId() & 0xFFFF; if (p == 0xFFFF) { #if 0 // wrong code? int count = 0; foreach(PortGroup *pg, pgl->mPortGroups) { count += pg->numPorts(); } //qDebug("RowCount (Mid) Exit: %d\n", count); return count; #endif if (parent.column() == 0) return pgl->mPortGroups.value(pgl->indexOfPortGroup(pg))->numPorts(); else return 0; } else { // Leaf Item return 0; } } int PortModel::columnCount(const QModelIndex &/*parent*/) const { return 1; // FIXME: hardcoding } Qt::ItemFlags PortModel::flags(const QModelIndex &index) const { return QAbstractItemModel::flags(index); // FIXME: no need for this func } QVariant PortModel::data(const QModelIndex &index, int role) const { DBG0("Enter PortModel data\n"); // Check for a valid index if (!index.isValid()) return QVariant(); DBG1("PortModel::data(index).row = %d", index.row()); DBG1("PortModel::data(index).column = %0d", index.column()); DBG1("PortModel::data(index).internalId = %08llx", index.internalId()); QModelIndex parent = index.parent(); if (!parent.isValid()) { // Top Level Item - PortGroup if ((role == Qt::DisplayRole)) { DBG0("Exit PortModel data 1\n"); return QString("Port Group %1: %2 [%3]:%4 (%5)"). arg(pgl->mPortGroups.at(index.row())->id()). arg(pgl->mPortGroups.at(index.row())->userAlias()). arg(pgl->mPortGroups.at(index.row())->serverName()). arg(pgl->mPortGroups.at(index.row())->serverPort()). arg(pgl->mPortGroups.value(index.row())->numPorts()); } else if ((role == Qt::DecorationRole)) { DBG0("Exit PortModel data 2\n"); switch(pgl->mPortGroups.at(index.row())->state()) { case QAbstractSocket::UnconnectedState: return QIcon(":/icons/bullet_red.png"); case QAbstractSocket::HostLookupState: return QIcon(":/icons/bullet_yellow.png"); case QAbstractSocket::ConnectingState: case QAbstractSocket::ClosingState: return QIcon(":/icons/bullet_orange.png"); case QAbstractSocket::ConnectedState: return QIcon(":/icons/bullet_green.png"); case QAbstractSocket::BoundState: case QAbstractSocket::ListeningState: default: return QIcon(":/icons/bullet_error.png"); } } else { DBG0("Exit PortModel data 3\n"); return QVariant(); } } else { if (pgl->mPortGroups.at(parent.row())->numPorts() == 0) { DBG0("Exit PortModel data 4\n"); return QVariant(); } Port *port = pgl->mPortGroups.at(parent.row())->mPorts[index.row()]; // Non Top Level - Port if ((role == Qt::DisplayRole)) { QString rsvdBy; if (!port->userName().isEmpty()) rsvdBy = "["+port->userName()+"] "; return QString("Port %1: %2 %3(%4)") .arg(port->id()) .arg(port->userAlias()) .arg(rsvdBy) .arg(port->description()); } else if ((role == Qt::DecorationRole)) { return portIconFactory[port->linkState()][port->hasExclusiveControl()]; } else if ((role == Qt::ForegroundRole)) { return port->isDirty() ? QBrush(Qt::red) : QVariant(); } else { DBG0("Exit PortModel data 6\n"); return QVariant(); } } return QVariant(); } QVariant PortModel::headerData(int /*section*/, Qt::Orientation orientation, int role) const { if (role != Qt::DisplayRole) return QVariant(); if (orientation == Qt::Horizontal) return QVariant(); else return QString("Name"); } QModelIndex PortModel::index (int row, int col, const QModelIndex & parent) const { if (!hasIndex(row, col, parent)) return QModelIndex(); //qDebug("index: R=%d, C=%d, PR=%d, PC=%d, PID=%llx\n", // row, col, parent.row(), parent.column(), parent.internalId()); if (!parent.isValid()) { // Top Level Item quint16 pg = pgl->mPortGroups.value(row)->id(), p = 0xFFFF; quint32 id = (pg << 16) | p; //qDebug("index (top) dbg: PG=%d, P=%d, ID=%x\n", pg, p, id); return createIndex(row, col, id); } else { quint16 pg = parent.internalId() >> 16; quint16 p = pgl->mPortGroups.value(parent.row())->mPorts.value(row)->id(); quint32 id = (pg << 16) | p; //qDebug("index (nontop) dbg: PG=%d, P=%d, ID=%x\n", pg, p, id); return createIndex(row, col, id); } } QModelIndex PortModel::parent(const QModelIndex &index) const { if (!index.isValid()) return QModelIndex(); //qDebug("parent: R=%d, C=%d ID=%llx\n", // index.row(), index.column(), index.internalId()); quint16 pg = index.internalId() >> 16; quint16 p = index.internalId() & 0x0000FFFF; //qDebug("parent dbg: PG=%d, P=%d\n", pg, p); if (p == 0xFFFF) { //qDebug("parent ret: NULL\n"); // Top Level Item - PG return QModelIndex(); } quint32 id = (pg << 16) | 0xFFFF; //qDebug("parent ret: R=%d, C=%d, ID=%x\n", pg, 0, id); return createIndex(pgl->indexOfPortGroup(pg), 0, id); } bool PortModel::isPortGroup(const QModelIndex& index) { if (index.isValid() && ((index.internalId() & 0xFFFF) == 0xFFFF)) return true; else return false; } bool PortModel::isPort(const QModelIndex& index) { if (index.isValid() && ((index.internalId() & 0xFFFF) != 0xFFFF)) return true; else return false; } quint32 PortModel::portGroupId(const QModelIndex& index) { return (index.internalId()) >> 16 & 0xFFFF; } quint32 PortModel::portId(const QModelIndex& index) { return (index.internalId()) & 0xFFFF; } // ---------------------------------------------- // Slots // ---------------------------------------------- void PortModel::when_portGroupDataChanged(int portGroupId, int portId) { QModelIndex index; int row; qDebug("portGroupId = %d, portId = %d", portGroupId, portId); if (portId == 0xFFFF) row = pgl->indexOfPortGroup(portGroupId); else row = portId; index = createIndex(row, 0, (portGroupId << 16) | portId); emit dataChanged(index, index); } void PortModel::portGroupAboutToBeAppended() { int row; row = pgl->mPortGroups.size(); beginInsertRows(QModelIndex(), row, row); } void PortModel::portGroupAppended() { endInsertRows(); } void PortModel::portGroupAboutToBeRemoved(PortGroup *portGroup) { int row; row = pgl->mPortGroups.indexOf(portGroup); beginRemoveRows(QModelIndex(), row, row); } void PortModel::portGroupRemoved() { endRemoveRows(); } void PortModel::when_portListChanged() { reset(); } ostinato-0.9/client/portmodel.h000066400000000000000000000044301321315675200166640ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _PORT_MODEL_H #define _PORT_MODEL_H #include #include class PortGroupList; class PortGroup; class PortModel : public QAbstractItemModel { Q_OBJECT friend class PortGroupList; public: PortModel(PortGroupList *p, QObject *parent = 0); int rowCount(const QModelIndex &parent = QModelIndex()) const; int columnCount(const QModelIndex &parent = QModelIndex()) const; Qt::ItemFlags flags(const QModelIndex &index) const; QVariant data(const QModelIndex &index, int role) const; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; QModelIndex index (int row, int col, const QModelIndex &parent = QModelIndex()) const; QModelIndex parent(const QModelIndex &index) const; bool isPortGroup(const QModelIndex& index); bool isPort(const QModelIndex& index); quint32 portGroupId(const QModelIndex& index); quint32 portId(const QModelIndex& index); private: PortGroupList *pgl; static const int kLinkStatesCount = 3; static const int kExclusiveStatesCount = 2; QIcon portIconFactory[kLinkStatesCount][kExclusiveStatesCount]; private slots: // FIXME: these are invoked from outside - how come they are "private"? void when_portGroupDataChanged(int portGroupId, int portId); void portGroupAboutToBeAppended(); void portGroupAppended(); void portGroupAboutToBeRemoved(PortGroup *portGroup); void portGroupRemoved(); void when_portListChanged(); #if 0 void triggerLayoutAboutToBeChanged(); void triggerLayoutChanged(); #endif }; #endif ostinato-0.9/client/portstatsfilter.ui000066400000000000000000000105711321315675200203210ustar00rootroot00000000000000 PortStatsFilterDialog 0 0 319 193 Select Ports :/icons/portstats_filter.png false false QAbstractItemView::NoDragDrop QAbstractItemView::ExtendedSelection QListView::Static Qt::Vertical 20 40 > :/icons/arrow_right.png < :/icons/arrow_left.png Qt::Vertical 20 40 true true false QAbstractItemView::InternalMove QAbstractItemView::ExtendedSelection QListView::Free Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::NoButton|QDialogButtonBox::Ok lvUnselected tbSelectIn tbSelectOut lvSelected buttonBox buttonBox accepted() PortStatsFilterDialog accept() 248 254 157 274 buttonBox rejected() PortStatsFilterDialog reject() 316 260 286 274 ostinato-0.9/client/portstatsfilterdialog.cpp000066400000000000000000000074211321315675200216460ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "portstatsfilterdialog.h" PortStatsFilterDialog::PortStatsFilterDialog(QWidget *parent) : QDialog(parent) { setupUi(this); mUnselected.setSortRole(kLogicalIndex); mSelected.setSortRole(kVisualIndex); lvUnselected->setModel(&mUnselected); lvSelected->setModel(&mSelected); } QList PortStatsFilterDialog::getItemList(bool* ok, QAbstractItemModel *model, Qt::Orientation orientation, QList initial) { QList ret; uint count = (orientation == Qt::Vertical) ? model->rowCount() : model->columnCount(); *ok = false; mUnselected.clear(); mSelected.clear(); for (uint i = 0; i < count; i++) { QStandardItem *item; item = new QStandardItem(model->headerData(i, orientation).toString()); item->setData(i, kLogicalIndex); item->setData(initial.indexOf(i), kVisualIndex); item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsDragEnabled //| Qt::ItemIsDropEnabled | Qt::ItemIsEnabled); if (initial.contains(i)) mSelected.appendRow(item); else mUnselected.appendRow(item); } mSelected.sort(0); // No need to sort right now 'coz we have inserted items in order if (exec() == QDialog::Accepted) { uint count = mSelected.rowCount(); for (uint i = 0; i < count; i++) { QModelIndex index = mSelected.index(i, 0, QModelIndex()); QStandardItem *item = mSelected.itemFromIndex(index); ret.append(item->data(kLogicalIndex).toInt()); } *ok = true; } return ret; } void PortStatsFilterDialog::on_tbSelectIn_clicked() { QList rows; foreach(QModelIndex idx, lvUnselected->selectionModel()->selectedIndexes()) rows.append(idx.row()); qSort(rows.begin(), rows.end(), qGreater()); QModelIndex idx = lvSelected->selectionModel()->currentIndex(); int insertAt = idx.isValid() ? idx.row() : mSelected.rowCount(); foreach(int row, rows) { QList items = mUnselected.takeRow(row); mSelected.insertRow(insertAt, items); } } void PortStatsFilterDialog::on_tbSelectOut_clicked() { QList rows; foreach(QModelIndex idx, lvSelected->selectionModel()->selectedIndexes()) rows.append(idx.row()); qSort(rows.begin(), rows.end(), qGreater()); foreach(int row, rows) { QList items = mSelected.takeRow(row); mUnselected.appendRow(items); } mUnselected.sort(0); } void PortStatsFilterDialog::on_lvUnselected_doubleClicked(const QModelIndex &index) { QList items = mUnselected.takeRow(index.row()); QModelIndex idx = lvSelected->selectionModel()->currentIndex(); int insertAt = idx.isValid() ? idx.row() : mSelected.rowCount(); mSelected.insertRow(insertAt, items); } void PortStatsFilterDialog::on_lvSelected_doubleClicked(const QModelIndex &index) { QList items = mSelected.takeRow(index.row()); mUnselected.appendRow(items); mUnselected.sort(0); } ostinato-0.9/client/portstatsfilterdialog.h000066400000000000000000000031041321315675200213050ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _PORT_STATS_FILTER_DIALOG_H #define _PORT_STATS_FILTER_DIALOG_H #include #include #include #include "ui_portstatsfilter.h" #include "portgrouplist.h" class PortStatsFilterDialog : public QDialog, public Ui::PortStatsFilterDialog { Q_OBJECT public: PortStatsFilterDialog(QWidget *parent = 0); QList getItemList(bool* ok, QAbstractItemModel *model, Qt::Orientation orientation = Qt::Vertical, QList initial = QList()); private: enum ItemRole { kLogicalIndex = Qt::UserRole + 1, kVisualIndex }; QStandardItemModel mUnselected; QStandardItemModel mSelected; private slots: void on_tbSelectIn_clicked(); void on_tbSelectOut_clicked(); void on_lvUnselected_doubleClicked(const QModelIndex &index); void on_lvSelected_doubleClicked(const QModelIndex &index); }; #endif ostinato-0.9/client/portstatsmodel.cpp000066400000000000000000000221461321315675200203020ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "portstatsmodel.h" #include "portgrouplist.h" #include PortStatsModel::PortStatsModel(PortGroupList *p, QObject *parent) : QAbstractTableModel(parent) { pgl = p; timer = new QTimer(); connect(timer, SIGNAL(timeout()), this, SLOT(updateStats())); timer->start(1000); } PortStatsModel::~PortStatsModel() { timer->stop(); delete timer; } int PortStatsModel::rowCount(const QModelIndex &parent) const { if (parent.isValid()) return 0; if (numPorts.isEmpty()) return 0; if (numPorts.last() == 0) return 0; return (int) e_STAT_MAX; } int PortStatsModel::columnCount(const QModelIndex &parent ) const { if (parent.isValid()) return 0; else if (numPorts.isEmpty()) return 0; else return numPorts.last(); } void PortStatsModel::getDomainIndexes(const QModelIndex &index, uint &portGroupIdx, uint &portIdx) const { int portNum; // TODO(LOW): Optimize using binary search: see qLowerBound() portNum = index.column() + 1; for (portGroupIdx = 0; portGroupIdx < (uint) numPorts.size(); portGroupIdx++) if (portNum <= numPorts.at(portGroupIdx)) break; if (portGroupIdx) { if (numPorts.at(portGroupIdx -1)) portIdx = (portNum - 1) - numPorts.at(portGroupIdx - 1); else portIdx = portNum - 1; } else portIdx = portNum - 1; //qDebug("PSM: %d - %d, %d", index.column(), portGroupIdx, portIdx); } QVariant PortStatsModel::data(const QModelIndex &index, int role) const { uint pgidx, pidx; int row; // Check for a valid index if (!index.isValid()) return QVariant(); // Check for row/column limits row = index.row(); if (row >= e_STAT_MAX) return QVariant(); if (numPorts.isEmpty()) return QVariant(); if (index.column() >= (numPorts.last())) return QVariant(); getDomainIndexes(index, pgidx, pidx); // Check role if (role == Qt::DisplayRole) { OstProto::PortStats stats; stats = pgl->mPortGroups.at(pgidx)->mPorts[pidx]->getStats(); switch(row) { // Info case e_INFO_USER: return pgl->mPortGroups.at(pgidx)->mPorts[pidx]->userName(); // States case e_LINK_STATE: return LinkStateName.at(stats.state().link_state()); case e_TRANSMIT_STATE: return BoolStateName.at(stats.state().is_transmit_on()); case e_CAPTURE_STATE: return BoolStateName.at(stats.state().is_capture_on()); // Statistics case e_STAT_FRAMES_RCVD: return QString("%L1").arg(quint64(stats.rx_pkts())); case e_STAT_FRAMES_SENT: return QString("%L1").arg(quint64(stats.tx_pkts())); case e_STAT_FRAME_SEND_RATE: return QString("%L1").arg(quint64(stats.tx_pps())); case e_STAT_FRAME_RECV_RATE: return QString("%L1").arg(quint64(stats.rx_pps())); case e_STAT_BYTES_RCVD: return QString("%L1").arg(quint64(stats.rx_bytes())); case e_STAT_BYTES_SENT: return QString("%L1").arg(quint64(stats.tx_bytes())); case e_STAT_BYTE_SEND_RATE: return QString("%L1").arg(quint64(stats.tx_bps())); case e_STAT_BYTE_RECV_RATE: return QString("%L1").arg(quint64(stats.rx_bps())); #if 0 case e_STAT_FRAMES_RCVD_NIC: return stats.rx_pkts_nic(); case e_STAT_FRAMES_SENT_NIC: return stats.tx_pkts_nic(); case e_STAT_BYTES_RCVD_NIC: return stats.rx_bytes_nic(); case e_STAT_BYTES_SENT_NIC: return stats.tx_bytes_nic(); #endif case e_STAT_RX_DROPS: return QString("%L1").arg(quint64(stats.rx_drops())); case e_STAT_RX_ERRORS: return QString("%L1").arg(quint64(stats.rx_errors())); case e_STAT_RX_FIFO_ERRORS: return QString("%L1").arg(quint64(stats.rx_fifo_errors())); case e_STAT_RX_FRAME_ERRORS: return QString("%L1").arg(quint64(stats.rx_frame_errors())); default: qWarning("%s: Unhandled stats id %d\n", __FUNCTION__, index.row()); return 0; } } else if (role == Qt::TextAlignmentRole) { if (row >= e_STATISTICS_START && row <= e_STATISTICS_END) return Qt::AlignRight; // right-align numbers else return Qt::AlignHCenter; // center-align everything else } else return QVariant(); } QVariant PortStatsModel::headerData(int section, Qt::Orientation orientation, int role) const { if (role == Qt::ToolTipRole) { if (orientation == Qt::Horizontal) { QString notes; uint portGroupIdx, portIdx; if (numPorts.isEmpty() || section >= numPorts.last()) return QVariant(); getDomainIndexes(index(0, section), portGroupIdx, portIdx); notes = pgl->mPortGroups.at(portGroupIdx)->mPorts[portIdx]->notes(); if (!notes.isEmpty()) return notes; else return QVariant(); } else return QVariant(); } if (role != Qt::DisplayRole) return QVariant(); if (orientation == Qt::Horizontal) { uint portGroupIdx, portIdx; QString portName; if (numPorts.isEmpty() || section >= numPorts.last()) return QVariant(); getDomainIndexes(index(0, section), portGroupIdx, portIdx); portName = QString("Port %1-%2") .arg(pgl->mPortGroups.at(portGroupIdx)->id()) .arg(pgl->mPortGroups.at(portGroupIdx)->mPorts.at(portIdx)->id()); if (portGroupIdx < (uint) pgl->mPortGroups.size() && portIdx < (uint) pgl->mPortGroups.at(portGroupIdx)->mPorts.size()) { if (!pgl->mPortGroups.at(portGroupIdx)->mPorts[portIdx]->notes() .isEmpty()) portName += " *"; } return portName; } else return PortStatName.at(section); } void PortStatsModel::portListFromIndex(QModelIndexList indices, QList &portList) { int i, j; QModelIndexList selectedCols(indices); portList.clear(); //selectedCols = indices.selectedColumns(); for (i = 0; i < selectedCols.size(); i++) { uint portGroupIdx, portIdx; getDomainIndexes(selectedCols.at(i), portGroupIdx, portIdx); for (j = 0; j < portList.size(); j++) { if (portList[j].portGroupId == portGroupIdx) break; } if (j >= portList.size()) { // PortGroup Not found PortGroupAndPortList p; p.portGroupId = portGroupIdx; p.portList.append(portIdx); portList.append(p); } else { // PortGroup found portList[j].portList.append(portIdx); } } } // // Slots // void PortStatsModel::when_portListChanged() { int i, count = 0; // recalc numPorts while (numPorts.size()) numPorts.removeFirst(); for (i = 0; i < pgl->mPortGroups.size(); i++) { count += pgl->mPortGroups.at(i)->numPorts(); numPorts.append(count); } reset(); } // FIXME: unused? if used, the index calculation row/column needs to be swapped #if 0 void PortStatsModel::on_portStatsUpdate(int port, void* /*stats*/) { QModelIndex topLeft = index(port, 0, QModelIndex()); QModelIndex bottomRight = index(port, e_STAT_MAX, QModelIndex()); emit dataChanged(topLeft, bottomRight); } #endif void PortStatsModel::updateStats() { // Request each portgroup to fetch updated stats - the port group // raises a signal once updated stats are available for (int i = 0; i < pgl->mPortGroups.size(); i++) pgl->mPortGroups[i]->getPortStats(); } void PortStatsModel::when_portGroup_stats_update(quint32 /*portGroupId*/) { // FIXME(MED): update only the changed ports, not all QModelIndex topLeft = index(0, 0, QModelIndex()); QModelIndex bottomRight = index(rowCount()-1, columnCount()-1, QModelIndex()); emit dataChanged(topLeft, bottomRight); } ostinato-0.9/client/portstatsmodel.h000066400000000000000000000073431321315675200177510ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _PORT_STATS_MODEL_H #define _PORT_STATS_MODEL_H #include #include class QTimer; typedef enum { // Info e_INFO_START = 0, e_INFO_USER = e_INFO_START, e_INFO_END = e_INFO_USER, // State e_STATE_START, e_LINK_STATE = e_STATE_START, e_TRANSMIT_STATE, e_CAPTURE_STATE, e_STATE_END = e_CAPTURE_STATE, // Statistics e_STATISTICS_START, e_STAT_FRAMES_RCVD = e_STATISTICS_START, e_STAT_FRAMES_SENT, e_STAT_FRAME_SEND_RATE, e_STAT_FRAME_RECV_RATE, e_STAT_BYTES_RCVD, e_STAT_BYTES_SENT, e_STAT_BYTE_SEND_RATE, e_STAT_BYTE_RECV_RATE, #if 0 e_STAT_FRAMES_RCVD_NIC, e_STAT_FRAMES_SENT_NIC, e_STAT_BYTES_RCVD_NIC, e_STAT_BYTES_SENT_NIC, #endif // Rx Errors e_STAT_RX_DROPS, e_STAT_RX_ERRORS, e_STAT_RX_FIFO_ERRORS, e_STAT_RX_FRAME_ERRORS, e_STATISTICS_END = e_STAT_RX_FRAME_ERRORS, e_STAT_MAX } PortStat; static QStringList PortStatName = (QStringList() << "User" << "Link State" << "Transmit State" << "Capture State" << "Frames Received" << "Frames Sent" << "Frame Send Rate (fps)" << "Frame Receive Rate (fps)" << "Bytes Received" << "Bytes Sent" << "Byte Send Rate (Bps)" << "Byte Receive Rate (Bps)" #if 0 << "Frames Received (NIC)" << "Frames Sent (NIC)" << "Bytes Received (NIC)" << "Bytes Sent (NIC)" #endif << "Receive Drops" << "Receive Errors" << "Receive Fifo Errors" << "Receive Frame Errors" ); static QStringList LinkStateName = (QStringList() << "Unknown" << "Down" << "Up" ); static QStringList BoolStateName = (QStringList() << "Off" << "On" ); class PortGroupList; class PortStatsModel : public QAbstractTableModel { Q_OBJECT public: PortStatsModel(PortGroupList *p, QObject *parent = 0); ~PortStatsModel(); int rowCount(const QModelIndex &parent = QModelIndex()) const; int columnCount(const QModelIndex &parent = QModelIndex()) const; QVariant data(const QModelIndex &index, int role) const; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; class PortGroupAndPortList { public: uint portGroupId; QList portList; }; void portListFromIndex(QModelIndexList indices, QList &portList); public slots: void when_portListChanged(); //void on_portStatsUpdate(int port, void*stats); void when_portGroup_stats_update(quint32 portGroupId); private slots: void updateStats(); private: PortGroupList *pgl; // numPorts stores the num of ports per portgroup // in the same order as the portgroups are index in the pgl // Also it stores them as cumulative totals QList numPorts; QTimer *timer; void getDomainIndexes(const QModelIndex &index, uint &portGroupIdx, uint &portIdx) const; }; #endif ostinato-0.9/client/portstatsproxymodel.h000066400000000000000000000030171321315675200210450ustar00rootroot00000000000000/* Copyright (C) 2015 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _PORT_STATS_PROXY_MODEL_H #define _PORT_STATS_PROXY_MODEL_H #include class PortStatsProxyModel : public QSortFilterProxyModel { Q_OBJECT public: PortStatsProxyModel(QObject *parent = 0) : QSortFilterProxyModel(parent) { } protected: bool filterAcceptsColumn(int sourceColumn, const QModelIndex &sourceParent) const { QModelIndex index = sourceModel()->index(0, sourceColumn, sourceParent); QString user = sourceModel()->data(index).toString(); return filterRegExp().exactMatch(user) ? true : false; } bool filterAcceptsRow(int sourceRow, const QModelIndex &/*sourceParent*/) const { // Hide row 0 - username (needed only by this filter class) return (sourceRow > 0) ? true : false; } }; #endif ostinato-0.9/client/portstatswindow.cpp000066400000000000000000000243351321315675200205130ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "portstatswindow.h" #include "portstatsfilterdialog.h" #include "portstatsmodel.h" #include "portstatsproxymodel.h" #include "streamstatsmodel.h" #include "streamstatswindow.h" #include "settings.h" #include #include #include extern QMainWindow *mainWindow; PortStatsWindow::PortStatsWindow(PortGroupList *pgl, QWidget *parent) : QWidget(parent), proxyStatsModel(NULL) { setupUi(this); this->pgl = pgl; model = pgl->getPortStatsModel(); proxyStatsModel = new PortStatsProxyModel(this); if (proxyStatsModel) { proxyStatsModel->setSourceModel(model); tvPortStats->setModel(proxyStatsModel); } else tvPortStats->setModel(model); tvPortStats->verticalHeader()->setHighlightSections(false); tvPortStats->verticalHeader()->setDefaultSectionSize( tvPortStats->verticalHeader()->minimumSectionSize()); connect(tvPortStats->selectionModel(), SIGNAL(selectionChanged( const QItemSelection&, const QItemSelection&)), SLOT(when_tvPortStats_selectionChanged( const QItemSelection&, const QItemSelection&))); when_tvPortStats_selectionChanged(QItemSelection(), QItemSelection()); } PortStatsWindow::~PortStatsWindow() { delete proxyStatsModel; } /* ------------- SLOTS (public) -------------- */ void PortStatsWindow::showMyReservedPortsOnly(bool enabled) { if (!proxyStatsModel) return; if (enabled) { QString rx(appSettings->value(kUserKey, kUserDefaultValue).toString()); proxyStatsModel->setFilterRegExp(QRegExp::escape(rx)); } else proxyStatsModel->setFilterRegExp(QRegExp(".*")); // match all } /* ------------- SLOTS (private) -------------- */ void PortStatsWindow::when_tvPortStats_selectionChanged( const QItemSelection& /*selected*/, const QItemSelection& /*deselected*/) { QModelIndexList indexList = tvPortStats->selectionModel()->selectedColumns(); if (proxyStatsModel) { selectedColumns.clear(); foreach(QModelIndex index, indexList) selectedColumns.append(proxyStatsModel->mapToSource(index)); } else selectedColumns = indexList; bool isEmpty = selectedColumns.isEmpty(); tbStartTransmit->setDisabled(isEmpty); tbStopTransmit->setDisabled(isEmpty); tbStartCapture->setDisabled(isEmpty); tbStopCapture->setDisabled(isEmpty); tbViewCapture->setDisabled(isEmpty); tbClear->setDisabled(isEmpty); tbGetStreamStats->setDisabled(isEmpty); tbResolveNeighbors->setDisabled(isEmpty); tbClearNeighbors->setDisabled(isEmpty); } void PortStatsWindow::on_tbStartTransmit_clicked() { QList pgpl; // Get selected ports model->portListFromIndex(selectedColumns, pgpl); // Clear selected ports, portgroup by portgroup for (int i = 0; i < pgpl.size(); i++) { pgl->portGroupByIndex(pgpl.at(i).portGroupId). startTx(&pgpl[i].portList); } } void PortStatsWindow::on_tbStopTransmit_clicked() { QList pgpl; // Get selected ports model->portListFromIndex(selectedColumns, pgpl); // Clear selected ports, portgroup by portgroup for (int i = 0; i < pgpl.size(); i++) { pgl->portGroupByIndex(pgpl.at(i).portGroupId). stopTx(&pgpl[i].portList); } } void PortStatsWindow::on_tbStartCapture_clicked() { // TODO(MED) QList pgpl; // Get selected ports model->portListFromIndex(selectedColumns, pgpl); // Clear selected ports, portgroup by portgroup for (int i = 0; i < pgpl.size(); i++) { pgl->portGroupByIndex(pgpl.at(i).portGroupId). startCapture(&pgpl[i].portList); } } void PortStatsWindow::on_tbStopCapture_clicked() { // TODO(MED) QList pgpl; // Get selected ports model->portListFromIndex(selectedColumns, pgpl); // Clear selected ports, portgroup by portgroup for (int i = 0; i < pgpl.size(); i++) { pgl->portGroupByIndex(pgpl.at(i).portGroupId). stopCapture(&pgpl[i].portList); } } void PortStatsWindow::on_tbViewCapture_clicked() { // TODO(MED) QList pgpl; // Get selected ports model->portListFromIndex(selectedColumns, pgpl); // Clear selected ports, portgroup by portgroup for (int i = 0; i < pgpl.size(); i++) { pgl->portGroupByIndex(pgpl.at(i).portGroupId). viewCapture(&pgpl[i].portList); } } void PortStatsWindow::on_tbResolveNeighbors_clicked() { QList portList; // Get selected ports model->portListFromIndex(selectedColumns, portList); // Resolve ARP/ND for selected ports, portgroup by portgroup for (int i = 0; i < portList.size(); i++) { pgl->portGroupByIndex(portList.at(i).portGroupId). resolveDeviceNeighbors(&portList[i].portList); } } void PortStatsWindow::on_tbClearNeighbors_clicked() { QList portList; // Get selected ports model->portListFromIndex(selectedColumns, portList); // Clear ARP/ND for ports, portgroup by portgroup for (int i = 0; i < portList.size(); i++) { pgl->portGroupByIndex(portList.at(i).portGroupId). clearDeviceNeighbors(&portList[i].portList); } } void PortStatsWindow::on_tbClear_clicked() { QList portList; // Get selected ports model->portListFromIndex(selectedColumns, portList); // Clear selected ports, portgroup by portgroup for (int i = 0; i < portList.size(); i++) { pgl->portGroupByIndex(portList.at(i).portGroupId). clearPortStats(&portList[i].portList); pgl->portGroupByIndex(portList.at(i).portGroupId). clearStreamStats(&portList[i].portList); } } // 'All' => all ports currently visible, not all ports in all portgroups void PortStatsWindow::on_tbClearAll_clicked() { QAbstractItemModel *mdl = tvPortStats->model(); QModelIndexList shownColumns; QList portList; // Find the 'visible' columns for(int vi = 0; vi < mdl->columnCount(); vi++) { int li = tvPortStats->horizontalHeader()->logicalIndex(vi); if (!tvPortStats->isColumnHidden(li)) { shownColumns.append(mdl->index(0, li)); } } // Get ports corresponding to the shown columns model->portListFromIndex(shownColumns, portList); // Clear shown ports, portgroup by portgroup for (int i = 0; i < portList.size(); i++) { pgl->portGroupByIndex(portList.at(i).portGroupId) .clearPortStats(&portList[i].portList); pgl->portGroupByIndex(portList.at(i).portGroupId) .clearStreamStats(&portList[i].portList); } } void PortStatsWindow::on_tbGetStreamStats_clicked() { QList portList; StreamStatsModel *streamStatsModel; // Get selected ports model->portListFromIndex(selectedColumns, portList); if (portList.size()) { QDockWidget *dock = new QDockWidget(mainWindow); streamStatsModel = new StreamStatsModel(dock); dock->setWidget(new StreamStatsWindow(streamStatsModel, dock)); dock->setWindowTitle(dock->widget()->windowTitle()); dock->setObjectName("streamStatsDock"); dock->setAttribute(Qt::WA_DeleteOnClose); QDockWidget *statsDock = mainWindow->findChild( "statsDock"); mainWindow->addDockWidget(Qt::BottomDockWidgetArea, dock); mainWindow->tabifyDockWidget(statsDock, dock); dock->show(); dock->raise(); } // Get stream stats for selected ports, portgroup by portgroup for (int i = 0; i < portList.size(); i++) { PortGroup &pg = pgl->portGroupByIndex(portList.at(i).portGroupId); if (pg.getStreamStats(&portList[i].portList)) { connect(&pg,SIGNAL(streamStatsReceived( quint32, const OstProto::StreamStatsList*)), streamStatsModel, SLOT(appendStreamStatsList( quint32, const OstProto::StreamStatsList*))); } } } void PortStatsWindow::on_tbFilter_clicked() { bool ok; QList currentColumns, newColumns; PortStatsFilterDialog dialog; QAbstractItemModel *mdl = tvPortStats->model(); // create the input list for the filter dialog - // list of logical-indexes ordered by their current visual indexes for(int vi = 0; vi < mdl->columnCount(); vi++) { int li = tvPortStats->horizontalHeader()->logicalIndex(vi); if (!tvPortStats->isColumnHidden(li)) { currentColumns.append(li); } } // return list from the filter dialog - // list of logical-indexes ordered by their new visual indexes newColumns = dialog.getItemList(&ok, mdl, Qt::Horizontal, currentColumns); if (ok) { QHeaderView *hv = tvPortStats->horizontalHeader(); // hide/show sections first ... for(int li = 0; li < mdl->columnCount(); li++) tvPortStats->setColumnHidden(li, !newColumns.contains(li)); // ... then for the 'shown' columns, set the visual index for(int vi = 0; vi < newColumns.size(); vi++) hv->moveSection(hv->visualIndex(newColumns.at(vi)), vi); } } ostinato-0.9/client/portstatswindow.h000066400000000000000000000035621321315675200201570ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _PORT_STATS_WINDOW_H #define _PORT_STATS_WINDOW_H #include #include #include "ui_portstatswindow.h" #include "portgrouplist.h" #include "portstatsmodel.h" class QSortFilterProxyModel; class PortStatsWindow : public QWidget, public Ui::PortStatsWindow { Q_OBJECT public: PortStatsWindow(PortGroupList *pgl, QWidget *parent = 0); ~PortStatsWindow(); public slots: void showMyReservedPortsOnly(bool enabled); private slots: void when_tvPortStats_selectionChanged(const QItemSelection &selected, const QItemSelection &deselected); void on_tbStartTransmit_clicked(); void on_tbStopTransmit_clicked(); void on_tbStartCapture_clicked(); void on_tbStopCapture_clicked(); void on_tbViewCapture_clicked(); void on_tbClear_clicked(); void on_tbClearAll_clicked(); void on_tbGetStreamStats_clicked(); void on_tbResolveNeighbors_clicked(); void on_tbClearNeighbors_clicked(); void on_tbFilter_clicked(); private: PortGroupList *pgl; PortStatsModel *model; QSortFilterProxyModel *proxyStatsModel; QModelIndexList selectedColumns; }; #endif ostinato-0.9/client/portstatswindow.ui000066400000000000000000000216101321315675200203370ustar00rootroot00000000000000 PortStatsWindow 0 0 502 415 Form QFrame::Panel QFrame::Raised Transmit Start Tx Starts transmit on selected port(s) Start :/icons/control_play.png:/icons/control_play.png Stop Tx Stops transmit on selected port(s) Stop :/icons/control_stop.png:/icons/control_stop.png Qt::Vertical Stats Fetch Selected Port Stream Stats Fetches stream statistics from the selected port(s) Fetch Stream Stats :/icons/stream_stats.png Clear Selected Port Stats Clears statistics of the selected port(s) Clear :/icons/portstats_clear.png:/icons/portstats_clear.png Clear All Ports Stats Clears statistics of all ports Clear All :/icons/portstats_clear_all.png:/icons/portstats_clear_all.png Qt::Vertical Capture Start Capture Captures packets on the selected port(s) Start :/icons/sound_none.png:/icons/sound_none.png Stop Capture End capture on selecteed port(s) Stop :/icons/sound_mute.png:/icons/sound_mute.png View Capture Buffer View captured packets on selected port(s) View :/icons/magnifier.png:/icons/magnifier.png Qt::Vertical ARP/ND Resolve Neighbors Resolve Device Neighbors on selected port(s) Resolve Neighbors :/icons/neighbor_resolve.png:/icons/neighbor_resolve.png Clear Neighbors Clear Device Neighbors on selected port(s) Clear Neighbors :/icons/neighbor_clear.png:/icons/neighbor_clear.png Qt::Vertical Qt::Horizontal 40 20 Select which ports to view Filter :/icons/portstats_filter.png:/icons/portstats_filter.png QAbstractItemView::SelectColumns ostinato-0.9/client/portswindow.cpp000066400000000000000000000777041321315675200176270ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "portswindow.h" #include "deviceswidget.h" #include "portconfigdialog.h" #include "settings.h" #include "streamconfigdialog.h" #include "streamfileformat.h" #include "streamlistdelegate.h" #include "fileformat.pb.h" #include #include #include #include #include #include extern QMainWindow *mainWindow; PortsWindow::PortsWindow(PortGroupList *pgl, QWidget *parent) : QWidget(parent), proxyPortModel(NULL) { QAction *sep; delegate = new StreamListDelegate; proxyPortModel = new QSortFilterProxyModel(this); //slm = new StreamListModel(); //plm = new PortGroupList(); plm = pgl; setupUi(this); devicesWidget->setPortGroupList(plm); tvPortList->header()->hide(); tvStreamList->setItemDelegate(delegate); tvStreamList->verticalHeader()->setDefaultSectionSize( tvStreamList->verticalHeader()->minimumSectionSize()); // Populate PortList Context Menu Actions tvPortList->addAction(actionNew_Port_Group); tvPortList->addAction(actionDelete_Port_Group); tvPortList->addAction(actionConnect_Port_Group); tvPortList->addAction(actionDisconnect_Port_Group); tvPortList->addAction(actionExclusive_Control); tvPortList->addAction(actionPort_Configuration); // Populate StreamList Context Menu Actions tvStreamList->addAction(actionNew_Stream); tvStreamList->addAction(actionEdit_Stream); tvStreamList->addAction(actionDuplicate_Stream); tvStreamList->addAction(actionDelete_Stream); sep = new QAction(this); sep->setSeparator(true); tvStreamList->addAction(sep); tvStreamList->addAction(actionOpen_Streams); tvStreamList->addAction(actionSave_Streams); // PortList, StreamList, DeviceWidget actions combined // make this window's actions addActions(tvPortList->actions()); sep = new QAction(this); sep->setSeparator(true); addAction(sep); addActions(tvStreamList->actions()); sep = new QAction(this); sep->setSeparator(true); addAction(sep); addActions(devicesWidget->actions()); tvStreamList->setModel(plm->getStreamModel()); // XXX: It would be ideal if we only needed to do the below to // get the proxy model to do its magic. However, the QModelIndex // used by the source model and the proxy model are different // i.e. the row, column, internalId/internalPtr used by both // will be different. Since our domain objects - PortGroupList, // PortGroup, Port etc. use these attributes, we need to map the // proxy's index to the source's index before invoking any domain // object methods // TODO: research if we can skip the mapping when the domain // objects' design is reviewed if (proxyPortModel) { proxyPortModel->setSourceModel(plm->getPortModel()); tvPortList->setModel(proxyPortModel); } else tvPortList->setModel(plm->getPortModel()); connect( plm->getPortModel(), SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)), this, SLOT(when_portModel_dataChanged(const QModelIndex&, const QModelIndex&))); connect(plm->getPortModel(), SIGNAL(modelReset()), SLOT(when_portModel_reset())); connect( tvPortList->selectionModel(), SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)), this, SLOT(when_portView_currentChanged(const QModelIndex&, const QModelIndex&))); connect(this, SIGNAL(currentPortChanged(const QModelIndex&, const QModelIndex&)), devicesWidget, SLOT(setCurrentPortIndex(const QModelIndex&))); connect(plm->getStreamModel(), SIGNAL(rowsInserted(QModelIndex, int, int)), SLOT(updateStreamViewActions())); connect(plm->getStreamModel(), SIGNAL(rowsRemoved(QModelIndex, int, int)), SLOT(updateStreamViewActions())); connect(tvStreamList->selectionModel(), SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)), SLOT(updateStreamViewActions())); connect(tvStreamList->selectionModel(), SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)), SLOT(updateStreamViewActions())); tvStreamList->resizeColumnToContents(StreamModel::StreamIcon); tvStreamList->resizeColumnToContents(StreamModel::StreamStatus); // Initially we don't have any ports/streams/devices // - so send signal triggers when_portView_currentChanged(QModelIndex(), QModelIndex()); updateStreamViewActions(); connect(plm->getStreamModel(), SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)), this, SLOT(streamModelDataChanged())); connect(plm->getStreamModel(), SIGNAL(modelReset()), this, SLOT(streamModelDataChanged())); } void PortsWindow::streamModelDataChanged() { QModelIndex current = tvPortList->currentIndex(); if (proxyPortModel) current = proxyPortModel->mapToSource(current); if (plm->isPort(current)) plm->port(current).recalculateAverageRates(); } PortsWindow::~PortsWindow() { delete delegate; delete proxyPortModel; } int PortsWindow::portGroupCount() { return plm->numPortGroups(); } int PortsWindow::reservedPortCount() { int count = 0; int n = portGroupCount(); for (int i = 0; i < n; i++) count += plm->portGroupByIndex(i).numReservedPorts(); return count; } //! Always return true bool PortsWindow::openSession( const OstProto::SessionContent *session, QString & /*error*/) { QProgressDialog progress("Opening Session", NULL, 0, session->port_groups_size(), mainWindow); progress.show(); progress.setEnabled(true); // since parent (mainWindow) is disabled plm->removeAllPortGroups(); for (int i = 0; i < session->port_groups_size(); i++) { const OstProto::PortGroupContent &pgc = session->port_groups(i); PortGroup *pg = new PortGroup(QString::fromStdString( pgc.server_name()), quint16(pgc.server_port())); pg->setConfigAtConnect(&pgc); plm->addPortGroup(*pg); progress.setValue(i+1); } return true; } /*! * Prepare content to be saved for a session * * If port reservation is in use, saves only 'my' reserved ports * * Returns false, if user cancels op; true, otherwise */ bool PortsWindow::saveSession( OstProto::SessionContent *session, // OUT param QString & /*error*/, QProgressDialog *progress) { int n = portGroupCount(); QString myself; if (progress) { progress->setLabelText("Preparing Ports and PortGroups ..."); progress->setRange(0, n); } if (reservedPortCount()) myself = appSettings->value(kUserKey, kUserDefaultValue).toString(); for (int i = 0; i < n; i++) { PortGroup &pg = plm->portGroupByIndex(i); OstProto::PortGroupContent *pgc = session->add_port_groups(); pgc->set_server_name(pg.serverName().toStdString()); pgc->set_server_port(pg.serverPort()); for (int j = 0; j < pg.numPorts(); j++) { if (myself != pg.mPorts.at(j)->userName()) continue; OstProto::PortContent *pc = pgc->add_ports(); OstProto::Port *p = pc->mutable_port_config(); // XXX: We save the entire OstProto::Port even though some // fields may be ephemeral; while opening we use only relevant // fields pg.mPorts.at(j)->protoDataCopyInto(p); for (int k = 0; k < pg.mPorts.at(j)->numStreams(); k++) { OstProto::Stream *s = pc->add_streams(); pg.mPorts.at(j)->streamByIndex(k)->protoDataCopyInto(*s); } for (int k = 0; k < pg.mPorts.at(j)->numDeviceGroups(); k++) { OstProto::DeviceGroup *dg = pc->add_device_groups(); dg->CopyFrom(*(pg.mPorts.at(j)->deviceGroupByIndex(k))); } } if (progress) { if (progress->wasCanceled()) return false; progress->setValue(i); } if (i % 2 == 0) qApp->processEvents(); } return true; } void PortsWindow::showMyReservedPortsOnly(bool enabled) { if (!proxyPortModel) return; if (enabled) { QString rx = "Port Group|\\[" + QRegExp::escape(appSettings->value(kUserKey, kUserDefaultValue).toString()) + "\\]"; qDebug("%s: regexp: <%s>", __FUNCTION__, qPrintable(rx)); proxyPortModel->setFilterRegExp(QRegExp(rx)); } else proxyPortModel->setFilterRegExp(QRegExp("")); } void PortsWindow::on_tvStreamList_activated(const QModelIndex & index) { if (!index.isValid()) { qDebug("%s: invalid index", __FUNCTION__); return; } qDebug("stream list activated\n"); Port &curPort = plm->port(proxyPortModel ? proxyPortModel->mapToSource(tvPortList->currentIndex()) : tvPortList->currentIndex()); QList streams; streams.append(curPort.mutableStreamByIndex(index.row(), false)); StreamConfigDialog scd(streams, curPort, this); if (scd.exec() == QDialog::Accepted) { curPort.recalculateAverageRates(); curPort.setLocalConfigChanged(true); } } void PortsWindow::when_portView_currentChanged(const QModelIndex& currentIndex, const QModelIndex& previousIndex) { QModelIndex current = currentIndex; QModelIndex previous = previousIndex; if (proxyPortModel) { current = proxyPortModel->mapToSource(current); previous = proxyPortModel->mapToSource(previous); } plm->getStreamModel()->setCurrentPortIndex(current); updatePortViewActions(currentIndex); updateStreamViewActions(); qDebug("In %s", __FUNCTION__); if (previous.isValid() && plm->isPort(previous)) { disconnect(&(plm->port(previous)), SIGNAL(portRateChanged(int, int)), this, SLOT(updatePortRates())); disconnect(&(plm->port(previous)), SIGNAL(localConfigChanged(int, int, bool)), this, SLOT(updateApplyHint(int, int, bool))); } if (!current.isValid()) { qDebug("setting stacked widget to welcome page"); swDetail->setCurrentIndex(0); // welcome page } else { if (plm->isPortGroup(current)) { swDetail->setCurrentIndex(1); // portGroup detail page } else if (plm->isPort(current)) { swDetail->setCurrentIndex(2); // port detail page updatePortRates(); connect(&(plm->port(current)), SIGNAL(portRateChanged(int, int)), SLOT(updatePortRates())); connect(&(plm->port(current)), SIGNAL(localConfigChanged(int, int, bool)), SLOT(updateApplyHint(int, int, bool))); if (plm->port(current).isDirty()) updateApplyHint(plm->port(current).portGroupId(), plm->port(current).id(), true); else if (plm->port(current).numStreams()) applyHint->setText("Use the Statistics window to transmit " "packets"); else applyHint->setText(""); } } emit currentPortChanged(current, previous); } void PortsWindow::when_portModel_dataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight) { qDebug("In %s %d:(%d, %d) - %d:(%d, %d)", __FUNCTION__, topLeft.parent().isValid(), topLeft.row(), topLeft.column(), bottomRight.parent().isValid(), bottomRight.row(), bottomRight.column()); if (!topLeft.isValid() || !bottomRight.isValid()) return; if (topLeft.parent() != bottomRight.parent()) return; // If a port has changed, expand the port group if (topLeft.parent().isValid()) tvPortList->expand(proxyPortModel ? proxyPortModel->mapFromSource(topLeft.parent()) : topLeft.parent()); #if 0 // not sure why the >= <= operators are not overloaded in QModelIndex if ((tvPortList->currentIndex() >= topLeft) && (tvPortList->currentIndex() <= bottomRight)) #endif if (((topLeft < tvPortList->currentIndex()) || (topLeft == tvPortList->currentIndex())) && (((tvPortList->currentIndex() < bottomRight)) || (tvPortList->currentIndex() == bottomRight))) { // Update UI to reflect potential change in exclusive mode, // transmit mode et al when_portView_currentChanged(tvPortList->currentIndex(), tvPortList->currentIndex()); } } void PortsWindow::when_portModel_reset() { when_portView_currentChanged(QModelIndex(), tvPortList->currentIndex()); } void PortsWindow::on_averagePacketsPerSec_editingFinished() { QModelIndex current = tvPortList->currentIndex(); if (proxyPortModel) current = proxyPortModel->mapToSource(current); Q_ASSERT(plm->isPort(current)); bool isOk; double pps = QLocale().toDouble(averagePacketsPerSec->text(), &isOk); plm->port(current).setAveragePacketRate(pps); } void PortsWindow::on_averageBitsPerSec_editingFinished() { QModelIndex current = tvPortList->currentIndex(); if (proxyPortModel) current = proxyPortModel->mapToSource(current); Q_ASSERT(plm->isPort(current)); bool isOk; double bps = QLocale().toDouble(averageBitsPerSec->text(), &isOk); plm->port(current).setAverageBitRate(bps); } void PortsWindow::updatePortRates() { QModelIndex current = tvPortList->currentIndex(); if (proxyPortModel) current = proxyPortModel->mapToSource(current); if (!current.isValid()) return; if (!plm->isPort(current)) return; averagePacketsPerSec->setText(QString("%L1") .arg(plm->port(current).averagePacketRate(), 0, 'f', 4)); averageBitsPerSec->setText(QString("%L1") .arg(plm->port(current).averageBitRate(), 0, 'f', 0)); } void PortsWindow::updateStreamViewActions() { QModelIndex current = tvPortList->currentIndex(); if (proxyPortModel) current = proxyPortModel->mapToSource(current); // For some reason hasSelection() returns true even if selection size is 0 // so additional check for size introduced if (tvStreamList->selectionModel()->hasSelection() && (tvStreamList->selectionModel()->selection().size() > 0)) { qDebug("Has selection %d", tvStreamList->selectionModel()->selection().size()); // If more than one non-contiguous ranges selected, // disable "New" and "Edit" if (tvStreamList->selectionModel()->selection().size() > 1) { actionNew_Stream->setDisabled(true); actionEdit_Stream->setDisabled(true); } else { actionNew_Stream->setEnabled(true); actionEdit_Stream->setEnabled(true); } // Duplicate/Delete are always enabled as long as we have a selection actionDuplicate_Stream->setEnabled(true); actionDelete_Stream->setEnabled(true); } else { qDebug("No selection"); if (plm->isPort(current)) actionNew_Stream->setEnabled(true); else actionNew_Stream->setDisabled(true); actionEdit_Stream->setDisabled(true); actionDuplicate_Stream->setDisabled(true); actionDelete_Stream->setDisabled(true); } actionOpen_Streams->setEnabled(plm->isPort(current)); actionSave_Streams->setEnabled(tvStreamList->model()->rowCount() > 0); } void PortsWindow::updateApplyHint(int /*portGroupId*/, int /*portId*/, bool configChanged) { if (configChanged) applyHint->setText("Configuration has changed - " "click Apply " "to activate the changes"); else applyHint->setText("Configuration activated. Use the Statistics " "window to transmit packets"); } void PortsWindow::updatePortViewActions(const QModelIndex& currentIndex) { QModelIndex current = currentIndex; if (proxyPortModel) current = proxyPortModel->mapToSource(current); if (!current.isValid()) { qDebug("current is now invalid"); actionDelete_Port_Group->setDisabled(true); actionConnect_Port_Group->setDisabled(true); actionDisconnect_Port_Group->setDisabled(true); actionExclusive_Control->setDisabled(true); actionPort_Configuration->setDisabled(true); goto _EXIT; } qDebug("currentChanged %llx", current.internalId()); if (plm->isPortGroup(current)) { actionDelete_Port_Group->setEnabled(true); actionExclusive_Control->setDisabled(true); actionPort_Configuration->setDisabled(true); switch(plm->portGroup(current).state()) { case QAbstractSocket::UnconnectedState: case QAbstractSocket::ClosingState: qDebug("state = unconnected|closing"); actionConnect_Port_Group->setEnabled(true); actionDisconnect_Port_Group->setDisabled(true); break; case QAbstractSocket::HostLookupState: case QAbstractSocket::ConnectingState: case QAbstractSocket::ConnectedState: qDebug("state = lookup|connecting|connected"); actionConnect_Port_Group->setDisabled(true); actionDisconnect_Port_Group->setEnabled(true); break; case QAbstractSocket::BoundState: case QAbstractSocket::ListeningState: default: // FIXME(LOW): indicate error qDebug("unexpected state"); break; } } else if (plm->isPort(current)) { actionDelete_Port_Group->setDisabled(true); actionConnect_Port_Group->setDisabled(true); actionDisconnect_Port_Group->setDisabled(true); actionExclusive_Control->setEnabled(true); if (plm->port(current).hasExclusiveControl()) actionExclusive_Control->setChecked(true); else actionExclusive_Control->setChecked(false); actionPort_Configuration->setEnabled(true); } _EXIT: return; } void PortsWindow::on_pbApply_clicked() { QModelIndex curPort; QModelIndex curPortGroup; curPort = tvPortList->selectionModel()->currentIndex(); if (proxyPortModel) curPort = proxyPortModel->mapToSource(curPort); if (!curPort.isValid()) { qDebug("%s: curPort is invalid", __FUNCTION__); goto _exit; } if (!plm->isPort(curPort)) { qDebug("%s: curPort is not a port", __FUNCTION__); goto _exit; } if (plm->port(curPort).getStats().state().is_transmit_on()) { QMessageBox::information(0, "Configuration Change", "Please stop transmit on the port before applying any changes"); goto _exit; } curPortGroup = plm->getPortModel()->parent(curPort); if (!curPortGroup.isValid()) { qDebug("%s: curPortGroup is invalid", __FUNCTION__); goto _exit; } if (!plm->isPortGroup(curPortGroup)) { qDebug("%s: curPortGroup is not a portGroup", __FUNCTION__); goto _exit; } // FIXME(HI): shd this be a signal? //portGroup.when_configApply(port); // FIXME(MED): mixing port id and index!!! plm->portGroup(curPortGroup).when_configApply(plm->port(curPort).id()); _exit: return; #if 0 // TODO (LOW): This block is for testing only QModelIndex current = tvPortList->selectionModel()->currentIndex(); if (current.isValid()) qDebug("current = %llx", current.internalId()); else qDebug("current is invalid"); #endif } void PortsWindow::on_actionNew_Port_Group_triggered() { bool ok; QString text = QInputDialog::getText(this, "Add Port Group", "Port Group Address (HostName[:Port])", QLineEdit::Normal, lastNewPortGroup, &ok); if (ok) { QStringList addr = text.split(":"); quint16 port = DEFAULT_SERVER_PORT; if (addr.size() > 2) { // IPv6 Address // IPv6 addresses with port number SHOULD be specified as // [2001:db8::1]:80 (RFC5952 Sec6) to avoid ambiguity due to ':' addr = text.split("]:"); if (addr.size() > 1) port = addr[1].toUShort(); } else if (addr.size() == 2) // Hostname/IPv4 + Port specified port = addr[1].toUShort(); // Play nice and remove square brackets irrespective of addr type addr[0].remove(QChar('[')); addr[0].remove(QChar(']')); PortGroup *pg = new PortGroup(addr[0], port); plm->addPortGroup(*pg); lastNewPortGroup = text; } } void PortsWindow::on_actionDelete_Port_Group_triggered() { QModelIndex current = tvPortList->selectionModel()->currentIndex(); if (proxyPortModel) current = proxyPortModel->mapToSource(current); if (current.isValid()) plm->removePortGroup(plm->portGroup(current)); } void PortsWindow::on_actionConnect_Port_Group_triggered() { QModelIndex current = tvPortList->selectionModel()->currentIndex(); if (proxyPortModel) current = proxyPortModel->mapToSource(current); if (current.isValid()) plm->portGroup(current).connectToHost(); } void PortsWindow::on_actionDisconnect_Port_Group_triggered() { QModelIndex current = tvPortList->selectionModel()->currentIndex(); if (proxyPortModel) current = proxyPortModel->mapToSource(current); if (current.isValid()) plm->portGroup(current).disconnectFromHost(); } void PortsWindow::on_actionExclusive_Control_triggered(bool checked) { QModelIndex current = tvPortList->selectionModel()->currentIndex(); if (proxyPortModel) current = proxyPortModel->mapToSource(current); if (plm->isPort(current)) { OstProto::Port config; config.set_is_exclusive_control(checked); plm->portGroup(current.parent()).modifyPort(current.row(), config); } } void PortsWindow::on_actionPort_Configuration_triggered() { QModelIndex current = tvPortList->selectionModel()->currentIndex(); if (proxyPortModel) current = proxyPortModel->mapToSource(current); if (!plm->isPort(current)) return; Port &port = plm->port(current); OstProto::Port config; // XXX: we don't call Port::protoDataCopyInto() to get config b'coz // we want only the modifiable fields populated to send to Drone // TODO: extend Port::protoDataCopyInto() to accept an optional param // which says copy only modifiable fields //plm->port(current).protoDataCopyInto(&config); config.set_transmit_mode(port.transmitMode()); config.set_is_tracking_stream_stats(port.trackStreamStats()); config.set_is_exclusive_control(port.hasExclusiveControl()); config.set_user_name(port.userName().toStdString()); PortConfigDialog dialog(config, port.getStats().state(), this); if (dialog.exec() == QDialog::Accepted) plm->portGroup(current.parent()).modifyPort(current.row(), config); } void PortsWindow::on_actionNew_Stream_triggered() { qDebug("New Stream Action"); QItemSelectionModel* selectionModel = tvStreamList->selectionModel(); if (selectionModel->selection().size() > 1) { qDebug("%s: Unexpected selection size %d, can't add", __FUNCTION__, selectionModel->selection().size()); return; } // In case nothing is selected, insert 1 row at the end StreamModel *streamModel = plm->getStreamModel(); int row = streamModel->rowCount(), count = 1; // In case we have a single range selected; insert as many rows as // in the singe selected range before the top of the selected range if (selectionModel->selection().size() == 1) { row = selectionModel->selection().at(0).top(); count = selectionModel->selection().at(0).height(); } Port &curPort = plm->port(proxyPortModel ? proxyPortModel->mapToSource(tvPortList->currentIndex()) : tvPortList->currentIndex()); QList streams; for (int i = 0; i < count; i++) streams.append(new Stream); StreamConfigDialog scd(streams, curPort, this); scd.setWindowTitle(tr("Add Stream")); if (scd.exec() == QDialog::Accepted) streamModel->insert(row, streams); } void PortsWindow::on_actionEdit_Stream_triggered() { qDebug("Edit Stream Action"); QItemSelectionModel* streamModel = tvStreamList->selectionModel(); if (!streamModel->hasSelection()) return; Port &curPort = plm->port(proxyPortModel ? proxyPortModel->mapToSource(tvPortList->currentIndex()) : tvPortList->currentIndex()); QList streams; foreach(QModelIndex index, streamModel->selectedRows()) streams.append(curPort.mutableStreamByIndex(index.row(), false)); StreamConfigDialog scd(streams, curPort, this); if (scd.exec() == QDialog::Accepted) { curPort.recalculateAverageRates(); curPort.setLocalConfigChanged(true); } } void PortsWindow::on_actionDuplicate_Stream_triggered() { QItemSelectionModel* model = tvStreamList->selectionModel(); QModelIndex current = tvPortList->selectionModel()->currentIndex(); qDebug("Duplicate Stream Action"); if (proxyPortModel) current = proxyPortModel->mapToSource(current); if (model->hasSelection()) { bool isOk; int count = QInputDialog::getInteger(this, "Duplicate Streams", "Count", 1, 1, 9999, 1, &isOk); if (!isOk) return; QList list; foreach(QModelIndex index, model->selectedRows()) list.append(index.row()); plm->port(current).duplicateStreams(list, count); } else qDebug("No selection"); } void PortsWindow::on_actionDelete_Stream_triggered() { qDebug("Delete Stream Action"); QModelIndex index; if (tvStreamList->selectionModel()->hasSelection()) { qDebug("SelectedIndexes %d", tvStreamList->selectionModel()->selectedRows().size()); while(tvStreamList->selectionModel()->selectedRows().size()) { index = tvStreamList->selectionModel()->selectedRows().at(0); plm->getStreamModel()->removeRows(index.row(), 1); } } else qDebug("No selection"); } void PortsWindow::on_actionOpen_Streams_triggered() { qDebug("Open Streams Action"); QStringList fileTypes = StreamFileFormat::supportedFileTypes( StreamFileFormat::kOpenFile); QString fileType; QModelIndex current = tvPortList->selectionModel()->currentIndex(); static QString dirName; QString fileName; QString errorStr; bool append = true; bool ret; if (proxyPortModel) current = proxyPortModel->mapToSource(current); Q_ASSERT(plm->isPort(current)); if (fileTypes.size()) fileType = fileTypes.at(0); fileName = QFileDialog::getOpenFileName(this, tr("Open Streams"), dirName, fileTypes.join(";;"), &fileType); if (fileName.isEmpty()) goto _exit; if (tvStreamList->model()->rowCount()) { QMessageBox msgBox(QMessageBox::Question, qApp->applicationName(), tr("Append to existing streams? Or overwrite?"), QMessageBox::NoButton, this); QPushButton *appendBtn = msgBox.addButton(tr("Append"), QMessageBox::ActionRole); QPushButton *overwriteBtn = msgBox.addButton(tr("Overwrite"), QMessageBox::ActionRole); QPushButton *cancelBtn = msgBox.addButton(QMessageBox::Cancel); msgBox.exec(); if (msgBox.clickedButton() == cancelBtn) goto _exit; else if (msgBox.clickedButton() == appendBtn) append = true; else if (msgBox.clickedButton() == overwriteBtn) append = false; else Q_ASSERT(false); } ret = plm->port(current).openStreams(fileName, append, errorStr); if (!ret || !errorStr.isEmpty()) { QMessageBox msgBox(this); QStringList str = errorStr.split("\n\n\n\n"); msgBox.setIcon(ret ? QMessageBox::Warning : QMessageBox::Critical); msgBox.setWindowTitle(qApp->applicationName()); msgBox.setText(str.at(0)); if (str.size() > 1) msgBox.setDetailedText(str.at(1)); msgBox.setStandardButtons(QMessageBox::Ok); msgBox.exec(); } dirName = QFileInfo(fileName).absolutePath(); _exit: return; } void PortsWindow::on_actionSave_Streams_triggered() { qDebug("Save Streams Action"); QModelIndex current = tvPortList->selectionModel()->currentIndex(); static QString fileName; QStringList fileTypes = StreamFileFormat::supportedFileTypes( StreamFileFormat::kSaveFile); QString fileType; QString errorStr; QFileDialog::Options options; if (proxyPortModel) current = proxyPortModel->mapToSource(current); // On Mac OS with Native Dialog, getSaveFileName() ignores fileType // which we need #if defined(Q_OS_MAC) options |= QFileDialog::DontUseNativeDialog; #endif if (fileTypes.size()) fileType = fileTypes.at(0); Q_ASSERT(plm->isPort(current)); _retry: fileName = QFileDialog::getSaveFileName(this, tr("Save Streams"), fileName, fileTypes.join(";;"), &fileType, options); if (fileName.isEmpty()) goto _exit; if (QFileInfo(fileName).suffix().isEmpty()) { QString fileExt = fileType.section(QRegExp("[\\*\\)]"), 1, 1); qDebug("Adding extension '%s' to '%s'", qPrintable(fileExt), qPrintable(fileName)); fileName.append(fileExt); if (QFileInfo(fileName).exists()) { if (QMessageBox::warning(this, tr("Overwrite File?"), QString("The file \"%1\" already exists.\n\n" "Do you wish to overwrite it?") .arg(QFileInfo(fileName).fileName()), QMessageBox::Yes|QMessageBox::No, QMessageBox::No) != QMessageBox::Yes) goto _retry; } } fileType = fileType.remove(QRegExp("\\(.*\\)")).trimmed(); if (!fileType.startsWith("Ostinato") && !fileType.startsWith("Python")) { if (QMessageBox::warning(this, tr("Ostinato"), QString("You have chosen to save in %1 format. All stream " "attributes may not be saved in this format.\n\n" "It is recommended to save in native Ostinato format.\n\n" "Continue to save in %2 format?").arg(fileType).arg(fileType), QMessageBox::Yes|QMessageBox::No, QMessageBox::No) != QMessageBox::Yes) goto _retry; } // TODO: all or selected? if (!plm->port(current).saveStreams(fileName, fileType, errorStr)) QMessageBox::critical(this, qApp->applicationName(), errorStr); else if (!errorStr.isEmpty()) QMessageBox::warning(this, qApp->applicationName(), errorStr); fileName = QFileInfo(fileName).absolutePath(); _exit: return; } ostinato-0.9/client/portswindow.h000066400000000000000000000060711321315675200172610ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _PORTS_WINDOW_H #define _PORTS_WINDOW_H #include #include #include "ui_portswindow.h" #include "portgrouplist.h" class QAbstractItemDelegate; class QProgressDialog; class QSortFilterProxyModel; namespace OstProto { class SessionContent; } class PortsWindow : public QWidget, private Ui::PortsWindow { Q_OBJECT //QAbstractItemModel *slm; // stream list model PortGroupList *plm; public: PortsWindow(PortGroupList *pgl, QWidget *parent = 0); ~PortsWindow(); int portGroupCount(); int reservedPortCount(); bool openSession(const OstProto::SessionContent *session, QString &error); bool saveSession(OstProto::SessionContent *session, QString &error, QProgressDialog *progress = NULL); signals: void currentPortChanged(const QModelIndex ¤t, const QModelIndex &previous); private: QString lastNewPortGroup; QAbstractItemDelegate *delegate; QSortFilterProxyModel *proxyPortModel; public slots: void showMyReservedPortsOnly(bool enabled); private slots: void updateApplyHint(int portGroupId, int portId, bool configChanged); void updatePortViewActions(const QModelIndex& currentIndex); void updateStreamViewActions(); void on_averagePacketsPerSec_editingFinished(); void on_averageBitsPerSec_editingFinished(); void updatePortRates(); void on_tvStreamList_activated(const QModelIndex & index); void when_portView_currentChanged(const QModelIndex& currentIndex, const QModelIndex& previousIndex); void when_portModel_dataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight); void when_portModel_reset(); void on_pbApply_clicked(); void on_actionNew_Port_Group_triggered(); void on_actionDelete_Port_Group_triggered(); void on_actionConnect_Port_Group_triggered(); void on_actionDisconnect_Port_Group_triggered(); void on_actionExclusive_Control_triggered(bool checked); void on_actionPort_Configuration_triggered(); void on_actionNew_Stream_triggered(); void on_actionEdit_Stream_triggered(); void on_actionDuplicate_Stream_triggered(); void on_actionDelete_Stream_triggered(); void on_actionOpen_Streams_triggered(); void on_actionSave_Streams_triggered(); void streamModelDataChanged(); }; #endif ostinato-0.9/client/portswindow.ui000066400000000000000000000345121321315675200174500ustar00rootroot00000000000000 PortsWindow 0 0 663 352 Form Qt::Horizontal false 1 0 Qt::ActionsContextMenu QAbstractItemView::SingleSelection 2 0 2 <p><b>Welcome to Ostinato</b></p> <p>The port list on the left contains all the ports on which you can transmit packets.</p> <p>Ports belong to a port group. Make sure the Port Group has a <img src=":/icons/bullet_green.png"/> next to it, then double click the port group to show or hide the ports in the port group.</p> <p>To generate packets, you need to create and configure packet streams. A stream is a sequence of one or more packets.</p> <p>To create a stream, select the port on which you want to send packets.</p> <hr/> <p>Don't see the port that you want (or any ports at all) inside the port group? <a href="http://jump.ostinato.org/noports">Get Help!</a></p> Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop true true Qt::Vertical 20 40 <p>You have selected a port group in the port list on the left.</p> <p>You can transmit packets on any of the ports within the port group.</p> <p>Make sure the port group has a <img src=":/icons/bullet_green.png"/> next to it and then double click the port group to show or hide the ports in the port group.</p> <p>To generate packets, you need to create and configure packet streams. A stream is a sequence of one or more packets.</p> <p>To create a stream, select the port on which you want to send packets. </p> <hr/> <p>Don't see the port that you want (or any ports at all) inside the port group? <a href="http://jump.ostinato.org/noports">Get Help!</a></p> Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop true true Qt::Vertical 20 177 0 QFrame::Panel QFrame::Raised 3 Qt::Horizontal 40 20 Apply Hint Qt::Horizontal 40 20 Apply 0 Streams Avg pps true Avg bps false Qt::Horizontal 40 20 0 1 Qt::ActionsContextMenu This is the stream list for the selected port A stream is a sequence of one or more packets Right-click to create a stream QFrame::StyledPanel 1 QAbstractItemView::ExtendedSelection QAbstractItemView::SelectRows Devices :/icons/portgroup_add.png:/icons/portgroup_add.png New Port Group :/icons/portgroup_delete.png:/icons/portgroup_delete.png Delete Port Group :/icons/portgroup_connect.png:/icons/portgroup_connect.png Connect Port Group :/icons/portgroup_disconnect.png:/icons/portgroup_disconnect.png Disconnect Port Group :/icons/stream_add.png:/icons/stream_add.png New Stream :/icons/stream_delete.png:/icons/stream_delete.png Delete Stream :/icons/stream_edit.png:/icons/stream_edit.png Edit Stream true Exclusive Port Control (EXPERIMENTAL) Open Streams ... Save Streams ... Port Configuration ... :/icons/stream_duplicate.png:/icons/stream_duplicate.png Duplicate Stream DevicesWidget QWidget

deviceswidget.h
1 XTreeView QTreeView
xtreeview.h
XTableView QTableView
xtableview.h
radioButton toggled(bool) averagePacketsPerSec setEnabled(bool) 326 80 454 79 radioButton_2 toggled(bool) averageBitsPerSec setEnabled(bool) 523 80 651 88 ostinato-0.9/client/preferences.cpp000066400000000000000000000100521321315675200175100ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "preferences.h" #include "../common/ostprotolib.h" #include "settings.h" #include #include #if defined(Q_OS_WIN32) QString kGzipPathDefaultValue; QString kDiffPathDefaultValue; QString kAwkPathDefaultValue; #endif QString kUserDefaultValue; Preferences::Preferences() { Q_ASSERT(appSettings); setupUi(this); wiresharkPathEdit->setText(appSettings->value(kWiresharkPathKey, kWiresharkPathDefaultValue).toString()); tsharkPathEdit->setText(appSettings->value(kTsharkPathKey, kTsharkPathDefaultValue).toString()); gzipPathEdit->setText(appSettings->value(kGzipPathKey, kGzipPathDefaultValue).toString()); diffPathEdit->setText(appSettings->value(kDiffPathKey, kDiffPathDefaultValue).toString()); awkPathEdit->setText(appSettings->value(kAwkPathKey, kAwkPathDefaultValue).toString()); // TODO(only if required): kUserKey } Preferences::~Preferences() { } void Preferences::initDefaults() { #if defined(Q_OS_WIN32) kGzipPathDefaultValue = QApplication::applicationDirPath() + "/gzip.exe"; kDiffPathDefaultValue = QApplication::applicationDirPath() + "/diff.exe"; kAwkPathDefaultValue = QApplication::applicationDirPath() + "/gawk.exe"; #endif // Read default username from the environment #ifdef Q_OS_WIN32 kUserDefaultValue = QString(qgetenv("USERNAME").constData()); #else kUserDefaultValue = QString(qgetenv("USER").constData()); #endif qDebug("current user <%s>", qPrintable(kUserDefaultValue)); } void Preferences::accept() { appSettings->setValue(kWiresharkPathKey, wiresharkPathEdit->text()); appSettings->setValue(kTsharkPathKey, tsharkPathEdit->text()); appSettings->setValue(kGzipPathKey, gzipPathEdit->text()); appSettings->setValue(kDiffPathKey, diffPathEdit->text()); appSettings->setValue(kAwkPathKey, awkPathEdit->text()); OstProtoLib::setExternalApplicationPaths( appSettings->value(kTsharkPathKey, kTsharkPathDefaultValue).toString(), appSettings->value(kGzipPathKey, kGzipPathDefaultValue).toString(), appSettings->value(kDiffPathKey, kDiffPathDefaultValue).toString(), appSettings->value(kAwkPathKey, kAwkPathDefaultValue).toString()); QDialog::accept(); } void Preferences::on_wiresharkPathButton_clicked() { QString path; path = QFileDialog::getOpenFileName(0, "Locate Wireshark", wiresharkPathEdit->text()); if (!path.isEmpty()) wiresharkPathEdit->setText(path); } void Preferences::on_tsharkPathButton_clicked() { QString path; path = QFileDialog::getOpenFileName(0, "Locate tshark", tsharkPathEdit->text()); if (!path.isEmpty()) tsharkPathEdit->setText(path); } void Preferences::on_gzipPathButton_clicked() { QString path; path = QFileDialog::getOpenFileName(0, "Locate gzip", gzipPathEdit->text()); if (!path.isEmpty()) gzipPathEdit->setText(path); } void Preferences::on_diffPathButton_clicked() { QString path; path = QFileDialog::getOpenFileName(0, "Locate diff", diffPathEdit->text()); if (!path.isEmpty()) diffPathEdit->setText(path); } void Preferences::on_awkPathButton_clicked() { QString path; path = QFileDialog::getOpenFileName(0, "Locate awk", awkPathEdit->text()); if (!path.isEmpty()) awkPathEdit->setText(path); } ostinato-0.9/client/preferences.h000066400000000000000000000022361321315675200171620ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _PREFERENCES_H #define _PREFERENCES_H #include "ui_preferences.h" #include class Preferences : public QDialog, private Ui::Preferences { Q_OBJECT public: Preferences(); ~Preferences(); static void initDefaults(); public slots: void accept(); private slots: void on_wiresharkPathButton_clicked(); void on_tsharkPathButton_clicked(); void on_gzipPathButton_clicked(); void on_diffPathButton_clicked(); void on_awkPathButton_clicked(); }; #endif ostinato-0.9/client/preferences.ui000066400000000000000000000137751321315675200173620ustar00rootroot00000000000000 Preferences 0 0 400 220 Preferences :/icons/preferences.png QFrame::Box QFrame::Sunken 'wireshark' Path wiresharkPathEdit false ... 'tshark' Path tsharkPathEdit false ... 'gzip' Path diffPathEdit false ... 'diff' Path diffPathEdit false ... 'awk' Path awkPathEdit false ... Qt::Vertical 21 61 Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::NoButton|QDialogButtonBox::Ok wiresharkPathEdit wiresharkPathButton tsharkPathEdit tsharkPathButton gzipPathEdit gzipPathButton diffPathEdit diffPathButton awkPathEdit awkPathButton buttonBox buttonBox accepted() Preferences accept() 248 254 157 274 buttonBox rejected() Preferences reject() 316 260 286 274 ostinato-0.9/client/settings.h000066400000000000000000000047511321315675200165250ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _SETTINGS_H #define _SETTINGS_H #include #include extern QSettings *appSettings; const QString kWiresharkPathKey("WiresharkPath"); #if defined(Q_OS_WIN32) const QString kWiresharkPathDefaultValue( "C:/Program Files/Wireshark/wireshark.exe"); #elif defined(Q_OS_MAC) const QString kWiresharkPathDefaultValue( "/Applications/Wireshark.app/Contents/Resources/bin/wireshark"); #else const QString kWiresharkPathDefaultValue("/usr/bin/wireshark"); #endif const QString kTsharkPathKey("TsharkPath"); #if defined(Q_OS_WIN32) const QString kTsharkPathDefaultValue( "C:/Program Files/Wireshark/tshark.exe"); #elif defined(Q_OS_MAC) const QString kTsharkPathDefaultValue( "/Applications/Wireshark.app/Contents/Resources/bin/tshark"); #else const QString kTsharkPathDefaultValue("/usr/bin/tshark"); #endif const QString kGzipPathKey("GzipPath"); #if defined(Q_OS_WIN32) extern QString kGzipPathDefaultValue; #elif defined(Q_OS_MAC) const QString kGzipPathDefaultValue("/usr/bin/gzip"); #else const QString kGzipPathDefaultValue("/usr/bin/gzip"); #endif const QString kDiffPathKey("DiffPath"); #if defined(Q_OS_WIN32) extern QString kDiffPathDefaultValue; #elif defined(Q_OS_MAC) const QString kDiffPathDefaultValue("/usr/bin/diff"); #else const QString kDiffPathDefaultValue("/usr/bin/diff"); #endif const QString kAwkPathKey("AwkPath"); #if defined(Q_OS_WIN32) extern QString kAwkPathDefaultValue; #elif defined(Q_OS_MAC) const QString kAwkPathDefaultValue("/usr/bin/awk"); #else const QString kAwkPathDefaultValue("/usr/bin/awk"); #endif const QString kUserKey("User"); extern QString kUserDefaultValue; // // LastUse Section Keys // const QString kApplicationWindowGeometryKey("LastUse/ApplicationWindowGeometry"); const QString kApplicationWindowLayout("LastUse/ApplicationWindowLayout"); #endif ostinato-0.9/client/stream.cpp000066400000000000000000000026141321315675200165070ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ /*! * \todo Remove this class */ #include #include #include "stream.h" #include "../common/protocollistiterator.h" #include "../common/abstractprotocol.h" Stream::Stream() { //mId = 0xFFFFFFFF; setEnabled(true); } Stream::~Stream() { } void Stream::loadProtocolWidgets() { qWarning("%s: DOES NOTHING", __PRETTY_FUNCTION__); return; } void Stream::storeProtocolWidgets() { qWarning("%s: DOES NOTHING", __PRETTY_FUNCTION__); return; } quint64 getDeviceMacAddress( int /*portId*/, int /*streamId*/, int /*frameIndex*/) { return 0; } quint64 getNeighborMacAddress( int /*portId*/, int /*streamId*/, int /*frameIndex*/) { return 0; } ostinato-0.9/client/stream.h000066400000000000000000000020031321315675200161440ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _STREAM_H #define _STREAM_H #include #include #include #include "../common/protocol.pb.h" #include "../common/streambase.h" class Stream : public StreamBase { //quint32 mId; public: Stream(); ~Stream(); void loadProtocolWidgets(); void storeProtocolWidgets(); }; #endif ostinato-0.9/client/streamconfigdialog.cpp000066400000000000000000001204351321315675200210570ustar00rootroot00000000000000/* Copyright (C) 2010-2011 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include #include "streamconfigdialog.h" #include "stream.h" #include "abstractprotocol.h" #include "abstractprotocolconfig.h" #include "protocollistiterator.h" #include "modeltest.h" #include "../common/protocolmanager.h" #include "../common/protocolwidgetfactory.h" extern ProtocolManager *OstProtocolManager; extern ProtocolWidgetFactory *OstProtocolWidgetFactory; QRect StreamConfigDialog::lastGeometry; int StreamConfigDialog::lastTopLevelTabIndex = 0; int StreamConfigDialog::lastProtocolDataIndex = 0; static const uint kEthFrameOverHead = 20; StreamConfigDialog::StreamConfigDialog( QList &streamList, const Port &port, QWidget *parent) : QDialog (parent), _userStreamList(streamList), mPort(port) { mCurrentStreamIndex = 0; Q_ASSERT(_userStreamList.size() > 0); // Create a copy of the user provided stream list // We need a copy because user may edit multiple streams and then // discard the edit - in this case the user provided stream list // should not be modified on return foreach(Stream* stm, _userStreamList) { OstProto::Stream s; stm->protoDataCopyInto(s); _streamList.append(new Stream()); _streamList.last()->protoDataCopyFrom(s); } mpStream = _streamList.at(mCurrentStreamIndex); _iter = mpStream->createProtocolListIterator(); isUpdateInProgress = false; setupUi(this); setupUiExtra(); _windowTitle = windowTitle(); for (int i = ProtoMin; i < ProtoMax; i++) { bgProto[i]->setProperty("ProtocolLevel", i); bgProto[i]->setProperty("ProtocolId", ButtonIdNone); connect(bgProto[i], SIGNAL(buttonClicked(int)), this, SLOT(updateProtocol(int))); } //! \todo causes a crash! #if 0 connect(lePktLen, SIGNAL(textEdited(QString)), this, SLOT(updateContents())); #endif // Time to play match the signals and slots! // If L1/L2(FT)/L3/L4 = None, // force subsequent protocol level(s) also to None connect(rbL1None, SIGNAL(toggled(bool)), SLOT(forceProtocolNone(bool))); connect(rbFtNone, SIGNAL(toggled(bool)), SLOT(forceProtocolNone(bool))); connect(rbL3None, SIGNAL(toggled(bool)), SLOT(forceProtocolNone(bool))); connect(rbL4None, SIGNAL(toggled(bool)), SLOT(forceProtocolNone(bool))); // If L1/L2(FT)/L3/L4/L5 = Other, force subsequent protocol to Other and // disable the subsequent protocol group as well connect(rbL1Other, SIGNAL(toggled(bool)), rbFtOther, SLOT(setChecked(bool))); connect(rbL1Other, SIGNAL(toggled(bool)), gbFrameType, SLOT(setDisabled(bool))); connect(rbFtOther, SIGNAL(toggled(bool)), rbL3Other, SLOT(setChecked(bool))); connect(rbFtOther, SIGNAL(toggled(bool)), gbL3Proto, SLOT(setDisabled(bool))); connect(rbL3Other, SIGNAL(toggled(bool)), rbL4Other, SLOT(setChecked(bool))); connect(rbL3Other, SIGNAL(toggled(bool)), gbL4Proto, SLOT(setDisabled(bool))); connect(rbL4Other, SIGNAL(toggled(bool)), rbL5Other, SLOT(setChecked(bool))); connect(rbL4Other, SIGNAL(toggled(bool)), gbL5Proto, SLOT(setDisabled(bool))); connect(rbL5Other, SIGNAL(toggled(bool)), rbPayloadOther, SLOT(setChecked(bool))); connect(rbL5Other, SIGNAL(toggled(bool)), gbPayloadProto, SLOT(setDisabled(bool))); // Setup valid subsequent protocols for L2 to L4 protocols for (int i = ProtoL2; i <= ProtoL4; i++) { foreach(QAbstractButton *btn1, bgProto[i]->buttons()) { int id1 = bgProto[i]->id(btn1); if (id1 != ButtonIdNone && id1 != ButtonIdOther) { int validProtocolCount = 0; foreach(QAbstractButton *btn2, bgProto[i+1]->buttons()) { int id2 = bgProto[i+1]->id(btn2); if (id2 != ButtonIdNone && id2 != ButtonIdOther) { if (OstProtocolManager->isValidNeighbour(id1, id2)) { connect(btn1, SIGNAL(toggled(bool)), btn2, SLOT(setEnabled(bool))); validProtocolCount++; } else connect(btn1, SIGNAL(toggled(bool)), btn2, SLOT(setDisabled(bool))); } } // If btn1 has no subsequent valid protocols, // force subsequent Protocol to 'None' if (validProtocolCount == 0) connect(btn1, SIGNAL(clicked(bool)), bgProto[i+1]->button(ButtonIdNone), SLOT(click())); // If the id1 protocol doesn't have a payload (e.g. IGMP) // force payload protocol to 'None' if (!OstProtocolManager->protocolHasPayload(id1)) { connect(btn1, SIGNAL(clicked(bool)), bgProto[ProtoPayload]->button(ButtonIdNone), SLOT(click())); } } } } mpAvailableProtocolsModel = new QStringListModel( OstProtocolManager->protocolDatabase(), this); lvAllProtocols->setModel(mpAvailableProtocolsModel); lvAllProtocols->setEditTriggers(QAbstractItemView::NoEditTriggers); mpSelectedProtocolsModel = new QStringListModel(this); lvSelectedProtocols->setModel(mpSelectedProtocolsModel); lvSelectedProtocols->setEditTriggers(QAbstractItemView::NoEditTriggers); connect(lvAllProtocols->selectionModel(), SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)), this, SLOT(when_lvAllProtocols_selectionChanged( const QItemSelection&, const QItemSelection&))); connect(lvSelectedProtocols->selectionModel(), SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)), this, SLOT(when_lvSelectedProtocols_currentChanged(const QModelIndex&, const QModelIndex&))); LoadCurrentStream(); mpPacketModel = new PacketModel(this); tvPacketTree->setModel(mpPacketModel); #ifdef QT_NO_DEBUG mpPacketModelTester = NULL; #else mpPacketModelTester = new ModelTest(mpPacketModel); #endif tvPacketTree->header()->hide(); vwPacketDump->setModel(mpPacketModel); vwPacketDump->setSelectionModel(tvPacketTree->selectionModel()); pbPrev->setDisabled(mCurrentStreamIndex == 0); pbNext->setDisabled(int(mCurrentStreamIndex) == (_streamList.size()-1)); //! \todo Support Goto Stream Id leStreamId->setHidden(true); disconnect(rbActionGotoStream, SIGNAL(toggled(bool)), leStreamId, SLOT(setEnabled(bool))); switch(mPort.transmitMode()) { case OstProto::kSequentialTransmit: rbModeFixed->setChecked(true); rbModeContinuous->setDisabled(true); break; case OstProto::kInterleavedTransmit: rbModeContinuous->setChecked(true); rbModeFixed->setDisabled(true); nextWhat->setHidden(true); break; default: Q_ASSERT(false); // Unreachable } // Finally, restore the saved last geometry and selected tab for the // various tab widgets if (!lastGeometry.isNull()) setGeometry(lastGeometry); twTopLevel->setCurrentIndex(lastTopLevelTabIndex); } void StreamConfigDialog::setupUiExtra() { QRegExp reHex2B("[0-9,a-f,A-F]{1,4}"); QRegExp reHex4B("[0-9,a-f,A-F]{1,8}"); QRegExp reMac("([0-9,a-f,A-F]{2,2}[:-]){5,5}[0-9,a-f,A-F]{2,2}"); // ---- Setup default stuff that cannot be done in designer ---- bgProto[ProtoL1] = new QButtonGroup(); bgProto[ProtoL1]->addButton(rbL1None, ButtonIdNone); bgProto[ProtoL1]->addButton(rbL1Mac, OstProto::Protocol::kMacFieldNumber); bgProto[ProtoL1]->addButton(rbL1Other, ButtonIdOther); bgProto[ProtoL2] = new QButtonGroup(); #if 0 foreach(QRadioButton *btn, gbFrameType->findChildren()) bgL2Proto->addButton(btn); #else bgProto[ProtoL2]->addButton(rbFtNone, ButtonIdNone); bgProto[ProtoL2]->addButton(rbFtEthernet2, OstProto::Protocol::kEth2FieldNumber); bgProto[ProtoL2]->addButton(rbFt802Dot3Raw, OstProto::Protocol::kDot3FieldNumber); bgProto[ProtoL2]->addButton(rbFt802Dot3Llc, OstProto::Protocol::kDot2LlcFieldNumber); bgProto[ProtoL2]->addButton(rbFtLlcSnap, OstProto::Protocol::kDot2SnapFieldNumber); bgProto[ProtoL2]->addButton(rbFtOther, ButtonIdOther); #endif bgProto[ProtoVlan] = new QButtonGroup(); bgProto[ProtoVlan]->addButton(rbVlanNone, ButtonIdNone); bgProto[ProtoVlan]->addButton(rbVlanSingle, OstProto::Protocol::kVlanFieldNumber); bgProto[ProtoVlan]->addButton(rbVlanDouble, OstProto::Protocol::kVlanStackFieldNumber); bgProto[ProtoL3] = new QButtonGroup(); #if 0 foreach(QRadioButton *btn, gbL3Proto->findChildren()) bgProto[ProtoL3]->addButton(btn); #else bgProto[ProtoL3]->addButton(rbL3None, ButtonIdNone); bgProto[ProtoL3]->addButton(rbL3Arp, OstProto::Protocol::kArpFieldNumber); bgProto[ProtoL3]->addButton(rbL3Ipv4, OstProto::Protocol::kIp4FieldNumber); bgProto[ProtoL3]->addButton(rbL3Ipv6, OstProto::Protocol::kIp6FieldNumber); bgProto[ProtoL3]->addButton(rbL3Ip6over4, OstProto::Protocol::kIp6over4FieldNumber); bgProto[ProtoL3]->addButton(rbL3Ip4over6, OstProto::Protocol::kIp4over6FieldNumber); bgProto[ProtoL3]->addButton(rbL3Ip4over4, OstProto::Protocol::kIp4over4FieldNumber); bgProto[ProtoL3]->addButton(rbL3Ip6over6, OstProto::Protocol::kIp6over6FieldNumber); bgProto[ProtoL3]->addButton(rbL3Other, ButtonIdOther); #endif bgProto[ProtoL4] = new QButtonGroup(); #if 0 foreach(QRadioButton *btn, gbL4Proto->findChildren()) bgProto[ProtoL4]->addButton(btn); #else bgProto[ProtoL4]->addButton(rbL4None, ButtonIdNone); bgProto[ProtoL4]->addButton(rbL4Tcp, OstProto::Protocol::kTcpFieldNumber); bgProto[ProtoL4]->addButton(rbL4Udp, OstProto::Protocol::kUdpFieldNumber); bgProto[ProtoL4]->addButton(rbL4Icmp, OstProto::Protocol::kIcmpFieldNumber); bgProto[ProtoL4]->addButton(rbL4Igmp, OstProto::Protocol::kIgmpFieldNumber); bgProto[ProtoL4]->addButton(rbL4Mld, OstProto::Protocol::kMldFieldNumber); bgProto[ProtoL4]->addButton(rbL4Other, ButtonIdOther); #endif bgProto[ProtoL5] = new QButtonGroup(); #if 0 foreach(QRadioButton *btn, gbL5Proto->findChildren()) bgProto[ProtoL5]->addButton(btn); #else bgProto[ProtoL5]->addButton(rbL5None, ButtonIdNone); bgProto[ProtoL5]->addButton(rbL5Text, OstProto::Protocol::kTextProtocolFieldNumber); bgProto[ProtoL5]->addButton(rbL5Other, ButtonIdOther); #endif bgProto[ProtoPayload] = new QButtonGroup(); #if 0 foreach(QRadioButton *btn, gbPayloadProto->findChildren()) bgProto[ProtoPayload]->addButton(btn); #else bgProto[ProtoPayload]->addButton(rbPayloadNone, ButtonIdNone); bgProto[ProtoPayload]->addButton(rbPayloadPattern, OstProto::Protocol::kPayloadFieldNumber); bgProto[ProtoPayload]->addButton(rbPayloadHexDump, OstProto::Protocol::kHexDumpFieldNumber); bgProto[ProtoPayload]->addButton(rbPayloadOther, ButtonIdOther); #endif // Special bgProto[ProtoSign] = new QButtonGroup(); bgProto[ProtoSign]->addButton(rbSpecialNone, ButtonIdNone); bgProto[ProtoSign]->addButton(rbSignature, OstProto::Protocol::kSignFieldNumber); // Trailer bgProto[ProtoTrailer] = new QButtonGroup(); bgProto[ProtoTrailer]->addButton(rbTrailerNone, ButtonIdNone); bgProto[ProtoTrailer]->addButton(rbTrailerOther, ButtonIdOther); /* ** Setup Validators */ // Meta Data lePktLen->setValidator(new QIntValidator(MIN_PKT_LEN, MAX_PKT_LEN, this)); lePktLenMin->setValidator(new QIntValidator(MIN_PKT_LEN, MAX_PKT_LEN,this)); lePktLenMax->setValidator(new QIntValidator(MIN_PKT_LEN, MAX_PKT_LEN,this)); lePacketsPerBurst->setValidator(new QIntValidator(1, 0x7FFFFFFF,this)); /* ** Setup Connections */ connect(rbSendPackets, SIGNAL(toggled(bool)), this, SLOT(update_NumPacketsAndNumBursts())); connect(rbSendBursts, SIGNAL(toggled(bool)), this, SLOT(update_NumPacketsAndNumBursts())); connect(rbModeFixed, SIGNAL(toggled(bool)), this, SLOT(update_NumPacketsAndNumBursts())); connect(rbModeContinuous, SIGNAL(toggled(bool)), this, SLOT(update_NumPacketsAndNumBursts())); } StreamConfigDialog::~StreamConfigDialog() { delete mpPacketModelTester; delete mpPacketModel; for (int i = ProtoMin; i < ProtoMax; i++) delete bgProto[i]; foreach (AbstractProtocolConfigForm* w, _protocolWidgets) { OstProtocolWidgetFactory->deleteConfigWidget(w); } delete _iter; while (!_streamList.isEmpty()) delete _streamList.takeFirst(); } void StreamConfigDialog::setWindowTitle(const QString &title) { _windowTitle = title; QDialog::setWindowTitle(title); } void StreamConfigDialog::loadProtocolWidgets() { ProtocolListIterator *iter; // NOTE: Protocol Widgets are created on demand. Once created we // store them in _protocolWidgets indexed by the protocol // object's address (to ensure unique widgets for multiple // objects of the same class). Subsequently we check // _protocolWidgets before creating a new widget iter = mpStream->createProtocolListIterator(); while (iter->hasNext()) { AbstractProtocol* p = iter->next(); AbstractProtocolConfigForm *w = _protocolWidgets.value(p); if (!w) { w = OstProtocolWidgetFactory->createConfigWidget( p->protocolNumber()); _protocolWidgets.insert(p, w); } w->loadWidget(p); } delete iter; } void StreamConfigDialog::storeProtocolWidgets() { ProtocolListIterator *iter; // NOTE: After creating a widget, we need to call loadWidget() // to load the protocol's default values iter = mpStream->createProtocolListIterator(); while (iter->hasNext()) { AbstractProtocol* p = iter->next(); AbstractProtocolConfigForm *w = _protocolWidgets.value(p); if (!w) { w = OstProtocolWidgetFactory->createConfigWidget( p->protocolNumber()); w->loadWidget(p); _protocolWidgets.insert(p, w); } w->storeWidget(p); } delete iter; } void StreamConfigDialog::on_cmbPktLenMode_currentIndexChanged(QString mode) { if (mode == "Fixed") { lePktLen->setEnabled(true); lePktLenMin->setDisabled(true); lePktLenMax->setDisabled(true); } else if (mode == "Increment") { lePktLen->setDisabled(true); lePktLenMin->setEnabled(true); lePktLenMax->setEnabled(true); } else if (mode == "Decrement") { lePktLen->setDisabled(true); lePktLenMin->setEnabled(true); lePktLenMax->setEnabled(true); } else if (mode == "Random") { lePktLen->setDisabled(true); lePktLenMin->setEnabled(true); lePktLenMax->setEnabled(true); } else { qWarning("Unhandled/Unknown PktLenMode = %s", mode.toAscii().data()); } } void StreamConfigDialog::on_tbSelectProtocols_currentChanged(int index) { qDebug("%s, index = %d", __FUNCTION__, index); switch (index) { case 0: updateSelectProtocolsSimpleWidget(); break; case 1: updateSelectProtocolsAdvancedWidget(); break; default: qFatal("%s: unexpected index = %d", __FUNCTION__, index); } } void StreamConfigDialog::when_lvAllProtocols_selectionChanged( const QItemSelection &/*selected*/, const QItemSelection &/*deselected*/) { int size = lvAllProtocols->selectionModel()->selectedIndexes().size(); qDebug("%s: selected.indexes().size = %d\n", __FUNCTION__, size); tbAdd->setEnabled(size > 0); } void StreamConfigDialog::when_lvSelectedProtocols_currentChanged( const QModelIndex ¤t, const QModelIndex &/*previous*/) { qDebug("%s: currentRow = %d\n", __FUNCTION__, current.row()); tbDelete->setEnabled(current.isValid()); tbUp->setEnabled(current.isValid() && (current.row() != 0)); tbDown->setEnabled(current.isValid() && (current.row() != (current.model()->rowCount() - 1))); } void StreamConfigDialog::on_tbAdd_clicked() { int n = 0; QModelIndex idx2; QModelIndexList selection; selection = lvAllProtocols->selectionModel()->selectedIndexes(); // Validation if (selection.size() == 0) return; idx2 = lvSelectedProtocols->currentIndex(); if (idx2.isValid()) n = idx2.row(); _iter->toFront(); while (n--) { if (!_iter->hasNext()) return; _iter->next(); } foreach(QModelIndex idx, selection) _iter->insert(OstProtocolManager->createProtocol( mpAvailableProtocolsModel->stringList().at(idx.row()), mpStream)); updateSelectProtocolsAdvancedWidget(); lvSelectedProtocols->setCurrentIndex(idx2); } void StreamConfigDialog::on_tbDelete_clicked() { int n; QModelIndex idx; AbstractProtocol *p = NULL; idx = lvSelectedProtocols->currentIndex(); // Validation if (!idx.isValid()) return; n = idx.row() + 1; _iter->toFront(); while (n--) { if (!_iter->hasNext()) return; p = _iter->next(); } Q_CHECK_PTR(p); _iter->remove(); // Free both protocol and associated widget delete _protocolWidgets.take(p); delete p; updateSelectProtocolsAdvancedWidget(); lvSelectedProtocols->setCurrentIndex(idx); } void StreamConfigDialog::on_tbUp_clicked() { int m, n; QModelIndex idx; AbstractProtocol *p = NULL; idx = lvSelectedProtocols->currentIndex(); // Validation if (!idx.isValid() || idx.row() == 0) return; m = n = idx.row() + 1; _iter->toFront(); while (n--) { if (!_iter->hasNext()) return; p = _iter->next(); } Q_CHECK_PTR(p); _iter->remove(); _iter->previous(); _iter->insert(p); updateSelectProtocolsAdvancedWidget(); lvSelectedProtocols->setCurrentIndex(idx.sibling(m-2, 0)); } void StreamConfigDialog::on_tbDown_clicked() { int m, n; QModelIndex idx; AbstractProtocol *p = NULL; idx = lvSelectedProtocols->currentIndex(); // Validation if (!idx.isValid() || idx.row() == idx.model()->rowCount()) return; m = n = idx.row() + 1; _iter->toFront(); while (n--) { if (!_iter->hasNext()) return; p = _iter->next(); } Q_CHECK_PTR(p); _iter->remove(); _iter->next(); _iter->insert(p); updateSelectProtocolsAdvancedWidget(); lvSelectedProtocols->setCurrentIndex(idx.sibling(m,0)); } void StreamConfigDialog::updateSelectProtocolsAdvancedWidget() { QStringList selProtoList; qDebug("%s", __FUNCTION__); _iter->toFront(); while(_iter->hasNext()) { AbstractProtocol* p = _iter->next(); qDebug("%p -- %d", p, p->protocolNumber()); selProtoList.append(p->shortName()); } mpSelectedProtocolsModel->setStringList(selProtoList); } void StreamConfigDialog::on_twTopLevel_currentChanged(int index) { switch (index) { // Protocol Data case 1: { // Hide the ToolBox before modifying it - else we have a crash !!! tbProtocolData->hide(); // Remove all existing protocol widgets while (tbProtocolData->count() > 0) { QWidget* w = tbProtocolData->widget(0); tbProtocolData->removeItem(0); w->setParent(0); } // Repopulate the widgets - create new ones, if required _iter->toFront(); while (_iter->hasNext()) { AbstractProtocol* p = _iter->next(); AbstractProtocolConfigForm *w = _protocolWidgets.value(p); if (!w) { w = OstProtocolWidgetFactory->createConfigWidget( p->protocolNumber()); w->loadWidget(p); _protocolWidgets.insert(p, w); } tbProtocolData->addItem(w, p->name()); } if (lastProtocolDataIndex < tbProtocolData->count()) tbProtocolData->setCurrentIndex(lastProtocolDataIndex); tbProtocolData->show(); break; } // Variable Fields case 2: { StoreCurrentStream(); // Stream protocols may have changed - clear and reload variableFieldsWidget->clear(); variableFieldsWidget->load(); break; } // Stream Control case 3: { StoreCurrentStream(); break; } // Packet View case 4: { StoreCurrentStream(); mpPacketModel->setSelectedProtocols(*_iter); break; } default: break; } lastProtocolDataIndex = tbProtocolData->currentIndex(); } void StreamConfigDialog::update_NumPacketsAndNumBursts() { if (rbSendPackets->isChecked() && rbModeFixed->isChecked()) leNumPackets->setEnabled(true); else leNumPackets->setEnabled(false); if (rbSendBursts->isChecked() && rbModeFixed->isChecked()) leNumBursts->setEnabled(true); else leNumBursts->setEnabled(false); } #if 0 void StreamConfigDialog::on_lePattern_editingFinished() { ulong num = 0; bool isOk; QString str; num = lePattern->text().remove(QChar(' ')).toULong(&isOk, 16); qDebug("editfinished (%s | %x)\n", lePattern->text().toAscii().data(), num); lePattern->setText(uintToHexStr(num, str, 4)); qDebug("editfinished (%s | %x)\n", lePattern->text().toAscii().data(), num); } #endif /*! Skip protocols upto and including the layer specified. */ bool StreamConfigDialog::skipProtocols(int layer) { _iter->toFront(); for (int i = ProtoMin; i <= layer; i++) { if(_iter->hasNext()) { int id; QAbstractButton *btn; id = _iter->peekNext()->protocolNumber(); btn = bgProto[i]->button(id); if (btn) _iter->next(); } } return true; } /*! Protocol choices (except "None" and "Other") for a protocol button group are disabled if checked is true, else they are enabled */ void StreamConfigDialog::disableProtocols(QButtonGroup *protocolGroup, bool checked) { qDebug("%s: btnGrp = %p, chk? = %d", __FUNCTION__, protocolGroup, checked); foreach(QAbstractButton *btn, protocolGroup->buttons()) { int id = protocolGroup->id(btn); if ((id != ButtonIdNone) && (id != ButtonIdOther)) btn->setDisabled(checked); } } void StreamConfigDialog::forceProtocolNone(bool checked) { QObject *btn; btn = sender(); Q_ASSERT(btn != NULL); qDebug("%s: chk? = %d, btn = %p, L1 = %p, L2 = %p, L3 = %p", __FUNCTION__, checked, btn, rbL1None, rbFtNone, rbL3None); if (btn == rbL1None) { if (checked) { bgProto[ProtoVlan]->button(ButtonIdNone)->click(); bgProto[ProtoL2]->button(ButtonIdNone)->click(); bgProto[ProtoPayload]->button(ButtonIdNone)->click(); } disableProtocols(bgProto[ProtoVlan], checked); disableProtocols(bgProto[ProtoL2], checked); disableProtocols(bgProto[ProtoPayload], checked); } else if (btn == rbFtNone) { if (checked) bgProto[ProtoL3]->button(ButtonIdNone)->click(); disableProtocols(bgProto[ProtoL3], checked); } else if (btn == rbL3None) { if (checked) bgProto[ProtoL4]->button(ButtonIdNone)->click(); disableProtocols(bgProto[ProtoL4], checked); } else if (btn == rbL4None) { if (checked) bgProto[ProtoL5]->button(ButtonIdNone)->click(); disableProtocols(bgProto[ProtoL5], checked); } else { Q_ASSERT(1 == 0); // Unreachable code! } } // Button 'newId' has been clicked // - update the protocol list correspondingly void StreamConfigDialog::updateProtocol(int newId) { int level; QButtonGroup *btnGrp; btnGrp = static_cast(sender()); Q_ASSERT(btnGrp != NULL); level = btnGrp->property("ProtocolLevel").toInt(); Q_ASSERT(btnGrp == bgProto[level]); __updateProtocol(level, newId); } // Button 'newId' belonging to layer 'level' has been clicked // - update the protocol list correspondingly void StreamConfigDialog::__updateProtocol(int level, int newId) { int oldId; QButtonGroup *btnGrp; Q_ASSERT((level >= ProtoMin) && (level <= ProtoMax)); btnGrp = bgProto[level]; oldId = btnGrp->property("ProtocolId").toInt(); qDebug("%s: level = %d old id = %d new id = %d upd? = %d", __FUNCTION__, level, oldId, newId, isUpdateInProgress); if (newId == oldId) return; if (!isUpdateInProgress) { int ret; AbstractProtocol *p; ret = skipProtocols(level-1); Q_ASSERT(ret == true); Q_UNUSED(ret); Q_ASSERT(oldId != newId); Q_ASSERT(newId != ButtonIdOther); switch (oldId) { case ButtonIdNone: _iter->insert(OstProtocolManager->createProtocol( newId, mpStream)); break; case ButtonIdOther: default: Q_ASSERT(_iter->hasNext()); p =_iter->next(); if (newId) _iter->setValue(OstProtocolManager->createProtocol( newId, mpStream)); else _iter->remove(); // Free both protocol and associated widget delete _protocolWidgets.take(p); delete p; if (level == ProtoTrailer) { while (_iter->hasNext()) { p = _iter->next(); _iter->remove(); // Free both protocol and associated widget delete _protocolWidgets.take(p); delete p; } } break; } } btnGrp->setProperty("ProtocolId", newId); return; } //! Traverse the ProtocolList and update the SelectProtocols (Simple) widget void StreamConfigDialog::updateSelectProtocolsSimpleWidget() { int i; quint32 id; QAbstractButton *btn; qDebug("%s", __FUNCTION__); isUpdateInProgress = true; // Reset to default state ... for (i = ProtoMin; i < ProtoMax; i++) bgProto[i]->button(ButtonIdNone)->click(); // ... now iterate and update _iter->toFront(); for (i = ProtoMin; i < ProtoMax; i++) { if (!_iter->hasNext()) goto _done; id = _iter->next()->protocolNumber(); btn = bgProto[i]->button(id); if (btn) // we have a button for this protocol { if (btn->isEnabled()) btn->click(); else { btn->setChecked(true); __updateProtocol(i, id); } } else // we don't have a button for this protocol { switch (i) { case ProtoVlan: // No vlan - proto may belong to next layer _iter->previous(); break; case ProtoSign: // No sign - but we have a trailer _iter->previous(); break; case ProtoPayload: goto _other; default: // viz. L1, L2, L3, L4, L5, Trailer // Is this a Payload layer protocol? // (maybe intermediate layers are not present) btn = bgProto[ProtoPayload]->button(id); if (btn && btn->isEnabled()) { btn->click(); i = ProtoPayload; continue; } else goto _other; } } } // If more protocol(s) beyond trailer ... if (_iter->hasNext()) { i = ProtoTrailer; goto _other; } Q_ASSERT(!_iter->hasNext()); // At end of the ProtocolList goto _done; _other: // Set remaining protocols as 'Other' for (int j = i; j < ProtoMax; j++) { // VLAN/Sign doesn't have a "Other" button if ((j == ProtoVlan) || (j == ProtoSign)) continue; bgProto[j]->button(ButtonIdOther)->setChecked(true); __updateProtocol(j, ButtonIdOther); } _done: isUpdateInProgress = false; } void StreamConfigDialog::LoadCurrentStream() { QString str; qDebug("loading mpStream %p", mpStream); variableFieldsWidget->setStream(mpStream); QDialog::setWindowTitle(QString("%1 [%2]").arg(_windowTitle) .arg(mpStream->name().isEmpty() ? tr("") : mpStream->name())); // Meta Data { name->setText(mpStream->name()); enabled->setChecked(mpStream->isEnabled()); cmbPktLenMode->setCurrentIndex(mpStream->lenMode()); lePktLen->setText(str.setNum(mpStream->frameLen())); lePktLenMin->setText(str.setNum(mpStream->frameLenMin())); lePktLenMax->setText(str.setNum(mpStream->frameLenMax())); } // Protocols { updateSelectProtocolsSimpleWidget(); updateSelectProtocolsAdvancedWidget(); loadProtocolWidgets(); } // Variable Fields { variableFieldsWidget->clear(); variableFieldsWidget->load(); } // Stream Control { switch (mpStream->sendUnit()) { case Stream::e_su_packets: rbSendPackets->setChecked(true); break; case Stream::e_su_bursts: rbSendBursts->setChecked(true); break; default: qWarning("Unhandled sendUnit = %d\n", mpStream->sendUnit()); } switch (mpStream->sendMode()) { case Stream::e_sm_fixed: rbModeFixed->setChecked(true); break; case Stream::e_sm_continuous: rbModeContinuous->setChecked(true); break; default: qWarning("Unhandled sendMode = %d\n", mpStream->sendMode()); } switch(mpStream->nextWhat()) { case Stream::e_nw_stop: rbActionStop->setChecked(true); break; case Stream::e_nw_goto_next: rbActionGotoNext->setChecked(true); break; case Stream::e_nw_goto_id: rbActionGotoStream->setChecked(true); break; default: qWarning("Unhandled nextAction = %d\n", mpStream->nextWhat()); } leNumPackets->setText(QString().setNum(mpStream->numPackets())); leNumBursts->setText(QString().setNum(mpStream->numBursts())); lePacketsPerBurst->setText(QString().setNum(mpStream->burstSize())); lePacketsPerSec->setText( QString("%L1").arg(mpStream->packetRate(), 0, 'f', 4)); leBurstsPerSec->setText( QString("%L1").arg(mpStream->burstRate(), 0, 'f', 4)); // TODO(MED): Change this when we support goto to specific stream leStreamId->setText(QString("0")); leGapIsg->setText("0.0"); } qDebug("loading stream done"); } void StreamConfigDialog::StoreCurrentStream() { QString str; bool isOk; Stream *pStream = mpStream; qDebug("storing pStream %p", pStream); // Meta Data pStream->setName(name->text()); pStream->setEnabled(enabled->isChecked()); pStream->setLenMode((Stream::FrameLengthMode) cmbPktLenMode->currentIndex()); pStream->setFrameLen(lePktLen->text().toULong(&isOk)); pStream->setFrameLenMin(lePktLenMin->text().toULong(&isOk)); pStream->setFrameLenMax(lePktLenMax->text().toULong(&isOk)); // Protocols { storeProtocolWidgets(); } // Variable Fields { variableFieldsWidget->store(); } // Stream Control { if (rbSendPackets->isChecked()) pStream->setSendUnit(Stream::e_su_packets); if (rbSendBursts->isChecked()) pStream->setSendUnit(Stream::e_su_bursts); if (rbModeFixed->isChecked()) pStream->setSendMode(Stream::e_sm_fixed); if (rbModeContinuous->isChecked()) pStream->setSendMode(Stream::e_sm_continuous); if (rbActionStop->isChecked()) pStream->setNextWhat(Stream::e_nw_stop); if (rbActionGotoNext->isChecked()) pStream->setNextWhat(Stream::e_nw_goto_next); if (rbActionGotoStream->isChecked()) pStream->setNextWhat(Stream::e_nw_goto_id); pStream->setNumPackets(leNumPackets->text().toULong(&isOk)); pStream->setNumBursts(leNumBursts->text().toULong(&isOk)); pStream->setBurstSize(lePacketsPerBurst->text().toULong(&isOk)); pStream->setPacketRate( QLocale().toDouble(lePacketsPerSec->text(), &isOk)); pStream->setBurstRate( QLocale().toDouble(leBurstsPerSec->text(), &isOk)); } } void StreamConfigDialog::on_tbProtocolData_currentChanged(int /*index*/) { // Refresh protocol widgets in case there is any dependent data between // protocols e.g. TCP/UDP port numbers are dependent on Port/Protocol // selection in TextProtocol #if 0 // FIXME: temp mask to avoid crash till we fix it storeProtocolWidgets(); loadProtocolWidgets(); #endif } void StreamConfigDialog::on_rbPacketsPerSec_toggled(bool checked) { if (checked) on_lePacketsPerSec_textChanged(lePacketsPerSec->text()); } void StreamConfigDialog::on_rbBurstsPerSec_toggled(bool checked) { if (checked) on_leBurstsPerSec_textChanged(leBurstsPerSec->text()); } void StreamConfigDialog::on_lePacketsPerBurst_textChanged(const QString &/*text*/) { if (rbSendBursts->isChecked()) on_leBurstsPerSec_textChanged(leBurstsPerSec->text()); } void StreamConfigDialog::on_lePacketsPerSec_textChanged(const QString &text) { bool isOk; Stream *pStream = mpStream; uint frameLen; if (pStream->lenMode() == Stream::e_fl_fixed) frameLen = pStream->frameLen(); else frameLen = (pStream->frameLenMin() + pStream->frameLenMax())/2; if (rbSendPackets->isChecked()) { double pktsPerSec = QLocale().toDouble(text, &isOk); double bitsPerSec = pktsPerSec * double((frameLen+kEthFrameOverHead)*8); if (rbPacketsPerSec->isChecked()) leBitsPerSec->setText(QString("%L1").arg(bitsPerSec, 0, 'f', 0)); leGapIbg->setText(QString("0.0")); leGapIpg->setText(QString("%L1").arg(1/double(pktsPerSec), 0, 'f', 9)); } } void StreamConfigDialog::on_leBurstsPerSec_textChanged(const QString &text) { bool isOk; Stream *pStream = mpStream; uint burstSize = lePacketsPerBurst->text().toULong(&isOk); uint frameLen; qDebug("start of %s(%s)", __FUNCTION__, text.toAscii().constData()); if (pStream->lenMode() == Stream::e_fl_fixed) frameLen = pStream->frameLen(); else frameLen = (pStream->frameLenMin() + pStream->frameLenMax())/2; if (rbSendBursts->isChecked()) { double burstsPerSec = QLocale().toDouble(text, &isOk); double bitsPerSec = burstsPerSec * double(burstSize * (frameLen + kEthFrameOverHead) * 8); if (rbBurstsPerSec->isChecked()) leBitsPerSec->setText(QString("%L1").arg(bitsPerSec, 0, 'f', 0)); leGapIbg->setText(QString("%L1").arg(1/double(burstsPerSec), 0, 'f',9)); leGapIpg->setText(QString("0.0")); } qDebug("end of %s", __FUNCTION__); } void StreamConfigDialog::on_leBitsPerSec_textEdited(const QString &text) { bool isOk; Stream *pStream = mpStream; uint burstSize = lePacketsPerBurst->text().toULong(&isOk); uint frameLen; if (pStream->lenMode() == Stream::e_fl_fixed) frameLen = pStream->frameLen(); else frameLen = (pStream->frameLenMin() + pStream->frameLenMax())/2; if (rbSendPackets->isChecked()) { double pktsPerSec = QLocale().toDouble(text, &isOk)/ double((frameLen+kEthFrameOverHead)*8); lePacketsPerSec->setText(QString("%L1").arg(pktsPerSec, 0, 'f', 4)); } else if (rbSendBursts->isChecked()) { double burstsPerSec = QLocale().toDouble(text, &isOk)/ double(burstSize * (frameLen + kEthFrameOverHead) * 8); leBurstsPerSec->setText(QString("%L1").arg(burstsPerSec, 0, 'f', 4)); } } bool StreamConfigDialog::isCurrentStreamValid() { QStringList log; if ((mPort.transmitMode() == OstProto::kInterleavedTransmit) && (mpStream->isFrameVariable())) { log << tr("In 'Interleaved Streams' transmit mode, the count for " "varying fields at transmit time may not be same as configured"); } if (!mPort.trackStreamStats() && mpStream->hasProtocol(OstProto::Protocol::kSignFieldNumber)) { log << tr("Stream contains special signature, but per stream statistics " "will not be available till it is enabled on the port"); } mpStream->preflightCheck(log); if (log.size()) { if (QMessageBox::warning(this, "Preflight Check", tr("

We found possible problems with this stream -

") + "
    " + log.replaceInStrings(QRegExp("(.*)"), "
  • \\1
  • ") .join("\n") + "
" + tr("

Ignore?

"), QMessageBox::Yes | QMessageBox::No, QMessageBox::No) == QMessageBox::No) return false; } return true; } void StreamConfigDialog::on_pbPrev_clicked() { Q_ASSERT(mCurrentStreamIndex > 0); StoreCurrentStream(); if (!isCurrentStreamValid()) return; delete _iter; mpStream = _streamList.at(--mCurrentStreamIndex); _iter = mpStream->createProtocolListIterator(); LoadCurrentStream(); on_twTopLevel_currentChanged(twTopLevel->currentIndex()); pbPrev->setDisabled(mCurrentStreamIndex == 0); pbNext->setDisabled(int(mCurrentStreamIndex) == (_streamList.size()-1)); } void StreamConfigDialog::on_pbNext_clicked() { Q_ASSERT(int(mCurrentStreamIndex) < (_streamList.size()-1)); StoreCurrentStream(); if (!isCurrentStreamValid()) return; delete _iter; mpStream = _streamList.at(++mCurrentStreamIndex); _iter = mpStream->createProtocolListIterator(); LoadCurrentStream(); on_twTopLevel_currentChanged(twTopLevel->currentIndex()); pbPrev->setDisabled(mCurrentStreamIndex == 0); pbNext->setDisabled(int(mCurrentStreamIndex) == (_streamList.size()-1)); } void StreamConfigDialog::on_pbOk_clicked() { // Store dialog contents into current stream StoreCurrentStream(); if (!isCurrentStreamValid()) return; // Copy the working copy of streams to user provided streams Q_ASSERT(_userStreamList.size() == _streamList.size()); for (int i = 0; i < _streamList.size(); i++) { OstProto::Stream s; _streamList.at(i)->protoDataCopyInto(s); _userStreamList[i]->protoDataCopyFrom(s); } qDebug("stream stored"); lastGeometry = geometry(); lastTopLevelTabIndex = twTopLevel->currentIndex(); lastProtocolDataIndex = tbProtocolData->currentIndex(); accept(); } ostinato-0.9/client/streamconfigdialog.h000066400000000000000000000104611321315675200205210ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _STREAM_CONFIG_DIALOG_H #define _STREAM_CONFIG_DIALOG_H #include #include "ui_streamconfigdialog.h" #include "port.h" #include "stream.h" #include "packetmodel.h" #include "modeltest.h" #define MAX_MAC_ITER_COUNT 256 #define MIN_PKT_LEN 64 #define MAX_PKT_LEN 16384 /* ** TODO ** \todo Improve HexStr handling ** */ class AbstractProtocolConfigForm; class StreamConfigDialog : public QDialog, public Ui::StreamConfigDialog { Q_OBJECT public: StreamConfigDialog(QList &streamList, const Port &port, QWidget *parent = 0); ~StreamConfigDialog(); void setWindowTitle(const QString &title); private: enum ButtonId { ButtonIdNone = 0, ButtonIdOther = -2 }; enum ProtoButtonGroup { ProtoMin, ProtoL1 = 0, ProtoVlan = 1, ProtoL2 = 2, ProtoL3 = 3, ProtoL4 = 4, ProtoL5 = 5, ProtoPayload = 6, ProtoSign = 7, ProtoTrailer = 8, ProtoMax }; QButtonGroup *bgProto[ProtoMax]; QStringListModel *mpAvailableProtocolsModel; QStringListModel *mpSelectedProtocolsModel; QList _userStreamList; QList _streamList; const Port& mPort; QString _windowTitle; uint mCurrentStreamIndex; Stream *mpStream; ProtocolListIterator *_iter; QHash _protocolWidgets; bool isUpdateInProgress; PacketModel *mpPacketModel; ModelTest *mpPacketModelTester; // The following static variables are used to track the "selected" tab // for the various tab widgets so that it can be restored when the dialog // is opened the next time. We also track the last Dialog geometry. static QRect lastGeometry; static int lastTopLevelTabIndex; static int lastProtocolDataIndex; void setupUiExtra(); bool isCurrentStreamValid(); void LoadCurrentStream(); void StoreCurrentStream(); void loadProtocolWidgets(); void storeProtocolWidgets(); private slots: void on_cmbPktLenMode_currentIndexChanged(QString mode); void update_NumPacketsAndNumBursts(); void on_twTopLevel_currentChanged(int index); void on_tbSelectProtocols_currentChanged(int index); // "Simple" Protocol Selection related bool skipProtocols(int layer); void disableProtocols(QButtonGroup *protocolGroup, bool checked); void forceProtocolNone(bool checked); void updateProtocol(int newId); void __updateProtocol(int level, int newId); void updateSelectProtocolsSimpleWidget(); // "Advanced" Protocol Selection related void when_lvAllProtocols_selectionChanged( const QItemSelection &selected, const QItemSelection &deselected); void when_lvSelectedProtocols_currentChanged( const QModelIndex ¤t, const QModelIndex &previous); void on_tbAdd_clicked(); void on_tbDelete_clicked(); void on_tbUp_clicked(); void on_tbDown_clicked(); void updateSelectProtocolsAdvancedWidget(); // "Protocol Data" related void on_tbProtocolData_currentChanged(int index); // "Stream Control" related void on_rbPacketsPerSec_toggled(bool checked); void on_rbBurstsPerSec_toggled(bool checked); void on_lePacketsPerBurst_textChanged(const QString &text); void on_lePacketsPerSec_textChanged(const QString &text); void on_leBurstsPerSec_textChanged(const QString &text); void on_leBitsPerSec_textEdited(const QString &text); void on_pbPrev_clicked(); void on_pbNext_clicked(); void on_pbOk_clicked(); }; #endif ostinato-0.9/client/streamconfigdialog.ui000066400000000000000000001407351321315675200207170ustar00rootroot00000000000000 StreamConfigDialog Qt::ApplicationModal 0 0 647 549 0 0 Edit Stream :/icons/stream_edit.png:/icons/stream_edit.png QLineEdit:enabled[inputMask = "HH; "], QLineEdit:enabled[inputMask = "HH HH; "], QLineEdit:enabled[inputMask = "HH HH HH; "], QLineEdit:enabled[inputMask = "HH HH HH HH; "], QLineEdit:enabled[inputMask = "HH HH HH HH HH HH; "] { background-color: #ccccff } true 0 Protocol Selection Basics Name name Enabled Frame Length (including FCS) Fixed Increment Decrement Random Min false Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Max false Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 0 0 0 605 311 Simple L1 None true Mac false false Other true L2 None true Ethernet II false 802.3 Raw 802.3 LLC false 802.3 LLC SNAP false Other true L3 None true false ARP false IPv4 false false IPv6 false IP 6over4 false false IP 4over6 false false IP 4over4 false false IP 6over6 false false Other true VLAN false false Untagged true Tagged Stacked true L4 None true false ICMP false IGMP false TCP false UDP false Other false MLD true L5 None true false Text false Other true Payload None true Pattern false Hex Dump false Other true Special None true Signature true Trailer None true false Other 0 0 250 135 Advanced Available Protocols true QAbstractItemView::ExtendedSelection QAbstractItemView::SelectRows Qt::Vertical 20 40 false > :/icons/arrow_right.png:/icons/arrow_right.png Qt::Vertical 20 40 Selected Protocols false ^ :/icons/arrow_up.png:/icons/arrow_up.png false v :/icons/arrow_down.png:/icons/arrow_down.png false - :/icons/delete.png:/icons/delete.png Qt::Horizontal 40 20 QAbstractItemView::SelectRows Protocol Data -1 Variable Fields Stream Control Send Packets true Bursts Numbers Number of Packets leNumPackets Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Number of Bursts leNumBursts false Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Packets per Burst lePacketsPerBurst false Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Rate false false Packets/Sec true Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter false Bursts/Sec false Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Bits/Sec false Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter After this stream Stop Goto Next Stream true Goto First false Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Qt::Horizontal 20 41 Mode Fixed true Continuous true Gaps (in seconds) false false :/icons/gaps.png ISG leGapIsg false Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter IBG leGapIbg false Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter IPG leGapIpg false Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Qt::Vertical 153 21 Packet View Qt::Vertical QAbstractItemView::SelectItems QAbstractItemView::ScrollPerPixel true Prev Next Qt::Horizontal 191 20 OK true Cancel DumpView QWidget
dumpview.h
1
VariableFieldsWidget QWidget
variablefieldswidget.h
1
twTopLevel cmbPktLenMode lePktLen lePktLenMin lePktLenMax rbL1None rbL1Mac rbL1Other rbVlanNone rbVlanSingle rbVlanDouble rbFtNone rbFtEthernet2 rbFt802Dot3Raw rbFt802Dot3Llc rbFtLlcSnap rbFtOther rbL3None rbL3Arp rbL3Ipv4 rbL3Ipv6 rbL3Ip6over4 rbL3Ip4over6 rbL3Ip4over4 rbL3Ip6over6 rbL3Other rbL4None rbL4Icmp rbL4Igmp rbL4Mld rbL4Tcp rbL4Udp rbL4Other rbL5None rbL5Text rbL5Other rbPayloadNone rbPayloadPattern rbPayloadHexDump rbPayloadOther lvAllProtocols tbAdd tbUp tbDown tbDelete lvSelectedProtocols rbSendPackets rbSendBursts rbModeFixed rbModeContinuous leNumPackets leNumBursts lePacketsPerBurst lePacketsPerSec leBurstsPerSec rbBitsPerSec leBitsPerSec rbActionStop rbActionGotoNext rbActionGotoStream leStreamId leGapIsg leGapIbg leGapIpg tvPacketTree pbPrev pbNext pbOk pbCancel pbCancel clicked() StreamConfigDialog reject() 623 496 533 466 rbActionGotoStream toggled(bool) leStreamId setEnabled(bool) 463 143 463 177 rbSendPackets toggled(bool) rbPacketsPerSec setEnabled(bool) 30 68 299 82 rbSendBursts toggled(bool) rbBurstsPerSec setEnabled(bool) 30 95 299 132 rbSendBursts toggled(bool) lePacketsPerBurst setEnabled(bool) 30 95 134 189 rbPacketsPerSec toggled(bool) lePacketsPerSec setEnabled(bool) 299 82 299 108 rbBurstsPerSec toggled(bool) leBurstsPerSec setEnabled(bool) 299 132 299 158 rbBitsPerSec toggled(bool) leBitsPerSec setEnabled(bool) 299 182 299 208 rbSendPackets toggled(bool) rbPacketsPerSec setChecked(bool) 95 70 299 82 rbSendBursts toggled(bool) rbBurstsPerSec setChecked(bool) 96 98 299 132 rbModeContinuous toggled(bool) leNumPackets setDisabled(bool) 73 196 164 108 rbModeContinuous toggled(bool) leNumBursts setDisabled(bool) 96 199 226 155
ostinato-0.9/client/streamlistdelegate.cpp000066400000000000000000000126021321315675200210740ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include #include #include #include #include "streammodel.h" #include "streamlistdelegate.h" StreamListDelegate::StreamListDelegate(QObject *parent) : QItemDelegate(parent) { } QWidget *StreamListDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const { QWidget *editor = NULL; switch ((StreamModel::StreamFields) index.column()) { case StreamModel::StreamStatus: { editor = new QCheckBox(parent); goto _handled; } case StreamModel::StreamNextWhat: { editor = new QComboBox(parent); static_cast(editor)->insertItems(0, StreamModel::nextWhatOptionList()); goto _handled; } case StreamModel::StreamIcon: case StreamModel::StreamName: default: break; } editor = QItemDelegate::createEditor(parent, option, index); _handled: return editor; } void StreamListDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const { switch ((StreamModel::StreamFields) index.column()) { case StreamModel::StreamStatus: { QCheckBox *cb = static_cast(editor); cb->setChecked( index.model()->data(index, Qt::EditRole).toBool()); goto _handled; } case StreamModel::StreamNextWhat: { QComboBox *cb = static_cast(editor); cb->setCurrentIndex( index.model()->data(index, Qt::EditRole).toInt()); goto _handled; } case StreamModel::StreamIcon: case StreamModel::StreamName: default: break; } QItemDelegate::setEditorData(editor, index); _handled: return; } void StreamListDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const { switch ((StreamModel::StreamFields) index.column()) { case StreamModel::StreamStatus: { QCheckBox *cb = static_cast(editor); model->setData(index, cb->isChecked(), Qt::EditRole); goto _handled; } case StreamModel::StreamNextWhat: { QComboBox *cb = static_cast(editor); model->setData(index, cb->currentIndex(), Qt::EditRole); goto _handled; } case StreamModel::StreamIcon: case StreamModel::StreamName: default: break; } QItemDelegate::setModelData(editor, model, index); _handled: return; } void StreamListDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const { switch ((StreamModel::StreamFields) index.column()) { case StreamModel::StreamStatus: { /* * extra 'coz QItemDelegate does it - otherwise the editor * placement is incorrect */ int extra = 2 * (qApp->style()->pixelMetric( QStyle::PM_FocusFrameHMargin, 0) + 1); editor->setGeometry(option.rect.translated(extra, 0)); goto _handled; } case StreamModel::StreamIcon: case StreamModel::StreamName: case StreamModel::StreamNextWhat: default: break; } QItemDelegate::updateEditorGeometry(editor, option, index); _handled: return; } bool StreamListDelegate::editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index) { /* * Special Handling so that user can use the "Stream status" checkbox * without double clicking first. Copied from QItemDelegate::editorEvent() * and modified suitably */ if ((StreamModel::StreamFields)index.column() == StreamModel::StreamStatus) { // make sure that we have the right event type if ((event->type() == QEvent::MouseButtonRelease) || (event->type() == QEvent::MouseButtonDblClick)) { QRect checkRect = check(option, option.rect, Qt::Checked); QRect emptyRect; doLayout(option, &checkRect, &emptyRect, &emptyRect, false); if (!checkRect.contains(static_cast(event)->pos())) return false; Qt::CheckState state = (static_cast(index.data( Qt::CheckStateRole).toInt()) == Qt::Checked ? Qt::Unchecked : Qt::Checked); return model->setData(index, state, Qt::CheckStateRole); } } return QItemDelegate::editorEvent(event, model, option, index); } ostinato-0.9/client/streamlistdelegate.h000066400000000000000000000027031321315675200205420ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef STREAM_LIST_DELEGATE_H #define STREAM_LIST_DELEGATE_H #include #include class StreamListDelegate : public QItemDelegate { Q_OBJECT public: StreamListDelegate(QObject *parent = 0); QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const; void setEditorData(QWidget *editor, const QModelIndex &index) const; void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const; void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const; bool editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index); }; #endif ostinato-0.9/client/streammodel.cpp000066400000000000000000000204251321315675200175300ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "stream.h" #include "streammodel.h" #include "portgrouplist.h" #include "qicon.h" StreamModel::StreamModel(PortGroupList *p, QObject *parent) : QAbstractTableModel(parent) { pgl = p; mCurrentPort = NULL; } int StreamModel::rowCount(const QModelIndex &parent) const { if (parent.isValid()) return 0; if (mCurrentPort) return mCurrentPort->numStreams(); else return 0; } int StreamModel::columnCount(const QModelIndex &/*parent*/) const { int count = StreamMaxFields; if (mCurrentPort && (mCurrentPort->transmitMode() == OstProto::kInterleavedTransmit)) count--; return count; } Qt::ItemFlags StreamModel::flags(const QModelIndex &index) const { Qt::ItemFlags flags = QAbstractTableModel::flags(index); switch (index.column()) { case StreamIcon: break; case StreamName: flags |= Qt::ItemIsEditable; break; case StreamStatus: flags |= Qt::ItemIsUserCheckable; break; case StreamNextWhat: flags |= Qt::ItemIsEditable; break; default: //qFatal("Missed case in switch!"); break; } return flags; } QVariant StreamModel::data(const QModelIndex &index, int role) const { // Check for a valid index if (!index.isValid()) return QVariant(); // Check for row/column limits if (index.row() >= mCurrentPort->numStreams()) return QVariant(); if (index.column() >= StreamMaxFields) return QVariant(); if (mCurrentPort == NULL) return QVariant(); // Return data based on field and role switch(index.column()) { case StreamIcon: { if (role == Qt::DecorationRole) return QIcon(":/icons/stream_edit.png"); else return QVariant(); break; } case StreamName: { if ((role == Qt::DisplayRole) || (role == Qt::EditRole)) return mCurrentPort->streamByIndex(index.row())->name(); else return QVariant(); break; } case StreamStatus: { if ((role == Qt::CheckStateRole)) { if (mCurrentPort->streamByIndex(index.row())->isEnabled()) return Qt::Checked; else return Qt::Unchecked; } else return QVariant(); break; } case StreamNextWhat: { int val = mCurrentPort->streamByIndex(index.row())->nextWhat(); if (role == Qt::DisplayRole) return nextWhatOptionList().at(val); else if (role == Qt::EditRole) return val; else return QVariant(); break; } default: qFatal("-------------UNHANDLED STREAM FIELD----------------"); } return QVariant(); } bool StreamModel::setData(const QModelIndex &index, const QVariant &value, int role) { if (mCurrentPort == NULL) return false; if (index.isValid()) { switch (index.column()) { // Edit Supported Fields case StreamName: mCurrentPort->mutableStreamByIndex(index.row()) ->setName(value.toString()); emit(dataChanged(index, index)); return true; case StreamStatus: mCurrentPort->mutableStreamByIndex(index.row()) ->setEnabled(value.toBool()); emit(dataChanged(index, index)); return true; case StreamNextWhat: if (role == Qt::EditRole) { mCurrentPort->mutableStreamByIndex(index.row())->setNextWhat( (Stream::NextWhat)value.toInt()); emit(dataChanged(index, index)); return true; } else return false; // Edit Not Supported Fields case StreamIcon: return false; // Unhandled Stream Field default: qDebug("-------------UNHANDLED STREAM FIELD----------------"); break; } } return false; } QVariant StreamModel::headerData(int section, Qt::Orientation orientation, int role) const { if (role != Qt::DisplayRole) return QVariant(); if (orientation == Qt::Horizontal) { switch(section) { case StreamIcon: return QString(""); break; case StreamName: return QString("Name"); break; case StreamStatus: return QString(""); break; case StreamNextWhat: return QString("Goto"); break; default: qDebug("-------------UNHANDLED STREAM FIELD----------------"); break; } } else return QString("%1").arg(section+1); return QVariant(); } /*! * Inserts streams before the given row * * StreamModel takes ownership of the passed streams; caller should * not try to access them after calling this function */ bool StreamModel::insert(int row, QList &streams) { int count = streams.size(); qDebug("insert row = %d", row); qDebug("insert count = %d", count); beginInsertRows(QModelIndex(), row, row+count-1); for (int i = 0; i < count; i++) { OstProto::Stream s; streams.at(i)->protoDataCopyInto(s); mCurrentPort->newStreamAt(row+i, &s); delete streams.at(i); } streams.clear(); endInsertRows(); return true; } bool StreamModel::insertRows(int row, int count, const QModelIndex &/*parent*/) { qDebug("insertRows() row = %d", row); qDebug("insertRows() count = %d", count); beginInsertRows(QModelIndex(), row, row+count-1); for (int i = 0; i < count; i++) mCurrentPort->newStreamAt(row); endInsertRows(); return true; } bool StreamModel::removeRows(int row, int count, const QModelIndex &/*parent*/) { qDebug("removeRows() row = %d", row); qDebug("removeRows() count = %d", count); beginRemoveRows(QModelIndex(), row, row+count-1); for (int i = 0; i < count; i++) { mCurrentPort->deleteStreamAt(row); } endRemoveRows(); return true; } // --------------------- SLOTS ------------------------ void StreamModel::setCurrentPortIndex(const QModelIndex ¤t) { if (!current.isValid() || !pgl->isPort(current)) { qDebug("current is either invalid or not a port"); mCurrentPort = NULL; } else { qDebug("change to valid port"); // Disconnect any existing connection to avoid duplication // Qt 4.6 has Qt::UniqueConnection, but we want to remain compatible // with earlier Qt versions if (mCurrentPort) { disconnect(mCurrentPort, SIGNAL(streamListChanged(int, int)), this, SLOT(when_mCurrentPort_streamListChanged(int, int))); } quint16 pg = current.internalId() >> 16; mCurrentPort = pgl->mPortGroups[pgl->indexOfPortGroup(pg)]->mPorts[current.row()]; connect(mCurrentPort, SIGNAL(streamListChanged(int, int)), this, SLOT(when_mCurrentPort_streamListChanged(int, int))); } reset(); } void StreamModel::when_mCurrentPort_streamListChanged(int portGroupId, int portId) { qDebug("In %s", __FUNCTION__); if (mCurrentPort) { if ((quint32(portGroupId) == mCurrentPort->portGroupId()) && (quint32(portId) == mCurrentPort->id())) reset(); } } ostinato-0.9/client/streammodel.h000066400000000000000000000045761321315675200172060ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _STREAM_MODEL_H #define _STREAM_MODEL_H #include #include #include "port.h" class PortGroupList; class Stream; class StreamModel : public QAbstractTableModel { Q_OBJECT Port *mCurrentPort; PortGroupList *pgl; public: StreamModel(PortGroupList *p, QObject *parent = 0); int rowCount(const QModelIndex &parent = QModelIndex()) const; int columnCount(const QModelIndex &parent = QModelIndex()) const; Qt::ItemFlags flags(const QModelIndex &index) const; QVariant data(const QModelIndex &index, int role) const; bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole); QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; bool insert(int row, QList &streams); bool insertRows (int row, int count, const QModelIndex & parent = QModelIndex()); bool removeRows (int row, int count, const QModelIndex & parent = QModelIndex()); #if 0 // CleanedUp! // FIXME(HIGH): This *is* like a kludge QList* currentPortStreamList() { return &mCurrentPort->mStreams; } #endif public: enum StreamFields { StreamIcon = 0, StreamStatus, StreamName, StreamNextWhat, StreamMaxFields }; static QStringList nextWhatOptionList() { return QStringList() << "Stop" << "Next" << "Goto first"; } public slots: void setCurrentPortIndex(const QModelIndex ¤t); private slots: void when_mCurrentPort_streamListChanged(int portGroupId, int portId); }; #endif ostinato-0.9/client/streamstatsfiltermodel.h000066400000000000000000000026751321315675200214710ustar00rootroot00000000000000/* Copyright (C) 2017 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _STREAM_STATS_FILTER_MODEL_H #define _STREAM_STATS_FILTER_MODEL_H #include class StreamStatsFilterModel : public QSortFilterProxyModel { Q_OBJECT public: StreamStatsFilterModel(QObject *parent = 0) : QSortFilterProxyModel(parent) { } protected: bool filterAcceptsColumn(int sourceColumn, const QModelIndex &/*sourceParent*/) const { QString title = sourceModel()->headerData(sourceColumn, Qt::Horizontal) .toString(); return filterRegExp().exactMatch(title) ? true : false; } bool filterAcceptsRow(int /*sourceRow*/, const QModelIndex &/*sourceParent*/) const { return true; } }; #endif ostinato-0.9/client/streamstatsmodel.cpp000066400000000000000000000147161321315675200206150ustar00rootroot00000000000000/* Copyright (C) 2016 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "streamstatsmodel.h" #include "protocol.pb.h" #include // XXX: Keep the enum in sync with it's string enum { kTxPkts, kRxPkts, kTxBytes, kRxBytes, kMaxStreamStats }; static QStringList statTitles = QStringList() << "Tx Pkts" << "Rx Pkts" << "Tx Bytes" << "Rx Bytes"; // XXX: Keep the enum in sync with it's string enum { kAggrTxPkts, kAggrRxPkts, kAggrPktLoss, kMaxAggrStreamStats }; static QStringList aggrStatTitles = QStringList() << "Total\nTx Pkts" << "Total\nRx Pkts" << "Total\nPkt Loss"; static const uint kAggrGuid = 0xffffffff; StreamStatsModel::StreamStatsModel(QObject *parent) : QAbstractTableModel(parent) { clearStats(); } int StreamStatsModel::rowCount(const QModelIndex &parent) const { return guidList_.size(); } int StreamStatsModel::columnCount(const QModelIndex &parent) const { if (!portList_.size()) return 0; return kMaxAggrStreamStats + portList_.size() * kMaxStreamStats; } QVariant StreamStatsModel::headerData( int section, Qt::Orientation orientation, int role) const { if (role != Qt::DisplayRole) return QVariant(); switch (orientation) { case Qt::Horizontal: // Column Header if (section < kMaxAggrStreamStats) return aggrStatTitles.at(section % kMaxAggrStreamStats); section -= kMaxAggrStreamStats; return QString("Port %1-%2\n%3") .arg(portList_.at(section/kMaxStreamStats).first) .arg(portList_.at(section/kMaxStreamStats).second) .arg(statTitles.at(section % kMaxStreamStats)); case Qt::Vertical: // Row Header if (section == (guidList_.size() - 1)) return QString("GUID Total"); return QString("Stream GUID %1") .arg(guidList_.at(section)); default: break; } return QVariant(); } QVariant StreamStatsModel::data(const QModelIndex &index, int role) const { if (role == Qt::TextAlignmentRole) return Qt::AlignRight; int portColumn = index.column() - kMaxAggrStreamStats; if (role == Qt::BackgroundRole) { if (portColumn < 0) return QBrush(QColor("lavender")); // Aggregate Column if (index.row() == (guidList_.size() - 1)) return QBrush(QColor("burlywood")); // Aggregate Row else if ((portColumn/kMaxStreamStats) & 1) return QBrush(QColor("beige")); // Color alternate Ports } Guid guid = guidList_.at(index.row()); if (role == Qt::ForegroundRole) { if ((index.column() == kAggrPktLoss) && aggrGuidStats_.value(guid).pktLoss) return QBrush(QColor("firebrick")); } if (role != Qt::DisplayRole) return QVariant(); if (index.column() < kMaxAggrStreamStats) { int stat = index.column() % kMaxAggrStreamStats; switch (stat) { case kAggrRxPkts: return QString("%L1").arg(aggrGuidStats_.value(guid).rxPkts); case kAggrTxPkts: return QString("%L1").arg(aggrGuidStats_.value(guid).txPkts); case kAggrPktLoss: return QString("%L1").arg(aggrGuidStats_.value(guid).pktLoss); default: break; }; return QVariant(); } PortGroupPort pgp = portList_.at(portColumn/kMaxStreamStats); int stat = portColumn % kMaxStreamStats; switch (stat) { case kRxPkts: return QString("%L1").arg(streamStats_.value(guid).value(pgp).rxPkts); case kTxPkts: return QString("%L1").arg(streamStats_.value(guid).value(pgp).txPkts); case kRxBytes: return QString("%L1").arg(streamStats_.value(guid).value(pgp).rxBytes); case kTxBytes: return QString("%L1").arg(streamStats_.value(guid).value(pgp).txBytes); default: break; } return QVariant(); } // --------------------------------------------- // // Slots // --------------------------------------------- // void StreamStatsModel::clearStats() { #if QT_VERSION >= 0x040600 beginResetModel(); #endif guidList_.clear(); portList_.clear(); streamStats_.clear(); aggrGuidStats_.clear(); #if QT_VERSION >= 0x040600 endResetModel(); #else reset(); #endif } void StreamStatsModel::appendStreamStatsList( quint32 portGroupId, const OstProto::StreamStatsList *stats) { int n = stats->stream_stats_size(); #if QT_VERSION >= 0x040600 beginResetModel(); #endif for (int i = 0; i < n; i++) { const OstProto::StreamStats &s = stats->stream_stats(i); PortGroupPort pgp = PortGroupPort(portGroupId, s.port_id().id()); Guid guid = s.stream_guid().id(); StreamStats &ss = streamStats_[guid][pgp]; StreamStats &aggrPort = streamStats_[kAggrGuid][pgp]; AggrGuidStats &aggrGuid = aggrGuidStats_[guid]; AggrGuidStats &aggrAggr = aggrGuidStats_[kAggrGuid]; ss.rxPkts = s.rx_pkts(); ss.txPkts = s.tx_pkts(); ss.rxBytes = s.rx_bytes(); ss.txBytes = s.tx_bytes(); aggrPort.rxPkts += ss.rxPkts; aggrPort.txPkts += ss.txPkts; aggrPort.rxBytes += ss.rxBytes; aggrPort.txBytes += ss.txBytes; aggrGuid.rxPkts += ss.rxPkts; aggrGuid.txPkts += ss.txPkts; aggrGuid.pktLoss += ss.txPkts - ss.rxPkts; aggrAggr.rxPkts += ss.rxPkts; aggrAggr.txPkts += ss.txPkts; aggrAggr.pktLoss += ss.txPkts - ss.rxPkts; if (!portList_.contains(pgp)) portList_.append(pgp); if (!guidList_.contains(guid)) guidList_.append(guid); } if (guidList_.size()) guidList_.append(kAggrGuid); #if QT_VERSION >= 0x040600 endResetModel(); #else reset(); #endif // Prevent receiving any future updates from this sender disconnect(sender(), 0, this, 0); } ostinato-0.9/client/streamstatsmodel.h000066400000000000000000000037721321315675200202620ustar00rootroot00000000000000/* Copyright (C) 2016 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _STREAM_STATS_MODEL_H #define _STREAM_STATS_MODEL_H #include #include #include #include #include namespace OstProto { class StreamStatsList; } class StreamStatsModel: public QAbstractTableModel { Q_OBJECT public: StreamStatsModel(QObject *parent = 0); int rowCount(const QModelIndex &parent=QModelIndex()) const; int columnCount(const QModelIndex &parent=QModelIndex()) const; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; public slots: void clearStats(); void appendStreamStatsList(quint32 portGroupId, const OstProto::StreamStatsList *stats); private: typedef QPair PortGroupPort; // Pair = (PortGroupId, PortId) typedef uint Guid; struct StreamStats { quint64 rxPkts; quint64 txPkts; quint64 rxBytes; quint64 txBytes; }; struct AggrGuidStats { quint64 rxPkts; quint64 txPkts; qint64 pktLoss; }; QList guidList_; QList portList_; QHash > streamStats_; QHash aggrGuidStats_; }; #endif ostinato-0.9/client/streamstatswindow.cpp000066400000000000000000000034431321315675200210170ustar00rootroot00000000000000/* Copyright (C) 2016 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "streamstatswindow.h" #include "streamstatsfiltermodel.h" #include #include static int id; static int count; StreamStatsWindow::StreamStatsWindow(QAbstractItemModel *model, QWidget *parent) : QWidget(parent) { setupUi(this); streamStats->addAction(actionShowByteCounters); if (id) setWindowTitle(windowTitle() + QString("(%1)").arg(id)); id++; count++; filterModel_ = new StreamStatsFilterModel(this); filterModel_->setFilterRegExp(QRegExp(".*Pkt.*")); filterModel_->setSourceModel(model); streamStats->setModel(filterModel_); streamStats->verticalHeader()->setHighlightSections(false); streamStats->verticalHeader()->setDefaultSectionSize( streamStats->verticalHeader()->minimumSectionSize()); } StreamStatsWindow::~StreamStatsWindow() { delete filterModel_; count--; if (count == 0) id = 0; } void StreamStatsWindow::on_actionShowByteCounters_triggered(bool checked) { if (checked) filterModel_->setFilterRegExp(QRegExp(".*")); else filterModel_->setFilterRegExp(QRegExp(".*Pkt.*")); } ostinato-0.9/client/streamstatswindow.h000066400000000000000000000022061321315675200204600ustar00rootroot00000000000000/* Copyright (C) 2016 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _STREAM_STATS_WINDOW_H #define _STREAM_STATS_WINDOW_H #include "ui_streamstatswindow.h" class QAbstractItemModel; class QSortFilterProxyModel; class StreamStatsWindow: public QWidget, private Ui::StreamStatsWindow { Q_OBJECT public: StreamStatsWindow(QAbstractItemModel *model, QWidget *parent = 0); ~StreamStatsWindow(); private slots: void on_actionShowByteCounters_triggered(bool checked); private: QSortFilterProxyModel *filterModel_; }; #endif ostinato-0.9/client/streamstatswindow.ui000066400000000000000000000023401321315675200206450ustar00rootroot00000000000000 StreamStatsWindow 0 0 551 452 Stream Statistics Qt::ActionsContextMenu Oops! We don't seem to have any stream statistics for the requested port(s) Wait a little bit to see if they appear, otherwise verify your stream stats configuration true Show Byte Counters XTableView QTableView
xtableview.h
ostinato-0.9/client/variablefieldswidget.cpp000066400000000000000000000324761321315675200214050ustar00rootroot00000000000000/* Copyright (C) 2015 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "variablefieldswidget.h" #include "abstractprotocol.h" #include "protocollistiterator.h" #include "stream.h" #include #include #include Q_DECLARE_METATYPE(AbstractProtocol*); Q_DECLARE_METATYPE(OstProto::VariableField); QStringList typeNames = QStringList() << "Counter8" << "Counter16" << "Counter32"; QStringList modeNames = QStringList() << "Increment" << "Decrement" << "Random"; #define uintToHexStr(num, bytes) \ QString("%1").arg(num, bytes*2, BASE_HEX, QChar('0')).toUpper() #define hexStrToUInt(str) \ str.toUInt(NULL, BASE_HEX) /* * NOTES: * 1. We use a QSpinBox for all numeric values except for 'value' because * QSpinBox value is of type 'int' - we would like the ability to store * quint32 * 2. This widget will keep the stream always updated - every editing change * of a attribute is immediately updated in the stream; the consequence * of this design is that an explicit 'store' of widget contents to the * stream is no longer required - we still define a store() method in * case we need to change the design later */ VariableFieldsWidget::VariableFieldsWidget(QWidget *parent) : QWidget(parent) { stream_ = NULL; isProgLoad_ = false; lastSelectedProtocolIndex_ = 0; lastSelectedVariableFieldIndex_ = 0; setupUi(this); attribGroup->setHidden(true); type->addItems(typeNames); mode->addItems(modeNames); valueRange_ = new QIntValidator(this); // FIXME: we can't use QIntValidator - since we want value to be able // to enter a quint32 //value->setValidator(valueRange_); connect(type, SIGNAL(currentIndexChanged(int)), SLOT(updateCurrentVariableField())); connect(offset, SIGNAL(valueChanged(int)), SLOT(updateCurrentVariableField())); connect(bitmask, SIGNAL(textChanged(QString)), SLOT(updateCurrentVariableField())); connect(mode, SIGNAL(currentIndexChanged(int)), SLOT(updateCurrentVariableField())); connect(value, SIGNAL(textChanged(QString)), SLOT(updateCurrentVariableField())); connect(count, SIGNAL(valueChanged(int)), SLOT(updateCurrentVariableField())); connect(step, SIGNAL(valueChanged(int)), SLOT(updateCurrentVariableField())); } void VariableFieldsWidget::setStream(Stream *stream) { stream_ = stream; } void VariableFieldsWidget::load() { Q_ASSERT(stream_); Q_ASSERT(protocolList->count() == 0); Q_ASSERT(variableFieldList->count() == 0); ProtocolListIterator *iter = stream_->createProtocolListIterator(); while (iter->hasNext()) { AbstractProtocol *proto = iter->next(); QListWidgetItem *protoItem = new QListWidgetItem; protoItem->setData(Qt::UserRole, QVariant::fromValue(proto)); protoItem->setText(proto->shortName()); protocolList->addItem(protoItem); } delete iter; if (lastSelectedProtocolIndex_ < protocolList->count()) protocolList->setCurrentRow(lastSelectedProtocolIndex_); // XXX: protocolList->setCurrentRow() above will emit currentItemChanged // which will load variableFieldsList - no need to load it explicitly if (lastSelectedVariableFieldIndex_ < variableFieldList->count()) variableFieldList->setCurrentRow(lastSelectedVariableFieldIndex_); } void VariableFieldsWidget::store() { /* Do Nothing - see Notes at the top of the file */ } void VariableFieldsWidget::clear() { protocolList->clear(); variableFieldList->clear(); } void VariableFieldsWidget::on_protocolList_currentItemChanged( QListWidgetItem *current, QListWidgetItem *previous) { AbstractProtocol *proto; qDebug("%s: curr = %p, prev = %p", __FUNCTION__, current, previous); if (current == NULL) goto _exit; proto = current->data(Qt::UserRole).value(); loadProtocolFields(proto); variableFieldList->clear(); for (int i = 0; i < proto->variableFieldCount(); i++) { OstProto::VariableField vf = proto->variableField(i); QListWidgetItem *vfItem = new QListWidgetItem; setVariableFieldItem(vfItem, proto, vf); variableFieldList->addItem(vfItem); } // While switching protocols, we want to setup the attrib group // validation/ranges/masks for the current protocol, which is done // by the field/type signal handlers - so clear field/type index // now so that signals are emitted when we add/select a VF field->setCurrentIndex(-1); type->setCurrentIndex(-1); lastSelectedProtocolIndex_ = protocolList->currentRow(); _exit: addButton->setEnabled(current != NULL); } void VariableFieldsWidget::on_variableFieldList_currentItemChanged( QListWidgetItem *current, QListWidgetItem *previous) { OstProto::VariableField vf; qDebug("%s: curr = %p, prev = %p", __FUNCTION__, current, previous); if (current == NULL) goto _exit; vf = current->data(Qt::UserRole).value(); isProgLoad_ = true; field->setCurrentIndex(fieldIndex(vf)); type->setCurrentIndex(vf.type()); offset->setValue(vf.offset()); bitmask->setText(uintToHexStr(vf.mask(), typeSize(vf.type()))); value->setText(QString().setNum(vf.value())); mode->setCurrentIndex(vf.mode()); count->setValue(vf.count()); step->setValue(vf.step()); isProgLoad_ = false; lastSelectedVariableFieldIndex_ = variableFieldList->currentRow(); _exit: attribGroup->setHidden(current == NULL); deleteButton->setEnabled(current != NULL); } void VariableFieldsWidget::on_addButton_clicked() { QListWidgetItem *protoItem = protocolList->currentItem(); if (!protoItem) return; AbstractProtocol *proto = protoItem->data(Qt::UserRole) .value(); OstProto::VariableField vf; QListWidgetItem *vfItem = new QListWidgetItem; proto->appendVariableField(vf); setVariableFieldItem(vfItem, proto, vf); variableFieldList->addItem(vfItem); } void VariableFieldsWidget::on_deleteButton_clicked() { QListWidgetItem *protoItem = protocolList->currentItem(); int vfIdx = variableFieldList->currentRow(); if (!protoItem || (vfIdx < 0)) return; AbstractProtocol *proto = protoItem->data(Qt::UserRole) .value(); proto->removeVariableField(vfIdx); delete variableFieldList->takeItem(vfIdx); } void VariableFieldsWidget::on_field_currentIndexChanged(int index) { if (index < 0) return; QVariantMap vm = field->itemData(index).toMap(); if (index) { // standard frame fields offset->setValue(vm["offset"].toUInt()); offset->setDisabled(true); type->setCurrentIndex(vm["type"].toUInt()); type->setDisabled(true); bitmask->setText(uintToHexStr( vm["mask"].toUInt(), typeSize(OstProto::VariableField::Type( vm["type"].toUInt())))); bitmask->setDisabled(true); } else { // custom field offset->setEnabled(true); type->setEnabled(true); bitmask->setEnabled(true); } } void VariableFieldsWidget::on_type_currentIndexChanged(int index) { if ((index < 0) || !protocolList->currentItem()) return; AbstractProtocol *proto = protocolList->currentItem()->data(Qt::UserRole) .value(); int protoSize = proto->protocolFrameSize(); switch (index) { case OstProto::VariableField::kCounter8: offset->setRange(0, protoSize - 1); bitmask->setInputMask("HH"); bitmask->setText("FF"); valueRange_->setRange(0, 0xFF); count->setRange(0, 0xFF); step->setRange(0, 0xFF); break; case OstProto::VariableField::kCounter16: offset->setRange(0, protoSize - 2); bitmask->setInputMask("HHHH"); bitmask->setText("FFFF"); valueRange_->setRange(0, 0xFFFF); count->setRange(0, 0xFFFF); step->setRange(0, 0xFFFF); break; case OstProto::VariableField::kCounter32: offset->setRange(0, protoSize - 4); bitmask->setInputMask("HHHHHHHH"); bitmask->setText("FFFFFFFF"); valueRange_->setRange(0, 0xFFFFFFFF); count->setRange(0, 0xFFFF); step->setRange(0, 0xFFFF); break; default: Q_ASSERT(false); // unreachable break; } } void VariableFieldsWidget::updateCurrentVariableField() { // Prevent recursion if (isProgLoad_) return; if (!protocolList->currentItem()) return; if (!variableFieldList->currentItem()) return; OstProto::VariableField vf; vf.set_type(OstProto::VariableField::Type(type->currentIndex())); vf.set_offset(offset->value()); vf.set_mask(hexStrToUInt(bitmask->text())); vf.set_value(value->text().toUInt()); vf.set_mode(OstProto::VariableField::Mode(mode->currentIndex())); vf.set_count(count->value()); vf.set_step(step->value()); QListWidgetItem *protoItem = protocolList->currentItem(); AbstractProtocol *proto = protoItem->data(Qt::UserRole) .value(); proto->mutableVariableField(variableFieldList->currentRow())->CopyFrom(vf); setVariableFieldItem(variableFieldList->currentItem(), proto, vf); } void VariableFieldsWidget::loadProtocolFields( const AbstractProtocol *protocol) { QVariantMap vm; field->clear(); field->addItem("Custom"); for (int i = 0; i < protocol->fieldCount(); i++) { if (!protocol->fieldFlags(i).testFlag(AbstractProtocol::FrameField)) continue; QString name = protocol->fieldData(i, AbstractProtocol::FieldName) .toString(); int bitOfs = protocol->fieldFrameBitOffset(i); int byteOfs = bitOfs >> 3; uint bitSize = protocol->fieldData(i, AbstractProtocol::FieldBitSize) .toInt(); vm["offset"] = byteOfs; if (bitSize <= 8) { vm["type"] = int(OstProto::VariableField::kCounter8); vm["mask"] = ((0xFF << (8 - bitSize)) & 0xFF) >> (bitOfs & 0x7); } else if (bitSize <= 16) { vm["type"] = int(OstProto::VariableField::kCounter16); vm["mask"] = ((0xFFFF << (16 - bitSize)) & 0xFFFF) >> (bitOfs & 0x7); } else if (bitSize <= 32) { vm["type"] = int(OstProto::VariableField::kCounter32); vm["mask"] = ((0xFFFFFFFF << (32 - bitSize)) & 0xFFFFFFFF) >> (bitOfs & 0x7); } else { vm["type"] = int(OstProto::VariableField::kCounter32); vm["mask"] = 0xFFFFFFFF; } field->addItem(name, vm); } } /*! Given a VariableField::Type, return corresponding size in bytes */ int VariableFieldsWidget::typeSize(OstProto::VariableField::Type type) { switch(type) { case OstProto::VariableField::kCounter8 : return 1; case OstProto::VariableField::kCounter16: return 2; case OstProto::VariableField::kCounter32: return 4; default: break; } return 4; } /*! Given a variableField, return corresponding index in the field ComboBox */ int VariableFieldsWidget::fieldIndex(const OstProto::VariableField &vf) { QVariantMap vm; vm["type"] = int(vf.type()); vm["offset"] = vf.offset(); vm["mask"] = vf.mask(); int index = field->findData(vm); qDebug("vm %d %d 0x%x => index %d", vf.type(), vf.offset(), vf.mask(), index); // Not found? Use 'Custom' if (index < 0) index = 0; return index; } void VariableFieldsWidget::setVariableFieldItem( QListWidgetItem *item, const AbstractProtocol *protocol, const OstProto::VariableField &vf) { uint from = vf.value() & vf.mask(); uint to; QString fieldName = field->itemText(fieldIndex(vf)); QString itemText; if (vf.mode() == OstProto::VariableField::kDecrement) to = (vf.value() - (vf.count()-1)*vf.step()) & vf.mask(); else to = (vf.value() + (vf.count()-1)*vf.step()) & vf.mask(); item->setData(Qt::UserRole, QVariant::fromValue(vf)); itemText = QString("%1 %2 %3 from %4 to %5") .arg(protocol->shortName()) .arg(fieldName) .arg(modeNames.at(vf.mode())) .arg(from) .arg(to); if (vf.step() != 1) itemText.append(QString(" step %1").arg(vf.step())); item->setText(itemText); } ostinato-0.9/client/variablefieldswidget.h000066400000000000000000000041471321315675200210440ustar00rootroot00000000000000/* Copyright (C) 2015 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _VARIABLE_FIELDS_WIDGET_H #define _VARIABLE_FIELDS_WIDGET_H #include "protocol.pb.h" #include "ui_variablefieldswidget.h" #include class AbstractProtocol; class Stream; class QListWidgetItem; class VariableFieldsWidget : public QWidget, private Ui::VariableFieldsWidget { Q_OBJECT public: VariableFieldsWidget(QWidget *parent = 0); void setStream(Stream *stream); void load(); void store(); void clear(); private slots: void on_protocolList_currentItemChanged( QListWidgetItem *current, QListWidgetItem *previous); void on_variableFieldList_currentItemChanged( QListWidgetItem *current, QListWidgetItem *previous); void on_addButton_clicked(); void on_deleteButton_clicked(); void on_field_currentIndexChanged(int index); void on_type_currentIndexChanged(int index); void updateCurrentVariableField(); private: void loadProtocolFields(const AbstractProtocol *protocol); int typeSize(OstProto::VariableField::Type type); int fieldIndex(const OstProto::VariableField &vf); void setVariableFieldItem( QListWidgetItem *item, const AbstractProtocol *protocol, const OstProto::VariableField &vf); Stream *stream_; QIntValidator *valueRange_; bool isProgLoad_; // FIXME: make the lastXXX vars static? int lastSelectedProtocolIndex_; int lastSelectedVariableFieldIndex_; }; #endif ostinato-0.9/client/variablefieldswidget.ui000066400000000000000000000200121321315675200212170ustar00rootroot00000000000000 VariableFieldsWidget 0 0 579 384 Form 0 Qt::Horizontal false 2 0 6 0 QFrame::Panel QFrame::Sunken 0 0 0 0 Qt::Vertical 20 40 false + false - Qt::Vertical 20 40 0 0 Field 3 0 QComboBox::NoInsert Type 2 0 QComboBox::NoInsert Offset 2 0 true Mask bitmask 2 0 Mode mode QComboBox::NoInsert Value Count Step Qt::Vertical 561 41 protocolList variableFieldList addButton deleteButton field type offset bitmask mode value count step ostinato-0.9/client/xtableview.h000066400000000000000000000024551321315675200170360ustar00rootroot00000000000000/* Copyright (C) 2017 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _X_TABLE_VIEW_H #define _X_TABLE_VIEW_H #include #include class XTableView : public QTableView { public: XTableView(QWidget *parent) : QTableView(parent) {} virtual ~XTableView() {} protected: virtual void paintEvent(QPaintEvent *event) { if (!model()->hasChildren()) { QPainter painter(viewport()); style()->drawItemText(&painter, viewport()->rect(), layoutDirection() | Qt::AlignCenter, palette(), true, whatsThis(), QPalette::WindowText); } else QTableView::paintEvent(event); } }; #endif ostinato-0.9/client/xtreeview.h000066400000000000000000000023071321315675200167020ustar00rootroot00000000000000/* Copyright (C) 2017 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _X_TREE_VIEW_H #define _X_TREE_VIEW_H #include #include #if QT_VERSION >= 0x050000 #error "Do we even need this anymore?" #endif class XTreeView : public QTreeView { public: XTreeView(QWidget *parent) : QTreeView(parent) {} virtual ~XTreeView() {} private: virtual void mousePressEvent(QMouseEvent *event) { QModelIndex item = indexAt(event->pos()); if (!item.isValid()) setCurrentIndex(QModelIndex()); QTreeView::mousePressEvent(event); } }; #endif ostinato-0.9/common/000077500000000000000000000000001321315675200145175ustar00rootroot00000000000000ostinato-0.9/common/abstractprotocol.cpp000066400000000000000000001024231321315675200206120ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "abstractprotocol.h" #include "protocollistiterator.h" #include "streambase.h" #include "bswap.h" #include /*! \class AbstractProtocol AbstractProtocol is the base abstract class which provides the interface for all protocols. All protocols supported by Ostinato are derived from AbstractProtocol. Apart from defining the interface for a protocol, it also provides sensible default implementations for methods so that the subclasses need not re-implement. It also provides convenience functions for subclasses to use such as methods to retrieve payload size, checksum etc. A subclass typically needs to reimplement the following methods - - name() - shortName() - createInstance() - protocolNumber() - protoDataCopyInto() [pure virtual] - protoDataCopyFrom() [pure virtual] - fieldCount() - fieldFlags() - fieldData() - setFieldData() Depending on certain conditions, subclasses may need to reimplement the following additional methods - - protocolIdType() - protocolId() - protocolFrameSize() - isProtocolFrameSizeVariable() - protocolFrameVariableCount() See the description of the methods for more information. Most of the above methods just need some standard boilerplate code - the SampleProtocol implementation includes the boilerplate */ /*! Constructs an abstract protocol for the given stream and parent parent is typically NULL except for protocols which are part of a ComboProtocol */ AbstractProtocol::AbstractProtocol(StreamBase *stream, AbstractProtocol *parent) { //qDebug("%s: &prev = %p &next = %p", __FUNCTION__, &prev, &next); mpStream = stream; this->parent = parent; prev = next = NULL; _metaFieldCount = -1; _frameFieldCount = -1; _frameVariableCount = -1; protoSize = -1; _hasPayload = true; _cacheFlags |= FieldFrameBitOffsetCache; } /*! Destroys the abstract protocol */ AbstractProtocol::~AbstractProtocol() { } /*! Allocates and returns a new instance of the class. Caller is responsible for freeing up after use. Subclasses MUST implement this function */ AbstractProtocol* AbstractProtocol::createInstance(StreamBase* /* stream */, AbstractProtocol* /* parent */) { return NULL; } /*! Returns the protocol's field number as defined in message 'Protocol', enum 'k' (file: protocol.proto) Subclasses MUST implement this function \todo convert this to a protected data member instead of a virtual function */ quint32 AbstractProtocol::protocolNumber() const { qFatal("Something wrong!!!"); return 0xFFFFFFFF; } /*! Copies the common data (not specific to individual protocols) in the protocol member protobuf data into the passed in protocol parameter. The individual protocol specific protobuf data is copied using protoDataCopyInto() */ void AbstractProtocol::commonProtoDataCopyInto(OstProto::Protocol &protocol) const { protocol.clear_variable_field(); for (int i = 0; i < _data.variable_field_size(); i++) { OstProto::VariableField *vf; vf = protocol.add_variable_field(); vf->CopyFrom(_data.variable_field(i)); } } /*! Copies the common data (not specific to individual protocols) from the passed in param protocol protobuf into the member protobuf data. The individual protocol specific protobuf data is copied using protoDataCopyFrom() */ void AbstractProtocol::commonProtoDataCopyFrom(const OstProto::Protocol &protocol) { _data.clear_variable_field(); for (int i = 0; i < protocol.variable_field_size(); i++) { OstProto::VariableField *vf; vf = _data.add_variable_field(); vf->CopyFrom(protocol.variable_field(i)); } } /*! \fn virtual void AbstractProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const = 0 Copy the protocol's protobuf as an extension into the passed in protocol In the base class this is a pure virtual function. Subclasses MUST implement this function. See the SampleProtocol for an example */ /*! \fn virtual void AbstractProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) = 0 Copy and update the protocol's protobuf member data variable from the passed in protocol In the base class this is a pure virtual function. Subclasses MUST implement this function. See the SampleProtocol for an example */ /*! Returns the full name of the protocol The default implementation returns a null string */ QString AbstractProtocol::name() const { return QString(); } /*! Returns the short name or abbreviation of the protocol The default implementation forms and returns an abbreviation composed of all the upper case chars in name() \n The default implementation caches the abbreviation on its first invocation and subsequently returns the cached abbreviation */ QString AbstractProtocol::shortName() const { if (protoAbbr.isNull()) { QString abbr; for (int i = 0; i < name().size(); i++) if (name().at(i).isUpper()) abbr.append(name().at(i)); if (abbr.size()) protoAbbr = abbr; else protoAbbr = QString(""); } return protoAbbr; } /*! Returns the number of fields in the protocol (both Frame fields and Meta fields) The default implementation returns zero. Subclasses MUST implement this function. */ int AbstractProtocol::fieldCount() const { return 0; } /*! Returns the number of meta fields The default implementation counts and returns the number of fields for which the MetaField flag is set\n The default implementation caches the count on its first invocation and subsequently returns the cached count */ int AbstractProtocol::metaFieldCount() const { if (_metaFieldCount < 0) { int c = 0; for (int i = 0; i < fieldCount() ; i++) if (fieldFlags(i).testFlag(MetaField)) c++; _metaFieldCount = c; } return _metaFieldCount; } /*! Returns the number of frame fields The default implementation counts and returns the number of fields for which the FrameField flag is set\n The default implementation caches the count on its first invocation and subsequently returns the cached count Subclasses which export different sets of fields based on a opcode/type (e.g. icmp) should re-implement this function */ int AbstractProtocol::frameFieldCount() const { if (_frameFieldCount < 0) { int c = 0; for (int i = 0; i < fieldCount() ; i++) if (fieldFlags(i).testFlag(FrameField)) c++; _frameFieldCount = c; } return _frameFieldCount; } /*! Returns the field flags for the passed in field index The default implementation assumes all fields to be frame fields and returns 'FrameField'. Subclasses must reimplement this method if they have any meta fields or checksum fields. See the SampleProtocol for an example. */ AbstractProtocol::FieldFlags AbstractProtocol::fieldFlags(int /*index*/) const { return FrameField; } /*! Returns the requested field attribute data Protocols which have meta fields that vary a frame field across streams may use the streamIndex to return the appropriate field value \n Some field attributes e.g. FieldName may be invariant across streams\n The FieldTextValue attribute may include additional information about the field's value e.g. a checksum field may include "(correct)" or "(incorrect)" alongwith the actual checksum value. \n The default implementation returns a empty string for FieldName and FieldTextValue; empty byte array of size 0 for FieldFrameValue; 0 for FieldValue; subclasses are expected to return meaning values for all these attributes. The only exception is the 'FieldBitSize' attribute - the default implementation takes the (byte) size of FieldFrameValue, multiplies it with 8 and returns the result - this can be used by subclasses for fields which are an integral multiple of bytes; for fields whose size are a non-integral multiple of bytes or smaller than a byte, subclasses should return the correct value. Also for fields which represent checksums, subclasses should return a value for FieldBitSize - even if it is an integral multiple of bytes. \note If a subclass uses any of the below functions to derive FieldFrameValue, the subclass should handle and return a value for FieldBitSize to prevent endless recursion - - protocolFrameCksum() - protocolFramePayloadSize() */ QVariant AbstractProtocol::fieldData(int index, FieldAttrib attrib, int streamIndex) const { switch (attrib) { case FieldName: return QString(); case FieldBitSize: Q_ASSERT_X(!fieldFlags(index).testFlag(CksumField), "AbstractProtocol::fieldData()", "FieldBitSize for checksum fields need to be handled by the subclass"); return fieldData(index, FieldFrameValue, streamIndex). toByteArray().size() * 8; case FieldValue: return 0; case FieldFrameValue: return QByteArray(); case FieldTextValue: return QString(); default: qFatal("%s:%d: unhandled case %d\n", __FUNCTION__, __LINE__, attrib); } return QVariant(); } /*! Sets the value of a field corresponding to index This method is called by the GUI code to store a user specified value into the protocol's protoBuf. Currently this method is called with FieldAttrib = FieldValue only. Returns true if field is successfully set, false otherwise. The default implementation always returns false. Subclasses should reimplement this method. See SampleProtocol for an example. */ bool AbstractProtocol::setFieldData(int /*index*/, const QVariant& /*value*/, FieldAttrib /*attrib*/) { return false; } /*! * Returns the bit offset where the specified field starts within the * protocolFrameValue() */ int AbstractProtocol::fieldFrameBitOffset(int index, int streamIndex) const { int ofs = 0; if ((index < 0) || (index >= fieldCount()) || !fieldFlags(index).testFlag(FrameField)) return -1; // Lookup Cache; if not available calculate and cache (if enabled) if (_fieldFrameBitOffset.contains(index)) { ofs = _fieldFrameBitOffset.value(index); goto _exit; } for (int i = 0; i < index; i++) { if ((_cacheFlags & FieldFrameBitOffsetCache) && !_fieldFrameBitOffset.contains(i)) _fieldFrameBitOffset.insert(i, ofs); ofs += fieldData(i, FieldBitSize, streamIndex).toInt(); } if ((_cacheFlags & FieldFrameBitOffsetCache)) _fieldFrameBitOffset.insert(index, ofs); qDebug("======> ffbo index: %d, ofs: %d", index, ofs); _exit: return ofs; } /*! * Returns the count of variableFields in the protocol */ int AbstractProtocol::variableFieldCount() const { return _data.variable_field_size(); } /*! * Appends a variableField to the protocol */ void AbstractProtocol::appendVariableField(const OstProto::VariableField &vf) { _data.add_variable_field()->CopyFrom(vf); // Update the cached value _frameVariableCount = lcm(_frameVariableCount, vf.count()); } /*! * Removes the variableField from the protocol at the specified index */ void AbstractProtocol::removeVariableField(int index) { OstProto::Protocol temp; if (index >= _data.variable_field_size()) { qWarning("%s: %s variableField[%d] out of range; count: %d)", __FUNCTION__, qPrintable(shortName()), index, _data.variable_field_size()); return; } // TODO: this is inefficient - evaluate using RepeatedPtrField? for (int i = 0; i < _data.variable_field_size(); i++) { if (i == index) continue; temp.add_variable_field()->CopyFrom(_data.variable_field(i)); } _data.clear_variable_field(); _frameVariableCount = 1; for (int i = 0; i < temp.variable_field_size(); i++) { _data.add_variable_field()->CopyFrom(temp.variable_field(i)); // Recalculate the cached value _frameVariableCount = lcm(_frameVariableCount, _data.variable_field(i).count()); } } /*! * Returns the variableField at the specified index as a constant Reference * i.e. read-only */ const OstProto::VariableField& AbstractProtocol::variableField(int index) const { Q_ASSERT(index < _data.variable_field_size()); return _data.variable_field(index); } /*! * Returns the variableField at the specified index as a mutable pointer. * Changes made via the pointer will be reflected in the protocol */ OstProto::VariableField* AbstractProtocol::mutableVariableField(int index) { if ((index < 0) || (index >= _data.variable_field_size())) return NULL; // Invalidate the cached value as the caller may potentially modify it _frameVariableCount = -1; return _data.mutable_variable_field(index); } /*! Returns the protocolIdType for the protocol The default implementation returns ProtocolIdNone. If a subclass has a protocolId field it should return the appropriate value e.g. IP protocol will return ProtocolIdIp, Ethernet will return ProtocolIdEth etc. */ AbstractProtocol::ProtocolIdType AbstractProtocol::protocolIdType() const { return ProtocolIdNone; } /*! Returns the protocol id of the protocol for the given type The default implementation returns 0. If a subclass represents a protocol which has a particular protocol id, it should return the appropriate value. If a protocol does not have an id for the given type, it should defer to the base class. e.g. IGMP will return 2 for ProtocolIdIp, and defer to the base class for the remaining ProtocolIdTypes; IP will return 0x800 for ProtocolIdEth type, 0x060603 for ProtocolIdLlc and 0x04 for ProtocolIdIp etc. */ quint32 AbstractProtocol::protocolId(ProtocolIdType /*type*/) const { return 0; } /*! Returns the protocol id of the payload protocol (the protocol that immediately follows the current one) A subclass which has a protocol id field, can use this to retrieve the appropriate value */ quint32 AbstractProtocol::payloadProtocolId(ProtocolIdType type) const { quint32 id; if (next) id = next->protocolId(type); else if (parent) id = parent->payloadProtocolId(type); else id = 0xFFFFFFFF; qDebug("%s: payloadProtocolId = 0x%x", __FUNCTION__, id); return id; } /*! Returns the protocol's size in bytes The default implementation sums up the individual field bit sizes and returns it. The default implementation calculates the caches the size on the first invocation and subsequently returns the cached size. If the subclass protocol has a varying protocol size, it MUST reimplement this method, otherwise the default implementation is sufficient. */ int AbstractProtocol::protocolFrameSize(int streamIndex) const { if (protoSize < 0) { int bitsize = 0; for (int i = 0; i < fieldCount(); i++) { if (fieldFlags(i).testFlag(FrameField)) bitsize += fieldData(i, FieldBitSize, streamIndex).toUInt(); } protoSize = (bitsize+7)/8; } qDebug("%s: protoSize = %d", __FUNCTION__, protoSize); return protoSize; } /*! Returns the byte offset in the packet where the protocol starts This method is useful only for "padding" protocols i.e. protocols which fill up the remaining space for the user defined packet size e.g. the PatternPayload protocol */ int AbstractProtocol::protocolFrameOffset(int streamIndex) const { int size = 0; AbstractProtocol *p = prev; while (p) { size += p->protocolFrameSize(streamIndex); p = p->prev; } if (parent) size += parent->protocolFrameOffset(streamIndex); qDebug("%s: ofs = %d", __FUNCTION__, size); return size; } /*! Returns the size of the payload in bytes. The payload includes all protocols subsequent to the current This method is useful for protocols which need to fill in a payload size field */ int AbstractProtocol::protocolFramePayloadSize(int streamIndex) const { int size = 0; AbstractProtocol *p = next; while (p) { size += p->protocolFrameSize(streamIndex); p = p->next; } if (parent) size += parent->protocolFramePayloadSize(streamIndex); qDebug("%s: payloadSize = %d", __FUNCTION__, size); return size; } /*! Returns a byte array encoding the protocol (and its fields) which can be inserted into the stream's frame The default implementation forms and returns an ordered concatenation of the FrameValue of all the 'frame' fields of the protocol also taking care of fields which are not an integral number of bytes\n */ QByteArray AbstractProtocol::protocolFrameValue(int streamIndex, bool forCksum) const { QByteArray proto, field; uint bits, lastbitpos = 0; FieldFlags flags; for (int i=0; i < fieldCount() ; i++) { flags = fieldFlags(i); if (flags.testFlag(FrameField)) { bits = fieldData(i, FieldBitSize, streamIndex).toUInt(); if (bits == 0) continue; Q_ASSERT(bits > 0); if (forCksum && flags.testFlag(CksumField)) { field.resize((bits+7)/8); field.fill('\0'); } else field = fieldData(i, FieldFrameValue, streamIndex).toByteArray(); qDebug("<<< (%d, %db) %s >>>", proto.size(), lastbitpos, QString(proto.toHex()).toAscii().constData()); qDebug(" < %d: (%db/%dB) %s >", i, bits, field.size(), QString(field.toHex()).toAscii().constData()); if (bits == (uint) field.size() * 8) { if (lastbitpos == 0) proto.append(field); else { Q_ASSERT(field.size() > 0); char c = proto[proto.size() - 1]; proto[proto.size() - 1] = c | ((uchar)field.at(0) >> lastbitpos); for (int j = 0; j < field.size() - 1; j++) proto.append(field.at(j) << lastbitpos | (uchar)field.at(j+1) >> lastbitpos); proto.append(field.at(field.size() - 1) << lastbitpos); } } else if (bits < (uint) field.size() * 8) { uchar c; uint v; v = (field.size()*8) - bits; Q_ASSERT(v < 8); if (lastbitpos == 0) { for (int j = 0; j < field.size(); j++) { c = field.at(j) << v; if ((j+1) < field.size()) c |= ((uchar)field.at(j+1) >> (8-v)); proto.append(c); } lastbitpos = (lastbitpos + bits) % 8; } else { Q_ASSERT(proto.size() > 0); for (int j = 0; j < field.size(); j++) { uchar d; c = field.at(j) << v; if ((j+1) < field.size()) c |= ((uchar) field.at(j+1) >> (8-v)); d = proto[proto.size() - 1]; proto[proto.size() - 1] = d | ((uchar) c >> lastbitpos); if (bits > (8*j + (8 - v))) proto.append(c << (8-lastbitpos)); } lastbitpos = (lastbitpos + bits) % 8; } } else // if (bits > field.size() * 8) { qFatal("bitsize more than FrameValue size. skipping..."); continue; } } } // Overwrite proto with the variable fields, if any for (int i = 0; i < _data.variable_field_size(); i++) { OstProto::VariableField vf = _data.variable_field(i); varyProtocolFrameValue(proto, streamIndex, vf); } return proto; } /*! Returns true if the protocol varies one or more of its fields at run-time, false otherwise */ bool AbstractProtocol::isProtocolFrameValueVariable() const { return (protocolFrameVariableCount() > 1); } /*! Returns true if the protocol varies its size at run-time, false otherwise The default implmentation returns false. A subclass should reimplement if it varies its size at run-time e.g. a Payload protocol for a stream with incrementing/decrementing frame lengths */ bool AbstractProtocol::isProtocolFrameSizeVariable() const { return false; } /*! Returns the minimum number of frames required for the protocol to vary its fields This is the lowest common multiple (LCM) of the counts of all the varying fields in the protocol. Use the AbstractProtocol::lcm() static utility function to calculate the LCM. The default implementation returns the LCM of all variableFields A subclass should reimplement if it has varying fields e.g. an IP protocol that increments/decrements the IP address with every packet.\n Subclasses should call the base class method to retreive the count and do a LCM with the subclass' own varying fields */ int AbstractProtocol::protocolFrameVariableCount() const { if (_frameVariableCount > 0) return _frameVariableCount; _frameVariableCount = 1; for (int i = 0; i < _data.variable_field_size(); i++) _frameVariableCount = lcm(_frameVariableCount, _data.variable_field(i).count()); return _frameVariableCount; } /*! Returns true if the payload content for a protocol varies at run-time, false otherwise This is useful for subclasses which have fields dependent on payload content (e.g. UDP has a checksum field that varies if the payload varies) */ bool AbstractProtocol::isProtocolFramePayloadValueVariable() const { // TODO: it is simpler to do the following - // return (protocolFramePayloadVariableCount() > 1) // However, it may be inefficient till the time we cache the // variable count AbstractProtocol *p = next; while (p) { if (p->isProtocolFrameValueVariable()) return true; p = p->next; } if (parent && parent->isProtocolFramePayloadValueVariable()) return true; return false; } /*! Returns true if the payload size for a protocol varies at run-time, false otherwise This is useful for subclasses which have fields dependent on payload size (e.g. UDP has a checksum field that varies if the payload varies) */ bool AbstractProtocol::isProtocolFramePayloadSizeVariable() const { AbstractProtocol *p = next; while (p) { if (p->isProtocolFrameSizeVariable()) return true; p = p->next; } if (parent && parent->isProtocolFramePayloadSizeVariable()) return true; return false; } /*! Returns true if the payload size for a protocol varies at run-time, false otherwise This is useful for subclasses which have fields dependent on payload size (e.g. UDP has a checksum field that varies if the payload varies) */ int AbstractProtocol::protocolFramePayloadVariableCount() const { int count = 1; AbstractProtocol *p = next; while (p) { if (p->isProtocolFrameValueVariable() || p->isProtocolFrameSizeVariable()) count = lcm(count, p->protocolFrameVariableCount()); p = p->next; } if (parent && (parent->isProtocolFramePayloadValueVariable() || parent->isProtocolFramePayloadSizeVariable())) count = lcm(count, parent->protocolFramePayloadVariableCount()); return false; } /*! Returns true if the protocol typically contains a payload or other protocols following it e.g. TCP, UDP have payloads, while ARP, IGMP do not The default implementation returns true. If a subclass does not have a payload, it should set the _hasPayload data member to false */ bool AbstractProtocol::protocolHasPayload() const { return _hasPayload; } /*! Returns the checksum (of the requested type) of the protocol's contents Useful for protocols which have a checksum field \note If a subclass uses protocolFrameCksum() from within fieldData() to derive a cksum field, it MUST handle and return the 'FieldBitSize' attribute also for that particular field instead of using the default AbstractProtocol implementation for 'FieldBitSize' - this is required to prevent infinite recursion */ quint32 AbstractProtocol::protocolFrameCksum(int streamIndex, CksumType cksumType) const { static int recursionCount = 0; quint32 cksum = 0xFFFFFFFF; recursionCount++; Q_ASSERT_X(recursionCount < 10, "protocolFrameCksum", "potential infinite recursion - does a protocol checksum field not implement FieldBitSize?"); switch(cksumType) { case CksumIp: { QByteArray fv; quint16 *ip; quint32 len, sum = 0; fv = protocolFrameValue(streamIndex, true); ip = (quint16*) fv.constData(); len = fv.size(); while(len > 1) { sum += *ip; if(sum & 0x80000000) sum = (sum & 0xFFFF) + (sum >> 16); ip++; len -= 2; } if (len) sum += (unsigned short) *(unsigned char *)ip; while(sum>>16) sum = (sum & 0xFFFF) + (sum >> 16); cksum = qFromBigEndian((quint16) ~sum); break; } case CksumTcpUdp: { quint16 cks; quint32 sum = 0; cks = protocolFrameCksum(streamIndex, CksumIp); sum += (quint16) ~cks; cks = protocolFramePayloadCksum(streamIndex, CksumIp); sum += (quint16) ~cks; cks = protocolFrameHeaderCksum(streamIndex, CksumIpPseudo); sum += (quint16) ~cks; while(sum>>16) sum = (sum & 0xFFFF) + (sum >> 16); cksum = (~sum) & 0xFFFF; break; } default: break; } recursionCount--; return cksum; } /*! Returns the checksum of the requested type for the protocol's header This is useful for subclasses which needs the header's checksum e.g. TCP/UDP require a "Pseudo-IP" checksum. The checksum is limited to the specified scope. Currently the default implementation supports only type CksumIpPseudo \note The default value for cksumScope is different for protocolFrameHeaderCksum() and protocolFramePayloadCksum() */ quint32 AbstractProtocol::protocolFrameHeaderCksum(int streamIndex, CksumType cksumType, CksumScope cksumScope) const { quint32 sum = 0; quint16 cksum; AbstractProtocol *p = prev; Q_ASSERT(cksumType == CksumIpPseudo); while (p) { cksum = p->protocolFrameCksum(streamIndex, cksumType); sum += (quint16) ~cksum; qDebug("%s: sum = %u, cksum = %u", __FUNCTION__, sum, cksum); if (cksumScope == CksumScopeAdjacentProtocol) goto out; p = p->prev; } if (parent) { cksum = parent->protocolFrameHeaderCksum(streamIndex, cksumType, cksumScope); sum += (quint16) ~cksum; } out: while(sum>>16) sum = (sum & 0xFFFF) + (sum >> 16); return (quint16) ~sum; } /*! Returns the checksum of the requested type for the protocol's payload This is useful for subclasses which needs the payload's checksum e.g. TCP/UDP require a IP checksum of the payload (to be combined with other checksums to derive the final checksum). The checksum is limited to the specified scope. Currently the default implementation supports only type CksumIp \note The default value for cksumScope is different for protocolFrameHeaderCksum() and protocolFramePayloadCksum() */ quint32 AbstractProtocol::protocolFramePayloadCksum(int streamIndex, CksumType cksumType, CksumScope cksumScope) const { quint32 sum; quint16 cksum; AbstractProtocol *p = next; Q_ASSERT(cksumType == CksumIp); if (!p) return 0xFFFF; cksum = p->protocolFrameCksum(streamIndex, cksumType); sum = (quint16) ~cksum; if (cksumScope == CksumScopeAdjacentProtocol) goto out; p = p->next; while (p) { // when combining cksums, a non-first protocol starting at odd offset // needs a byte swap (see RFC 1071 section(s) 2A, 2B) cksum = p->protocolFrameCksum(streamIndex, cksumType); if (p->protocolFrameOffset(streamIndex) & 0x1) cksum = swap16(cksum); sum += (quint16) ~cksum; p = p->next; } if (parent) { cksum = parent->protocolFramePayloadCksum(streamIndex, cksumType, cksumScope); sum += (quint16) ~cksum; } out: while(sum>>16) sum = (sum & 0xFFFF) + (sum >> 16); cksum = (quint16) ~sum; qDebug("%s: cksum = %u", __FUNCTION__, cksum); return cksum; } // Stein's binary GCD algo - from wikipedia quint64 AbstractProtocol::gcd(quint64 u, quint64 v) { int shift; /* GCD(0,x) := x */ if (u == 0 || v == 0) return u | v; /* Let shift := lg K, where K is the greatest power of 2 dividing both u and v. */ for (shift = 0; ((u | v) & 1) == 0; ++shift) { u >>= 1; v >>= 1; } while ((u & 1) == 0) u >>= 1; /* From here on, u is always odd. */ do { while ((v & 1) == 0) /* Loop X */ v >>= 1; /* Now u and v are both odd, so diff(u, v) is even. Let u = min(u, v), v = diff(u, v)/2. */ if (u < v) { v -= u; } else { quint64 diff = u - v; u = v; v = diff; } v >>= 1; } while (v != 0); return u << shift; } quint64 AbstractProtocol::lcm(quint64 u, quint64 v) { #if 0 /* LCM(0,x) := x */ if (u == 0 || v == 0) return u | v; #else /* For our use case, neither u nor v can ever be 0, the minimum value is 1; we do this correction silently here */ if (u == 0) u = 1; if (v == 0) v = 1; if (u == 1 || v == 1) return (u * v); #endif return (u * v)/gcd(u, v); } /* * XXX: varyCounter() is not a member of AbstractProtocol to avoid * moving it into the header file and thereby keeping the header file * clean */ template bool varyCounter(QString protocolName, QByteArray &buf, int frameIndex, const OstProto::VariableField &varField) { int x = (frameIndex % varField.count()) * varField.step(); T oldfv, newfv; if ((varField.offset() + sizeof(T)) > uint(buf.size())) { qWarning("%s varField ofs %d beyond protocol frame %d - skipping", qPrintable(protocolName), varField.offset(), buf.size()); return false; } oldfv = *((T*)((uchar*)buf.constData() + varField.offset())); if (sizeof(T) > sizeof(quint8)) oldfv = qFromBigEndian(oldfv); switch(varField.mode()) { case OstProto::VariableField::kIncrement: newfv = (oldfv & ~varField.mask()) | ((varField.value() + x) & varField.mask()); break; case OstProto::VariableField::kDecrement: newfv = (oldfv & ~varField.mask()) | ((varField.value() - x) & varField.mask()); break; case OstProto::VariableField::kRandom: newfv = (oldfv & ~varField.mask()) | ((varField.value() + qrand()) & varField.mask()); break; default: qWarning("%s Unsupported varField mode %d", qPrintable(protocolName), varField.mode()); return false; } if (sizeof(T) == sizeof(quint8)) *((uchar*)buf.constData() + varField.offset()) = newfv; else qToBigEndian(newfv, (uchar*)buf.constData() + varField.offset()); qDebug("%s varField ofs %d oldfv %x newfv %x", qPrintable(protocolName), varField.offset(), oldfv, newfv); return true; } void AbstractProtocol::varyProtocolFrameValue(QByteArray &buf, int frameIndex, const OstProto::VariableField &varField) const { switch (varField.type()) { case OstProto::VariableField::kCounter8: varyCounter(shortName(), buf, frameIndex, varField); break; case OstProto::VariableField::kCounter16: varyCounter(shortName(), buf, frameIndex, varField); break; case OstProto::VariableField::kCounter32: varyCounter(shortName(), buf, frameIndex, varField); break; default: break; } return; } ostinato-0.9/common/abstractprotocol.h000066400000000000000000000144611321315675200202630ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _ABSTRACT_PROTOCOL_H #define _ABSTRACT_PROTOCOL_H #include #include #include #include #include #include #include //#include "../rpc/pbhelper.h" #include "protocol.pb.h" #define BASE_BIN (2) #define BASE_OCT (8) #define BASE_DEC (10) #define BASE_HEX (16) class StreamBase; class ProtocolListIterator; class AbstractProtocol { template friend class ComboProtocol; friend class ProtocolListIterator; private: mutable int _metaFieldCount; mutable int _frameFieldCount; mutable int _frameVariableCount; mutable int protoSize; mutable QString protoAbbr; mutable QHash _fieldFrameBitOffset; OstProto::Protocol _data; protected: StreamBase *mpStream; //!< Stream that this protocol belongs to AbstractProtocol *parent; //!< Parent protocol, if any AbstractProtocol *prev; //!< Protocol preceding this protocol AbstractProtocol *next; //!< Protocol succeeding this protocol //! Is protocol typically followed by payload or another protocol bool _hasPayload; //! Caching Control Flags enum CacheFlag { FieldFrameBitOffsetCache = 0x1 }; quint32 _cacheFlags; public: //! Properties of a field, can be OR'd enum FieldFlag { FrameField = 0x1, //!< field appears in frame content MetaField = 0x2, //!< field does not appear in frame, is meta data CksumField = 0x4 //!< field is a checksum and appears in frame content }; Q_DECLARE_FLAGS(FieldFlags, FieldFlag); //!< \private abcd //! Various attributes of a field enum FieldAttrib { FieldName, //!< name FieldValue, //!< value in host byte order (user editable) FieldTextValue, //!< value as text FieldFrameValue, //!< frame encoded value in network byte order FieldBitSize, //!< size in bits }; //! Supported Protocol Id types enum ProtocolIdType { ProtocolIdNone, //!< Marker representing non-existent protocol id ProtocolIdLlc, //!< LLC (802.2) ProtocolIdEth, //!< Ethernet II ProtocolIdIp, //!< IP ProtocolIdTcpUdp, //!< TCP/UDP Port Number }; //! Supported checksum types enum CksumType { CksumIp, //!< Standard IP Checksum CksumIpPseudo, //!< Standard checksum for Pseudo-IP header CksumTcpUdp, //!< Standard TCP/UDP checksum including pseudo-IP CksumMax //!< Marker for number of cksum types }; //! Supported checksum scopes enum CksumScope { CksumScopeAdjacentProtocol, //!< Cksum only the adjacent protocol CksumScopeAllProtocols, //!< Cksum over all the protocols }; AbstractProtocol(StreamBase *stream, AbstractProtocol *parent = 0); virtual ~AbstractProtocol(); static AbstractProtocol* createInstance(StreamBase *stream, AbstractProtocol *parent = 0); virtual quint32 protocolNumber() const; void commonProtoDataCopyInto(OstProto::Protocol &protocol) const; void commonProtoDataCopyFrom(const OstProto::Protocol &protocol); virtual void protoDataCopyInto(OstProto::Protocol &protocol) const = 0; virtual void protoDataCopyFrom(const OstProto::Protocol &protocol) = 0; virtual QString name() const; virtual QString shortName() const; virtual ProtocolIdType protocolIdType() const; virtual quint32 protocolId(ProtocolIdType type) const; quint32 payloadProtocolId(ProtocolIdType type) const; virtual int fieldCount() const; int metaFieldCount() const; virtual int frameFieldCount() const; virtual FieldFlags fieldFlags(int index) const; virtual QVariant fieldData(int index, FieldAttrib attrib, int streamIndex = 0) const; virtual bool setFieldData(int index, const QVariant &value, FieldAttrib attrib = FieldValue); int fieldFrameBitOffset(int index, int streamIndex = 0) const; int variableFieldCount() const; void appendVariableField(const OstProto::VariableField &vf); void removeVariableField(int index); const OstProto::VariableField& variableField(int index) const; OstProto::VariableField* mutableVariableField(int index); QByteArray protocolFrameValue(int streamIndex = 0, bool forCksum = false) const; virtual int protocolFrameSize(int streamIndex = 0) const; int protocolFrameOffset(int streamIndex = 0) const; int protocolFramePayloadSize(int streamIndex = 0) const; virtual bool isProtocolFrameValueVariable() const; virtual bool isProtocolFrameSizeVariable() const; virtual int protocolFrameVariableCount() const; bool isProtocolFramePayloadValueVariable() const; bool isProtocolFramePayloadSizeVariable() const; int protocolFramePayloadVariableCount() const; bool protocolHasPayload() const; virtual quint32 protocolFrameCksum(int streamIndex = 0, CksumType cksumType = CksumIp) const; quint32 protocolFrameHeaderCksum(int streamIndex = 0, CksumType cksumType = CksumIp, CksumScope cksumScope = CksumScopeAdjacentProtocol) const; quint32 protocolFramePayloadCksum(int streamIndex = 0, CksumType cksumType = CksumIp, CksumScope cksumScope = CksumScopeAllProtocols) const; static quint64 lcm(quint64 u, quint64 v); static quint64 gcd(quint64 u, quint64 v); private: void varyProtocolFrameValue(QByteArray &buf, int frameIndex, const OstProto::VariableField &varField) const; }; Q_DECLARE_OPERATORS_FOR_FLAGS(AbstractProtocol::FieldFlags); #endif ostinato-0.9/common/abstractprotocolconfig.h000066400000000000000000000056231321315675200214510ustar00rootroot00000000000000/* Copyright (C) 2013-2014 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _ABSTRACT_PROTOCOL_CONFIG_H #define _ABSTRACT_PROTOCOL_CONFIG_H #include class AbstractProtocol; /*! Convenience Macro - can be used by loadWidget() methods */ #define uintToHexStr(num, bytes) \ QString("%1").arg(num, bytes*2, BASE_HEX, QChar('0')) class AbstractProtocolConfigForm : public QWidget { Q_OBJECT public: /*! Constructs the widget */ AbstractProtocolConfigForm(QWidget *parent = 0) : QWidget(parent) { // Do nothing! } /*! Destroys the widget */ virtual ~AbstractProtocolConfigForm() { // Do nothing! } /*! Allocates and returns a new instance of the widget. Caller is responsible for freeing up after use. Subclasses MUST implement this function */ static AbstractProtocolConfigForm* createInstance() { return NULL; } /*! Loads data from the protocol using it's fieldData() method into this widget. Any conversion to user friendly display/editing formats (e.g. hex format) SHOULD be done by this method. Subclasses MUST implement this function. See the SampleProtocol for an example */ virtual void loadWidget(AbstractProtocol* /*proto*/) { // Do nothing! } /*! Stores data from this widget into the protocol using the protocol's setFieldData() method. Field values MUST be converted from any user friendly display/editing formats (e.g. hex format) to simple Qt-style integers/strings before passing to setFieldData() Subclasses MUST implement this function. See the SampleProtocol for an example */ virtual void storeWidget(AbstractProtocol* /*proto*/) { // Do nothing! } /*! Convenience Method - can be used by storeWidget() implementations */ uint hexStrToUInt(QString text, bool *ok=NULL) { bool isOk; uint a_uint = text.remove(QChar(' ')).toUInt(&isOk, 16); if (ok) *ok = isOk; return a_uint; } /*! Convenience Method - can be used by storeWidget() implementations */ quint64 hexStrToUInt64(QString text, bool *ok=NULL) { bool isOk; quint64 a_uint = text.remove(QChar(' ')).toULongLong(&isOk, 16); if (ok) *ok = isOk; return a_uint; } }; #endif ostinato-0.9/common/arp.cpp000066400000000000000000000611761321315675200160200ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "arp.h" #include #include #define uintToMacStr(num) \ QString("%1").arg(num, 6*2, BASE_HEX, QChar('0')) \ .replace(QRegExp("([0-9a-fA-F]{2}\\B)"), "\\1:").toUpper() ArpProtocol::ArpProtocol(StreamBase *stream, AbstractProtocol *parent) : AbstractProtocol(stream, parent) { _hasPayload = false; } ArpProtocol::~ArpProtocol() { } AbstractProtocol* ArpProtocol::createInstance(StreamBase *stream, AbstractProtocol *parent) { return new ArpProtocol(stream, parent); } quint32 ArpProtocol::protocolNumber() const { return OstProto::Protocol::kArpFieldNumber; } void ArpProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const { protocol.MutableExtension(OstProto::arp)->CopyFrom(data); protocol.mutable_protocol_id()->set_id(protocolNumber()); } void ArpProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) { if (protocol.protocol_id().id() == protocolNumber() && protocol.HasExtension(OstProto::arp)) data.MergeFrom(protocol.GetExtension(OstProto::arp)); } QString ArpProtocol::name() const { return QString("Address Resolution Protocol"); } QString ArpProtocol::shortName() const { return QString("ARP"); } /*! Return the ProtocolIdType for your protocol \n If your protocol doesn't have a protocolId field, you don't need to reimplement this method - the base class implementation will do the right thing */ #if 0 AbstractProtocol::ProtocolIdType ArpProtocol::protocolIdType() const { return ProtocolIdIp; } #endif /*! Return the protocolId for your protocol based on the 'type' requested \n If not all types are valid for your protocol, handle the valid type(s) and for the remaining fallback to the base class implementation; if your protocol doesn't have a protocolId at all, you don't need to reimplement this method - the base class will do the right thing */ quint32 ArpProtocol::protocolId(ProtocolIdType type) const { switch(type) { case ProtocolIdEth: return 0x0806; default:break; } return AbstractProtocol::protocolId(type); } int ArpProtocol::fieldCount() const { return arp_fieldCount; } AbstractProtocol::FieldFlags ArpProtocol::fieldFlags(int index) const { AbstractProtocol::FieldFlags flags; flags = AbstractProtocol::fieldFlags(index); switch (index) { case arp_hwType: case arp_protoType: case arp_hwAddrLen: case arp_protoAddrLen: case arp_opCode: case arp_senderHwAddr: case arp_senderProtoAddr: case arp_targetHwAddr: case arp_targetProtoAddr: break; case arp_senderHwAddrMode: case arp_senderHwAddrCount: case arp_senderProtoAddrMode: case arp_senderProtoAddrCount: case arp_senderProtoAddrMask: case arp_targetHwAddrMode: case arp_targetHwAddrCount: case arp_targetProtoAddrMode: case arp_targetProtoAddrCount: case arp_targetProtoAddrMask: flags &= ~FrameField; flags |= MetaField; break; default: qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, index); break; } return flags; } QVariant ArpProtocol::fieldData(int index, FieldAttrib attrib, int streamIndex) const { switch (index) { case arp_hwType: { switch(attrib) { case FieldName: return QString("Hardware Type"); case FieldValue: return data.hw_type(); case FieldTextValue: return QString("%1").arg(data.hw_type()); case FieldFrameValue: { QByteArray fv; fv.resize(2); qToBigEndian((quint16) data.hw_type(), (uchar*) fv.data()); return fv; } default: break; } break; } case arp_protoType: { switch(attrib) { case FieldName: return QString("Protocol Type"); case FieldValue: return data.proto_type(); case FieldTextValue: return QString("%1").arg(data.proto_type(), 4, BASE_HEX, QChar('0')); case FieldFrameValue: { QByteArray fv; fv.resize(2); qToBigEndian((quint16) data.proto_type(), (uchar*) fv.data()); return fv; } default: break; } break; } case arp_hwAddrLen: { switch(attrib) { case FieldName: return QString("Hardware Address Length"); case FieldValue: return data.hw_addr_len(); case FieldTextValue: return QString("%1").arg(data.hw_addr_len()); case FieldFrameValue: return QByteArray(1, (char) data.hw_addr_len()); default: break; } break; } case arp_protoAddrLen: { switch(attrib) { case FieldName: return QString("Protocol Address Length"); case FieldValue: return data.proto_addr_len(); case FieldTextValue: return QString("%1").arg(data.proto_addr_len()); case FieldFrameValue: return QByteArray(1, (char) data.proto_addr_len()); default: break; } break; } case arp_opCode: { switch(attrib) { case FieldName: return QString("Operation Code"); case FieldValue: return data.op_code(); case FieldTextValue: return QString("%1").arg(data.op_code()); case FieldFrameValue: { QByteArray fv; fv.resize(2); qToBigEndian((quint16) data.op_code(), (uchar*) fv.data()); return fv; } default: break; } break; } case arp_senderHwAddr: { int u; const int hwAddrStep = 1; quint64 hwAddr = 0; switch (data.sender_hw_addr_mode()) { case OstProto::Arp::kFixed: hwAddr = data.sender_hw_addr(); break; case OstProto::Arp::kIncrement: u = (streamIndex % data.sender_hw_addr_count()) * hwAddrStep; hwAddr = data.sender_hw_addr() + u; break; case OstProto::Arp::kDecrement: u = (streamIndex % data.sender_hw_addr_count()) * hwAddrStep; hwAddr = data.sender_hw_addr() - u; break; default: qWarning("Unhandled hw_addr_mode %d", data.sender_hw_addr_mode()); } switch(attrib) { case FieldName: return QString("Sender Hardware Address"); case FieldValue: return hwAddr; case FieldTextValue: return uintToMacStr(hwAddr); case FieldFrameValue: { QByteArray fv; fv.resize(8); qToBigEndian((quint64) hwAddr, (uchar*) fv.data()); fv.remove(0, 2); return fv; } default: break; } break; } case arp_senderProtoAddr: { int u; quint32 subnet, host, protoAddr = 0; switch(data.sender_proto_addr_mode()) { case OstProto::Arp::kFixedHost: protoAddr = data.sender_proto_addr(); break; case OstProto::Arp::kIncrementHost: u = streamIndex % data.sender_proto_addr_count(); subnet = data.sender_proto_addr() & data.sender_proto_addr_mask(); host = (((data.sender_proto_addr() & ~data.sender_proto_addr_mask()) + u) & ~data.sender_proto_addr_mask()); protoAddr = subnet | host; break; case OstProto::Arp::kDecrementHost: u = streamIndex % data.sender_proto_addr_count(); subnet = data.sender_proto_addr() & data.sender_proto_addr_mask(); host = (((data.sender_proto_addr() & ~data.sender_proto_addr_mask()) - u) & ~data.sender_proto_addr_mask()); protoAddr = subnet | host; break; case OstProto::Arp::kRandomHost: subnet = data.sender_proto_addr() & data.sender_proto_addr_mask(); host = (qrand() & ~data.sender_proto_addr_mask()); protoAddr = subnet | host; break; default: qWarning("Unhandled sender_proto_addr_mode = %d", data.sender_proto_addr_mode()); } switch(attrib) { case FieldName: return QString("Sender Protocol Address"); case FieldValue: return protoAddr; case FieldFrameValue: { QByteArray fv; fv.resize(4); qToBigEndian((quint32) protoAddr, (uchar*) fv.data()); return fv; } case FieldTextValue: return QHostAddress(protoAddr).toString(); default: break; } break; } case arp_targetHwAddr: { int u; const int hwAddrStep = 1; quint64 hwAddr = 0; switch (data.target_hw_addr_mode()) { case OstProto::Arp::kFixed: hwAddr = data.target_hw_addr(); break; case OstProto::Arp::kIncrement: u = (streamIndex % data.target_hw_addr_count()) * hwAddrStep; hwAddr = data.target_hw_addr() + u; break; case OstProto::Arp::kDecrement: u = (streamIndex % data.target_hw_addr_count()) * hwAddrStep; hwAddr = data.target_hw_addr() - u; break; default: qWarning("Unhandled hw_addr_mode %d", data.target_hw_addr_mode()); } switch(attrib) { case FieldName: return QString("Target Hardware Address"); case FieldValue: return hwAddr; case FieldTextValue: return uintToMacStr(hwAddr); case FieldFrameValue: { QByteArray fv; fv.resize(8); qToBigEndian((quint64) hwAddr, (uchar*) fv.data()); fv.remove(0, 2); return fv; } default: break; } break; } case arp_targetProtoAddr: { int u; quint32 subnet, host, protoAddr = 0; switch(data.target_proto_addr_mode()) { case OstProto::Arp::kFixed: protoAddr = data.target_proto_addr(); break; case OstProto::Arp::kIncrementHost: u = streamIndex % data.target_proto_addr_count(); subnet = data.target_proto_addr() & data.target_proto_addr_mask(); host = (((data.target_proto_addr() & ~data.target_proto_addr_mask()) + u) & ~data.target_proto_addr_mask()); protoAddr = subnet | host; break; case OstProto::Arp::kDecrementHost: u = streamIndex % data.target_proto_addr_count(); subnet = data.target_proto_addr() & data.target_proto_addr_mask(); host = (((data.target_proto_addr() & ~data.target_proto_addr_mask()) - u) & ~data.target_proto_addr_mask()); protoAddr = subnet | host; break; case OstProto::Arp::kRandomHost: subnet = data.target_proto_addr() & data.target_proto_addr_mask(); host = (qrand() & ~data.target_proto_addr_mask()); protoAddr = subnet | host; break; default: qWarning("Unhandled target_proto_addr_mode = %d", data.target_proto_addr_mode()); } switch(attrib) { case FieldName: return QString("Target Protocol Address"); case FieldValue: return protoAddr; case FieldFrameValue: { QByteArray fv; fv.resize(4); qToBigEndian((quint32) protoAddr, (uchar*) fv.data()); return fv; } case FieldTextValue: return QHostAddress(protoAddr).toString(); default: break; } break; } // Meta fields case arp_senderHwAddrMode: switch(attrib) { case FieldName: return QString("Sender Hardware Address Mode"); case FieldValue: return data.sender_hw_addr_mode(); default: break; } break; case arp_senderHwAddrCount: switch(attrib) { case FieldName: return QString("Sender Hardware Address Count"); case FieldValue: return data.sender_hw_addr_count(); default: break; } break; case arp_senderProtoAddrMode: switch(attrib) { case FieldName: return QString("Sender Protocol Address Mode"); case FieldValue: return data.sender_proto_addr_mode(); default: break; } break; case arp_senderProtoAddrCount: switch(attrib) { case FieldName: return QString("Sender Protocol Address Count"); case FieldValue: return data.sender_proto_addr_count(); default: break; } break; case arp_senderProtoAddrMask: switch(attrib) { case FieldName: return QString("Sender Protocol Address Mask"); case FieldValue: return data.sender_proto_addr_mask(); default: break; } break; case arp_targetHwAddrMode: switch(attrib) { case FieldName: return QString("Target Hardware Address Mode"); case FieldValue: return data.target_hw_addr_mode(); default: break; } break; case arp_targetHwAddrCount: switch(attrib) { case FieldName: return QString("Target Hardware Address Count"); case FieldValue: return data.target_hw_addr_count(); default: break; } break; case arp_targetProtoAddrMode: switch(attrib) { case FieldName: return QString("Target Protocol Address Mode"); case FieldValue: return data.target_proto_addr_mode(); default: break; } break; case arp_targetProtoAddrCount: switch(attrib) { case FieldName: return QString("Target Protocol Address Count"); case FieldValue: return data.target_proto_addr_count(); default: break; } break; case arp_targetProtoAddrMask: switch(attrib) { case FieldName: return QString("Target Protocol Address Mask"); case FieldValue: return data.target_proto_addr_mask(); default: break; } break; default: qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, index); break; } return AbstractProtocol::fieldData(index, attrib, streamIndex); } bool ArpProtocol::setFieldData(int index, const QVariant &value, FieldAttrib attrib) { bool isOk = false; if (attrib != FieldValue) goto _exit; switch (index) { case arp_hwType: { uint hwType = value.toUInt(&isOk); if (isOk) data.set_hw_type(hwType); break; } case arp_protoType: { uint protoType = value.toUInt(&isOk); if (isOk) data.set_proto_type(protoType); break; } case arp_hwAddrLen: { uint hwAddrLen = value.toUInt(&isOk); if (isOk) data.set_hw_addr_len(hwAddrLen); break; } case arp_protoAddrLen: { uint protoAddrLen = value.toUInt(&isOk); if (isOk) data.set_proto_addr_len(protoAddrLen); break; } case arp_opCode: { uint opCode = value.toUInt(&isOk); if (isOk) data.set_op_code(opCode); break; } case arp_senderHwAddr: { quint64 hwAddr = value.toULongLong(&isOk); if (isOk) data.set_sender_hw_addr(hwAddr); break; } case arp_senderHwAddrMode: { uint mode = value.toUInt(&isOk); if (isOk && data.HwAddrMode_IsValid(mode)) data.set_sender_hw_addr_mode((OstProto::Arp::HwAddrMode) mode); else isOk = false; break; } case arp_senderHwAddrCount: { uint count = value.toUInt(&isOk); if (isOk) data.set_sender_hw_addr_count(count); break; } case arp_senderProtoAddr: { uint protoAddr = value.toUInt(&isOk); if (isOk) data.set_sender_proto_addr(protoAddr); break; } case arp_senderProtoAddrMode: { uint mode = value.toUInt(&isOk); if (isOk && data.ProtoAddrMode_IsValid(mode)) data.set_sender_proto_addr_mode( (OstProto::Arp::ProtoAddrMode)mode); else isOk = false; break; } case arp_senderProtoAddrCount: { uint count = value.toUInt(&isOk); if (isOk) data.set_sender_proto_addr_count(count); break; } case arp_senderProtoAddrMask: { uint mask = value.toUInt(&isOk); if (isOk) data.set_sender_proto_addr_mask(mask); break; } case arp_targetHwAddr: { quint64 hwAddr = value.toULongLong(&isOk); if (isOk) data.set_target_hw_addr(hwAddr); break; } case arp_targetHwAddrMode: { uint mode = value.toUInt(&isOk); if (isOk && data.HwAddrMode_IsValid(mode)) data.set_target_hw_addr_mode((OstProto::Arp::HwAddrMode)mode); else isOk = false; break; } case arp_targetHwAddrCount: { uint count = value.toUInt(&isOk); if (isOk) data.set_target_hw_addr_count(count); break; } case arp_targetProtoAddr: { uint protoAddr = value.toUInt(&isOk); if (isOk) data.set_target_proto_addr(protoAddr); break; } case arp_targetProtoAddrMode: { uint mode = value.toUInt(&isOk); if (isOk && data.ProtoAddrMode_IsValid(mode)) data.set_target_proto_addr_mode( (OstProto::Arp::ProtoAddrMode)mode); else isOk = false; break; } case arp_targetProtoAddrCount: { uint count = value.toUInt(&isOk); if (isOk) data.set_target_proto_addr_count(count); break; } case arp_targetProtoAddrMask: { uint mask = value.toUInt(&isOk); if (isOk) data.set_target_proto_addr_mask(mask); break; } default: qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, index); break; } _exit: return isOk; } int ArpProtocol::protocolFrameVariableCount() const { int count = AbstractProtocol::protocolFrameVariableCount(); if (fieldData(arp_senderHwAddrMode, FieldValue).toUInt() != uint(OstProto::Arp::kFixed)) { count = AbstractProtocol::lcm(count, fieldData(arp_senderHwAddrCount, FieldValue).toUInt()); } if (fieldData(arp_senderProtoAddrMode, FieldValue).toUInt() != uint(OstProto::Arp::kFixed)) { count = AbstractProtocol::lcm(count, fieldData(arp_senderProtoAddrCount, FieldValue).toUInt()); } if (fieldData(arp_targetHwAddrMode, FieldValue).toUInt() != uint(OstProto::Arp::kFixed)) { count = AbstractProtocol::lcm(count, fieldData(arp_targetHwAddrCount, FieldValue).toUInt()); } if (fieldData(arp_targetProtoAddrMode, FieldValue).toUInt() != uint(OstProto::Arp::kFixed)) { count = AbstractProtocol::lcm(count, fieldData(arp_targetProtoAddrCount, FieldValue).toUInt()); } return count; } ostinato-0.9/common/arp.h000066400000000000000000000053671321315675200154650ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _ARP_H #define _ARP_H #include "abstractprotocol.h" #include "arp.pb.h" /* Arp Protocol Frame Format - +------+------+------+------+------+---------+-------+---------+-------+ | HTYP | PTYP | HLEN | PLEN | OPER | SHA | SPA | THA | TPA | | (2) | (2) | (1) | (1) | (2) | (6) | (4) | (6) | (4) | +------+------+------+------+------+---------+-------+---------+-------+ Figures in brackets represent field width in bytes */ class ArpProtocol : public AbstractProtocol { public: enum arpfield { // Frame Fields arp_hwType, arp_protoType, arp_hwAddrLen, arp_protoAddrLen, arp_opCode, arp_senderHwAddr, arp_senderProtoAddr, arp_targetHwAddr, arp_targetProtoAddr, // Meta Fields arp_senderHwAddrMode, arp_senderHwAddrCount, arp_senderProtoAddrMode, arp_senderProtoAddrCount, arp_senderProtoAddrMask, arp_targetHwAddrMode, arp_targetHwAddrCount, arp_targetProtoAddrMode, arp_targetProtoAddrCount, arp_targetProtoAddrMask, arp_fieldCount }; ArpProtocol(StreamBase *stream, AbstractProtocol *parent = 0); virtual ~ArpProtocol(); static AbstractProtocol* createInstance(StreamBase *stream, AbstractProtocol *parent = 0); virtual quint32 protocolNumber() const; virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); virtual quint32 protocolId(ProtocolIdType type) const; virtual QString name() const; virtual QString shortName() const; virtual int fieldCount() const; virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; virtual QVariant fieldData(int index, FieldAttrib attrib, int streamIndex = 0) const; virtual bool setFieldData(int index, const QVariant &value, FieldAttrib attrib = FieldValue); virtual int protocolFrameVariableCount() const; private: OstProto::Arp data; }; #endif ostinato-0.9/common/arp.proto000066400000000000000000000042161321315675200163710ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ import "protocol.proto"; package OstProto; // ARP Protocol message Arp { enum HwAddrMode { kFixed = 0; kIncrement = 1; kDecrement = 2; } enum ProtoAddrMode { kFixedHost = 0; kIncrementHost = 1; kDecrementHost = 2; kRandomHost = 3; } optional uint32 hw_type = 1 [default = 1]; optional uint32 proto_type = 2 [default = 0x800]; optional uint32 hw_addr_len = 3 [default = 6]; optional uint32 proto_addr_len = 4 [default = 4]; optional uint32 op_code = 5 [default = 1]; // 1 => ARP Request optional uint64 sender_hw_addr = 6; optional HwAddrMode sender_hw_addr_mode = 7 [default = kFixed]; optional uint32 sender_hw_addr_count = 8 [default = 16]; optional uint32 sender_proto_addr = 9; optional ProtoAddrMode sender_proto_addr_mode = 10 [default = kFixedHost]; optional uint32 sender_proto_addr_count = 11 [default = 16]; optional fixed32 sender_proto_addr_mask = 12 [default = 0xFFFFFF00]; optional uint64 target_hw_addr = 13; optional HwAddrMode target_hw_addr_mode = 14 [default = kFixed]; optional uint32 target_hw_addr_count = 15 [default = 16]; optional uint32 target_proto_addr = 16; optional ProtoAddrMode target_proto_addr_mode = 17 [default = kFixedHost]; optional uint32 target_proto_addr_count = 18 [default = 16]; optional fixed32 target_proto_addr_mask = 19 [default = 0xFFFFFF00]; } extend Protocol { optional Arp arp = 300; } ostinato-0.9/common/arp.ui000066400000000000000000000345271321315675200156530ustar00rootroot00000000000000 Arp 0 0 528 286 Form Hardware Type hwType false Hardware Address Length hwAddrLen false Protocol Type protoType false Protocol Address Length protoAddrLen false Operation Code 1 0 true QComboBox::NoInsert Qt::Horizontal 161 20 false Qt::Horizontal 101 20 Address Mode Count Mask Sender Hardware senderHwAddr >HH HH HH HH HH HH; Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Fixed Increment Decrement false 255 0 Sender Protocol senderProtoAddr 009.009.009.009; ... Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Fixed Increment Host Decrement Host Random Host false 255 0 false 009.009.009.009; ... Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Target Hardware targetHwAddr 120 0 >HH HH HH HH HH HH; Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Fixed Increment Decrement false 255 0 0 Target Protocol targetProtoAddr 000.000.000.000; ... Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Fixed Increment Host Decrement Host Random Host false 255 0 false 009.009.009.009; ... Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Qt::Vertical 20 61 IntComboBox QComboBox
intcombobox.h
hwType protoType hwAddrLen protoAddrLen senderHwAddr senderHwAddrMode senderHwAddrCount senderProtoAddr senderProtoAddrMode senderProtoAddrCount senderProtoAddrMask targetHwAddr targetHwAddrMode targetHwAddrCount targetProtoAddr targetProtoAddrMode targetProtoAddrCount targetProtoAddrMask
ostinato-0.9/common/arpconfig.cpp000066400000000000000000000212401321315675200171720ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "arpconfig.h" #include "arp.h" #include ArpConfigForm::ArpConfigForm(QWidget *parent) : AbstractProtocolConfigForm(parent) { setupUi(this); opCodeCombo->setValidator(new QIntValidator(0, 0xFFFF, this)); opCodeCombo->addItem(1, "ARP Request"); opCodeCombo->addItem(2, "ARP Reply"); connect(senderHwAddrMode, SIGNAL(currentIndexChanged(int)), SLOT(on_senderHwAddrMode_currentIndexChanged(int))); connect(senderProtoAddrMode, SIGNAL(currentIndexChanged(int)), SLOT(on_senderProtoAddrMode_currentIndexChanged(int))); connect(targetHwAddrMode, SIGNAL(currentIndexChanged(int)), SLOT(on_targetHwAddrMode_currentIndexChanged(int))); connect(targetProtoAddrMode, SIGNAL(currentIndexChanged(int)), SLOT(on_targetProtoAddrMode_currentIndexChanged(int))); } ArpConfigForm::~ArpConfigForm() { } ArpConfigForm* ArpConfigForm::createInstance() { return new ArpConfigForm; } void ArpConfigForm::loadWidget(AbstractProtocol *proto) { hwType->setText( proto->fieldData( ArpProtocol::arp_hwType, AbstractProtocol::FieldValue ).toString()); protoType->setText(uintToHexStr( proto->fieldData( ArpProtocol::arp_protoType, AbstractProtocol::FieldValue ).toUInt(), 2)); hwAddrLen->setText( proto->fieldData( ArpProtocol::arp_hwAddrLen, AbstractProtocol::FieldValue ).toString()); protoAddrLen->setText( proto->fieldData( ArpProtocol::arp_protoAddrLen, AbstractProtocol::FieldValue ).toString()); opCodeCombo->setValue( proto->fieldData( ArpProtocol::arp_opCode, AbstractProtocol::FieldValue ).toUInt()); senderHwAddr->setText(uintToHexStr( proto->fieldData( ArpProtocol::arp_senderHwAddr, AbstractProtocol::FieldValue ).toULongLong(), 6)); senderHwAddrMode->setCurrentIndex( proto->fieldData( ArpProtocol::arp_senderHwAddrMode, AbstractProtocol::FieldValue ).toUInt()); senderHwAddrCount->setText( proto->fieldData( ArpProtocol::arp_senderHwAddrCount, AbstractProtocol::FieldValue ).toString()); senderProtoAddr->setText(QHostAddress( proto->fieldData( ArpProtocol::arp_senderProtoAddr, AbstractProtocol::FieldValue ).toUInt()).toString()); senderProtoAddrMode->setCurrentIndex( proto->fieldData( ArpProtocol::arp_senderProtoAddrMode, AbstractProtocol::FieldValue ).toUInt()); senderProtoAddrCount->setText( proto->fieldData( ArpProtocol::arp_senderProtoAddrCount, AbstractProtocol::FieldValue ).toString()); senderProtoAddrMask->setText(QHostAddress( proto->fieldData( ArpProtocol::arp_senderProtoAddrMask, AbstractProtocol::FieldValue ).toUInt()).toString()); targetHwAddr->setText(uintToHexStr( proto->fieldData( ArpProtocol::arp_targetHwAddr, AbstractProtocol::FieldValue ).toULongLong(), 6)); targetHwAddrMode->setCurrentIndex( proto->fieldData( ArpProtocol::arp_targetHwAddrMode, AbstractProtocol::FieldValue ).toUInt()); targetHwAddrCount->setText( proto->fieldData( ArpProtocol::arp_targetHwAddrCount, AbstractProtocol::FieldValue ).toString()); targetProtoAddr->setText(QHostAddress( proto->fieldData( ArpProtocol::arp_targetProtoAddr, AbstractProtocol::FieldValue ).toUInt()).toString()); targetProtoAddrMode->setCurrentIndex( proto->fieldData( ArpProtocol::arp_targetProtoAddrMode, AbstractProtocol::FieldValue ).toUInt()); targetProtoAddrCount->setText( proto->fieldData( ArpProtocol::arp_targetProtoAddrCount, AbstractProtocol::FieldValue ).toString()); targetProtoAddrMask->setText(QHostAddress( proto->fieldData( ArpProtocol::arp_targetProtoAddrMask, AbstractProtocol::FieldValue ).toUInt()).toString()); } void ArpConfigForm::storeWidget(AbstractProtocol *proto) { proto->setFieldData( ArpProtocol::arp_hwType, hwType->text()); proto->setFieldData( ArpProtocol::arp_protoType, hexStrToUInt(protoType->text())); proto->setFieldData( ArpProtocol::arp_hwAddrLen, hwAddrLen->text()); proto->setFieldData( ArpProtocol::arp_protoAddrLen, protoAddrLen->text()); proto->setFieldData( ArpProtocol::arp_opCode, opCodeCombo->currentValue()); proto->setFieldData( ArpProtocol::arp_senderHwAddr, hexStrToUInt64(senderHwAddr->text())); proto->setFieldData( ArpProtocol::arp_senderHwAddrMode, senderHwAddrMode->currentIndex()); proto->setFieldData( ArpProtocol::arp_senderHwAddrCount, senderHwAddrCount->text()); proto->setFieldData( ArpProtocol::arp_senderProtoAddr, QHostAddress(senderProtoAddr->text()).toIPv4Address()); proto->setFieldData( ArpProtocol::arp_senderProtoAddrMode, senderProtoAddrMode->currentIndex()); proto->setFieldData( ArpProtocol::arp_senderProtoAddrCount, senderProtoAddrCount->text()); proto->setFieldData( ArpProtocol::arp_senderProtoAddrMask, QHostAddress(senderProtoAddrMask->text()).toIPv4Address()); proto->setFieldData( ArpProtocol::arp_targetHwAddr, hexStrToUInt64(targetHwAddr->text())); proto->setFieldData( ArpProtocol::arp_targetHwAddrMode, targetHwAddrMode->currentIndex()); proto->setFieldData( ArpProtocol::arp_targetHwAddrCount, targetHwAddrCount->text()); proto->setFieldData( ArpProtocol::arp_targetProtoAddr, QHostAddress(targetProtoAddr->text()).toIPv4Address()); proto->setFieldData( ArpProtocol::arp_targetProtoAddrMode, targetProtoAddrMode->currentIndex()); proto->setFieldData( ArpProtocol::arp_targetProtoAddrCount, targetProtoAddrCount->text()); proto->setFieldData( ArpProtocol::arp_targetProtoAddrMask, QHostAddress(targetProtoAddrMask->text()).toIPv4Address()); } /* * ------------ Private Slots -------------- */ void ArpConfigForm::on_senderHwAddrMode_currentIndexChanged(int index) { if (index == OstProto::Arp::kFixed) senderHwAddrCount->setDisabled(true); else senderHwAddrCount->setEnabled(true); } void ArpConfigForm::on_targetHwAddrMode_currentIndexChanged(int index) { if (index == OstProto::Arp::kFixed) targetHwAddrCount->setDisabled(true); else targetHwAddrCount->setEnabled(true); } void ArpConfigForm::on_senderProtoAddrMode_currentIndexChanged(int index) { if (index == OstProto::Arp::kFixedHost) { senderProtoAddrCount->setDisabled(true); senderProtoAddrMask->setDisabled(true); } else { senderProtoAddrCount->setEnabled(true); senderProtoAddrMask->setEnabled(true); } } void ArpConfigForm::on_targetProtoAddrMode_currentIndexChanged(int index) { if (index == OstProto::Arp::kFixedHost) { targetProtoAddrCount->setDisabled(true); targetProtoAddrMask->setDisabled(true); } else { targetProtoAddrCount->setEnabled(true); targetProtoAddrMask->setEnabled(true); } } ostinato-0.9/common/arpconfig.h000066400000000000000000000025461321315675200166470ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _ARP_CONFIG_H #define _ARP_CONFIG_H #include "abstractprotocolconfig.h" #include "ui_arp.h" class ArpConfigForm : public AbstractProtocolConfigForm, private Ui::Arp { Q_OBJECT public: ArpConfigForm(QWidget *parent = 0); virtual ~ArpConfigForm(); static ArpConfigForm* createInstance(); virtual void loadWidget(AbstractProtocol *proto); virtual void storeWidget(AbstractProtocol *proto); private slots: void on_senderHwAddrMode_currentIndexChanged(int index); void on_senderProtoAddrMode_currentIndexChanged(int index); void on_targetHwAddrMode_currentIndexChanged(int index); void on_targetProtoAddrMode_currentIndexChanged(int index); }; #endif ostinato-0.9/common/arppdml.cpp000066400000000000000000000024671321315675200166730ustar00rootroot00000000000000/* Copyright (C) 2011 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "arppdml.h" #include "arp.pb.h" PdmlArpProtocol::PdmlArpProtocol() { ostProtoId_ = OstProto::Protocol::kArpFieldNumber; fieldMap_.insert("arp.opcode", OstProto::Arp::kOpCodeFieldNumber); fieldMap_.insert("arp.src.hw_mac", OstProto::Arp::kSenderHwAddrFieldNumber); fieldMap_.insert("arp.src.proto_ipv4", OstProto::Arp::kSenderProtoAddrFieldNumber); fieldMap_.insert("arp.dst.hw_mac", OstProto::Arp::kTargetHwAddrFieldNumber); fieldMap_.insert("arp.dst.proto_ipv4", OstProto::Arp::kTargetProtoAddrFieldNumber); } PdmlProtocol* PdmlArpProtocol::createInstance() { return new PdmlArpProtocol(); } ostinato-0.9/common/arppdml.h000066400000000000000000000015651321315675200163360ustar00rootroot00000000000000/* Copyright (C) 2011 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _ARP_PDML_H #define _ARP_PDML_H #include "pdmlprotocol.h" class PdmlArpProtocol : public PdmlProtocol { public: static PdmlProtocol* createInstance(); protected: PdmlArpProtocol(); }; #endif ostinato-0.9/common/bswap.h000066400000000000000000000020621321315675200160040ustar00rootroot00000000000000/* Copyright (C) 2010-2016 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _B_SWAP_H #define _B_SWAP_H #include static inline quint32 swap32(quint32 val) { return (((val >> 24) & 0x000000FF) | ((val >> 16) & 0x0000FF00) | ((val << 16) & 0x00FF0000) | ((val << 24) & 0xFF000000)); } static inline quint16 swap16(quint16 val) { return (((val >> 8) & 0x00FF) | ((val << 8) & 0xFF00)); } #endif ostinato-0.9/common/comboprotocol.h000066400000000000000000000133001321315675200175460ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _COMBO_PROTOCOL_H #define _COMBO_PROTOCOL_H #include "abstractprotocol.h" template class ComboProtocol : public AbstractProtocol { protected: ProtoA *protoA; ProtoB *protoB; public: ComboProtocol(StreamBase *stream, AbstractProtocol *parent = 0) : AbstractProtocol(stream, parent) { protoA = new ProtoA(stream, this); protoB = new ProtoB(stream, this); protoA->next = protoB; protoB->prev = protoA; qDebug("%s: protoNumber = %d, %p <--> %p", __FUNCTION__, protoNumber, protoA, protoB); } virtual ~ComboProtocol() { delete protoA; delete protoB; } static ComboProtocol* createInstance(StreamBase *stream, AbstractProtocol *parent) { return new ComboProtocol(stream, parent); } virtual quint32 protocolNumber() const { return protoNumber; } virtual void protoDataCopyInto(OstProto::Protocol &protocol) const { protoA->protoDataCopyInto(protocol); protoB->protoDataCopyInto(protocol); protocol.mutable_protocol_id()->set_id(protocolNumber()); } virtual void protoDataCopyFrom(const OstProto::Protocol &protocol) { if (protocol.protocol_id().id() == protocolNumber()) { OstProto::Protocol proto; // NOTE: To use protoX->protoDataCopyFrom() we need to arrange // so that it sees its own protocolNumber() - but since the // input param 'protocol' is 'const', we work on a copy proto.CopyFrom(protocol); proto.mutable_protocol_id()->set_id(protoA->protocolNumber()); protoA->protoDataCopyFrom(proto); proto.mutable_protocol_id()->set_id(protoB->protocolNumber()); protoB->protoDataCopyFrom(proto); } } virtual QString name() const { return protoA->name() + "/" + protoB->name(); } virtual QString shortName() const { return protoA->shortName() + "/" + protoB->shortName(); } virtual ProtocolIdType protocolIdType() const { return protoB->protocolIdType(); } virtual quint32 protocolId(ProtocolIdType type) const { return protoA->protocolId(type); } //quint32 payloadProtocolId(ProtocolIdType type) const; virtual int fieldCount() const { return protoA->fieldCount() + protoB->fieldCount(); } //virtual int metaFieldCount() const; //int frameFieldCount() const; virtual FieldFlags fieldFlags(int index) const { int cnt = protoA->fieldCount(); if (index < cnt) return protoA->fieldFlags(index); else return protoB->fieldFlags(index - cnt); } virtual QVariant fieldData(int index, FieldAttrib attrib, int streamIndex = 0) const { int cnt = protoA->fieldCount(); if (index < cnt) return protoA->fieldData(index, attrib, streamIndex); else return protoB->fieldData(index - cnt, attrib, streamIndex); } virtual bool setFieldData(int index, const QVariant &value, FieldAttrib attrib = FieldValue) { int cnt = protoA->fieldCount(); if (index < cnt) return protoA->setFieldData(index, value, attrib); else return protoB->setFieldData(index - cnt, value, attrib); } #if 0 QByteArray protocolFrameValue(int streamIndex = 0, bool forCksum = false) const; virtual int protocolFrameSize() const; int protocolFrameOffset() const; int protocolFramePayloadSize() const; #endif virtual bool isProtocolFrameSizeVariable() const { return (protoA->isProtocolFrameSizeVariable() || protoB->isProtocolFrameSizeVariable()); } virtual int protocolFrameVariableCount() const { int count = AbstractProtocol::protocolFrameVariableCount(); count = AbstractProtocol::lcm( count, protoA->protocolFrameVariableCount()); count = AbstractProtocol::lcm( count, protoB->protocolFrameVariableCount()); return count; } virtual quint32 protocolFrameCksum(int streamIndex = 0, CksumType cksumType = CksumIp) const { // For a Pseudo IP cksum, we assume it is the succeeding protocol // that is requesting it and hence return protoB's cksum; if (cksumType == CksumIpPseudo) return protoB->protocolFrameCksum(streamIndex, cksumType); return AbstractProtocol::protocolFrameCksum(streamIndex, cksumType); } #if 0 quint32 protocolFrameHeaderCksum(int streamIndex = 0, CksumType cksumType = CksumIp) const; quint32 protocolFramePayloadCksum(int streamIndex = 0, CksumType cksumType = CksumIp) const; #endif template friend class ComboProtocolConfigForm; }; #endif ostinato-0.9/common/comboprotocolconfig.h000066400000000000000000000061071321315675200207430ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _COMBO_PROTOCOL_CONFIG_H #define _COMBO_PROTOCOL_CONFIG_H #include "abstractprotocolconfig.h" #include "comboprotocol.h" template class ComboProtocolConfigForm : public AbstractProtocolConfigForm { public: ComboProtocolConfigForm(QWidget *parent = 0) : AbstractProtocolConfigForm(parent) { QVBoxLayout *layout = new QVBoxLayout; formA = new FormA(this); formB = new FormB(this); layout->addWidget(formA); layout->addWidget(formB); layout->setSpacing(0); layout->setContentsMargins(0, 0, 0, 0); setLayout(layout); qDebug("%s: protoNumber = %d, %p <--> %p", __FUNCTION__, protoNumber, formA, formB); } virtual ~ComboProtocolConfigForm() { formA->setParent(0); formB->setParent(0); delete formA; delete formB; } static ComboProtocolConfigForm* createInstance() { return new ComboProtocolConfigForm; } virtual void loadWidget(AbstractProtocol *proto) { class ComboProtocol *comboProto = dynamic_cast*>(proto); Q_ASSERT_X(comboProto != NULL, QString("ComboProtocolConfigForm{%1}::loadWidget()") .arg(protoNumber).toAscii().constData(), QString("Protocol{%1} is not a instance of ComboProtocol") .arg(proto->protocolNumber()).toAscii().constData()); formA->loadWidget(comboProto->protoA); formB->loadWidget(comboProto->protoB); } virtual void storeWidget(AbstractProtocol *proto) { class ComboProtocol *comboProto = dynamic_cast*>(proto); Q_ASSERT_X(comboProto != NULL, QString("ComboProtocolConfigForm{%1}::loadWidget()") .arg(protoNumber).toAscii().constData(), QString("Protocol{%1} is not a instance of ComboProtocol") .arg(proto->protocolNumber()).toAscii().constData()); formA->storeWidget(comboProto->protoA); formB->storeWidget(comboProto->protoB); } protected: FormA *formA; FormB *formB; }; #endif ostinato-0.9/common/crc32c.cpp000066400000000000000000000134231321315675200163050ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ /******************************************************************* ** IMPORTANT NOTE: ** This code is from RFC 4960 Stream Control Transmission Protocol ** It has been modified suitably while keeping the algorithm intact. ********************************************************************/ #include "crc32c.h" #define CRC32C(c,d) (c=(c>>8)^crc_c[(c^(d))&0xFF]) quint32 crc_c[256] = { 0x00000000L, 0xF26B8303L, 0xE13B70F7L, 0x1350F3F4L, 0xC79A971FL, 0x35F1141CL, 0x26A1E7E8L, 0xD4CA64EBL, 0x8AD958CFL, 0x78B2DBCCL, 0x6BE22838L, 0x9989AB3BL, 0x4D43CFD0L, 0xBF284CD3L, 0xAC78BF27L, 0x5E133C24L, 0x105EC76FL, 0xE235446CL, 0xF165B798L, 0x030E349BL, 0xD7C45070L, 0x25AFD373L, 0x36FF2087L, 0xC494A384L, 0x9A879FA0L, 0x68EC1CA3L, 0x7BBCEF57L, 0x89D76C54L, 0x5D1D08BFL, 0xAF768BBCL, 0xBC267848L, 0x4E4DFB4BL, 0x20BD8EDEL, 0xD2D60DDDL, 0xC186FE29L, 0x33ED7D2AL, 0xE72719C1L, 0x154C9AC2L, 0x061C6936L, 0xF477EA35L, 0xAA64D611L, 0x580F5512L, 0x4B5FA6E6L, 0xB93425E5L, 0x6DFE410EL, 0x9F95C20DL, 0x8CC531F9L, 0x7EAEB2FAL, 0x30E349B1L, 0xC288CAB2L, 0xD1D83946L, 0x23B3BA45L, 0xF779DEAEL, 0x05125DADL, 0x1642AE59L, 0xE4292D5AL, 0xBA3A117EL, 0x4851927DL, 0x5B016189L, 0xA96AE28AL, 0x7DA08661L, 0x8FCB0562L, 0x9C9BF696L, 0x6EF07595L, 0x417B1DBCL, 0xB3109EBFL, 0xA0406D4BL, 0x522BEE48L, 0x86E18AA3L, 0x748A09A0L, 0x67DAFA54L, 0x95B17957L, 0xCBA24573L, 0x39C9C670L, 0x2A993584L, 0xD8F2B687L, 0x0C38D26CL, 0xFE53516FL, 0xED03A29BL, 0x1F682198L, 0x5125DAD3L, 0xA34E59D0L, 0xB01EAA24L, 0x42752927L, 0x96BF4DCCL, 0x64D4CECFL, 0x77843D3BL, 0x85EFBE38L, 0xDBFC821CL, 0x2997011FL, 0x3AC7F2EBL, 0xC8AC71E8L, 0x1C661503L, 0xEE0D9600L, 0xFD5D65F4L, 0x0F36E6F7L, 0x61C69362L, 0x93AD1061L, 0x80FDE395L, 0x72966096L, 0xA65C047DL, 0x5437877EL, 0x4767748AL, 0xB50CF789L, 0xEB1FCBADL, 0x197448AEL, 0x0A24BB5AL, 0xF84F3859L, 0x2C855CB2L, 0xDEEEDFB1L, 0xCDBE2C45L, 0x3FD5AF46L, 0x7198540DL, 0x83F3D70EL, 0x90A324FAL, 0x62C8A7F9L, 0xB602C312L, 0x44694011L, 0x5739B3E5L, 0xA55230E6L, 0xFB410CC2L, 0x092A8FC1L, 0x1A7A7C35L, 0xE811FF36L, 0x3CDB9BDDL, 0xCEB018DEL, 0xDDE0EB2AL, 0x2F8B6829L, 0x82F63B78L, 0x709DB87BL, 0x63CD4B8FL, 0x91A6C88CL, 0x456CAC67L, 0xB7072F64L, 0xA457DC90L, 0x563C5F93L, 0x082F63B7L, 0xFA44E0B4L, 0xE9141340L, 0x1B7F9043L, 0xCFB5F4A8L, 0x3DDE77ABL, 0x2E8E845FL, 0xDCE5075CL, 0x92A8FC17L, 0x60C37F14L, 0x73938CE0L, 0x81F80FE3L, 0x55326B08L, 0xA759E80BL, 0xB4091BFFL, 0x466298FCL, 0x1871A4D8L, 0xEA1A27DBL, 0xF94AD42FL, 0x0B21572CL, 0xDFEB33C7L, 0x2D80B0C4L, 0x3ED04330L, 0xCCBBC033L, 0xA24BB5A6L, 0x502036A5L, 0x4370C551L, 0xB11B4652L, 0x65D122B9L, 0x97BAA1BAL, 0x84EA524EL, 0x7681D14DL, 0x2892ED69L, 0xDAF96E6AL, 0xC9A99D9EL, 0x3BC21E9DL, 0xEF087A76L, 0x1D63F975L, 0x0E330A81L, 0xFC588982L, 0xB21572C9L, 0x407EF1CAL, 0x532E023EL, 0xA145813DL, 0x758FE5D6L, 0x87E466D5L, 0x94B49521L, 0x66DF1622L, 0x38CC2A06L, 0xCAA7A905L, 0xD9F75AF1L, 0x2B9CD9F2L, 0xFF56BD19L, 0x0D3D3E1AL, 0x1E6DCDEEL, 0xEC064EEDL, 0xC38D26C4L, 0x31E6A5C7L, 0x22B65633L, 0xD0DDD530L, 0x0417B1DBL, 0xF67C32D8L, 0xE52CC12CL, 0x1747422FL, 0x49547E0BL, 0xBB3FFD08L, 0xA86F0EFCL, 0x5A048DFFL, 0x8ECEE914L, 0x7CA56A17L, 0x6FF599E3L, 0x9D9E1AE0L, 0xD3D3E1ABL, 0x21B862A8L, 0x32E8915CL, 0xC083125FL, 0x144976B4L, 0xE622F5B7L, 0xF5720643L, 0x07198540L, 0x590AB964L, 0xAB613A67L, 0xB831C993L, 0x4A5A4A90L, 0x9E902E7BL, 0x6CFBAD78L, 0x7FAB5E8CL, 0x8DC0DD8FL, 0xE330A81AL, 0x115B2B19L, 0x020BD8EDL, 0xF0605BEEL, 0x24AA3F05L, 0xD6C1BC06L, 0xC5914FF2L, 0x37FACCF1L, 0x69E9F0D5L, 0x9B8273D6L, 0x88D28022L, 0x7AB90321L, 0xAE7367CAL, 0x5C18E4C9L, 0x4F48173DL, 0xBD23943EL, 0xF36E6F75L, 0x0105EC76L, 0x12551F82L, 0xE03E9C81L, 0x34F4F86AL, 0xC69F7B69L, 0xD5CF889DL, 0x27A40B9EL, 0x79B737BAL, 0x8BDCB4B9L, 0x988C474DL, 0x6AE7C44EL, 0xBE2DA0A5L, 0x4C4623A6L, 0x5F16D052L, 0xAD7D5351L, }; quint32 checksumCrc32C(quint8 *buffer, uint length) { uint i; quint32 crc32 = ~0L; quint32 result; quint8 byte0,byte1,byte2,byte3; for (i = 0; i < length; i++) { CRC32C(crc32, buffer[i]); } result = ~crc32; /* result now holds the negated polynomial remainder; * since the table and algorithm is "reflected" [williams95]. * That is, result has the same value as if we mapped the message * to a polynomial, computed the host-bit-order polynomial * remainder, performed final negation, then did an end-for-end * bit-reversal. * Note that a 32-bit bit-reversal is identical to four inplace * 8-bit reversals followed by an end-for-end byteswap. * In other words, the bytes of each bit are in the right order, * but the bytes have been byteswapped. So we now do an explicit * byteswap. On a little-endian machine, this byteswap and * the final ntohl cancel out and could be elided. */ byte0 = result & 0xff; byte1 = (result>>8) & 0xff; byte2 = (result>>16) & 0xff; byte3 = (result>>24) & 0xff; crc32 = ((byte0 << 24) | (byte1 << 16) | (byte2 << 8) | byte3); return ( crc32 ); } ostinato-0.9/common/crc32c.h000066400000000000000000000013551321315675200157530ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include quint32 checksumCrc32C(quint8 *buffer, uint length); ostinato-0.9/common/dot2llc.h000066400000000000000000000016021321315675200162320ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _DOT2_LLC_H #define _DOT2_LLC_H #include "comboprotocol.h" #include "dot3.h" #include "llc.h" typedef ComboProtocol Dot2LlcProtocol; #endif ostinato-0.9/common/dot2llc.proto000066400000000000000000000016031321315675200171470ustar00rootroot00000000000000/// (802.2 LLC) /* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ import "protocol.proto"; import "dot3.proto"; import "llc.proto"; package OstProto; message Dot2Llc { // Empty since this is a 'combo' protocol } extend Protocol { optional Dot2Llc dot2Llc = 206; } ostinato-0.9/common/dot2llcconfig.h000066400000000000000000000020141321315675200174160ustar00rootroot00000000000000/* Copyright (C) 2014 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _DOT2_LLC_CONFIG_H #define _DOT2_LLC_CONFIG_H #include "comboprotocolconfig.h" #include "dot3config.h" #include "llcconfig.h" #include "dot3.h" #include "llc.h" typedef ComboProtocolConfigForm < OstProto::Protocol::kDot2LlcFieldNumber, Dot3ConfigForm, LlcConfigForm, Dot3Protocol, LlcProtocol > Dot2LlcConfigForm; #endif ostinato-0.9/common/dot2snap.h000066400000000000000000000016161321315675200164260ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _DOT2_SNAP_H #define _DOT2_SNAP_H #include "comboprotocol.h" #include "dot2llc.h" #include "snap.h" typedef ComboProtocol Dot2SnapProtocol; #endif ostinato-0.9/common/dot2snap.proto000066400000000000000000000015541321315675200173430ustar00rootroot00000000000000/// (802.2 SNAP) /* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ import "protocol.proto"; package OstProto; // 802.2 SNAP message Dot2Snap { // Empty since this is a 'combo' protocol } extend Protocol { optional Dot2Snap dot2Snap = 207; } ostinato-0.9/common/dot2snapconfig.h000066400000000000000000000020321321315675200176050ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _DOT2_SNAP_CONFIG_H #define _DOT2_SNAP_CONFIG_H #include "comboprotocol.h" #include "dot2llcconfig.h" #include "snapconfig.h" #include "dot2llc.h" #include "snap.h" typedef ComboProtocolConfigForm < OstProto::Protocol::kDot2SnapFieldNumber, Dot2LlcConfigForm, SnapConfigForm, Dot2LlcProtocol, SnapProtocol > Dot2SnapConfigForm; #endif ostinato-0.9/common/dot3.cpp000066400000000000000000000115441321315675200161010ustar00rootroot00000000000000/* Copyright (C) 2010-2014 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "dot3.h" Dot3Protocol::Dot3Protocol(StreamBase *stream, AbstractProtocol *parent) : AbstractProtocol(stream, parent) { } Dot3Protocol::~Dot3Protocol() { } AbstractProtocol* Dot3Protocol::createInstance(StreamBase *stream, AbstractProtocol *parent) { return new Dot3Protocol(stream, parent); } quint32 Dot3Protocol::protocolNumber() const { return OstProto::Protocol::kDot3FieldNumber; } void Dot3Protocol::protoDataCopyInto(OstProto::Protocol &protocol) const { protocol.MutableExtension(OstProto::dot3)->CopyFrom(data); protocol.mutable_protocol_id()->set_id(protocolNumber()); } void Dot3Protocol::protoDataCopyFrom(const OstProto::Protocol &protocol) { if (protocol.protocol_id().id() == protocolNumber() && protocol.HasExtension(OstProto::dot3)) data.MergeFrom(protocol.GetExtension(OstProto::dot3)); } QString Dot3Protocol::name() const { return QString("802.3"); } QString Dot3Protocol::shortName() const { return QString("802.3"); } int Dot3Protocol::fieldCount() const { return dot3_fieldCount; } AbstractProtocol::FieldFlags Dot3Protocol::fieldFlags(int index) const { AbstractProtocol::FieldFlags flags; flags = AbstractProtocol::fieldFlags(index); switch (index) { case dot3_length: break; // Meta fields case dot3_is_override_length: flags &= ~FrameField; flags |= MetaField; break; default: break; } return flags; } QVariant Dot3Protocol::fieldData(int index, FieldAttrib attrib, int streamIndex) const { switch (index) { case dot3_length: switch(attrib) { case FieldName: return QString("Length"); case FieldValue: { quint16 len = data.is_override_length() ? data.length() : protocolFramePayloadSize(streamIndex); return len; } case FieldTextValue: { quint16 len = data.is_override_length() ? data.length() : protocolFramePayloadSize(streamIndex); return QString("%1").arg(len); } case FieldFrameValue: { quint16 len = data.is_override_length() ? data.length() : protocolFramePayloadSize(streamIndex); QByteArray fv; fv.resize(2); qToBigEndian(len, (uchar*) fv.data()); return fv; } case FieldBitSize: return 16; default: break; } break; // Meta fields case dot3_is_override_length: { switch(attrib) { case FieldValue: return data.is_override_length(); default: break; } break; } default: qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, index); break; } return AbstractProtocol::fieldData(index, attrib, streamIndex); } bool Dot3Protocol::setFieldData(int index, const QVariant &value, FieldAttrib attrib) { bool isOk = false; if (attrib != FieldValue) return false; switch (index) { case dot3_length: { uint len = value.toUInt(&isOk); if (isOk) data.set_length(len); break; } case dot3_is_override_length: { bool ovr = value.toBool(); data.set_is_override_length(ovr); isOk = true; break; } default: qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, index); break; } return isOk; } int Dot3Protocol::protocolFrameVariableCount() const { return AbstractProtocol::lcm( AbstractProtocol::protocolFrameVariableCount(), protocolFramePayloadVariableCount()); } ostinato-0.9/common/dot3.h000066400000000000000000000034701321315675200155450ustar00rootroot00000000000000/* Copyright (C) 2010-2014 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _DOT3_H #define _DOT3_H #include "abstractprotocol.h" #include "dot3.pb.h" class Dot3Protocol : public AbstractProtocol { public: enum Dot3field { dot3_length, // Meta-fields dot3_is_override_length, dot3_fieldCount }; Dot3Protocol(StreamBase *stream, AbstractProtocol *parent = 0); virtual ~Dot3Protocol(); static AbstractProtocol* createInstance(StreamBase *stream, AbstractProtocol *parent = 0); virtual quint32 protocolNumber() const; virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); virtual QString name() const; virtual QString shortName() const; virtual int fieldCount() const; virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; virtual QVariant fieldData(int index, FieldAttrib attrib, int streamIndex = 0) const; virtual bool setFieldData(int index, const QVariant &value, FieldAttrib attrib = FieldValue); virtual int protocolFrameVariableCount() const; private: OstProto::Dot3 data; }; #endif ostinato-0.9/common/dot3.proto000066400000000000000000000015621321315675200164610ustar00rootroot00000000000000/// (802.3) /* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ import "protocol.proto"; package OstProto; // 802.3 message Dot3 { optional bool is_override_length = 2; optional uint32 length = 1; } extend Protocol { optional Dot3 dot3 = 201; } ostinato-0.9/common/dot3.ui000066400000000000000000000035531321315675200157350ustar00rootroot00000000000000 dot3 0 0 181 98 Form 802.3 Length false Qt::Horizontal 16 54 Qt::Vertical 20 40 cbOverrideLength toggled(bool) leLength setEnabled(bool) 55 39 84 43 ostinato-0.9/common/dot3config.cpp000066400000000000000000000032411321315675200172620ustar00rootroot00000000000000/* Copyright (C) 2010-2014 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "dot3config.h" #include "dot3.h" #include Dot3ConfigForm::Dot3ConfigForm(QWidget *parent) : AbstractProtocolConfigForm(parent) { setupUi(this); leLength->setValidator(new QIntValidator(0, 16384, this)); } Dot3ConfigForm::~Dot3ConfigForm() { } Dot3ConfigForm* Dot3ConfigForm::createInstance() { return new Dot3ConfigForm; } void Dot3ConfigForm::loadWidget(AbstractProtocol *proto) { cbOverrideLength->setChecked( proto->fieldData( Dot3Protocol::dot3_is_override_length, AbstractProtocol::FieldValue ).toBool()); leLength->setText( proto->fieldData( Dot3Protocol::dot3_length, AbstractProtocol::FieldValue ).toString()); } void Dot3ConfigForm::storeWidget(AbstractProtocol *proto) { proto->setFieldData( Dot3Protocol::dot3_is_override_length, cbOverrideLength->isChecked()); proto->setFieldData( Dot3Protocol::dot3_length, leLength->text()); } ostinato-0.9/common/dot3config.h000066400000000000000000000021531321315675200167300ustar00rootroot00000000000000/* Copyright (C) 2010-2014 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _DOT3_CONFIG_H #define _DOT3_CONFIG_H #include "abstractprotocolconfig.h" #include "ui_dot3.h" class Dot3ConfigForm : public AbstractProtocolConfigForm, private Ui::dot3 { Q_OBJECT public: Dot3ConfigForm(QWidget *parent = 0); virtual ~Dot3ConfigForm(); static Dot3ConfigForm* createInstance(); virtual void loadWidget(AbstractProtocol *proto); virtual void storeWidget(AbstractProtocol *proto); }; #endif ostinato-0.9/common/emulation.h000066400000000000000000000034261321315675200166720ustar00rootroot00000000000000/* Copyright (C) 2016 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _EMULATION_H #define _EMULATION_H #include "emulproto.pb.h" #include static inline OstProto::DeviceGroup* newDeviceGroup(uint portId) { OstProto::DeviceGroup *devGrp = new OstProto::DeviceGroup; // To ensure that DeviceGroup have a unique key, we assign // a random mac address upon creation; ideally, it would // have been good to inherit OstProto::DeviceGroup and define // a new constructor, but ProtoBuf forbids against inheriting // generated classes, so we use this helper function instead // // Create a mac address as per RFC 4814 Sec 4.2 // (RR & 0xFC):PP:PP:RR:RR:RR // where RR is a random number, PP:PP is 1-indexed port index // NOTE: although qrand() return type is a int, the max value // is RAND_MAX (stdlib.h) which is often 16-bit only, so we // use two random numbers quint32 r1 = qrand(), r2 = qrand(); quint64 mac; mac = quint64(r1 & 0xfc00) << 32 | quint64(portId + 1) << 24 | quint64((r1 & 0xff) << 16 | (r2 & 0xffff)); devGrp->MutableExtension(OstEmul::mac)->set_address(mac); return devGrp; } #endif ostinato-0.9/common/emulproto.proto000066400000000000000000000057701321315675200176430ustar00rootroot00000000000000/* Copyright (C) 2015 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ import "protocol.proto"; package OstEmul; // ======= // Encap // ======= message VlanEmulation { message Vlan { optional uint32 tpid = 1 [default = 0x8100]; // includes prio, cfi and vlanid optional uint32 vlan_tag = 2 [default = 100]; optional uint32 count = 10 [default = 1]; optional uint32 step = 11 [default = 1]; } repeated Vlan stack = 1; // outer to inner } extend OstProto.EncapEmulation { optional VlanEmulation vlan = 1000; } // =========== // Protocols // =========== message MacEmulation { optional uint64 address = 1; // no default - need unique value optional uint64 step = 10 [default = 1]; } // No default values for IP addresses - user needs to explicitly set that // 'coz we derive if a device has a single/dual or no IP stack on the basis // of whether OstProto.DeviceGroup.ip[46] is set message Ip4Emulation { optional uint32 address = 1; optional uint32 prefix_length = 2 [default = 24]; optional uint32 default_gateway = 3; optional uint32 step = 10 [default = 1]; // FIXME: step for gateway? } message Ip6Address { optional uint64 hi = 1; optional uint64 lo = 2; } message Ip6Emulation { optional Ip6Address address = 1; optional uint32 prefix_length = 2 [default = 64]; optional Ip6Address default_gateway = 3; optional Ip6Address step = 10; // FIXME: step for gateway? } extend OstProto.DeviceGroup { optional MacEmulation mac = 2001; optional Ip4Emulation ip4 = 3000; optional Ip6Emulation ip6 = 3001; } message Device { optional uint64 mac = 1; repeated uint32 vlan = 2; // includes tpid 'n vlan tag optional uint32 ip4 = 10; optional uint32 ip4_prefix_length = 11; optional uint32 ip4_default_gateway = 12; optional Ip6Address ip6 = 20; optional uint32 ip6_prefix_length = 21; optional Ip6Address ip6_default_gateway = 22; } extend OstProto.PortDeviceList { repeated Device device = 100; } message ArpEntry { optional uint32 ip4 = 1; optional uint64 mac = 2; } message NdpEntry { optional Ip6Address ip6 = 1; optional uint64 mac = 2; } message DeviceNeighborList { optional uint32 device_index = 1; repeated ArpEntry arp = 2; repeated NdpEntry ndp = 3; } extend OstProto.PortNeighborList { repeated DeviceNeighborList device_neighbor = 100; } ostinato-0.9/common/eth2.cpp000066400000000000000000000112631321315675200160700ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "eth2.h" Eth2Protocol::Eth2Protocol(StreamBase *stream, AbstractProtocol *parent) : AbstractProtocol(stream, parent) { } Eth2Protocol::~Eth2Protocol() { } AbstractProtocol* Eth2Protocol::createInstance(StreamBase *stream, AbstractProtocol *parent) { return new Eth2Protocol(stream, parent); } quint32 Eth2Protocol::protocolNumber() const { return OstProto::Protocol::kEth2FieldNumber; } void Eth2Protocol::protoDataCopyInto(OstProto::Protocol &protocol) const { protocol.MutableExtension(OstProto::eth2)->CopyFrom(data); protocol.mutable_protocol_id()->set_id(protocolNumber()); } void Eth2Protocol::protoDataCopyFrom(const OstProto::Protocol &protocol) { if (protocol.protocol_id().id() == protocolNumber() && protocol.HasExtension(OstProto::eth2)) data.MergeFrom(protocol.GetExtension(OstProto::eth2)); } QString Eth2Protocol::name() const { return QString("Ethernet II"); } QString Eth2Protocol::shortName() const { return QString("Eth II"); } AbstractProtocol::ProtocolIdType Eth2Protocol::protocolIdType() const { return ProtocolIdEth; } int Eth2Protocol::fieldCount() const { return eth2_fieldCount; } AbstractProtocol::FieldFlags Eth2Protocol::fieldFlags(int index) const { AbstractProtocol::FieldFlags flags; flags = AbstractProtocol::fieldFlags(index); switch (index) { case eth2_type: break; case eth2_is_override_type: flags &= ~FrameField; flags |= MetaField; break; default: break; } return flags; } QVariant Eth2Protocol::fieldData(int index, FieldAttrib attrib, int streamIndex) const { switch (index) { case eth2_type: { switch(attrib) { case FieldName: return QString("Type"); case FieldValue: { quint16 type = data.is_override_type() ? data.type() : payloadProtocolId(ProtocolIdEth); return type; } case FieldTextValue: { quint16 type = data.is_override_type() ? data.type() : payloadProtocolId(ProtocolIdEth); return QString("0x%1").arg(type, 4, BASE_HEX, QChar('0')); } case FieldFrameValue: { QByteArray fv; quint16 type = data.is_override_type() ? data.type() : payloadProtocolId(ProtocolIdEth); fv.resize(2); qToBigEndian((quint16) type, (uchar*) fv.data()); return fv; } default: break; } break; } // Meta fields case eth2_is_override_type: { switch(attrib) { case FieldValue: return data.is_override_type(); default: break; } break; } default: qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, index); break; } return AbstractProtocol::fieldData(index, attrib, streamIndex); } bool Eth2Protocol::setFieldData(int index, const QVariant &value, FieldAttrib attrib) { bool isOk = false; if (attrib != FieldValue) return false; switch (index) { case eth2_type: { uint type = value.toUInt(&isOk); if (isOk) data.set_type(type); break; } case eth2_is_override_type: { bool ovr = value.toBool(); data.set_is_override_type(ovr); isOk = true; break; } default: qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, index); break; } return isOk; } ostinato-0.9/common/eth2.h000066400000000000000000000034311321315675200155330ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _ETH2_H #define _ETH2_H #include "abstractprotocol.h" #include "eth2.pb.h" class Eth2Protocol : public AbstractProtocol { public: enum eth2field { eth2_type = 0, eth2_is_override_type, eth2_fieldCount }; Eth2Protocol(StreamBase *stream, AbstractProtocol *parent = 0); virtual ~Eth2Protocol(); static AbstractProtocol* createInstance(StreamBase *stream, AbstractProtocol *parent = 0); virtual quint32 protocolNumber() const; virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); virtual QString name() const; virtual QString shortName() const; virtual ProtocolIdType protocolIdType() const; virtual int fieldCount() const; virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; virtual QVariant fieldData(int index, FieldAttrib attrib, int streamIndex = 0) const; virtual bool setFieldData(int index, const QVariant &value, FieldAttrib attrib = FieldValue); private: OstProto::Eth2 data; }; #endif ostinato-0.9/common/eth2.proto000066400000000000000000000015511321315675200164500ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ import "protocol.proto"; package OstProto; // Ethernet II message Eth2 { optional bool is_override_type = 2; optional uint32 type = 1; } extend Protocol { optional Eth2 eth2 = 200; } ostinato-0.9/common/eth2.ui000066400000000000000000000033431321315675200157230ustar00rootroot00000000000000 eth2 0 0 190 64 Form Ethernet Type false >HH HH; Qt::Horizontal 40 20 Qt::Vertical 20 40 cbOverrideType toggled(bool) leType setEnabled(bool) 98 27 118 27 ostinato-0.9/common/eth2config.cpp000066400000000000000000000032261321315675200172560ustar00rootroot00000000000000/* Copyright (C) 2010,2014 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "eth2config.h" #include "eth2.h" Eth2ConfigForm::Eth2ConfigForm(QWidget *parent) : AbstractProtocolConfigForm(parent) { setupUi(this); } Eth2ConfigForm::~Eth2ConfigForm() { } Eth2ConfigForm* Eth2ConfigForm::createInstance() { return new Eth2ConfigForm; } void Eth2ConfigForm::loadWidget(AbstractProtocol *proto) { cbOverrideType->setChecked( proto->fieldData( Eth2Protocol::eth2_is_override_type, AbstractProtocol::FieldValue ).toBool()); leType->setText(uintToHexStr( proto->fieldData( Eth2Protocol::eth2_type, AbstractProtocol::FieldValue ).toUInt(), 2)); } void Eth2ConfigForm::storeWidget(AbstractProtocol *proto) { bool isOk; proto->setFieldData( Eth2Protocol::eth2_is_override_type, cbOverrideType->isChecked()); proto->setFieldData( Eth2Protocol::eth2_type, leType->text().remove(QChar(' ')).toUInt(&isOk, BASE_HEX)); } ostinato-0.9/common/eth2config.h000066400000000000000000000021761321315675200167260ustar00rootroot00000000000000/* Copyright (C) 2010,2014 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _ETH2_CONFIG_H #define _ETH2_CONFIG_H #include "abstractprotocolconfig.h" #include "eth2.pb.h" #include "ui_eth2.h" class Eth2ConfigForm : public AbstractProtocolConfigForm, public Ui::eth2 { Q_OBJECT public: Eth2ConfigForm(QWidget *parent = 0); virtual ~Eth2ConfigForm(); static Eth2ConfigForm* createInstance(); virtual void loadWidget(AbstractProtocol *proto); virtual void storeWidget(AbstractProtocol *proto); }; #endif ostinato-0.9/common/eth2pdml.cpp000066400000000000000000000073071321315675200167510ustar00rootroot00000000000000/* Copyright (C) 2011 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "eth2pdml.h" #include "dot3.pb.h" #include "eth2.pb.h" #include "mac.pb.h" #include "vlan.pb.h" PdmlEthProtocol::PdmlEthProtocol() { ostProtoId_ = OstProto::Protocol::kMacFieldNumber; fieldMap_.insert("eth.dst", OstProto::Mac::kDstMacFieldNumber); fieldMap_.insert("eth.src", OstProto::Mac::kSrcMacFieldNumber); } PdmlProtocol* PdmlEthProtocol::createInstance() { return new PdmlEthProtocol(); } void PdmlEthProtocol::unknownFieldHandler(QString name, int /*pos*/, int /*size*/, const QXmlStreamAttributes &attributes, OstProto::Protocol* /*pbProto*/, OstProto::Stream *stream) { if (name == "eth.vlan.tpid") { bool isOk; uint tpid = attributes.value("value").toString() .toUInt(&isOk, kBaseHex); OstProto::Protocol *proto = stream->add_protocol(); proto->mutable_protocol_id()->set_id( OstProto::Protocol::kVlanFieldNumber); OstProto::Vlan *vlan = proto->MutableExtension(OstProto::vlan); vlan->set_tpid(tpid); vlan->set_is_override_tpid(true); } else if (name == "eth.vlan.id") { bool isOk; uint tag = attributes.value("unmaskedvalue").isEmpty() ? attributes.value("value").toString().toUInt(&isOk, kBaseHex) : attributes.value("unmaskedvalue").toString().toUInt(&isOk,kBaseHex); OstProto::Vlan *vlan = stream->mutable_protocol( stream->protocol_size()-1)->MutableExtension(OstProto::vlan); vlan->set_vlan_tag(tag); } else if (name == "eth.type") { bool isOk; uint type = attributes.value("value").toString() .toUInt(&isOk, kBaseHex); OstProto::Protocol *proto = stream->add_protocol(); proto->mutable_protocol_id()->set_id( OstProto::Protocol::kEth2FieldNumber); OstProto::Eth2 *eth2 = proto->MutableExtension(OstProto::eth2); eth2->set_type(type); eth2->set_is_override_type(true); } else if (name == "eth.len") { OstProto::Protocol *proto = stream->add_protocol(); proto->mutable_protocol_id()->set_id( OstProto::Protocol::kDot3FieldNumber); OstProto::Dot3 *dot3 = proto->MutableExtension(OstProto::dot3); bool isOk; dot3->set_length(attributes.value("value").toString().toUInt(&isOk, kBaseHex)); dot3->set_is_override_length(true); } #if 0 else if (name == "eth.trailer") { QByteArray trailer = QByteArray::fromHex( attributes.value("value").toString().toUtf8()); stream->mutable_core()->mutable_name()->append(trailer.constData(), trailer.size()); } else if ((name == "eth.fcs") || attributes.value("show").toString().startsWith("Frame check sequence")) { QByteArray trailer = QByteArray::fromHex( attributes.value("value").toString().toUtf8()); stream->mutable_core()->mutable_name()->append(trailer.constData(), trailer.size()); } #endif } ostinato-0.9/common/eth2pdml.h000066400000000000000000000020701321315675200164060ustar00rootroot00000000000000/* Copyright (C) 2011 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _ETH2_PDML_H #define _ETH2_PDML_H #include "pdmlprotocol.h" class PdmlEthProtocol : public PdmlProtocol { public: static PdmlProtocol* createInstance(); virtual void unknownFieldHandler(QString name, int pos, int size, const QXmlStreamAttributes &attributes, OstProto::Protocol *pbProto, OstProto::Stream *stream); protected: PdmlEthProtocol(); }; #endif ostinato-0.9/common/fileformat.proto000066400000000000000000000071301321315675200177350ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ import "protocol.proto"; package OstProto; enum FileType { kReservedFileType = 0; kStreamsFileType = 1; kSessionFileType = 10; } message FileMetaData { required FileType file_type = 1; required uint32 format_version_major = 2; required uint32 format_version_minor = 3; required uint32 format_version_revision = 4; required string generator_name = 5; required string generator_version = 6; required string generator_revision = 7; } message PortContent { optional Port port_config = 1; repeated Stream streams = 2; repeated DeviceGroup device_groups = 3; } message PortGroupContent { optional string server_name = 1; optional uint32 server_port = 2; repeated PortContent ports = 15; } message SessionContent { repeated PortGroupContent port_groups = 1; } message FileContentMatter { optional StreamConfigList streams = 1; // TODO: optional DeviceGroupConfigList device_groups = 2; // TODO: optional PortContent port = 3; // FIXME: (single) portgroup? is there a usecase for this? optional SessionContent session = 10; } /* An Ostinato file is the binary encoding of the File message below STRICTLY in increasing order of field number for the top level fields We do not use field number '1' for magic value because its encoded key is '0a' (LF or \n) which occurs commonly in text files. Checksum should be the last field, so top level field numbers greater than 15 are not permitted. We use 15 as the checksum field number because it is the largest field number that can fit in a 1-byte tag The magic value is of a fixed length so that meta data has a fixed offset from the start in the encoded message. Checksum is fixed length so that it is at a fixed negative offset from the end in the encoded message. Because the protobuf serialization API does not _guarantee_ strict ordering, so we define wrapper messages for each top level field and serialize the individual wrapper messages. The field numbers MUST be the same in 'File' and the wrapper messages */ message File { // FieldNumber 1 is reserved and MUST not be used! required bytes magic_value = 2; required FileMetaData meta_data = 3; optional FileContentMatter content_matter = 9; required fixed32 checksum_value = 15; } /* The magic value is 10 bytes - "\xa7\xb7OSTINATO" The 1st-2nd byte has the MSB set to avoid mixup with text files The 3rd-10th byte spell OSTINATO (duh!) Encoded Size : Key(1) + Length(1) + Value(10) = 12 bytes Encoded Value: 120aa7b7 4f535449 4e41544f */ message FileMagic { required bytes value = 2; } message FileMeta { required FileMetaData data = 3; } message FileContent { optional FileContentMatter matter = 9; } /* Encoded Size : Key(1) + Value(4) = 5 bytes Encoded Value: 7d xxXXxxXX */ message FileChecksum { required fixed32 value = 15; // should always be a fixed 32-bit size } ostinato-0.9/common/gmp.cpp000077500000000000000000000525271321315675200160240ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "gmp.h" #include QHash GmpProtocol::frameFieldCountMap; GmpProtocol::GmpProtocol(StreamBase *stream, AbstractProtocol *parent) : AbstractProtocol(stream, parent) { // field count may change based on msgType - so don't cache field offsets _cacheFlags &= ~FieldFrameBitOffsetCache; } GmpProtocol::~GmpProtocol() { } AbstractProtocol::ProtocolIdType GmpProtocol::protocolIdType() const { return ProtocolIdIp; } int GmpProtocol::fieldCount() const { return FIELD_COUNT; } int GmpProtocol::frameFieldCount() const { int type = msgType(); // frameFieldCountMap contains the frameFieldCounts for each // msgType - this is built on demand and cached for subsequent use // lookup if we have already cached ... if (frameFieldCountMap.contains(type)) return frameFieldCountMap.value(type); // ... otherwise calculate and cache int count = 0; for (int i = 0; i < FIELD_COUNT; i++) { if (fieldFlags(i).testFlag(AbstractProtocol::FrameField)) count++; } frameFieldCountMap.insert(type, count); return count; } AbstractProtocol::FieldFlags GmpProtocol::fieldFlags(int index) const { AbstractProtocol::FieldFlags flags; flags = AbstractProtocol::fieldFlags(index); flags &= ~FrameField; switch(index) { // Frame Fields - check against msgType() case kType: case kRsvdMrtCode: flags |= FrameField; break; case kChecksum: flags |= FrameField; flags |= CksumField; break; case kMldMrt: case kMldRsvd: // MLD subclass should handle suitably break; case kGroupAddress: if (!isSsmReport()) flags |= FrameField; break; case kRsvd1: case kSFlag: case kQrv: case kQqic: case kSourceCount: case kSources: if (isSsmQuery()) flags |= FrameField; break; case kRsvd2: case kGroupRecordCount: case kGroupRecords: if (isSsmReport()) flags |= FrameField; break; // Meta Fields case kIsOverrideChecksum: case kGroupMode: case kGroupCount: case kGroupPrefix: case kIsOverrideSourceCount: case kIsOverrideGroupRecordCount: flags |= MetaField; break; default: qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, index); break; } return flags; } QVariant GmpProtocol::fieldData(int index, FieldAttrib attrib, int streamIndex) const { switch (index) { case kType: { uint type = data.type(); switch(attrib) { case FieldName: return QString("Type"); case FieldValue: return type; case FieldTextValue: return QString("%1").arg(quint8(type)); case FieldFrameValue: return QByteArray(1, quint8(type)); default: break; } break; } case kRsvdMrtCode: { quint8 rsvd = 0; switch(attrib) { case FieldName: return QString("Reserved"); case FieldValue: return rsvd; case FieldTextValue: return QString("%1").arg(rsvd); case FieldFrameValue: return QByteArray(1, rsvd); default: break; } break; } case kChecksum: { switch(attrib) { case FieldName: return QString("Checksum"); case FieldBitSize: return 16; default: break; } quint16 cksum = data.is_override_checksum() ? data.checksum() : checksum(streamIndex); switch(attrib) { case FieldValue: return cksum; case FieldFrameValue: { QByteArray fv; fv.resize(2); qToBigEndian(cksum, (uchar*) fv.data()); return fv; } case FieldTextValue: return QString("0x%1").arg(cksum, 4, BASE_HEX, QChar('0')); default: break; } break; } case kMldMrt: case kMldRsvd: // XXX: Present only in MLD - hence handled by the mld subclass break; case kGroupAddress: // XXX: Handled by each subclass break; case kRsvd1: { quint8 rsvd = 0; switch(attrib) { case FieldName: return QString("Reserved"); case FieldValue: return rsvd; case FieldTextValue: return QString("%1").arg(rsvd); case FieldFrameValue: return QByteArray(1, char(rsvd)); case FieldBitSize: return 4; default: break; } break; } case kSFlag: { switch(attrib) { case FieldName: return QString("S Flag"); case FieldValue: return data.s_flag(); case FieldTextValue: return data.s_flag() ? QString("True") : QString("False"); case FieldFrameValue: return QByteArray(1, char(data.s_flag())); case FieldBitSize: return 1; default: break; } break; } case kQrv: { int qrv = data.qrv() & 0x7; switch(attrib) { case FieldName: return QString("QRV"); case FieldValue: return qrv; case FieldTextValue: return QString("%1").arg(qrv); case FieldFrameValue: return QByteArray(1, char(qrv)); case FieldBitSize: return 3; default: break; } break; } case kQqic: { int qqi = data.qqi(); switch(attrib) { case FieldName: return QString("QQIC"); case FieldValue: return qqi; case FieldTextValue: return QString("%1").arg(qqi); case FieldFrameValue: { char qqicode = char(qqic(qqi)); return QByteArray(1, qqicode); } default: break; } break; } case kSourceCount: { quint16 count = data.sources_size(); if (data.is_override_source_count()) count = data.source_count(); switch(attrib) { case FieldName: return QString("Number of Sources"); case FieldValue: return count; case FieldTextValue: return QString("%1").arg(count); case FieldFrameValue: { QByteArray fv; fv.resize(2); qToBigEndian(count, (uchar*) fv.data()); return fv; } default: break; } break; } case kSources: // XXX: Handled by each subclass break; case kRsvd2: { quint16 rsvd = 0; switch(attrib) { case FieldName: return QString("Reserved"); case FieldValue: return rsvd; case FieldTextValue: return QString("%1").arg(rsvd); case FieldFrameValue: { QByteArray fv; fv.resize(2); qToBigEndian(rsvd, (uchar*) fv.data()); return fv; } default: break; } break; } case kGroupRecordCount: { quint16 count = data.group_records_size(); if (data.is_override_group_record_count()) count = data.group_record_count(); switch(attrib) { case FieldName: return QString("Number of Group Records"); case FieldValue: return count; case FieldTextValue: return QString("%1").arg(count); case FieldFrameValue: { QByteArray fv; fv.resize(2); qToBigEndian(count, (uchar*) fv.data()); return fv; } default: break; } break; } case kGroupRecords: { switch(attrib) { case FieldName: return QString("Group List"); case FieldValue: { QVariantList grpRecords; for (int i = 0; i < data.group_records_size(); i++) { QVariantMap grpRec; OstProto::Gmp::GroupRecord rec = data.group_records(i); grpRec["groupRecordType"] = rec.type(); // grpRec["groupRecordAddress"] = subclass responsibility grpRec["overrideGroupRecordSourceCount"] = rec.is_override_source_count(); grpRec["groupRecordSourceCount"] = rec.source_count(); // grpRec["groupRecordSourceList"] = subclass responsibility grpRec["overrideAuxDataLength"] = rec.is_override_aux_data_length(); grpRec["auxDataLength"] = rec.aux_data_length(); grpRec["auxData"] = QByteArray().append( QString::fromStdString(rec.aux_data())); grpRecords.append(grpRec); } return grpRecords; } case FieldFrameValue: { QVariantList fv; for (int i = 0; i < data.group_records_size(); i++) { OstProto::Gmp::GroupRecord rec = data.group_records(i); QByteArray rv; quint16 srcCount; rv.resize(4); rv[0] = rec.type(); rv[1] = rec.is_override_aux_data_length() ? rec.aux_data_length() : rec.aux_data().size()/4; if (rec.is_override_source_count()) srcCount = rec.source_count(); else srcCount = rec.sources_size(); qToBigEndian(srcCount, (uchar*)(rv.data()+2)); // group_address => subclass responsibility // source list => subclass responsibility rv.append(QString().fromStdString(rec.aux_data())); fv.append(rv); } return fv; } case FieldTextValue: { QStringList list; for (int i = 0; i < data.group_records_size(); i++) { OstProto::Gmp::GroupRecord rec = data.group_records(i); QString str; str.append(" Type: "); switch(rec.type()) { case OstProto::Gmp::GroupRecord::kIsInclude: str.append("IS_INCLUDE"); break; case OstProto::Gmp::GroupRecord::kIsExclude: str.append("IS_EXCLUDE"); break; case OstProto::Gmp::GroupRecord::kToInclude: str.append("TO_INCLUDE"); break; case OstProto::Gmp::GroupRecord::kToExclude: str.append("TO_EXCLUDE"); break; case OstProto::Gmp::GroupRecord::kAllowNew: str.append("ALLOW_NEW"); break; case OstProto::Gmp::GroupRecord::kBlockOld: str.append("BLOCK_OLD"); break; default: str.append("UNKNOWN"); break; } str.append(QString("; AuxLen: %1").arg( rec.is_override_aux_data_length() ? rec.aux_data_length() : rec.aux_data().size()/4)); str.append(QString("; Source Count: %1").arg( rec.is_override_source_count() ? rec.source_count(): rec.sources_size())); // NOTE: subclass should replace the XXX below with // group address and source list str.append(QString("; XXX")); str.append(QString("; AuxData: ").append( QByteArray().append(QString().fromStdString( rec.aux_data())).toHex())); list.append(str); } return list; } default: break; } break; } // Meta Fields case kIsOverrideChecksum: { switch(attrib) { case FieldValue: return data.is_override_checksum(); default: break; } break; } case kGroupMode: { switch(attrib) { case FieldValue: return data.group_mode(); default: break; } break; } case kGroupCount: { switch(attrib) { case FieldValue: return data.group_count(); default: break; } break; } case kGroupPrefix: { switch(attrib) { case FieldValue: return data.group_prefix(); default: break; } break; } case kIsOverrideSourceCount: { switch(attrib) { case FieldValue: return data.is_override_source_count(); default: break; } break; } case kIsOverrideGroupRecordCount: { switch(attrib) { case FieldValue: return data.is_override_group_record_count(); default: break; } break; } default: qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, index); break; } return AbstractProtocol::fieldData(index, attrib, streamIndex); } bool GmpProtocol::setFieldData(int index, const QVariant &value, FieldAttrib attrib) { bool isOk = false; if (attrib != FieldValue) goto _exit; switch (index) { case kType: { uint type = value.toUInt(&isOk); if (isOk) data.set_type(type); break; } case kRsvdMrtCode: { uint val = value.toUInt(&isOk); if (isOk) data.set_rsvd_code(val); break; } case kChecksum: { uint csum = value.toUInt(&isOk); if (isOk) data.set_checksum(csum); break; } case kMldMrt: { uint mrt = value.toUInt(&isOk); if (isOk) data.set_max_response_time(mrt); break; } case kGroupAddress: // XXX: Handled by subclass isOk = false; break; case kRsvd1: isOk = false; break; case kSFlag: { bool flag = value.toBool(); data.set_s_flag(flag); isOk = true; break; } case kQrv: { uint qrv = value.toUInt(&isOk); if (isOk) data.set_qrv(qrv); break; } case kQqic: { uint qqi = value.toUInt(&isOk); if (isOk) data.set_qqi(qqi); break; } case kSourceCount: { uint count = value.toUInt(&isOk); if (isOk) data.set_source_count(count); break; } case kSources: // XXX: Handled by subclass isOk = false; break; case kRsvd2: isOk = false; break; case kGroupRecordCount: { uint count = value.toUInt(&isOk); if (isOk) data.set_group_record_count(count); break; } case kGroupRecords: { QVariantList list = value.toList(); data.clear_group_records(); for (int i = 0; i < list.count(); i++) { QVariantMap grpRec = list.at(i).toMap(); OstProto::Gmp::GroupRecord *rec = data.add_group_records(); rec->set_type(OstProto::Gmp::GroupRecord::RecordType( grpRec["groupRecordType"].toInt())); // NOTE: rec->group_address => subclass responsibility rec->set_is_override_source_count( grpRec["overrideGroupRecordSourceCount"].toBool()); rec->set_source_count(grpRec["groupRecordSourceCount"].toUInt()); // NOTE: rec->sources => subclass responsibility rec->set_is_override_aux_data_length( grpRec["overrideAuxDataLength"].toBool()); rec->set_aux_data_length(grpRec["auxDataLength"].toUInt()); QByteArray ba = grpRec["auxData"].toByteArray(); // pad to word boundary if (ba.size() % 4) ba.append(QByteArray(4 - (ba.size() % 4), char(0))); rec->set_aux_data(std::string(ba.constData(), ba.size())); } break; } // Meta Fields case kIsOverrideChecksum: { bool ovr = value.toBool(); data.set_is_override_checksum(ovr); isOk = true; break; } case kGroupMode: { uint mode = value.toUInt(&isOk); if (isOk && data.GroupMode_IsValid(mode)) data.set_group_mode((OstProto::Gmp::GroupMode)mode); break; } case kGroupCount: { uint count = value.toUInt(&isOk); if (isOk) data.set_group_count(count); break; } case kGroupPrefix: { uint prefix = value.toUInt(&isOk); if (isOk) data.set_group_prefix(prefix); break; } case kIsOverrideSourceCount: { bool ovr = value.toBool(); data.set_is_override_source_count(ovr); isOk = true; break; } case kIsOverrideGroupRecordCount: { bool ovr = value.toBool(); data.set_is_override_group_record_count(ovr); isOk = true; break; } default: qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, index); break; } _exit: return isOk; } int GmpProtocol::protocolFrameSize(int streamIndex) const { // TODO: Calculate to reduce processing cost return AbstractProtocol::protocolFrameValue(streamIndex, true).size(); } int GmpProtocol::protocolFrameVariableCount() const { int count = AbstractProtocol::protocolFrameVariableCount(); // No fields vary for Ssm Report if (isSsmReport()) return count; // For all other msg types, check the group mode if (fieldData(kGroupMode, FieldValue).toUInt() != uint(OstProto::Gmp::kFixed)) { count = AbstractProtocol::lcm(count, fieldData(kGroupCount, FieldValue).toUInt()); } return count; } ostinato-0.9/common/gmp.h000077500000000000000000000062211321315675200154570ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _GMP_H #define _GMP_H #include "abstractprotocol.h" #include "gmp.pb.h" #include /* Gmp Protocol Frame Format - TODO: for now see the respective RFCs */ class GmpProtocol : public AbstractProtocol { public: enum GmpField { // ------------ // Frame Fields // ------------ // Fields used in all ASM and SSM messages, unless otherwise specified kType = 0, kRsvdMrtCode, kChecksum, kMldMrt, // MLD Only (except MLDv2 Report) kMldRsvd, // MLD Only (except MLDv2 Report) // Field used in ASM messages kGroupAddress, FIELD_COUNT_ASM_ALL, // Fields used in SSM Query kRsvd1 = FIELD_COUNT_ASM_ALL, kSFlag, kQrv, kQqic, kSourceCount, kSources, FIELD_COUNT_SSM_QUERY, // Fields used in SSM Report kRsvd2 = FIELD_COUNT_SSM_QUERY, kGroupRecordCount, kGroupRecords, FIELD_COUNT_SSM_REPORT, FRAME_FIELD_COUNT = FIELD_COUNT_SSM_REPORT, // ----------- // Meta Fields // ----------- kIsOverrideChecksum = FRAME_FIELD_COUNT, kGroupMode, kGroupCount, kGroupPrefix, kIsOverrideSourceCount, kIsOverrideGroupRecordCount, FIELD_COUNT }; GmpProtocol(StreamBase *stream, AbstractProtocol *parent = 0); virtual ~GmpProtocol(); virtual ProtocolIdType protocolIdType() const; virtual int fieldCount() const; virtual int frameFieldCount() const; virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; virtual QVariant fieldData(int index, FieldAttrib attrib, int streamIndex = 0) const; virtual bool setFieldData(int index, const QVariant &value, FieldAttrib attrib = FieldValue); virtual int protocolFrameSize(int streamIndex = 0) const; virtual int protocolFrameVariableCount() const; protected: OstProto::Gmp data; int msgType() const; virtual bool isSsmReport() const = 0; virtual bool isQuery() const = 0; virtual bool isSsmQuery() const = 0; int qqic(int value) const; virtual quint16 checksum(int streamIndex) const = 0; private: static QHash frameFieldCountMap; }; inline int GmpProtocol::msgType() const { return fieldData(kType, FieldValue).toInt(); } inline int GmpProtocol::qqic(int value) const { return quint8(value); // TODO: if value > 128 convert to mantissa/exp form } #endif ostinato-0.9/common/gmp.proto000077500000000000000000000055051321315675200163770ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ import "protocol.proto"; package OstProto; /// Group Management Protocol (i.e. IGMP and MLD) message Gmp { // // Common fields for both ASM and SSM messages // optional uint32 type = 1; optional bool is_override_rsvd_code = 2; optional uint32 rsvd_code = 3; // MaxRespTime is in milliseconds - MaxRespCode will be derived optional uint32 max_response_time = 4 [default = 100]; optional bool is_override_checksum = 5; optional uint32 checksum = 6; message IpAddress { optional fixed32 v4 = 1; optional fixed64 v6_hi = 2; optional fixed64 v6_lo = 3; } // // Fields used in ASM messages // enum GroupMode { kFixed = 0; kIncrementGroup = 1; kDecrementGroup = 2; kRandomGroup = 3; } optional IpAddress group_address = 10; optional GroupMode group_mode = 11 [default = kFixed]; optional uint32 group_count = 12 [default = 16]; optional uint32 group_prefix = 13 [default = 24]; // // Fields used in SSM Query // optional bool s_flag = 20; optional uint32 qrv = 21 [default = 2]; // QuerierQueryInterval is in seconds - QQIC will be derived optional uint32 qqi = 22 [default = 125]; repeated IpAddress sources = 23; optional bool is_override_source_count = 24; optional uint32 source_count = 25; // // Fields used in SSM Reports // message GroupRecord { enum RecordType { kReserved = 0; kIsInclude = 1; kIsExclude = 2; kToInclude = 3; kToExclude = 4; kAllowNew = 5; kBlockOld = 6; } optional RecordType type = 1 [default = kIsInclude]; optional IpAddress group_address = 2; repeated IpAddress sources = 3; optional bool is_override_source_count = 4; optional uint32 source_count = 5; optional bytes aux_data = 6; optional bool is_override_aux_data_length = 7; optional uint32 aux_data_length = 8; } repeated GroupRecord group_records = 30; optional bool is_override_group_record_count = 31; optional uint32 group_record_count = 32; } ostinato-0.9/common/gmp.ui000077500000000000000000000623551321315675200156570ustar00rootroot00000000000000 Gmp 0 0 509 355 Form Message Type msgTypeCombo Max Response Time (1/10s) maxResponseTime 0 0 Checksum false 0 0 >HHHH; Group Address groupAddress Mode msgTypeCombo Count msgTypeCombo Prefix msgTypeCombo 1 0 Fixed Increment Host Decrement Host Random Host false 0 0 false 0 0 /900; 1 0 0 0 0 S Flag (Suppress Router Processing) QRV qrv Qt::Horizontal 40 20 QQI qqi Qt::Vertical 61 41 Source List groupRecordAddress Qt::Horizontal 16 20 + – true QAbstractItemView::InternalMove Qt::Horizontal 40 20 Count false Qt::Vertical 20 40 0 0 0 0 Group Records groupRecordAddress Qt::Horizontal 16 20 + – true QAbstractItemView::InternalMove Number of Groups false false 0 0 0 0 Record Type groupRecordType Reserved Is Include Is Exclude To Include To Exclude Allow New Block Old Group Address groupRecordAddress Source List groupRecordAddress Qt::Horizontal 191 20 + – true QAbstractItemView::InternalMove Number of Sources false 0 0 Qt::Horizontal 81 20 Aux Data Qt::Horizontal 40 20 Length (x4) false 0 0 Qt::Vertical 101 21 IntComboBox QComboBox
intcombobox.h
msgTypeCombo maxResponseTime overrideChecksum checksum groupAddress groupMode groupCount groupPrefix overrideGroupRecordCount groupRecordCount groupRecordType groupRecordAddress overrideGroupRecordSourceCount groupRecordSourceCount overrideAuxDataLength auxDataLength auxData sFlag qrv qqi overrideSourceCount sourceCount overrideChecksum toggled(bool) checksum setEnabled(bool) 391 43 448 45 overrideGroupRecordSourceCount toggled(bool) groupRecordSourceCount setEnabled(bool) 402 202 436 204 overrideAuxDataLength toggled(bool) auxDataLength setEnabled(bool) 416 286 433 286 overrideGroupRecordCount toggled(bool) groupRecordCount setEnabled(bool) 112 309 138 312 overrideSourceCount toggled(bool) sourceCount setEnabled(bool) 413 154 434 151
ostinato-0.9/common/gmpconfig.cpp000066400000000000000000000302741321315675200172020ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "gmpconfig.h" #include "gmp.h" GmpConfigForm::GmpConfigForm(QWidget *parent) : AbstractProtocolConfigForm(parent) { setupUi(this); auxData->setValidator(new QRegExpValidator( QRegExp("[0-9A-Fa-f]*"), this)); } GmpConfigForm::~GmpConfigForm() { } void GmpConfigForm::loadWidget(AbstractProtocol *proto) { msgTypeCombo->setValue( proto->fieldData( GmpProtocol::kType, AbstractProtocol::FieldValue ).toUInt()); // XXX: maxResponseTime set by subclass overrideChecksum->setChecked( proto->fieldData( GmpProtocol::kIsOverrideChecksum, AbstractProtocol::FieldValue ).toBool()); checksum->setText(uintToHexStr( proto->fieldData( GmpProtocol::kChecksum, AbstractProtocol::FieldValue ).toUInt(), 2)); groupAddress->setText( proto->fieldData( GmpProtocol::kGroupAddress, AbstractProtocol::FieldValue ).toString()); groupMode->setCurrentIndex( proto->fieldData( GmpProtocol::kGroupMode, AbstractProtocol::FieldValue ).toUInt()); groupCount->setText( proto->fieldData( GmpProtocol::kGroupCount, AbstractProtocol::FieldValue ).toString()); groupPrefix->setText( proto->fieldData( GmpProtocol::kGroupPrefix, AbstractProtocol::FieldValue ).toString()); sFlag->setChecked( proto->fieldData( GmpProtocol::kSFlag, AbstractProtocol::FieldValue ).toBool()); qrv->setText( proto->fieldData( GmpProtocol::kQrv, AbstractProtocol::FieldValue ).toString()); qqi->setText( proto->fieldData( GmpProtocol::kQqic, AbstractProtocol::FieldValue ).toString()); QStringList sl = proto->fieldData( GmpProtocol::kSources, AbstractProtocol::FieldValue ).toStringList(); sourceList->clear(); foreach(QString src, sl) { QListWidgetItem *item = new QListWidgetItem(src); item->setFlags(item->flags() | Qt::ItemIsEditable); sourceList->addItem(item); } // NOTE: SourceCount should be loaded after sourceList overrideSourceCount->setChecked( proto->fieldData( GmpProtocol::kIsOverrideSourceCount, AbstractProtocol::FieldValue ).toBool()); sourceCount->setText( proto->fieldData( GmpProtocol::kSourceCount, AbstractProtocol::FieldValue ).toString()); QVariantList list = proto->fieldData( GmpProtocol::kGroupRecords, AbstractProtocol::FieldValue ).toList(); groupList->clear(); foreach (QVariant rec, list) { QVariantMap grpRec = rec.toMap(); QListWidgetItem *item = new QListWidgetItem; item->setData(Qt::UserRole, grpRec); item->setText(QString("%1: %2") .arg(groupRecordType->itemText( grpRec["groupRecordType"].toInt())) .arg(grpRec["groupRecordAddress"].toString())); groupList->addItem(item); } // NOTE: recordCount should be loaded after recordList overrideGroupRecordCount->setChecked( proto->fieldData( GmpProtocol::kIsOverrideGroupRecordCount, AbstractProtocol::FieldValue ).toBool()); groupRecordCount->setText( proto->fieldData( GmpProtocol::kGroupRecordCount, AbstractProtocol::FieldValue ).toString()); } void GmpConfigForm::storeWidget(AbstractProtocol *proto) { update(); proto->setFieldData( GmpProtocol::kType, msgTypeCombo->currentValue()); // XXX: maxResponseTime handled by subclass proto->setFieldData( GmpProtocol::kIsOverrideChecksum, overrideChecksum->isChecked()); proto->setFieldData( GmpProtocol::kChecksum, hexStrToUInt(checksum->text())); proto->setFieldData( GmpProtocol::kGroupAddress, groupAddress->text()); proto->setFieldData( GmpProtocol::kGroupMode, groupMode->currentIndex()); proto->setFieldData( GmpProtocol::kGroupCount, groupCount->text()); proto->setFieldData( GmpProtocol::kGroupPrefix, groupPrefix->text().remove('/')); proto->setFieldData( GmpProtocol::kSFlag, sFlag->isChecked()); proto->setFieldData( GmpProtocol::kQrv, qrv->text()); proto->setFieldData( GmpProtocol::kQqic, qqi->text()); QStringList list; for (int i = 0; i < sourceList->count(); i++) list.append(sourceList->item(i)->text()); proto->setFieldData( GmpProtocol::kSources, list); // sourceCount should be AFTER sources proto->setFieldData( GmpProtocol::kIsOverrideSourceCount, overrideSourceCount->isChecked()); proto->setFieldData( GmpProtocol::kSourceCount, sourceCount->text()); QVariantList grpList; for (int i = 0; i < groupList->count(); i++) { QVariant grp = groupList->item(i)->data(Qt::UserRole); grpList.append(grp.toMap()); } proto->setFieldData(GmpProtocol::kGroupRecords, grpList); // groupRecordCount should be AFTER groupRecords proto->setFieldData( GmpProtocol::kIsOverrideGroupRecordCount, overrideGroupRecordCount->isChecked()); proto->setFieldData( GmpProtocol::kGroupRecordCount, groupRecordCount->text()); } void GmpConfigForm::update() { // save the current group Record by simulating a currentItemChanged() on_groupList_currentItemChanged(groupList->currentItem(), groupList->currentItem()); } // // -- private slots // void GmpConfigForm::on_groupMode_currentIndexChanged(int index) { bool disabled = (index == 0); groupCount->setDisabled(disabled); groupPrefix->setDisabled(disabled); } void GmpConfigForm::on_addSource_clicked() { QListWidgetItem *item=new QListWidgetItem(_defaultSourceIp); item->setFlags(item->flags() | Qt::ItemIsEditable); sourceList->insertItem(sourceList->currentRow(), item); if (!overrideSourceCount->isChecked()) sourceCount->setText(QString().setNum(sourceList->count())); } void GmpConfigForm::on_deleteSource_clicked() { delete sourceList->takeItem(sourceList->currentRow()); if (!overrideSourceCount->isChecked()) sourceCount->setText(QString().setNum(sourceList->count())); } void GmpConfigForm::on_addGroupRecord_clicked() { OstProto::Gmp::GroupRecord defRec; QVariantMap grpRec; QListWidgetItem *item = new QListWidgetItem; grpRec["groupRecordType"] = defRec.type(); grpRec["groupRecordAddress"] = _defaultGroupIp; grpRec["overrideGroupRecordSourceCount"] =defRec.is_override_source_count(); grpRec["groupRecordSourceCount"] = defRec.source_count(); grpRec["groupRecordSourceList"] = QStringList(); grpRec["overrideAuxDataLength"] = defRec.is_override_aux_data_length(); grpRec["auxDataLength"] = defRec.aux_data_length(); grpRec["auxData"] = QByteArray().append( QString().fromStdString(defRec.aux_data())); item->setData(Qt::UserRole, grpRec); item->setText(QString("%1: %2") .arg(groupRecordType->itemText(grpRec["groupRecordType"].toInt())) .arg(grpRec["groupRecordAddress"].toString())); groupList->insertItem(groupList->currentRow(), item); if (!overrideGroupRecordCount->isChecked()) groupRecordCount->setText(QString().setNum(groupList->count())); } void GmpConfigForm::on_deleteGroupRecord_clicked() { delete groupList->takeItem(groupList->currentRow()); if (!overrideGroupRecordCount->isChecked()) groupRecordCount->setText(QString().setNum(groupList->count())); } void GmpConfigForm::on_groupList_currentItemChanged(QListWidgetItem *current, QListWidgetItem *previous) { QVariantMap rec; QStringList strList; qDebug("in %s", __FUNCTION__); // save previous record ... if (previous == NULL) goto _load_current_record; rec["groupRecordType"] = groupRecordType->currentIndex(); rec["groupRecordAddress"] = groupRecordAddress->text(); strList.clear(); while (groupRecordSourceList->count()) { QListWidgetItem *item = groupRecordSourceList->takeItem(0); strList.append(item->text()); delete item; } rec["groupRecordSourceList"] = strList; rec["overrideGroupRecordSourceCount"] = overrideGroupRecordSourceCount->isChecked(); rec["groupRecordSourceCount"] = groupRecordSourceCount->text().toUInt(); rec["overrideAuxDataLength"] = overrideAuxDataLength->isChecked(); rec["auxDataLength"] = auxDataLength->text().toUInt(); rec["auxData"] = QByteArray().fromHex(QByteArray().append(auxData->text())); previous->setData(Qt::UserRole, rec); previous->setText(QString("%1: %2") .arg(groupRecordType->itemText(rec["groupRecordType"].toInt())) .arg(rec["groupRecordAddress"].toString())); _load_current_record: // ... and load current record if (current == NULL) goto _exit; rec = current->data(Qt::UserRole).toMap(); groupRecordType->setCurrentIndex(rec["groupRecordType"].toInt()); groupRecordAddress->setText(rec["groupRecordAddress"].toString()); strList = rec["groupRecordSourceList"].toStringList(); groupRecordSourceList->clear(); foreach (QString str, strList) { QListWidgetItem *item = new QListWidgetItem(str, groupRecordSourceList); item->setFlags(item->flags() | Qt::ItemIsEditable); } overrideGroupRecordSourceCount->setChecked( rec["overrideGroupRecordSourceCount"].toBool()); groupRecordSourceCount->setText(QString().setNum( rec["groupRecordSourceCount"].toUInt())); overrideAuxDataLength->setChecked(rec["overrideAuxDataLength"].toBool()); auxDataLength->setText(QString().setNum(rec["auxDataLength"].toUInt())); auxData->setText(QString(rec["auxData"].toByteArray().toHex())); _exit: groupRecord->setEnabled(current != NULL); return; } void GmpConfigForm::on_addGroupRecordSource_clicked() { QListWidgetItem *item=new QListWidgetItem(_defaultSourceIp); item->setFlags(item->flags() | Qt::ItemIsEditable); groupRecordSourceList->insertItem(groupRecordSourceList->currentRow(),item); if (!overrideGroupRecordSourceCount->isChecked()) groupRecordSourceCount->setText(QString().setNum( groupRecordSourceList->count())); } void GmpConfigForm::on_deleteGroupRecordSource_clicked() { delete groupRecordSourceList->takeItem(groupRecordSourceList->currentRow()); if (!overrideGroupRecordSourceCount->isChecked()) groupRecordSourceCount->setText(QString().setNum( groupRecordSourceList->count())); } void GmpConfigForm::on_auxData_textChanged(const QString &text) { // auxDataLength is in units of words and each byte is 2 chars in text() if (!overrideAuxDataLength->isChecked()) auxDataLength->setText(QString().setNum((text.size()+7)/8)); } ostinato-0.9/common/gmpconfig.h000066400000000000000000000032551321315675200166460ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _GMP_CONFIG_H #define _GMP_CONFIG_H #include "abstractprotocolconfig.h" #include "ui_gmp.h" class GmpConfigForm : public AbstractProtocolConfigForm, protected Ui::Gmp { Q_OBJECT public: GmpConfigForm(QWidget *parent = 0); ~GmpConfigForm(); virtual void loadWidget(AbstractProtocol *proto); virtual void storeWidget(AbstractProtocol *proto); protected: QString _defaultGroupIp; QString _defaultSourceIp; enum { kSsmQueryPage = 0, kSsmReportPage = 1 }; private: void update(); private slots: void on_groupMode_currentIndexChanged(int index); void on_addSource_clicked(); void on_deleteSource_clicked(); void on_addGroupRecord_clicked(); void on_deleteGroupRecord_clicked(); void on_groupList_currentItemChanged(QListWidgetItem *current, QListWidgetItem *previous); void on_addGroupRecordSource_clicked(); void on_deleteGroupRecordSource_clicked(); void on_auxData_textChanged(const QString &text); }; #endif ostinato-0.9/common/hexdump.cpp000066400000000000000000000123271321315675200167020ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "hexdump.h" #include "streambase.h" HexDumpProtocol::HexDumpProtocol(StreamBase *stream, AbstractProtocol *parent) : AbstractProtocol(stream, parent) { } HexDumpProtocol::~HexDumpProtocol() { } AbstractProtocol* HexDumpProtocol::createInstance(StreamBase *stream, AbstractProtocol *parent) { return new HexDumpProtocol(stream, parent); } quint32 HexDumpProtocol::protocolNumber() const { return OstProto::Protocol::kHexDumpFieldNumber; } void HexDumpProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const { protocol.MutableExtension(OstProto::hexDump)->CopyFrom(data); protocol.mutable_protocol_id()->set_id(protocolNumber()); } void HexDumpProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) { if (protocol.protocol_id().id() == protocolNumber() && protocol.HasExtension(OstProto::hexDump)) data.MergeFrom(protocol.GetExtension(OstProto::hexDump)); } QString HexDumpProtocol::name() const { return QString("HexDump"); } QString HexDumpProtocol::shortName() const { return QString("HexDump"); } int HexDumpProtocol::fieldCount() const { return hexDump_fieldCount; } AbstractProtocol::FieldFlags HexDumpProtocol::fieldFlags(int index) const { AbstractProtocol::FieldFlags flags; flags = AbstractProtocol::fieldFlags(index); switch (index) { case hexDump_content: flags |= FrameField; break; case hexDump_pad_until_end: flags &= ~FrameField; flags |= MetaField; break; default: qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, index); break; } return flags; } QVariant HexDumpProtocol::fieldData(int index, FieldAttrib attrib, int streamIndex) const { switch (index) { case hexDump_content: { QByteArray ba; QByteArray pad; switch(attrib) { case FieldValue: case FieldTextValue: case FieldFrameValue: ba.append(QString().fromStdString(data.content())); if (data.pad_until_end()) { pad = QByteArray( protocolFrameSize(streamIndex) - ba.size(), '\0'); } break; default: break; } switch(attrib) { case FieldName: return QString("Content"); case FieldValue: return ba; case FieldTextValue: return ba.append(pad).toHex(); case FieldFrameValue: return ba.append(pad); default: break; } break; } // Meta fields case hexDump_pad_until_end: { switch(attrib) { case FieldValue: return data.pad_until_end(); default: break; } break; } default: qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, index); break; } return AbstractProtocol::fieldData(index, attrib, streamIndex); } bool HexDumpProtocol::setFieldData(int index, const QVariant &value, FieldAttrib attrib) { bool isOk = false; if (attrib != FieldValue) goto _exit; switch (index) { case hexDump_content: { QByteArray ba = value.toByteArray(); data.set_content(ba.constData(), ba.size()); isOk = true; break; } case hexDump_pad_until_end: { bool pad = value.toBool(); data.set_pad_until_end(pad); isOk = true; break; } default: qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, index); break; } _exit: return isOk; } int HexDumpProtocol::protocolFrameSize(int streamIndex) const { int len = data.content().size(); if (data.pad_until_end()) { int pad = mpStream->frameLen(streamIndex) - (protocolFrameOffset(streamIndex) + len + protocolFramePayloadSize(streamIndex) + kFcsSize); if (pad < 0) pad = 0; len += pad; } return len; } ostinato-0.9/common/hexdump.h000066400000000000000000000040011321315675200163350ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _HEXDUMP_H #define _HEXDUMP_H #include "abstractprotocol.h" #include "hexdump.pb.h" /* HexDump Protocol Frame Format - +---------+---------+ | User | Zero | | HexDump | Padding | +---------+---------+ */ class HexDumpProtocol : public AbstractProtocol { public: enum hexDumpfield { // Frame Fields hexDump_content = 0, // Meta Fields hexDump_pad_until_end, hexDump_fieldCount }; HexDumpProtocol(StreamBase *stream, AbstractProtocol *parent = 0); virtual ~HexDumpProtocol(); static AbstractProtocol* createInstance(StreamBase *stream, AbstractProtocol *parent = 0); virtual quint32 protocolNumber() const; virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); virtual QString name() const; virtual QString shortName() const; virtual int fieldCount() const; virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; virtual QVariant fieldData(int index, FieldAttrib attrib, int streamIndex = 0) const; virtual bool setFieldData(int index, const QVariant &value, FieldAttrib attrib = FieldValue); virtual int protocolFrameSize(int streamIndex = 0) const; private: OstProto::HexDump data; }; #endif ostinato-0.9/common/hexdump.proto000066400000000000000000000016061321315675200172610ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ import "protocol.proto"; package OstProto; // HexDump Protocol message HexDump { optional bytes content = 1; optional bool pad_until_end = 2 [default = true]; } extend Protocol { optional HexDump hexDump = 104; } ostinato-0.9/common/hexdump.ui000066400000000000000000000033761321315675200165410ustar00rootroot00000000000000 HexDump 0 0 511 190 Form Pad until end of packet Qt::Horizontal 281 20 50 0 QFrame::Panel QFrame::Sunken 1 Qt::AlignCenter QHexEdit QWidget
qhexedit.h
1
ostinato-0.9/common/hexdumpconfig.cpp000066400000000000000000000037071321315675200200720ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "hexdumpconfig.h" #include "hexdump.h" HexDumpConfigForm::HexDumpConfigForm(QWidget *parent) : AbstractProtocolConfigForm(parent) { setupUi(this); hexEdit->setFont(QFont("Courier")); hexEdit->setOverwriteMode(false); } HexDumpConfigForm::~HexDumpConfigForm() { } HexDumpConfigForm* HexDumpConfigForm::createInstance() { return new HexDumpConfigForm; } void HexDumpConfigForm::loadWidget(AbstractProtocol *proto) { hexEdit->setData( proto->fieldData( HexDumpProtocol::hexDump_content, AbstractProtocol::FieldValue ).toByteArray()); padUntilEnd->setChecked( proto->fieldData( HexDumpProtocol::hexDump_pad_until_end, AbstractProtocol::FieldValue ).toBool()); } void HexDumpConfigForm::storeWidget(AbstractProtocol *proto) { proto->setFieldData( HexDumpProtocol::hexDump_content, hexEdit->data()); proto->setFieldData( HexDumpProtocol::hexDump_pad_until_end, padUntilEnd->isChecked()); } // // ------------ private slots // void HexDumpConfigForm::on_hexEdit_overwriteModeChanged(bool isOverwriteMode) { if (isOverwriteMode) mode->setText(tr("Ovr")); else mode->setText(tr("Ins")); } ostinato-0.9/common/hexdumpconfig.h000066400000000000000000000023171321315675200175330ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _HEX_DUMP_CONFIG_H #define _HEX_DUMP_CONFIG_H #include "abstractprotocolconfig.h" #include "ui_hexdump.h" class HexDumpConfigForm : public AbstractProtocolConfigForm, private Ui::HexDump { Q_OBJECT public: HexDumpConfigForm(QWidget *parent = 0); virtual ~HexDumpConfigForm(); static HexDumpConfigForm* createInstance(); virtual void loadWidget(AbstractProtocol *proto); virtual void storeWidget(AbstractProtocol *proto); private slots: void on_hexEdit_overwriteModeChanged(bool isOverwriteMode); }; #endif ostinato-0.9/common/icmp.cpp000066400000000000000000000245441321315675200161640ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "icmp.h" #include "icmphelper.h" IcmpProtocol::IcmpProtocol(StreamBase *stream, AbstractProtocol *parent) : AbstractProtocol(stream, parent) { } IcmpProtocol::~IcmpProtocol() { // field count may change based on msgType - so don't cache field offsets _cacheFlags &= ~FieldFrameBitOffsetCache; } AbstractProtocol* IcmpProtocol::createInstance(StreamBase *stream, AbstractProtocol *parent) { return new IcmpProtocol(stream, parent); } quint32 IcmpProtocol::protocolNumber() const { return OstProto::Protocol::kIcmpFieldNumber; } void IcmpProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const { protocol.MutableExtension(OstProto::icmp)->CopyFrom(data); protocol.mutable_protocol_id()->set_id(protocolNumber()); } void IcmpProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) { if (protocol.protocol_id().id() == protocolNumber() && protocol.HasExtension(OstProto::icmp)) data.MergeFrom(protocol.GetExtension(OstProto::icmp)); } QString IcmpProtocol::name() const { return QString("Internet Control Message Protocol"); } QString IcmpProtocol::shortName() const { return QString("ICMP"); } quint32 IcmpProtocol::protocolId(ProtocolIdType type) const { switch(type) { case ProtocolIdIp: switch(icmpVersion()) { case OstProto::Icmp::kIcmp4: return 0x1; case OstProto::Icmp::kIcmp6: return 0x3A; default:break; } default:break; } return AbstractProtocol::protocolId(type); } int IcmpProtocol::fieldCount() const { return icmp_fieldCount; } int IcmpProtocol::frameFieldCount() const { int count; if (isIdSeqType(icmpVersion(), icmpType())) count = icmp_idSeqFrameFieldCount; else count = icmp_commonFrameFieldCount; return count; } AbstractProtocol::FieldFlags IcmpProtocol::fieldFlags(int index) const { AbstractProtocol::FieldFlags flags; flags = AbstractProtocol::fieldFlags(index); switch (index) { case icmp_type: case icmp_code: break; case icmp_checksum: flags |= CksumField; break; case icmp_identifier: case icmp_sequence: if (!isIdSeqType(icmpVersion(), icmpType())) flags &= ~FrameField; break; case icmp_version: case icmp_is_override_checksum: flags &= ~FrameField; flags |= MetaField; break; default: qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, index); break; } return flags; } QVariant IcmpProtocol::fieldData(int index, FieldAttrib attrib, int streamIndex) const { switch (index) { case icmp_type: { unsigned char type = data.type() & 0xFF; switch(attrib) { case FieldName: return QString("Type"); case FieldValue: return type; case FieldTextValue: return QString("%1").arg((uint) type); case FieldFrameValue: return QByteArray(1, type); default: break; } break; } case icmp_code: { unsigned char code = data.code() & 0xFF; switch(attrib) { case FieldName: return QString("Code"); case FieldValue: return code; case FieldTextValue: return QString("%1").arg((uint)code); case FieldFrameValue: return QByteArray(1, code); default: break; } break; } case icmp_checksum: { quint16 cksum; switch(attrib) { case FieldValue: case FieldFrameValue: case FieldTextValue: if (data.is_override_checksum()) { cksum = data.checksum(); } else { quint16 cks; quint32 sum = 0; cks = protocolFrameCksum(streamIndex, CksumIp); sum += (quint16) ~cks; cks = protocolFramePayloadCksum(streamIndex, CksumIp); sum += (quint16) ~cks; if (icmpVersion() == OstProto::Icmp::kIcmp6) { cks = protocolFrameHeaderCksum(streamIndex, CksumIpPseudo); sum += (quint16) ~cks; } while(sum>>16) sum = (sum & 0xFFFF) + (sum >> 16); cksum = (~sum) & 0xFFFF; } break; default: cksum = 0; // avoid the 'maybe used unitialized' warning break; } switch(attrib) { case FieldName: return QString("Checksum"); case FieldValue: return cksum; case FieldFrameValue: { QByteArray fv; fv.resize(2); qToBigEndian(cksum, (uchar*) fv.data()); return fv; } case FieldTextValue: return QString("0x%1").arg( cksum, 4, BASE_HEX, QChar('0'));; case FieldBitSize: return 16; default: break; } break; } case icmp_identifier: { switch(attrib) { case FieldName: return QString("Identifier"); case FieldValue: return data.identifier(); case FieldTextValue: return QString("%1").arg(data.identifier()); case FieldFrameValue: { QByteArray fv; fv.resize(2); qToBigEndian((quint16) data.identifier(), (uchar*) fv.data()); return fv; } default: break; } break; } case icmp_sequence: { switch(attrib) { case FieldName: return QString("Sequence"); case FieldValue: return data.sequence(); case FieldTextValue: return QString("%1").arg(data.sequence()); case FieldFrameValue: { QByteArray fv; fv.resize(2); qToBigEndian((quint16) data.sequence(), (uchar*) fv.data()); return fv; } default: break; } break; } // Meta fields case icmp_version: { switch(attrib) { case FieldValue: return data.icmp_version(); default: break; } break; } case icmp_is_override_checksum: { switch(attrib) { case FieldValue: return data.is_override_checksum(); default: break; } break; } default: qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, index); break; } return AbstractProtocol::fieldData(index, attrib, streamIndex); } bool IcmpProtocol::setFieldData(int index, const QVariant &value, FieldAttrib attrib) { bool isOk = false; if (attrib != FieldValue) goto _exit; switch (index) { case icmp_type: { uint type = value.toUInt(&isOk); if (isOk) data.set_type(type & 0xFF); break; } case icmp_code: { uint code = value.toUInt(&isOk); if (isOk) data.set_code(code & 0xFF); break; } case icmp_checksum: { uint csum = value.toUInt(&isOk); if (isOk) data.set_checksum(csum); break; } case icmp_identifier: { uint id = value.toUInt(&isOk); if (isOk) data.set_identifier(id); break; } case icmp_sequence: { uint seq = value.toUInt(&isOk); if (isOk) data.set_sequence(seq); break; } case icmp_version: { int ver = value.toUInt(&isOk); if (isOk) data.set_icmp_version(OstProto::Icmp::Version(ver)); break; } case icmp_is_override_checksum: { bool ovr = value.toBool(); data.set_is_override_checksum(ovr); isOk = true; break; } default: qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, index); break; } _exit: return isOk; } ostinato-0.9/common/icmp.h000066400000000000000000000052371321315675200156270ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _ICMP_H #define _ICMP_H #include "abstractprotocol.h" #include "icmp.pb.h" /* Icmp Protocol Frame Format - +-----+------+------+------+-------+ | TYP | CODE | CSUM | [ID] | [SEQ] | | (1) | (1) | (2) | (2) | (2) | +-----+------+------+------+-------+ Fields within [] are applicable only to certain TYPEs Figures in braces represent field width in bytes */ class IcmpProtocol : public AbstractProtocol { public: enum icmpfield { // Frame Fields icmp_type = 0, icmp_code, icmp_checksum, icmp_commonFrameFieldCount, icmp_identifier = icmp_commonFrameFieldCount, icmp_sequence, icmp_idSeqFrameFieldCount, // Meta Fields icmp_is_override_checksum = icmp_idSeqFrameFieldCount, icmp_version, icmp_fieldCount }; IcmpProtocol(StreamBase *stream, AbstractProtocol *parent = 0); virtual ~IcmpProtocol(); static AbstractProtocol* createInstance(StreamBase *stream, AbstractProtocol *parent = 0); virtual quint32 protocolNumber() const; virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); virtual quint32 protocolId(ProtocolIdType type) const; virtual QString name() const; virtual QString shortName() const; virtual int fieldCount() const; virtual int frameFieldCount() const; virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; virtual QVariant fieldData(int index, FieldAttrib attrib, int streamIndex = 0) const; virtual bool setFieldData(int index, const QVariant &value, FieldAttrib attrib = FieldValue); private: OstProto::Icmp data; OstProto::Icmp::Version icmpVersion() const { return OstProto::Icmp::Version( fieldData(icmp_version, FieldValue).toUInt()); } int icmpType() const { return fieldData(icmp_type, FieldValue).toInt(); } }; #endif ostinato-0.9/common/icmp.proto000066400000000000000000000022461321315675200165400ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ import "protocol.proto"; package OstProto; // Icmp Protocol message Icmp { enum Version { kIcmp4 = 4; kIcmp6 = 6; } optional Version icmp_version = 1 [default = kIcmp4]; optional bool is_override_checksum = 2; optional uint32 type = 6 [default = 0x8]; // echo request optional uint32 code = 7; optional uint32 checksum = 8; optional uint32 identifier = 9 [default = 1234]; optional uint32 sequence = 10; } extend Protocol { optional Icmp icmp = 402; } ostinato-0.9/common/icmp.ui000066400000000000000000000103741321315675200160130ustar00rootroot00000000000000 Icmp 0 0 373 166 Form Version ICMPv4 ICMPv6 Type typeCombo Code codeEdit Qt::Horizontal 31 20 Checksum false Identifier idEdit Sequence seqEdit Qt::Vertical 211 71 IntComboBox QComboBox
intcombobox.h
icmp4Button icmp6Button typeCombo codeEdit overrideCksum cksumEdit idEdit seqEdit overrideCksum toggled(bool) cksumEdit setEnabled(bool) 33 70 96 71
ostinato-0.9/common/icmp6pdml.cpp000066400000000000000000000055041321315675200171220ustar00rootroot00000000000000/* Copyright (C) 2011 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "icmp6pdml.h" #include "icmp.pb.h" #include "sample.pb.h" PdmlIcmp6Protocol::PdmlIcmp6Protocol() { ostProtoId_ = OstProto::Protocol::kSampleFieldNumber; proto_ = NULL; } PdmlProtocol* PdmlIcmp6Protocol::createInstance() { return new PdmlIcmp6Protocol(); } void PdmlIcmp6Protocol::preProtocolHandler(QString name, const QXmlStreamAttributes &attributes, int expectedPos, OstProto::Protocol *pbProto, OstProto::Stream *stream) { proto_ = NULL; ostProtoId_ = OstProto::Protocol::kSampleFieldNumber; icmp_.preProtocolHandler(name, attributes, expectedPos, pbProto, stream); mld_.preProtocolHandler(name, attributes, expectedPos, pbProto, stream); } void PdmlIcmp6Protocol::postProtocolHandler(OstProto::Protocol *pbProto, OstProto::Stream *stream) { if (proto_) proto_->postProtocolHandler(pbProto, stream); else stream->mutable_protocol()->RemoveLast(); proto_ = NULL; ostProtoId_ = OstProto::Protocol::kSampleFieldNumber; } void PdmlIcmp6Protocol::unknownFieldHandler(QString name, int pos, int size, const QXmlStreamAttributes &attributes, OstProto::Protocol *pbProto, OstProto::Stream *stream) { if (proto_) { proto_->unknownFieldHandler(name, pos, size, attributes, pbProto, stream); } else if (name == "icmpv6.type") { bool isOk; uint type = attributes.value("value").toString().toUInt( &isOk, kBaseHex); if (((type >= 130) && (type <= 132)) || (type == 143)) { // MLD proto_ = &mld_; fieldMap_ = mld_.fieldMap_; ostProtoId_ = OstProto::Protocol::kMldFieldNumber; } else { // ICMP proto_ = &icmp_; fieldMap_ = icmp_.fieldMap_; ostProtoId_ = OstProto::Protocol::kIcmpFieldNumber; } pbProto->mutable_protocol_id()->set_id(ostProtoId_); pbProto->MutableExtension(OstProto::sample)->Clear(); fieldHandler(name, attributes, pbProto, stream); } else { qDebug("unexpected field %s", name.toAscii().constData()); } } ostinato-0.9/common/icmp6pdml.h000066400000000000000000000027521321315675200165710ustar00rootroot00000000000000/* Copyright (C) 2011 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _ICMP6_PDML_H #define _ICMP6_PDML_H #include "pdmlprotocol.h" #include "icmppdml.h" #include "mldpdml.h" class PdmlIcmp6Protocol : public PdmlProtocol { public: static PdmlProtocol* createInstance(); virtual void preProtocolHandler(QString name, const QXmlStreamAttributes &attributes, int expectedPos, OstProto::Protocol *pbProto, OstProto::Stream *stream); virtual void postProtocolHandler(OstProto::Protocol *pbProto, OstProto::Stream *stream); virtual void unknownFieldHandler(QString name, int pos, int size, const QXmlStreamAttributes &attributes, OstProto::Protocol *pbProto, OstProto::Stream *stream); protected: PdmlIcmp6Protocol(); private: PdmlIcmpProtocol icmp_; PdmlMldProtocol mld_; PdmlProtocol *proto_; }; #endif ostinato-0.9/common/icmpconfig.cpp000066400000000000000000000142051321315675200173430ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "icmpconfig.h" #include "icmp.h" #include "icmphelper.h" #include IcmpConfigForm::IcmpConfigForm(QWidget *parent) : AbstractProtocolConfigForm(parent) { versionGroup = new QButtonGroup(this); setupUi(this); // auto-connect's not working, for some reason I can't figure out! // slot name changed to when_ instead of on_ so that connectSlotsByName() // doesn't complain connect(versionGroup, SIGNAL(buttonClicked(int)), SLOT(when_versionGroup_buttonClicked(int))); versionGroup->addButton(icmp4Button, OstProto::Icmp::kIcmp4); versionGroup->addButton(icmp6Button, OstProto::Icmp::kIcmp6); typeCombo->setValidator(new QIntValidator(0, 0xFF, this)); icmp4Button->click(); idEdit->setValidator(new QIntValidator(0, 0xFFFF, this)); seqEdit->setValidator(new QIntValidator(0, 0xFFFF, this)); } IcmpConfigForm::~IcmpConfigForm() { } IcmpConfigForm* IcmpConfigForm::createInstance() { return new IcmpConfigForm; } void IcmpConfigForm::loadWidget(AbstractProtocol *proto) { versionGroup->button( proto->fieldData( IcmpProtocol::icmp_version, AbstractProtocol::FieldValue ).toUInt())->click(); typeCombo->setValue( proto->fieldData( IcmpProtocol::icmp_type, AbstractProtocol::FieldValue ).toUInt()); codeEdit->setText( proto->fieldData( IcmpProtocol::icmp_code, AbstractProtocol::FieldValue ).toString()); overrideCksum->setChecked( proto->fieldData( IcmpProtocol::icmp_is_override_checksum, AbstractProtocol::FieldValue ).toBool()); cksumEdit->setText(uintToHexStr( proto->fieldData( IcmpProtocol::icmp_checksum, AbstractProtocol::FieldValue ).toUInt(), 2)); idEdit->setText( proto->fieldData( IcmpProtocol::icmp_identifier, AbstractProtocol::FieldValue ).toString()); seqEdit->setText( proto->fieldData( IcmpProtocol::icmp_sequence, AbstractProtocol::FieldValue ).toString()); } void IcmpConfigForm::storeWidget(AbstractProtocol *proto) { proto->setFieldData( IcmpProtocol::icmp_version, versionGroup->checkedId()); proto->setFieldData( IcmpProtocol::icmp_type, typeCombo->currentValue()); proto->setFieldData( IcmpProtocol::icmp_code, codeEdit->text()); proto->setFieldData( IcmpProtocol::icmp_is_override_checksum, overrideCksum->isChecked()); proto->setFieldData( IcmpProtocol::icmp_checksum, hexStrToUInt(cksumEdit->text())); proto->setFieldData( IcmpProtocol::icmp_identifier, idEdit->text()); proto->setFieldData( IcmpProtocol::icmp_sequence, seqEdit->text()); } // // -------- private slots // void IcmpConfigForm::on_typeCombo_currentIndexChanged(int /*index*/) { idSeqFrame->setVisible( isIdSeqType( OstProto::Icmp::Version(versionGroup->checkedId()), typeCombo->currentValue())); } void IcmpConfigForm::when_versionGroup_buttonClicked(int id) { int value = typeCombo->currentValue(); typeCombo->clear(); switch(id) { case OstProto::Icmp::kIcmp4: typeCombo->addItem(kIcmpEchoReply, "Echo Reply"); typeCombo->addItem(kIcmpDestinationUnreachable, "Destination Unreachable"); typeCombo->addItem(kIcmpSourceQuench, "Source Quench"); typeCombo->addItem(kIcmpRedirect, "Redirect"); typeCombo->addItem(kIcmpEchoRequest, "Echo Request"); typeCombo->addItem(kIcmpTimeExceeded, "Time Exceeded"); typeCombo->addItem(kIcmpParameterProblem, "Parameter Problem"); typeCombo->addItem(kIcmpTimestampRequest, "Timestamp Request"); typeCombo->addItem(kIcmpTimestampReply, "Timestamp Reply"); typeCombo->addItem(kIcmpInformationRequest, "Information Request"); typeCombo->addItem(kIcmpInformationReply, "Information Reply"); typeCombo->addItem(kIcmpAddressMaskRequest, "Address Mask Request"); typeCombo->addItem(kIcmpAddressMaskReply, "Address Mask Reply"); break; case OstProto::Icmp::kIcmp6: typeCombo->addItem(kIcmp6DestinationUnreachable, "Destination Unreachable"); typeCombo->addItem(kIcmp6PacketTooBig, "Packet Too Big"); typeCombo->addItem(kIcmp6TimeExceeded, "Time Exceeded"); typeCombo->addItem(kIcmp6ParameterProblem, "Parameter Problem"); typeCombo->addItem(kIcmp6EchoRequest, "Echo Request"); typeCombo->addItem(kIcmp6EchoReply, "Echo Reply"); typeCombo->addItem(kIcmp6RouterSolicitation, "Router Solicitation"); typeCombo->addItem(kIcmp6RouterAdvertisement, "Router Advertisement"); typeCombo->addItem(kIcmp6NeighbourSolicitation, "Neighbour Solicitation"); typeCombo->addItem(kIcmp6NeighbourAdvertisement, "Neighbour Advertisement"); typeCombo->addItem(kIcmp6Redirect, "Redirect"); typeCombo->addItem(kIcmp6InformationQuery, "Information Query"); typeCombo->addItem(kIcmp6InformationResponse, "Information Response"); break; default: Q_ASSERT(false); } typeCombo->setValue(value); } ostinato-0.9/common/icmpconfig.h000066400000000000000000000024341321315675200170110ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _ICMP_CONFIG_H #define _ICMP_CONFIG_H #include "abstractprotocolconfig.h" #include "ui_icmp.h" class QButtonGroup; class IcmpConfigForm : public AbstractProtocolConfigForm, private Ui::Icmp { Q_OBJECT public: IcmpConfigForm(QWidget *parent = 0); virtual ~IcmpConfigForm(); static IcmpConfigForm* createInstance(); virtual void loadWidget(AbstractProtocol *proto); virtual void storeWidget(AbstractProtocol *proto); private: QButtonGroup *versionGroup; private slots: void on_typeCombo_currentIndexChanged(int index); void when_versionGroup_buttonClicked(int id); }; #endif ostinato-0.9/common/icmphelper.h000066400000000000000000000047131321315675200170250ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _ICMP_HELPER_H #define _ICMP_HELPER_H #include "icmp.pb.h" #include enum IcmpType { kIcmpEchoReply = 0, kIcmpDestinationUnreachable = 3, kIcmpSourceQuench = 4, kIcmpRedirect = 5, kIcmpEchoRequest = 8, kIcmpTimeExceeded = 11, kIcmpParameterProblem = 12, kIcmpTimestampRequest = 13, kIcmpTimestampReply = 14, kIcmpInformationRequest = 15, kIcmpInformationReply = 16, kIcmpAddressMaskRequest = 17, kIcmpAddressMaskReply = 18 }; enum Icmp6Type { kIcmp6DestinationUnreachable = 1, kIcmp6PacketTooBig = 2, kIcmp6TimeExceeded = 3, kIcmp6ParameterProblem = 4, kIcmp6EchoRequest = 128, kIcmp6EchoReply = 129, kIcmp6RouterSolicitation = 133, kIcmp6RouterAdvertisement = 134, kIcmp6NeighbourSolicitation = 135, kIcmp6NeighbourAdvertisement = 136, kIcmp6Redirect = 137, kIcmp6InformationQuery = 139, kIcmp6InformationResponse = 140 }; static QSet icmpIdSeqSet = QSet() << kIcmpEchoRequest << kIcmpEchoReply << kIcmpInformationRequest << kIcmpInformationReply; static QSet icmp6IdSeqSet = QSet() << kIcmp6EchoRequest << kIcmp6EchoReply; bool inline isIdSeqType(OstProto::Icmp::Version ver, int type) { //qDebug("%s: ver = %d, type = %d", __FUNCTION__, ver, type); switch(ver) { case OstProto::Icmp::kIcmp4: return icmpIdSeqSet.contains(type); case OstProto::Icmp::kIcmp6: return icmp6IdSeqSet.contains(type); default: break; } Q_ASSERT(false); // unreachable return false; } #endif ostinato-0.9/common/icmppdml.cpp000066400000000000000000000064721321315675200170410ustar00rootroot00000000000000/* Copyright (C) 2011 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "icmppdml.h" #include "icmp.pb.h" PdmlIcmpProtocol::PdmlIcmpProtocol() { ostProtoId_ = OstProto::Protocol::kIcmpFieldNumber; fieldMap_.insert("icmp.type", OstProto::Icmp::kTypeFieldNumber); fieldMap_.insert("icmp.code", OstProto::Icmp::kCodeFieldNumber); fieldMap_.insert("icmp.checksum", OstProto::Icmp::kChecksumFieldNumber); fieldMap_.insert("icmp.ident", OstProto::Icmp::kIdentifierFieldNumber); fieldMap_.insert("icmp.seq", OstProto::Icmp::kSequenceFieldNumber); fieldMap_.insert("icmpv6.type", OstProto::Icmp::kTypeFieldNumber); fieldMap_.insert("icmpv6.code", OstProto::Icmp::kCodeFieldNumber); fieldMap_.insert("icmpv6.checksum", OstProto::Icmp::kChecksumFieldNumber); fieldMap_.insert("icmpv6.echo.identifier", OstProto::Icmp::kIdentifierFieldNumber); fieldMap_.insert("icmpv6.echo.sequence_number", OstProto::Icmp::kSequenceFieldNumber); } PdmlProtocol* PdmlIcmpProtocol::createInstance() { return new PdmlIcmpProtocol(); } void PdmlIcmpProtocol::preProtocolHandler(QString name, const QXmlStreamAttributes& /*attributes*/, int /*expectedPos*/, OstProto::Protocol *pbProto, OstProto::Stream* /*stream*/) { OstProto::Icmp *icmp = pbProto->MutableExtension(OstProto::icmp); if (name == "icmp") icmp->set_icmp_version(OstProto::Icmp::kIcmp4); else if (name == "icmpv6") icmp->set_icmp_version(OstProto::Icmp::kIcmp6); icmp->set_is_override_checksum(true); icmp->set_type(kIcmpInvalidType); } void PdmlIcmpProtocol::unknownFieldHandler(QString /*name*/, int /*pos*/, int /*size*/, const QXmlStreamAttributes &attributes, OstProto::Protocol *pbProto, OstProto::Stream* /*stream*/) { bool isOk; OstProto::Icmp *icmp = pbProto->MutableExtension(OstProto::icmp); if ((icmp->icmp_version() == OstProto::Icmp::kIcmp6) && (icmp->type() >= kIcmp6EchoRequest) && (icmp->type() <= kIcmp6EchoReply)) { QString addrHexStr = attributes.value("value").toString(); // Wireshark 1.4.x does not have these as filterable fields if (attributes.value("show").toString().startsWith("ID")) icmp->set_identifier(addrHexStr.toUInt(&isOk, kBaseHex)); else if (attributes.value("show").toString().startsWith("Sequence")) icmp->set_sequence(addrHexStr.toUInt(&isOk, kBaseHex)); } } void PdmlIcmpProtocol::postProtocolHandler(OstProto::Protocol *pbProto, OstProto::Stream *stream) { OstProto::Icmp *icmp = pbProto->MutableExtension(OstProto::icmp); if (icmp->type() == kIcmpInvalidType) stream->mutable_protocol()->RemoveLast(); } ostinato-0.9/common/icmppdml.h000066400000000000000000000030421321315675200164740ustar00rootroot00000000000000/* Copyright (C) 2011 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _ICMP_PDML_H #define _ICMP_PDML_H #include "pdmlprotocol.h" class PdmlIcmpProtocol : public PdmlProtocol { friend class PdmlIcmp6Protocol; public: static PdmlProtocol* createInstance(); virtual void preProtocolHandler(QString name, const QXmlStreamAttributes &attributes, int expectedPos, OstProto::Protocol *pbProto, OstProto::Stream *stream); virtual void unknownFieldHandler(QString name, int pos, int size, const QXmlStreamAttributes &attributes, OstProto::Protocol *pbProto, OstProto::Stream *stream); virtual void postProtocolHandler(OstProto::Protocol *pbProto, OstProto::Stream *stream); protected: PdmlIcmpProtocol(); private: static const uint kIcmpInvalidType = 0xFFFFFFFF; static const uint kIcmp6EchoRequest = 128; static const uint kIcmp6EchoReply = 129; }; #endif ostinato-0.9/common/igmp.cpp000066400000000000000000000247661321315675200161760ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "igmp.h" #include "iputils.h" #include #include IgmpProtocol::IgmpProtocol(StreamBase *stream, AbstractProtocol *parent) : GmpProtocol(stream, parent) { _hasPayload = false; data.set_type(kIgmpV2Query); } IgmpProtocol::~IgmpProtocol() { } AbstractProtocol* IgmpProtocol::createInstance(StreamBase *stream, AbstractProtocol *parent) { return new IgmpProtocol(stream, parent); } quint32 IgmpProtocol::protocolNumber() const { return OstProto::Protocol::kIgmpFieldNumber; } void IgmpProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const { protocol.MutableExtension(OstProto::igmp)->CopyFrom(data); protocol.mutable_protocol_id()->set_id(protocolNumber()); } void IgmpProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) { if (protocol.protocol_id().id() == protocolNumber() && protocol.HasExtension(OstProto::igmp)) data.MergeFrom(protocol.GetExtension(OstProto::igmp)); } QString IgmpProtocol::name() const { return QString("Internet Group Management Protocol"); } QString IgmpProtocol::shortName() const { return QString("IGMP"); } quint32 IgmpProtocol::protocolId(ProtocolIdType type) const { switch(type) { case ProtocolIdIp: return 0x2; default:break; } return AbstractProtocol::protocolId(type); } QVariant IgmpProtocol::fieldData(int index, FieldAttrib attrib, int streamIndex) const { switch (index) { case kRsvdMrtCode: { uint mrt = 0; quint8 mrcode = 0; if (msgType() == kIgmpV3Query) { mrt = data.max_response_time(); mrcode = quint8(mrc(mrt)); } else if (msgType() == kIgmpV2Query) { mrt = data.max_response_time(); mrcode = mrt & 0xFF; } switch(attrib) { case FieldName: if (isQuery()) return QString("Max Response Time"); else return QString("Reserved"); case FieldValue: return mrt; case FieldTextValue: return QString("%1").arg(mrt); case FieldFrameValue: return QByteArray(1, mrcode); default: break; } break; } case kGroupAddress: { quint32 grpIp = ipUtils::ipAddress( data.group_address().v4(), data.group_prefix(), ipUtils::AddrMode(data.group_mode()), data.group_count(), streamIndex); switch(attrib) { case FieldName: return QString("Group Address"); case FieldValue: case FieldTextValue: return QHostAddress(grpIp).toString(); case FieldFrameValue: { QByteArray fv; fv.resize(4); qToBigEndian(grpIp, (uchar*) fv.data()); return fv; } default: break; } break; } case kSources: { switch(attrib) { case FieldName: return QString("Source List"); case FieldValue: { QStringList list; for (int i = 0; i < data.sources_size(); i++) list.append(QHostAddress(data.sources(i).v4()).toString()); return list; } case FieldFrameValue: { QByteArray fv; fv.resize(4 * data.sources_size()); for (int i = 0; i < data.sources_size(); i++) qToBigEndian(data.sources(i).v4(), (uchar*)(fv.data()+4*i)); return fv; } case FieldTextValue: { QStringList list; for (int i = 0; i < data.sources_size(); i++) list.append(QHostAddress(data.sources(i).v4()).toString()); return list.join(", "); } default: break; } break; } case kGroupRecords: { switch(attrib) { case FieldValue: { QVariantList grpRecords = GmpProtocol::fieldData( index, attrib, streamIndex).toList(); for (int i = 0; i < data.group_records_size(); i++) { QVariantMap grpRec = grpRecords.at(i).toMap(); OstProto::Gmp::GroupRecord rec = data.group_records(i); grpRec["groupRecordAddress"] = QHostAddress( rec.group_address().v4()).toString(); QStringList sl; for (int j = 0; j < rec.sources_size(); j++) sl.append(QHostAddress(rec.sources(j).v4()).toString()); grpRec["groupRecordSourceList"] = sl; grpRecords.replace(i, grpRec); } return grpRecords; } case FieldFrameValue: { QVariantList list = GmpProtocol::fieldData( index, attrib, streamIndex).toList(); QByteArray fv; for (int i = 0; i < data.group_records_size(); i++) { OstProto::Gmp::GroupRecord rec = data.group_records(i); QByteArray rv = list.at(i).toByteArray(); rv.insert(4, QByteArray(4+4*rec.sources_size(), char(0))); qToBigEndian(rec.group_address().v4(), (uchar*)(rv.data()+4)); for (int j = 0; j < rec.sources_size(); j++) { qToBigEndian(rec.sources(j).v4(), (uchar*)(rv.data()+8+4*j)); } fv.append(rv); } return fv; } case FieldTextValue: { QStringList list = GmpProtocol::fieldData( index, attrib, streamIndex).toStringList(); for (int i = 0; i < data.group_records_size(); i++) { OstProto::Gmp::GroupRecord rec = data.group_records(i); QString recStr = list.at(i); QString str; str.append(QString("Group: %1").arg( QHostAddress(rec.group_address().v4()).toString())); str.append("; Sources: "); QStringList sl; for (int j = 0; j < rec.sources_size(); j++) sl.append(QHostAddress(rec.sources(j).v4()).toString()); str.append(sl.join(", ")); recStr.replace("XXX", str); list.replace(i, recStr); } return list.join("\n").insert(0, "\n"); } default: break; } break; } default: break; } return GmpProtocol::fieldData(index, attrib, streamIndex); } bool IgmpProtocol::setFieldData(int index, const QVariant &value, FieldAttrib attrib) { bool isOk = false; if (attrib != FieldValue) goto _exit; switch (index) { case kRsvdMrtCode: { uint mrt = value.toUInt(&isOk); if (isOk) data.set_max_response_time(mrt); break; } case kGroupAddress: { QHostAddress addr(value.toString()); quint32 ip = addr.toIPv4Address(); isOk = (addr.protocol() == QAbstractSocket::IPv4Protocol); if (isOk) data.mutable_group_address()->set_v4(ip); break; } case kSources: { QStringList list = value.toStringList(); data.clear_sources(); foreach(QString str, list) { quint32 ip = QHostAddress(str).toIPv4Address(); data.add_sources()->set_v4(ip); } break; } case kGroupRecords: { GmpProtocol::setFieldData(index, value, attrib); QVariantList list = value.toList(); for (int i = 0; i < list.count(); i++) { QVariantMap grpRec = list.at(i).toMap(); OstProto::Gmp::GroupRecord *rec = data.mutable_group_records(i); rec->mutable_group_address()->set_v4(QHostAddress( grpRec["groupRecordAddress"].toString()) .toIPv4Address()); QStringList srcList = grpRec["groupRecordSourceList"] .toStringList(); rec->clear_sources(); foreach (QString src, srcList) { rec->add_sources()->set_v4( QHostAddress(src).toIPv4Address()); } } break; } default: isOk = GmpProtocol::setFieldData(index, value, attrib); break; } _exit: return isOk; } quint16 IgmpProtocol::checksum(int streamIndex) const { quint16 cks; quint32 sum = 0; // TODO: add as a new CksumType (CksumIgmp?) and implement in AbsProto cks = protocolFrameCksum(streamIndex, CksumIp); sum += (quint16) ~cks; cks = protocolFramePayloadCksum(streamIndex, CksumIp); sum += (quint16) ~cks; while (sum >> 16) sum = (sum & 0xFFFF) + (sum >> 16); cks = (~sum) & 0xFFFF; return cks; } ostinato-0.9/common/igmp.h000066400000000000000000000051431321315675200156270ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _IGMP_H #define _IGMP_H #include "gmp.h" #include "igmp.pb.h" // IGMP uses the same msg type value for 'Query' messages across // versions despite the fields being different. To distinguish // Query messages of different versions, we use an additional // upper byte enum IgmpMsgType { kIgmpV1Query = 0x11, kIgmpV1Report = 0x12, kIgmpV2Query = 0xFF11, kIgmpV2Report = 0x16, kIgmpV2Leave = 0x17, kIgmpV3Query = 0xFE11, kIgmpV3Report = 0x22, }; class IgmpProtocol : public GmpProtocol { public: IgmpProtocol(StreamBase *stream, AbstractProtocol *parent = 0); virtual ~IgmpProtocol(); static AbstractProtocol* createInstance(StreamBase *stream, AbstractProtocol *parent = 0); virtual quint32 protocolNumber() const; virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); virtual quint32 protocolId(ProtocolIdType type) const; virtual QString name() const; virtual QString shortName() const; virtual QVariant fieldData(int index, FieldAttrib attrib, int streamIndex = 0) const; virtual bool setFieldData(int index, const QVariant &value, FieldAttrib attrib = FieldValue); protected: virtual bool isSsmReport() const; virtual bool isQuery() const; virtual bool isSsmQuery() const; virtual quint16 checksum(int streamIndex) const; private: int mrc(int value) const; }; inline bool IgmpProtocol::isSsmReport() const { return (msgType() == kIgmpV3Report); } inline bool IgmpProtocol::isQuery() const { return ((msgType() == kIgmpV1Query) || (msgType() == kIgmpV2Query) || (msgType() == kIgmpV3Query)); } inline bool IgmpProtocol::isSsmQuery() const { return (msgType() == kIgmpV3Query); } inline int IgmpProtocol::mrc(int value) const { return quint8(value); // TODO: if value > 128, convert to mantissa/exp form } #endif ostinato-0.9/common/igmp.proto000077500000000000000000000014241321315675200165440ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ import "protocol.proto"; import "gmp.proto"; package OstProto; extend Protocol { optional Gmp igmp = 403; } ostinato-0.9/common/igmpconfig.cpp000066400000000000000000000060141321315675200173460ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "igmpconfig.h" #include "igmp.h" #include "ipv4addressdelegate.h" IgmpConfigForm::IgmpConfigForm(QWidget *parent) : GmpConfigForm(parent) { connect(msgTypeCombo, SIGNAL(currentIndexChanged(int)), SLOT(on_msgTypeCombo_currentIndexChanged(int))); msgTypeCombo->setValueMask(0xFF); msgTypeCombo->addItem(kIgmpV1Query, "IGMPv1 Query"); msgTypeCombo->addItem(kIgmpV1Report, "IGMPv1 Report"); msgTypeCombo->addItem(kIgmpV2Query, "IGMPv2 Query"); msgTypeCombo->addItem(kIgmpV2Report, "IGMPv2 Report"); msgTypeCombo->addItem(kIgmpV2Leave, "IGMPv2 Leave"); msgTypeCombo->addItem(kIgmpV3Query, "IGMPv3 Query"); msgTypeCombo->addItem(kIgmpV3Report, "IGMPv3 Report"); _defaultGroupIp = "0.0.0.0"; _defaultSourceIp = "0.0.0.0"; groupAddress->setInputMask("009.009.009.009;"); // FIXME: use validator groupRecordAddress->setInputMask("009.009.009.009;"); // FIXME:use validator sourceList->setItemDelegate(new IPv4AddressDelegate(this)); groupRecordSourceList->setItemDelegate(new IPv4AddressDelegate(this)); } IgmpConfigForm::~IgmpConfigForm() { } IgmpConfigForm* IgmpConfigForm::createInstance() { return new IgmpConfigForm; } void IgmpConfigForm::loadWidget(AbstractProtocol *proto) { GmpConfigForm::loadWidget(proto); maxResponseTime->setText( proto->fieldData( IgmpProtocol::kRsvdMrtCode, AbstractProtocol::FieldValue ).toString()); } void IgmpConfigForm::storeWidget(AbstractProtocol *proto) { GmpConfigForm::storeWidget(proto); proto->setFieldData( IgmpProtocol::kRsvdMrtCode, maxResponseTime->text()); } // // -- private slots // void IgmpConfigForm::on_msgTypeCombo_currentIndexChanged(int /*index*/) { switch(msgTypeCombo->currentValue()) { case kIgmpV1Query: case kIgmpV1Report: case kIgmpV2Query: case kIgmpV2Report: case kIgmpV2Leave: asmGroup->show(); ssmWidget->hide(); break; case kIgmpV3Query: asmGroup->show(); ssmWidget->setCurrentIndex(kSsmQueryPage); ssmWidget->show(); break; case kIgmpV3Report: asmGroup->hide(); ssmWidget->setCurrentIndex(kSsmReportPage); ssmWidget->show(); break; default: asmGroup->hide(); ssmWidget->hide(); break; } } ostinato-0.9/common/igmpconfig.h000066400000000000000000000021441321315675200170130ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _IGMP_CONFIG_H #define _IGMP_CONFIG_H #include "gmpconfig.h" class IgmpConfigForm : public GmpConfigForm { Q_OBJECT public: IgmpConfigForm(QWidget *parent = 0); virtual ~IgmpConfigForm(); static IgmpConfigForm* createInstance(); virtual void loadWidget(AbstractProtocol *proto); virtual void storeWidget(AbstractProtocol *proto); private slots: void on_msgTypeCombo_currentIndexChanged(int index); }; #endif ostinato-0.9/common/igmppdml.cpp000066400000000000000000000114311321315675200170340ustar00rootroot00000000000000/* Copyright (C) 2011 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "igmppdml.h" #include "igmp.pb.h" PdmlIgmpProtocol::PdmlIgmpProtocol() { ostProtoId_ = OstProto::Protocol::kIgmpFieldNumber; fieldMap_.insert("igmp.max_resp", OstProto::Gmp::kMaxResponseTimeFieldNumber); // FIXME fieldMap_.insert("igmp.checksum", OstProto::Gmp::kChecksumFieldNumber); fieldMap_.insert("igmp.s", OstProto::Gmp::kSFlagFieldNumber); fieldMap_.insert("igmp.qrv", OstProto::Gmp::kQrvFieldNumber); fieldMap_.insert("igmp.qqic", OstProto::Gmp::kQqiFieldNumber); // FIXME fieldMap_.insert("igmp.num_grp_recs", OstProto::Gmp::kGroupRecordCountFieldNumber); } PdmlProtocol* PdmlIgmpProtocol::createInstance() { return new PdmlIgmpProtocol(); } void PdmlIgmpProtocol::preProtocolHandler(QString /*name*/, const QXmlStreamAttributes& /*attributes*/, int /*expectedPos*/, OstProto::Protocol *pbProto, OstProto::Stream* /*stream*/) { OstProto::Gmp *igmp = pbProto->MutableExtension(OstProto::igmp); igmp->set_is_override_rsvd_code(true); igmp->set_is_override_checksum(true); igmp->set_is_override_source_count(true); igmp->set_is_override_group_record_count(true); version_ = 0; } void PdmlIgmpProtocol::unknownFieldHandler(QString name, int /*pos*/, int /*size*/, const QXmlStreamAttributes &attributes, OstProto::Protocol *pbProto, OstProto::Stream* /*stream*/) { bool isOk; OstProto::Gmp *igmp = pbProto->MutableExtension(OstProto::igmp); QString valueHexStr = attributes.value("value").toString(); if (name == "igmp.version") { version_ = attributes.value("show").toString().toUInt(&isOk); } else if (name == "igmp.type") { uint type = valueHexStr.toUInt(&isOk, kBaseHex); if (type == kIgmpQuery) { switch(version_) { case 1: type = kIgmpV1Query; break; case 2: type = kIgmpV2Query; break; case 3: type = kIgmpV3Query; break; } } igmp->set_type(type); } else if (name == "igmp.record_type") { OstProto::Gmp::GroupRecord *rec = igmp->add_group_records(); rec->set_type(OstProto::Gmp::GroupRecord::RecordType( valueHexStr.toUInt(&isOk, kBaseHex))); rec->set_is_override_source_count(true); rec->set_is_override_aux_data_length(true); } else if (name == "igmp.aux_data_len") { igmp->mutable_group_records(igmp->group_records_size() - 1)-> set_aux_data_length(valueHexStr.toUInt(&isOk, kBaseHex)); } else if (name == "igmp.num_src") { if (igmp->group_record_count()) igmp->mutable_group_records(igmp->group_records_size() - 1)-> set_source_count(valueHexStr.toUInt(&isOk, kBaseHex)); else igmp->set_source_count(valueHexStr.toUInt(&isOk, kBaseHex)); } else if (name == "igmp.maddr") { if (igmp->group_record_count()) igmp->mutable_group_records(igmp->group_records_size() - 1)-> mutable_group_address()->set_v4( valueHexStr.toUInt(&isOk, kBaseHex)); else igmp->mutable_group_address()->set_v4( valueHexStr.toUInt(&isOk, kBaseHex)); } else if (name == "igmp.saddr") { if (igmp->group_record_count()) igmp->mutable_group_records(igmp->group_records_size() - 1)-> add_sources()->set_v4(valueHexStr.toUInt(&isOk, kBaseHex)); else igmp->add_sources()->set_v4(valueHexStr.toUInt(&isOk, kBaseHex)); } else if (name == "igmp.aux_data") { QByteArray ba = QByteArray::fromHex( attributes.value("value").toString().toUtf8()); igmp->mutable_group_records(igmp->group_records_size() - 1)-> set_aux_data(ba.constData(), ba.size()); } } void PdmlIgmpProtocol::postProtocolHandler(OstProto::Protocol* /*pbProto*/, OstProto::Stream *stream) { // version is 0 for IGMP like protocols such as RGMP which we don't // support currently if (version_ == 0) stream->mutable_protocol()->RemoveLast(); } ostinato-0.9/common/igmppdml.h000066400000000000000000000030521321315675200165010ustar00rootroot00000000000000/* Copyright (C) 2011 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _IGMP_PDML_H #define _IGMP_PDML_H #include "pdmlprotocol.h" class PdmlIgmpProtocol : public PdmlProtocol { public: static PdmlProtocol* createInstance(); virtual void preProtocolHandler(QString name, const QXmlStreamAttributes &attributes, int expectedPos, OstProto::Protocol *pbProto, OstProto::Stream *stream); virtual void unknownFieldHandler(QString name, int pos, int size, const QXmlStreamAttributes &attributes, OstProto::Protocol *pbProto, OstProto::Stream *stream); virtual void postProtocolHandler(OstProto::Protocol *pbProto, OstProto::Stream *stream); protected: PdmlIgmpProtocol(); private: static const uint kIgmpQuery = 0x11; static const uint kIgmpV1Query = 0x11; static const uint kIgmpV2Query = 0xFF11; static const uint kIgmpV3Query = 0xFE11; uint version_; }; #endif ostinato-0.9/common/intcombobox.h000066400000000000000000000032701321315675200172150ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef __INT_COMBO_BOX #define __INT_COMBO_BOX #include class IntComboBox : public QComboBox { public: IntComboBox(QWidget *parent = 0) : QComboBox(parent) { valueMask_ = 0xFFFFFFFF; setEditable(true); } void addItem(int value, const QString &text) { QComboBox::addItem( QString("%1 - %2").arg(value & valueMask_).arg(text), value); } int currentValue() { bool isOk; int index = findText(currentText()); if (index >= 0) return itemData(index).toInt(); else return currentText().toInt(&isOk, 0); } void setValue(int value) { int index = findData(value); if (index >= 0) setCurrentIndex(index); else setEditText(QString().setNum(value)); } uint valueMask() { return valueMask_; } void setValueMask(uint mask) { valueMask_ = mask; } private: uint valueMask_; }; #endif ostinato-0.9/common/intedit.h000066400000000000000000000017441321315675200163360ustar00rootroot00000000000000/* Copyright (C) 2016 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _INT_EDIT_H #define _INT_EDIT_H #include #include class IntEdit: public QSpinBox { public: IntEdit(QWidget *parent = 0); }; inline IntEdit::IntEdit(QWidget *parent) : QSpinBox(parent) { setRange(INT_MIN, INT_MAX); setButtonSymbols(QAbstractSpinBox::NoButtons); } #endif ostinato-0.9/common/ip4.cpp000066400000000000000000000627111321315675200157260ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "ip4.h" #include Ip4Protocol::Ip4Protocol(StreamBase *stream, AbstractProtocol *parent) : AbstractProtocol(stream, parent) { } Ip4Protocol::~Ip4Protocol() { } AbstractProtocol* Ip4Protocol::createInstance(StreamBase *stream, AbstractProtocol *parent) { return new Ip4Protocol(stream, parent); } quint32 Ip4Protocol::protocolNumber() const { return OstProto::Protocol::kIp4FieldNumber; } void Ip4Protocol::protoDataCopyInto(OstProto::Protocol &protocol) const { protocol.MutableExtension(OstProto::ip4)->CopyFrom(data); protocol.mutable_protocol_id()->set_id(protocolNumber()); } void Ip4Protocol::protoDataCopyFrom(const OstProto::Protocol &protocol) { if (protocol.protocol_id().id() == protocolNumber() && protocol.HasExtension(OstProto::ip4)) data.MergeFrom(protocol.GetExtension(OstProto::ip4)); } QString Ip4Protocol::name() const { return QString("Internet Protocol ver 4"); } QString Ip4Protocol::shortName() const { return QString("IPv4"); } AbstractProtocol::ProtocolIdType Ip4Protocol::protocolIdType() const { return ProtocolIdIp; } quint32 Ip4Protocol::protocolId(ProtocolIdType type) const { switch(type) { case ProtocolIdLlc: return 0x060603; case ProtocolIdEth: return 0x0800; case ProtocolIdIp: return 0x04; default:break; } return AbstractProtocol::protocolId(type); } int Ip4Protocol::fieldCount() const { return ip4_fieldCount; } AbstractProtocol::FieldFlags Ip4Protocol::fieldFlags(int index) const { AbstractProtocol::FieldFlags flags; flags = AbstractProtocol::fieldFlags(index); switch (index) { case ip4_ver: case ip4_hdrLen: case ip4_tos: case ip4_totLen: case ip4_id: case ip4_flags: case ip4_fragOfs: case ip4_ttl: case ip4_proto: break; case ip4_cksum: flags |= CksumField; break; case ip4_srcAddr: case ip4_dstAddr: case ip4_options: break; case ip4_isOverrideVer: case ip4_isOverrideHdrLen: case ip4_isOverrideTotLen: case ip4_isOverrideProto: case ip4_isOverrideCksum: case ip4_srcAddrMode: case ip4_srcAddrCount: case ip4_srcAddrMask: case ip4_dstAddrMode: case ip4_dstAddrCount: case ip4_dstAddrMask: flags &= ~FrameField; flags |= MetaField; break; default: break; } return flags; } QVariant Ip4Protocol::fieldData(int index, FieldAttrib attrib, int streamIndex) const { switch (index) { case ip4_ver: { int ver; ver = data.is_override_ver() ? (data.ver_hdrlen() >> 4) & 0x0F : 4; switch(attrib) { case FieldName: return QString("Version"); case FieldValue: return ver; case FieldTextValue: return QString("%1").arg(ver, 1, BASE_HEX, QChar('0')); case FieldFrameValue: return QByteArray(1, (char) ver); case FieldBitSize: return 4; default: break; } break; } case ip4_hdrLen: { int hdrlen; hdrlen = data.is_override_hdrlen() ? data.ver_hdrlen() : 5 + data.options().length()/4; hdrlen &= 0x0F; switch(attrib) { case FieldName: return QString("Header Length"); case FieldValue: return hdrlen; case FieldTextValue: return QString("%1").arg(hdrlen, 1, BASE_HEX, QChar('0')); case FieldFrameValue: return QByteArray(1, (char) hdrlen); case FieldBitSize: return 4; default: break; } break; } case ip4_tos: switch(attrib) { case FieldName: return QString("TOS/DSCP"); case FieldValue: return data.tos(); case FieldFrameValue: return QByteArray(1, (char) data.tos()); case FieldTextValue: return QString("0x%1"). arg(data.tos(), 2, BASE_HEX, QChar('0'));; default: break; } break; case ip4_totLen: { int ipLen = 20 + data.options().length(); switch(attrib) { case FieldName: return QString("Total Length"); case FieldValue: { int totlen; totlen = data.is_override_totlen() ? data.totlen() : (protocolFramePayloadSize(streamIndex) + ipLen); return totlen; } case FieldFrameValue: { QByteArray fv; int totlen; totlen = data.is_override_totlen() ? data.totlen() : (protocolFramePayloadSize(streamIndex) + ipLen); fv.resize(2); qToBigEndian((quint16) totlen, (uchar*) fv.data()); return fv; } case FieldTextValue: { int totlen; totlen = data.is_override_totlen() ? data.totlen() : (protocolFramePayloadSize(streamIndex) + ipLen); return QString("%1").arg(totlen); } case FieldBitSize: return 16; default: break; } break; } case ip4_id: switch(attrib) { case FieldName: return QString("Identification"); case FieldValue: return data.id(); case FieldFrameValue: { QByteArray fv; fv.resize(2); qToBigEndian((quint16) data.id(), (uchar*)fv.data()); return fv; } case FieldTextValue: return QString("0x%1"). arg(data.id(), 2, BASE_HEX, QChar('0'));; default: break; } break; case ip4_flags: switch(attrib) { case FieldName: return QString("Flags"); case FieldValue: return data.flags(); case FieldFrameValue: return QByteArray(1, (char) data.flags()); case FieldTextValue: { QString s; s.append("Unused:"); s.append(data.flags() & IP_FLAG_UNUSED ? "1" : "0"); s.append(" Don't Fragment:"); s.append(data.flags() & IP_FLAG_DF ? "1" : "0"); s.append(" More Fragments:"); s.append(data.flags() & IP_FLAG_MF ? "1" : "0"); return s; } case FieldBitSize: return 3; default: break; } break; case ip4_fragOfs: switch(attrib) { case FieldName: return QString("Fragment Offset"); case FieldValue: return data.frag_ofs(); case FieldFrameValue: { QByteArray fv; fv.resize(2); qToBigEndian((quint16) (data.frag_ofs()), (uchar*) fv.data()); return fv; } case FieldTextValue: return QString("%1").arg(data.frag_ofs()*8); case FieldBitSize: return 13; default: break; } break; case ip4_ttl: switch(attrib) { case FieldName: return QString("Time to Live"); case FieldValue: return data.ttl(); case FieldFrameValue: return QByteArray(1, (char)data.ttl()); case FieldTextValue: return QString("%1").arg(data.ttl()); default: break; } break; case ip4_proto: { switch(attrib) { case FieldName: return QString("Protocol"); case FieldValue: { unsigned char id = data.is_override_proto() ? data.proto() : payloadProtocolId(ProtocolIdIp); return id; } case FieldFrameValue: { unsigned char id = data.is_override_proto() ? data.proto() : payloadProtocolId(ProtocolIdIp); return QByteArray(1, (char) id); } case FieldTextValue: { unsigned char id = data.is_override_proto() ? data.proto() : payloadProtocolId(ProtocolIdIp); return QString("0x%1"). arg(id, 2, BASE_HEX, QChar('0')); } default: break; } break; } case ip4_cksum: { switch(attrib) { case FieldName: return QString("Header Checksum"); case FieldValue: { quint16 cksum; if (data.is_override_cksum()) cksum = data.cksum(); else cksum = protocolFrameCksum(streamIndex, CksumIp); return cksum; } case FieldFrameValue: { QByteArray fv; quint16 cksum; if (data.is_override_cksum()) cksum = data.cksum(); else cksum = protocolFrameCksum(streamIndex, CksumIp); fv.resize(2); qToBigEndian((quint16) cksum, (uchar*) fv.data()); return fv; } case FieldTextValue: { quint16 cksum; if (data.is_override_cksum()) cksum = data.cksum(); else cksum = protocolFrameCksum(streamIndex, CksumIp); return QString("0x%1"). arg(cksum, 4, BASE_HEX, QChar('0'));; } case FieldBitSize: return 16; default: break; } break; } case ip4_srcAddr: { int u; quint32 subnet, host, srcIp = 0; switch(data.src_ip_mode()) { case OstProto::Ip4::e_im_fixed: srcIp = data.src_ip(); break; case OstProto::Ip4::e_im_inc_host: u = streamIndex % data.src_ip_count(); subnet = data.src_ip() & data.src_ip_mask(); host = (((data.src_ip() & ~data.src_ip_mask()) + u) & ~data.src_ip_mask()); srcIp = subnet | host; break; case OstProto::Ip4::e_im_dec_host: u = streamIndex % data.src_ip_count(); subnet = data.src_ip() & data.src_ip_mask(); host = (((data.src_ip() & ~data.src_ip_mask()) - u) & ~data.src_ip_mask()); srcIp = subnet | host; break; case OstProto::Ip4::e_im_random_host: subnet = data.src_ip() & data.src_ip_mask(); host = (qrand() & ~data.src_ip_mask()); srcIp = subnet | host; break; default: qWarning("Unhandled src_ip_mode = %d", data.src_ip_mode()); } switch(attrib) { case FieldName: return QString("Source"); case FieldValue: return srcIp; case FieldFrameValue: { QByteArray fv; fv.resize(4); qToBigEndian(srcIp, (uchar*) fv.data()); return fv; } case FieldTextValue: return QHostAddress(srcIp).toString(); default: break; } break; } case ip4_dstAddr: { int u; quint32 subnet, host, dstIp = 0; switch(data.dst_ip_mode()) { case OstProto::Ip4::e_im_fixed: dstIp = data.dst_ip(); break; case OstProto::Ip4::e_im_inc_host: u = streamIndex % data.dst_ip_count(); subnet = data.dst_ip() & data.dst_ip_mask(); host = (((data.dst_ip() & ~data.dst_ip_mask()) + u) & ~data.dst_ip_mask()); dstIp = subnet | host; break; case OstProto::Ip4::e_im_dec_host: u = streamIndex % data.dst_ip_count(); subnet = data.dst_ip() & data.dst_ip_mask(); host = (((data.dst_ip() & ~data.dst_ip_mask()) - u) & ~data.dst_ip_mask()); dstIp = subnet | host; break; case OstProto::Ip4::e_im_random_host: subnet = data.dst_ip() & data.dst_ip_mask(); host = (qrand() & ~data.dst_ip_mask()); dstIp = subnet | host; break; default: qWarning("Unhandled dst_ip_mode = %d", data.dst_ip_mode()); } switch(attrib) { case FieldName: return QString("Destination"); case FieldValue: return dstIp; case FieldFrameValue: { QByteArray fv; fv.resize(4); qToBigEndian((quint32) dstIp, (uchar*) fv.data()); return fv; } case FieldTextValue: return QHostAddress(dstIp).toString(); default: break; } break; } case ip4_options: { QByteArray ba; switch(attrib) { case FieldValue: case FieldFrameValue: case FieldTextValue: ba.append(QString().fromStdString(data.options())); default: break; } switch(attrib) { case FieldName: return QString("Options"); case FieldValue: case FieldFrameValue: return ba; case FieldTextValue: return ba.toHex(); default: break; } break; } // Meta fields case ip4_isOverrideVer: switch(attrib) { case FieldValue: return data.is_override_ver(); default: break; } break; case ip4_isOverrideHdrLen: switch(attrib) { case FieldValue: return data.is_override_hdrlen(); default: break; } break; case ip4_isOverrideTotLen: switch(attrib) { case FieldValue: return data.is_override_totlen(); default: break; } break; case ip4_isOverrideProto: switch(attrib) { case FieldValue: return data.is_override_proto(); default: break; } break; case ip4_isOverrideCksum: switch(attrib) { case FieldValue: return data.is_override_cksum(); default: break; } break; case ip4_srcAddrMode: switch(attrib) { case FieldValue: return data.src_ip_mode(); default: break; } break; case ip4_srcAddrCount: switch(attrib) { case FieldValue: return data.src_ip_count(); default: break; } break; case ip4_srcAddrMask: switch(attrib) { case FieldValue: return data.src_ip_mask(); default: break; } break; case ip4_dstAddrMode: switch(attrib) { case FieldValue: return data.dst_ip_mode(); default: break; } break; case ip4_dstAddrCount: switch(attrib) { case FieldValue: return data.dst_ip_count(); default: break; } break; case ip4_dstAddrMask: switch(attrib) { case FieldValue: return data.dst_ip_mask(); default: break; } break; default: break; } return AbstractProtocol::fieldData(index, attrib, streamIndex); } bool Ip4Protocol::setFieldData(int index, const QVariant &value, FieldAttrib attrib) { bool isOk = false; if (attrib != FieldValue) goto _exit; switch (index) { case ip4_ver: { uint version = value.toUInt(&isOk); if (isOk) data.set_ver_hdrlen( ((version & 0xF) << 4) | (data.ver_hdrlen() & 0x0F)); break; } case ip4_hdrLen: { uint hdrLen = value.toUInt(&isOk); if (isOk) data.set_ver_hdrlen( (data.ver_hdrlen() & 0xF0) | (hdrLen & 0x0F)); break; } case ip4_tos: { uint tos = value.toUInt(&isOk); if (isOk) data.set_tos(tos); break; } case ip4_totLen: { uint totLen = value.toUInt(&isOk); if (isOk) data.set_totlen(totLen); break; } case ip4_id: { uint id = value.toUInt(&isOk); if (isOk) data.set_id(id); break; } case ip4_flags: { uint flags = value.toUInt(&isOk); if (isOk) data.set_flags(flags); break; } case ip4_fragOfs: { uint fragOfs = value.toUInt(&isOk); if (isOk) data.set_frag_ofs(fragOfs); break; } case ip4_ttl: { uint ttl = value.toUInt(&isOk); if (isOk) data.set_ttl(ttl); break; } case ip4_proto: { uint proto = value.toUInt(&isOk); if (isOk) data.set_proto(proto); break; } case ip4_cksum: { uint cksum = value.toUInt(&isOk); if (isOk) data.set_cksum(cksum); break; } case ip4_srcAddr: { quint32 srcIp = value.toUInt(&isOk); if (isOk) data.set_src_ip(srcIp); break; } case ip4_dstAddr: { quint32 dstIp = value.toUInt(&isOk); if (isOk) data.set_dst_ip(dstIp); break; } case ip4_options: { QByteArray ba = value.toByteArray(); int pad = (4 - (ba.size() % 4)) % 4; if (pad) ba.append(QByteArray(pad, 0)); data.set_options(ba.constData(), ba.size()); isOk = true; break; } // Meta-fields case ip4_isOverrideVer: { bool ovr = value.toBool(); data.set_is_override_ver(ovr); isOk = true; break; } case ip4_isOverrideHdrLen: { bool ovr = value.toBool(); data.set_is_override_hdrlen(ovr); isOk = true; break; } case ip4_isOverrideTotLen: { bool ovr = value.toBool(); data.set_is_override_totlen(ovr); isOk = true; break; } case ip4_isOverrideProto: { bool ovr = value.toBool(); data.set_is_override_proto(ovr); isOk = true; break; } case ip4_isOverrideCksum: { bool ovr = value.toBool(); data.set_is_override_cksum(ovr); isOk = true; break; } case ip4_srcAddrMode: { uint mode = value.toUInt(&isOk); if (isOk && data.IpAddrMode_IsValid(mode)) data.set_src_ip_mode(OstProto::Ip4::IpAddrMode(mode)); else isOk = false; break; } case ip4_srcAddrCount: { uint count = value.toUInt(&isOk); if (isOk) data.set_src_ip_count(count); break; } case ip4_srcAddrMask: { quint32 mask = value.toUInt(&isOk); if (isOk) data.set_src_ip_mask(mask); break; } case ip4_dstAddrMode: { uint mode = value.toUInt(&isOk); if (isOk && data.IpAddrMode_IsValid(mode)) data.set_dst_ip_mode(OstProto::Ip4::IpAddrMode(mode)); else isOk = false; break; } case ip4_dstAddrCount: { uint count = value.toUInt(&isOk); if (isOk) data.set_dst_ip_count(count); break; } case ip4_dstAddrMask: { quint32 mask = value.toUInt(&isOk); if (isOk) data.set_dst_ip_mask(mask); break; } default: qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, index); break; } _exit: return isOk; } int Ip4Protocol::protocolFrameVariableCount() const { int count = AbstractProtocol::protocolFrameVariableCount(); if (data.src_ip_mode() != OstProto::Ip4::e_im_fixed) count = AbstractProtocol::lcm(count, data.src_ip_count()); if (data.dst_ip_mode() != OstProto::Ip4::e_im_fixed) count = AbstractProtocol::lcm(count, data.dst_ip_count()); return count; } quint32 Ip4Protocol::protocolFrameCksum(int streamIndex, CksumType cksumType) const { switch (cksumType) { case CksumIpPseudo: { quint32 sum = 0; QByteArray fv = protocolFrameValue(streamIndex); const quint8 *p = (quint8*) fv.constData(); sum += *((quint16*)(p + 12)); // src-ip hi sum += *((quint16*)(p + 14)); // src-ip lo sum += *((quint16*)(p + 16)); // dst-ip hi sum += *((quint16*)(p + 18)); // dst-ip lo sum += qToBigEndian((quint16) protocolFramePayloadSize(streamIndex)); // len sum += qToBigEndian((quint16) *(p + 9)); // proto while(sum>>16) sum = (sum & 0xFFFF) + (sum >> 16); return ~qFromBigEndian((quint16)sum); } default: break; } return AbstractProtocol::protocolFrameCksum(streamIndex, cksumType); } ostinato-0.9/common/ip4.h000066400000000000000000000051211321315675200153630ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _IPV4_H #define _IPV4_H #include "abstractprotocol.h" #include "ip4.pb.h" #define IP_FLAG_MF 0x1 #define IP_FLAG_DF 0x2 #define IP_FLAG_UNUSED 0x4 class Ip4Protocol : public AbstractProtocol { public: enum ip4field { ip4_ver = 0, ip4_hdrLen, ip4_tos, ip4_totLen, ip4_id, ip4_flags, ip4_fragOfs, ip4_ttl, ip4_proto, ip4_cksum, ip4_srcAddr, ip4_dstAddr, ip4_options, // Meta-fields ip4_isOverrideVer, ip4_isOverrideHdrLen, ip4_isOverrideTotLen, ip4_isOverrideProto, ip4_isOverrideCksum, ip4_srcAddrMode, ip4_srcAddrCount, ip4_srcAddrMask, ip4_dstAddrMode, ip4_dstAddrCount, ip4_dstAddrMask, ip4_fieldCount }; Ip4Protocol(StreamBase *stream, AbstractProtocol *parent = 0); virtual ~Ip4Protocol(); static AbstractProtocol* createInstance(StreamBase *stream, AbstractProtocol *parent = 0); virtual quint32 protocolNumber() const; virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); virtual QString name() const; virtual QString shortName() const; virtual ProtocolIdType protocolIdType() const; virtual quint32 protocolId(ProtocolIdType type) const; virtual int fieldCount() const; virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; virtual QVariant fieldData(int index, FieldAttrib attrib, int streamIndex = 0) const; virtual bool setFieldData(int index, const QVariant &value, FieldAttrib attrib = FieldValue); virtual int protocolFrameVariableCount() const; virtual quint32 protocolFrameCksum(int streamIndex = 0, CksumType cksumType = CksumIp) const; private: OstProto::Ip4 data; }; #endif ostinato-0.9/common/ip4.proto000066400000000000000000000040021321315675200162740ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ import "protocol.proto"; package OstProto; // IPv4 message Ip4 { enum IpAddrMode { e_im_fixed = 0; e_im_inc_host = 1; e_im_dec_host = 2; e_im_random_host = 3; } optional bool is_override_ver = 1; optional bool is_override_hdrlen = 2; optional bool is_override_totlen = 3; optional bool is_override_proto = 30; optional bool is_override_cksum = 4; optional uint32 ver_hdrlen = 5 [default = 0x45]; optional uint32 tos = 6; optional uint32 totlen = 7; optional uint32 id = 8 [default = 1234]; optional uint32 flags = 9; optional uint32 frag_ofs = 10; optional uint32 ttl = 11 [default = 127]; optional uint32 proto = 12; optional uint32 cksum = 13; // Source IP optional fixed32 src_ip = 14; optional IpAddrMode src_ip_mode = 15 [default = e_im_fixed]; optional uint32 src_ip_count = 16 [default = 16]; optional fixed32 src_ip_mask = 17 [default = 0xFFFFFF00]; // Destination IP optional fixed32 dst_ip = 18; optional IpAddrMode dst_ip_mode = 19 [default = e_im_fixed]; optional uint32 dst_ip_count = 20 [default = 16]; optional fixed32 dst_ip_mask = 21 [default = 0xFFFFFF00]; optional bytes options = 22; } extend Protocol { optional Ip4 ip4 = 301; } ostinato-0.9/common/ip4.ui000066400000000000000000000323611321315675200155570ustar00rootroot00000000000000 ip4 0 0 507 308 Form Override Version false Override Header Length (x4) false TOS/DSCP >HH; Override Length false Identification >HH HH; Fragment Offset (x8) Don't Fragment More Fragments Time To Live (TTL) false >HH; Override Checksum false >HH HH; Override Protocol false Qt::Horizontal 101 20 Mode Count Mask Source 009.009.009.009; ... Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Fixed Increment Host Decrement Host Random Host false false 009.009.009.009; ... Destination 000.000.000.000; ... Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Fixed Increment Host Decrement Host Random Host false false 009.009.009.009; ... Options false ... Qt::Vertical 20 40 cbIpVersionOverride leIpVersion cbIpHdrLenOverride leIpHdrLen leIpTos cbIpLengthOverride leIpLength leIpId leIpFragOfs cbIpFlagsDf cbIpFlagsMf leIpTtl cbIpProtocolOverride leIpProto cbIpCksumOverride leIpCksum leIpSrcAddr cmbIpSrcAddrMode leIpSrcAddrCount leIpSrcAddrMask leIpDstAddr cmbIpDstAddrMode leIpDstAddrCount leIpDstAddrMask leIpOptions tbIpOptionsEdit cbIpVersionOverride toggled(bool) leIpVersion setEnabled(bool) 108 11 195 11 cbIpHdrLenOverride toggled(bool) leIpHdrLen setEnabled(bool) 113 67 166 43 cbIpLengthOverride toggled(bool) leIpLength setEnabled(bool) 89 118 236 119 cbIpCksumOverride toggled(bool) leIpCksum setEnabled(bool) 387 140 406 122 cbIpProtocolOverride toggled(bool) leIpProto setEnabled(bool) 363 95 398 94 ostinato-0.9/common/ip4config.cpp000066400000000000000000000226441321315675200171150ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "ip4config.h" #include "ip4.h" #include Ip4ConfigForm::Ip4ConfigForm(QWidget *parent) : AbstractProtocolConfigForm(parent) { setupUi(this); leIpVersion->setValidator(new QIntValidator(0, 15, this)); leIpOptions->setValidator(new QRegExpValidator(QRegExp("[0-9a-fA-F]*"), this)); connect(cmbIpSrcAddrMode, SIGNAL(currentIndexChanged(int)), this, SLOT(on_cmbIpSrcAddrMode_currentIndexChanged(int))); connect(cmbIpDstAddrMode, SIGNAL(currentIndexChanged(int)), this, SLOT(on_cmbIpDstAddrMode_currentIndexChanged(int))); } Ip4ConfigForm::~Ip4ConfigForm() { } Ip4ConfigForm* Ip4ConfigForm::createInstance() { return new Ip4ConfigForm; } void Ip4ConfigForm::loadWidget(AbstractProtocol *proto) { cbIpVersionOverride->setChecked( proto->fieldData( Ip4Protocol::ip4_isOverrideVer, AbstractProtocol::FieldValue ).toBool()); leIpVersion->setText( proto->fieldData( Ip4Protocol::ip4_ver, AbstractProtocol::FieldValue ).toString()); cbIpHdrLenOverride->setChecked( proto->fieldData( Ip4Protocol::ip4_isOverrideHdrLen, AbstractProtocol::FieldValue ).toBool()); leIpHdrLen->setText( proto->fieldData( Ip4Protocol::ip4_hdrLen, AbstractProtocol::FieldValue ).toString()); leIpTos->setText(uintToHexStr( proto->fieldData( Ip4Protocol::ip4_tos, AbstractProtocol::FieldValue ).toUInt(), 1)); cbIpLengthOverride->setChecked( proto->fieldData( Ip4Protocol::ip4_isOverrideTotLen, AbstractProtocol::FieldValue ).toBool()); leIpLength->setText( proto->fieldData( Ip4Protocol::ip4_totLen, AbstractProtocol::FieldValue ).toString()); leIpId->setText(uintToHexStr( proto->fieldData( Ip4Protocol::ip4_id, AbstractProtocol::FieldValue ).toUInt(), 2)); leIpFragOfs->setText( proto->fieldData( Ip4Protocol::ip4_fragOfs, AbstractProtocol::FieldValue ).toString()); cbIpFlagsDf->setChecked(( proto->fieldData( Ip4Protocol::ip4_flags, AbstractProtocol::FieldValue ).toUInt() & IP_FLAG_DF) > 0); cbIpFlagsMf->setChecked(( proto->fieldData( Ip4Protocol::ip4_flags, AbstractProtocol::FieldValue ).toUInt() & IP_FLAG_MF) > 0); leIpTtl->setText( proto->fieldData( Ip4Protocol::ip4_ttl, AbstractProtocol::FieldValue ).toString()); cbIpProtocolOverride->setChecked( proto->fieldData( Ip4Protocol::ip4_isOverrideProto, AbstractProtocol::FieldValue ).toBool()); leIpProto->setText(uintToHexStr( proto->fieldData( Ip4Protocol::ip4_proto, AbstractProtocol::FieldValue ).toUInt(), 1)); cbIpCksumOverride->setChecked( proto->fieldData( Ip4Protocol::ip4_isOverrideCksum, AbstractProtocol::FieldValue ).toBool()); leIpCksum->setText(uintToHexStr( proto->fieldData( Ip4Protocol::ip4_cksum, AbstractProtocol::FieldValue ).toUInt(), 2)); leIpSrcAddr->setText(QHostAddress( proto->fieldData( Ip4Protocol::ip4_srcAddr, AbstractProtocol::FieldValue ).toUInt()).toString()); cmbIpSrcAddrMode->setCurrentIndex( proto->fieldData( Ip4Protocol::ip4_srcAddrMode, AbstractProtocol::FieldValue ).toUInt()); leIpSrcAddrCount->setText( proto->fieldData( Ip4Protocol::ip4_srcAddrCount, AbstractProtocol::FieldValue ).toString()); leIpSrcAddrMask->setText(QHostAddress( proto->fieldData( Ip4Protocol::ip4_srcAddrMask, AbstractProtocol::FieldValue ).toUInt()).toString()); leIpDstAddr->setText(QHostAddress( proto->fieldData( Ip4Protocol::ip4_dstAddr, AbstractProtocol::FieldValue ).toUInt()).toString()); cmbIpDstAddrMode->setCurrentIndex( proto->fieldData( Ip4Protocol::ip4_dstAddrMode, AbstractProtocol::FieldValue ).toUInt()); leIpDstAddrCount->setText( proto->fieldData( Ip4Protocol::ip4_dstAddrCount, AbstractProtocol::FieldValue ).toString()); leIpDstAddrMask->setText(QHostAddress( proto->fieldData( Ip4Protocol::ip4_dstAddrMask, AbstractProtocol::FieldValue ).toUInt()).toString()); leIpOptions->setText( proto->fieldData( Ip4Protocol::ip4_options, AbstractProtocol::FieldValue ).toByteArray().toHex()); } void Ip4ConfigForm::storeWidget(AbstractProtocol *proto) { uint ff = 0; proto->setFieldData( Ip4Protocol::ip4_isOverrideVer, cbIpVersionOverride->isChecked()); proto->setFieldData( Ip4Protocol::ip4_ver, leIpVersion->text()); proto->setFieldData( Ip4Protocol::ip4_isOverrideHdrLen, cbIpHdrLenOverride->isChecked()); proto->setFieldData( Ip4Protocol::ip4_hdrLen, leIpHdrLen->text()); proto->setFieldData( Ip4Protocol::ip4_tos, hexStrToUInt(leIpTos->text())); proto->setFieldData( Ip4Protocol::ip4_totLen, leIpLength->text()); proto->setFieldData( Ip4Protocol::ip4_isOverrideTotLen, cbIpLengthOverride->isChecked()); proto->setFieldData( Ip4Protocol::ip4_id, hexStrToUInt(leIpId->text())); proto->setFieldData( Ip4Protocol::ip4_fragOfs, leIpFragOfs->text()); if (cbIpFlagsDf->isChecked()) ff |= IP_FLAG_DF; if (cbIpFlagsMf->isChecked()) ff |= IP_FLAG_MF; proto->setFieldData( Ip4Protocol::ip4_flags, ff); proto->setFieldData( Ip4Protocol::ip4_ttl, leIpTtl->text()); proto->setFieldData( Ip4Protocol::ip4_isOverrideProto, cbIpProtocolOverride->isChecked()); proto->setFieldData( Ip4Protocol::ip4_proto, hexStrToUInt(leIpProto->text())); proto->setFieldData( Ip4Protocol::ip4_isOverrideCksum, cbIpCksumOverride->isChecked()); proto->setFieldData( Ip4Protocol::ip4_cksum, hexStrToUInt(leIpCksum->text())); proto->setFieldData( Ip4Protocol::ip4_srcAddr, QHostAddress(leIpSrcAddr->text()).toIPv4Address()); proto->setFieldData( Ip4Protocol::ip4_srcAddrMode, (OstProto::Ip4_IpAddrMode)cmbIpSrcAddrMode->currentIndex()); proto->setFieldData( Ip4Protocol::ip4_srcAddrCount, leIpSrcAddrCount->text()); proto->setFieldData( Ip4Protocol::ip4_srcAddrMask, QHostAddress(leIpSrcAddrMask->text()).toIPv4Address()); proto->setFieldData( Ip4Protocol::ip4_dstAddr, QHostAddress(leIpDstAddr->text()).toIPv4Address()); proto->setFieldData( Ip4Protocol::ip4_dstAddrMode, (OstProto::Ip4_IpAddrMode)cmbIpDstAddrMode->currentIndex()); proto->setFieldData( Ip4Protocol::ip4_dstAddrCount, leIpDstAddrCount->text()); proto->setFieldData( Ip4Protocol::ip4_dstAddrMask, QHostAddress(leIpDstAddrMask->text()).toIPv4Address()); proto->setFieldData( Ip4Protocol::ip4_options, QByteArray::fromHex(QByteArray().append(leIpOptions->text()))); } /* * Slots */ void Ip4ConfigForm::on_cmbIpSrcAddrMode_currentIndexChanged(int index) { if (index == OstProto::Ip4::e_im_fixed) { leIpSrcAddrCount->setDisabled(true); leIpSrcAddrMask->setDisabled(true); } else { leIpSrcAddrCount->setEnabled(true); leIpSrcAddrMask->setEnabled(true); } } void Ip4ConfigForm::on_cmbIpDstAddrMode_currentIndexChanged(int index) { if (index == OstProto::Ip4::e_im_fixed) { leIpDstAddrCount->setDisabled(true); leIpDstAddrMask->setDisabled(true); } else { leIpDstAddrCount->setEnabled(true); leIpDstAddrMask->setEnabled(true); } } ostinato-0.9/common/ip4config.h000066400000000000000000000023511321315675200165530ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _IPV4_CONFIG_H #define _IPV4_CONFIG_H #include "abstractprotocolconfig.h" #include "ui_ip4.h" class Ip4ConfigForm : public AbstractProtocolConfigForm, private Ui::ip4 { Q_OBJECT public: Ip4ConfigForm(QWidget *parent = 0); virtual ~Ip4ConfigForm(); static Ip4ConfigForm* createInstance(); virtual void loadWidget(AbstractProtocol *proto); virtual void storeWidget(AbstractProtocol *proto); private slots: void on_cmbIpSrcAddrMode_currentIndexChanged(int index); void on_cmbIpDstAddrMode_currentIndexChanged(int index); }; #endif ostinato-0.9/common/ip4edit.h000066400000000000000000000022561321315675200162370ustar00rootroot00000000000000/* Copyright (C) 2016 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _IP4_EDIT_H #define _IP4_EDIT_H #include #include class Ip4Edit: public QLineEdit { public: Ip4Edit(QWidget *parent = 0); quint32 value(); void setValue(quint32 val); }; inline Ip4Edit::Ip4Edit(QWidget *parent) : QLineEdit(parent) { setInputMask(QString("000.000.000.000; ")); } inline quint32 Ip4Edit::value() { return QHostAddress(text()).toIPv4Address(); } inline void Ip4Edit::setValue(quint32 val) { setText(QHostAddress(val).toString()); } #endif ostinato-0.9/common/ip4over4.h000066400000000000000000000057251321315675200163550ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _IP_4_OVER_4_H #define _IP_4_OVER_4_H #include "ip4over4.pb.h" #include "comboprotocol.h" #include "ip4.h" typedef ComboProtocol Ip4over4Combo; class Ip4over4Protocol : public Ip4over4Combo { public: Ip4over4Protocol(StreamBase *stream, AbstractProtocol *parent = 0) : Ip4over4Combo(stream, parent) { } static Ip4over4Protocol* createInstance(StreamBase *stream, AbstractProtocol *parent) { return new Ip4over4Protocol(stream, parent); } virtual void protoDataCopyInto(OstProto::Protocol &protocol) const { OstProto::Protocol tempProto; protoA->protoDataCopyInto(tempProto); protocol.MutableExtension(OstProto::ip4over4) ->MutableExtension(OstProto::ip4_outer) ->CopyFrom(tempProto.GetExtension(OstProto::ip4)); tempProto.Clear(); protoB->protoDataCopyInto(tempProto); protocol.MutableExtension(OstProto::ip4over4) ->MutableExtension(OstProto::ip4_inner) ->CopyFrom(tempProto.GetExtension(OstProto::ip4)); protocol.mutable_protocol_id()->set_id(protocolNumber()); } virtual void protoDataCopyFrom(const OstProto::Protocol &protocol) { if (protocol.protocol_id().id() == protocolNumber() && protocol.HasExtension(OstProto::ip4over4)) { OstProto::Protocol tempProto; // NOTE: To use protoX->protoDataCopyFrom() we need to arrange // so that it sees its own protocolNumber() and its own extension // in 'protocol' tempProto.mutable_protocol_id()->set_id(protoA->protocolNumber()); tempProto.MutableExtension(OstProto::ip4)->CopyFrom( protocol.GetExtension(OstProto::ip4over4).GetExtension( OstProto::ip4_outer)); protoA->protoDataCopyFrom(tempProto); tempProto.Clear(); tempProto.mutable_protocol_id()->set_id(protoB->protocolNumber()); tempProto.MutableExtension(OstProto::ip4)->CopyFrom( protocol.GetExtension(OstProto::ip4over4).GetExtension( OstProto::ip4_inner)); protoB->protoDataCopyFrom(tempProto); } } }; #endif ostinato-0.9/common/ip4over4.proto000066400000000000000000000016771321315675200172730ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ import "protocol.proto"; import "ip4.proto"; package OstProto; // IP 4over4 (also called IPIP) message Ip4over4 { extensions 1 to 2; } extend Ip4over4 { optional Ip4 ip4_outer = 1; optional Ip4 ip4_inner = 2; } extend Protocol { optional Ip4over4 ip4over4 = 305; } ostinato-0.9/common/ip4over4config.h000066400000000000000000000020011321315675200175230ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _IP_4_OVER_4_CONFIG_H #define _IP_4_OVER_4_CONFIG_H #include "comboprotocolconfig.h" #include "ip4config.h" #include "ip4.h" #include "protocol.pb.h" typedef ComboProtocolConfigForm < OstProto::Protocol::kIp4over4FieldNumber, Ip4ConfigForm, Ip4ConfigForm, Ip4Protocol, Ip4Protocol > Ip4over4ConfigForm; #endif ostinato-0.9/common/ip4over6.h000066400000000000000000000016101321315675200163440ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _IP_4_OVER_6_H #define _IP_4_OVER_6_H #include "comboprotocol.h" #include "ip4.h" #include "ip6.h" typedef ComboProtocol Ip4over6Protocol; #endif ostinato-0.9/common/ip4over6.proto000066400000000000000000000015521321315675200172650ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ import "protocol.proto"; package OstProto; // IP Tunelling - IP 4over6 message Ip4over6 { // Empty since this is a 'combo' protocol } extend Protocol { optional Ip4over6 ip4over6 = 304; } ostinato-0.9/common/ip4over6config.h000066400000000000000000000020171321315675200175340ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _IP_4_OVER_6_CONFIG_H #define _IP_4_OVER_6_CONFIG_H #include "comboprotocolconfig.h" #include "ip4config.h" #include "ip6config.h" #include "ip4.h" #include "ip6.h" typedef ComboProtocolConfigForm < OstProto::Protocol::kIp4over6FieldNumber, Ip6ConfigForm, Ip4ConfigForm, Ip6Protocol, Ip4Protocol > Ip4over6ConfigForm; #endif ostinato-0.9/common/ip4pdml.cpp000066400000000000000000000066731321315675200166100ustar00rootroot00000000000000/* Copyright (C) 2011 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "ip4pdml.h" #include "hexdump.pb.h" #include "ip4.pb.h" PdmlIp4Protocol::PdmlIp4Protocol() { ostProtoId_ = OstProto::Protocol::kIp4FieldNumber; fieldMap_.insert("ip.dsfield", OstProto::Ip4::kTosFieldNumber); fieldMap_.insert("ip.len", OstProto::Ip4::kTotlenFieldNumber); fieldMap_.insert("ip.id", OstProto::Ip4::kIdFieldNumber); //fieldMap_.insert("ip.flags", OstProto::Ip4::kFlagsFieldNumber); fieldMap_.insert("ip.frag_offset", OstProto::Ip4::kFragOfsFieldNumber); fieldMap_.insert("ip.ttl", OstProto::Ip4::kTtlFieldNumber); fieldMap_.insert("ip.proto", OstProto::Ip4::kProtoFieldNumber); fieldMap_.insert("ip.checksum", OstProto::Ip4::kCksumFieldNumber); fieldMap_.insert("ip.src", OstProto::Ip4::kSrcIpFieldNumber); fieldMap_.insert("ip.dst", OstProto::Ip4::kDstIpFieldNumber); } PdmlProtocol* PdmlIp4Protocol::createInstance() { return new PdmlIp4Protocol(); } void PdmlIp4Protocol::unknownFieldHandler(QString name, int /*pos*/, int /*size*/, const QXmlStreamAttributes &attributes, OstProto::Protocol *pbProto, OstProto::Stream* /*stream*/) { bool isOk; if (name == "ip.version") { OstProto::Ip4 *ip4 = pbProto->MutableExtension(OstProto::ip4); if (!attributes.value("unmaskedvalue").isEmpty()) ip4->set_ver_hdrlen(attributes.value("unmaskedvalue") .toString().toUInt(&isOk, kBaseHex)); else ip4->set_ver_hdrlen(attributes.value("value") .toString().toUInt(&isOk, kBaseHex)); } else if ((name == "ip.options") || attributes.value("show").toString().startsWith("Options")) { options_ = QByteArray::fromHex( attributes.value("value").toString().toUtf8()); } else if (name == "ip.flags") { OstProto::Ip4 *ip4 = pbProto->MutableExtension(OstProto::ip4); ip4->set_flags(attributes.value("value").toString().toUInt(&isOk, kBaseHex) >> 5); } } void PdmlIp4Protocol::postProtocolHandler(OstProto::Protocol *pbProto, OstProto::Stream *stream) { OstProto::Ip4 *ip4 = pbProto->MutableExtension(OstProto::ip4); ip4->set_is_override_ver(true); ip4->set_is_override_hdrlen(true); ip4->set_is_override_totlen(true); ip4->set_is_override_proto(true); ip4->set_is_override_cksum(true); if (options_.size()) { OstProto::Protocol *proto = stream->add_protocol(); proto->mutable_protocol_id()->set_id( OstProto::Protocol::kHexDumpFieldNumber); OstProto::HexDump *hexDump = proto->MutableExtension(OstProto::hexDump); hexDump->mutable_content()->append(options_.constData(), options_.size()); hexDump->set_pad_until_end(false); options_.resize(0); } } ostinato-0.9/common/ip4pdml.h000066400000000000000000000023001321315675200162340ustar00rootroot00000000000000/* Copyright (C) 2011 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _IP4_PDML_H #define _IP4_PDML_H #include "pdmlprotocol.h" class PdmlIp4Protocol : public PdmlProtocol { public: static PdmlProtocol* createInstance(); virtual void unknownFieldHandler(QString name, int pos, int size, const QXmlStreamAttributes &attributes, OstProto::Protocol *pbProto, OstProto::Stream *stream); virtual void postProtocolHandler(OstProto::Protocol *pbProto, OstProto::Stream *stream); protected: PdmlIp4Protocol(); private: QByteArray options_; }; #endif ostinato-0.9/common/ip6.cpp000066400000000000000000000571601321315675200157320ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "ip6.h" #include Ip6Protocol::Ip6Protocol(StreamBase *stream, AbstractProtocol *parent) : AbstractProtocol(stream, parent) { } Ip6Protocol::~Ip6Protocol() { } AbstractProtocol* Ip6Protocol::createInstance(StreamBase *stream, AbstractProtocol *parent) { return new Ip6Protocol(stream, parent); } quint32 Ip6Protocol::protocolNumber() const { return OstProto::Protocol::kIp6FieldNumber; } void Ip6Protocol::protoDataCopyInto(OstProto::Protocol &protocol) const { protocol.MutableExtension(OstProto::ip6)->CopyFrom(data); protocol.mutable_protocol_id()->set_id(protocolNumber()); } void Ip6Protocol::protoDataCopyFrom(const OstProto::Protocol &protocol) { if (protocol.protocol_id().id() == protocolNumber() && protocol.HasExtension(OstProto::ip6)) data.MergeFrom(protocol.GetExtension(OstProto::ip6)); } QString Ip6Protocol::name() const { return QString("Internet Protocol ver 6"); } QString Ip6Protocol::shortName() const { return QString("IPv6"); } AbstractProtocol::ProtocolIdType Ip6Protocol::protocolIdType() const { return ProtocolIdIp; } quint32 Ip6Protocol::protocolId(ProtocolIdType type) const { switch(type) { case ProtocolIdEth: return 0x86dd; case ProtocolIdIp: return 0x29; default:break; } return AbstractProtocol::protocolId(type); } int Ip6Protocol::fieldCount() const { return ip6_fieldCount; } AbstractProtocol::FieldFlags Ip6Protocol::fieldFlags(int index) const { AbstractProtocol::FieldFlags flags; flags = AbstractProtocol::fieldFlags(index); switch (index) { case ip6_version: case ip6_trafficClass: case ip6_flowLabel: case ip6_payloadLength: case ip6_nextHeader: case ip6_hopLimit: case ip6_srcAddress: case ip6_dstAddress: break; case ip6_isOverrideVersion: case ip6_isOverridePayloadLength: case ip6_isOverrideNextHeader: case ip6_srcAddrMode: case ip6_srcAddrCount: case ip6_srcAddrPrefix: case ip6_dstAddrMode: case ip6_dstAddrCount: case ip6_dstAddrPrefix: flags &= ~FrameField; flags |= MetaField; break; default: qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, index); break; } return flags; } QVariant Ip6Protocol::fieldData(int index, FieldAttrib attrib, int streamIndex) const { switch (index) { case ip6_version: { quint8 ver; switch(attrib) { case FieldValue: case FieldFrameValue: case FieldTextValue: if (data.is_override_version()) ver = data.version() & 0xF; else ver = 0x6; break; default: ver = 0; // avoid the 'maybe used unitialized' warning break; } switch(attrib) { case FieldName: return QString("Version"); case FieldValue: return ver; case FieldTextValue: return QString("%1").arg(ver); case FieldFrameValue: return QByteArray(1, char(ver)); case FieldBitSize: return 4; default: break; } break; } case ip6_trafficClass: { switch(attrib) { case FieldName: return QString("Traffic Class"); case FieldValue: return data.traffic_class() & 0xFF; case FieldTextValue: return QString("%1").arg(data.traffic_class() & 0xFF, 2, BASE_HEX, QChar('0')); case FieldFrameValue: return QByteArray(1, char(data.traffic_class() & 0xFF)); default: break; } break; } case ip6_flowLabel: { switch(attrib) { case FieldName: return QString("Flow Label"); case FieldValue: return data.flow_label() & 0xFFFFF; case FieldTextValue: return QString("%1").arg(data.flow_label() & 0xFFFFF, 5, BASE_HEX, QChar('0')); case FieldFrameValue: { QByteArray fv; fv.resize(4); qToBigEndian((quint32) data.flow_label() & 0xFFFFF, (uchar*) fv.data()); fv = fv.right(3); return fv; } case FieldBitSize: return 20; default: break; } break; } case ip6_payloadLength: { quint16 len; switch(attrib) { case FieldValue: case FieldFrameValue: case FieldTextValue: if (data.is_override_payload_length()) len = data.payload_length(); else len = protocolFramePayloadSize(streamIndex); break; default: len = 0; // avoid the 'maybe used unitialized' warning break; } switch(attrib) { case FieldName: return QString("Payload Length"); case FieldValue: return len; case FieldFrameValue: { QByteArray fv; fv.resize(2); qToBigEndian(len, (uchar*) fv.data()); return fv; } case FieldTextValue: return QString("%1").arg(len); case FieldBitSize: return 16; default: break; } break; } case ip6_nextHeader: { quint8 nextHdr; switch(attrib) { case FieldValue: case FieldFrameValue: case FieldTextValue: if (data.is_override_next_header()) { nextHdr = data.next_header(); } else { nextHdr = payloadProtocolId(ProtocolIdIp); if ((nextHdr == 0) && next && (next->protocolIdType() == ProtocolIdNone)) { nextHdr = 0x3b; // IPv6 No-Next-Header } } break; default: nextHdr = 0; // avoid the 'maybe used unitialized' warning break; } switch(attrib) { case FieldName: return QString("Next Header"); case FieldValue: return nextHdr; case FieldTextValue: return QString("%1").arg(nextHdr, 2, BASE_HEX, QChar('0')); case FieldFrameValue: return QByteArray(1, char(nextHdr)); default: break; } break; } case ip6_hopLimit: { switch(attrib) { case FieldName: return QString("Hop Limit"); case FieldValue: return data.hop_limit() & 0xFF; case FieldTextValue: return QString("%1").arg(data.hop_limit() & 0xFF); case FieldFrameValue: return QByteArray(1, char(data.hop_limit() & 0xFF)); default: break; } break; } case ip6_srcAddress: { int u, p, q; quint64 maskHi = 0, maskLo = 0; quint64 prefixHi, prefixLo; quint64 hostHi = 0, hostLo = 0; quint64 srcHi = 0, srcLo = 0; switch(data.src_addr_mode()) { case OstProto::Ip6::kFixed: srcHi = data.src_addr_hi(); srcLo = data.src_addr_lo(); break; case OstProto::Ip6::kIncHost: case OstProto::Ip6::kDecHost: case OstProto::Ip6::kRandomHost: u = streamIndex % data.src_addr_count(); if (data.src_addr_prefix() > 64) { p = 64; q = data.src_addr_prefix() - 64; } else { p = data.src_addr_prefix(); q = 0; } if (p > 0) maskHi = ~((quint64(1) << p) - 1); if (q > 0) maskLo = ~((quint64(1) << q) - 1); prefixHi = data.src_addr_hi() & maskHi; prefixLo = data.src_addr_lo() & maskLo; if (data.src_addr_mode() == OstProto::Ip6::kIncHost) { hostHi = ((data.src_addr_hi() & ~maskHi) + u) & ~maskHi; hostLo = ((data.src_addr_lo() & ~maskLo) + u) & ~maskLo; } else if (data.src_addr_mode() == OstProto::Ip6::kDecHost) { hostHi = ((data.src_addr_hi() & ~maskHi) - u) & ~maskHi; hostLo = ((data.src_addr_lo() & ~maskLo) - u) & ~maskLo; } else if (data.src_addr_mode()==OstProto::Ip6::kRandomHost) { hostHi = qrand() & ~maskHi; hostLo = qrand() & ~maskLo; } srcHi = prefixHi | hostHi; srcLo = prefixLo | hostLo; break; default: qWarning("Unhandled src_addr_mode = %d", data.src_addr_mode()); } switch(attrib) { case FieldName: return QString("Source"); case FieldValue: case FieldFrameValue: case FieldTextValue: { QByteArray fv; fv.resize(16); qToBigEndian(srcHi, (uchar*) fv.data()); qToBigEndian(srcLo, (uchar*) (fv.data() + 8)); if (attrib == FieldTextValue) return QHostAddress((quint8*)fv.constData()).toString(); else return fv; } default: break; } break; } case ip6_dstAddress: { int u, p, q; quint64 maskHi = 0, maskLo = 0; quint64 prefixHi, prefixLo; quint64 hostHi = 0, hostLo = 0; quint64 dstHi = 0, dstLo = 0; switch(data.dst_addr_mode()) { case OstProto::Ip6::kFixed: dstHi = data.dst_addr_hi(); dstLo = data.dst_addr_lo(); break; case OstProto::Ip6::kIncHost: case OstProto::Ip6::kDecHost: case OstProto::Ip6::kRandomHost: u = streamIndex % data.dst_addr_count(); if (data.dst_addr_prefix() > 64) { p = 64; q = data.dst_addr_prefix() - 64; } else { p = data.dst_addr_prefix(); q = 0; } if (p > 0) maskHi = ~((quint64(1) << p) - 1); if (q > 0) maskLo = ~((quint64(1) << q) - 1); prefixHi = data.dst_addr_hi() & maskHi; prefixLo = data.dst_addr_lo() & maskLo; if (data.dst_addr_mode() == OstProto::Ip6::kIncHost) { hostHi = ((data.dst_addr_hi() & ~maskHi) + u) & ~maskHi; hostLo = ((data.dst_addr_lo() & ~maskLo) + u) & ~maskLo; } else if (data.dst_addr_mode() == OstProto::Ip6::kDecHost) { hostHi = ((data.dst_addr_hi() & ~maskHi) - u) & ~maskHi; hostLo = ((data.dst_addr_lo() & ~maskLo) - u) & ~maskLo; } else if (data.dst_addr_mode()==OstProto::Ip6::kRandomHost) { hostHi = qrand() & ~maskHi; hostLo = qrand() & ~maskLo; } dstHi = prefixHi | hostHi; dstLo = prefixLo | hostLo; break; default: qWarning("Unhandled dst_addr_mode = %d", data.dst_addr_mode()); } switch(attrib) { case FieldName: return QString("Destination"); case FieldValue: case FieldFrameValue: case FieldTextValue: { QByteArray fv; fv.resize(16); qToBigEndian(dstHi, (uchar*) fv.data()); qToBigEndian(dstLo, (uchar*) (fv.data() + 8)); if (attrib == FieldTextValue) return QHostAddress((quint8*)fv.constData()).toString(); else return fv; } default: break; } break; } // Meta-Fields case ip6_isOverrideVersion: { switch(attrib) { case FieldValue: return data.is_override_version(); default: break; } break; } case ip6_isOverridePayloadLength: { switch(attrib) { case FieldValue: return data.is_override_payload_length(); default: break; } break; } case ip6_isOverrideNextHeader: { switch(attrib) { case FieldValue: return data.is_override_next_header(); default: break; } break; } case ip6_srcAddrMode: { switch(attrib) { case FieldValue: return data.src_addr_mode(); default: break; } break; } case ip6_srcAddrCount: { switch(attrib) { case FieldValue: return data.src_addr_count(); default: break; } break; } case ip6_srcAddrPrefix: { switch(attrib) { case FieldValue: return data.src_addr_prefix(); default: break; } break; } case ip6_dstAddrMode: { switch(attrib) { case FieldValue: return data.dst_addr_mode(); default: break; } break; } case ip6_dstAddrCount: { switch(attrib) { case FieldValue: return data.dst_addr_count(); default: break; } break; } case ip6_dstAddrPrefix: { switch(attrib) { case FieldValue: return data.dst_addr_prefix(); default: break; } break; } default: qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, index); break; } return AbstractProtocol::fieldData(index, attrib, streamIndex); } bool Ip6Protocol::setFieldData(int index, const QVariant &value, FieldAttrib attrib) { bool isOk = false; if (attrib != FieldValue) goto _exit; switch (index) { case ip6_version: { uint ver = value.toUInt(&isOk); if (isOk) data.set_version(ver & 0xF); break; } case ip6_trafficClass: { uint trfClass = value.toUInt(&isOk); if (isOk) data.set_traffic_class(trfClass & 0xFF); break; } case ip6_flowLabel: { uint fl = value.toUInt(&isOk); if (isOk) data.set_flow_label(fl & 0xFFFFF); break; } case ip6_payloadLength: { uint len = value.toUInt(&isOk); if (isOk) data.set_payload_length(len & 0xFFFF); break; } case ip6_nextHeader: { uint ver = value.toUInt(&isOk); if (isOk) data.set_next_header(ver & 0xFF); break; } case ip6_hopLimit: { uint hl = value.toUInt(&isOk); if (isOk) data.set_hop_limit(hl & 0xFF); break; } case ip6_srcAddress: { Q_IPV6ADDR addr = QHostAddress(value.toString()).toIPv6Address(); quint64 x; x = (quint64(addr[0]) << 56) | (quint64(addr[1]) << 48) | (quint64(addr[2]) << 40) | (quint64(addr[3]) << 32) | (quint64(addr[4]) << 24) | (quint64(addr[5]) << 16) | (quint64(addr[6]) << 8) | (quint64(addr[7]) << 0); data.set_src_addr_hi(x); x = (quint64(addr[ 8]) << 56) | (quint64(addr[ 9]) << 48) | (quint64(addr[10]) << 40) | (quint64(addr[11]) << 32) | (quint64(addr[12]) << 24) | (quint64(addr[13]) << 16) | (quint64(addr[14]) << 8) | (quint64(addr[15]) << 0); data.set_src_addr_lo(x); break; } case ip6_dstAddress: { Q_IPV6ADDR addr = QHostAddress(value.toString()).toIPv6Address(); quint64 x; x = (quint64(addr[0]) << 56) | (quint64(addr[1]) << 48) | (quint64(addr[2]) << 40) | (quint64(addr[3]) << 32) | (quint64(addr[4]) << 24) | (quint64(addr[5]) << 16) | (quint64(addr[6]) << 8) | (quint64(addr[7]) << 0); data.set_dst_addr_hi(x); x = (quint64(addr[ 8]) << 56) | (quint64(addr[ 9]) << 48) | (quint64(addr[10]) << 40) | (quint64(addr[11]) << 32) | (quint64(addr[12]) << 24) | (quint64(addr[13]) << 16) | (quint64(addr[14]) << 8) | (quint64(addr[15]) << 0); data.set_dst_addr_lo(x); break; } // Meta-Fields case ip6_isOverrideVersion: { bool ovr = value.toBool(); data.set_is_override_version(ovr); isOk = true; break; } case ip6_isOverridePayloadLength: { bool ovr = value.toBool(); data.set_is_override_payload_length(ovr); isOk = true; break; } case ip6_isOverrideNextHeader: { bool ovr = value.toBool(); data.set_is_override_next_header(ovr); isOk = true; break; } case ip6_srcAddrMode: { uint mode = value.toUInt(&isOk); if (isOk && data.AddrMode_IsValid(mode)) data.set_src_addr_mode((OstProto::Ip6::AddrMode) mode); else isOk = false; break; } case ip6_srcAddrCount: { uint count = value.toUInt(&isOk); if (isOk) data.set_src_addr_count(count); break; } case ip6_srcAddrPrefix: { uint prefix = value.toUInt(&isOk); if (isOk) data.set_src_addr_prefix(prefix); break; } case ip6_dstAddrMode: { uint mode = value.toUInt(&isOk); if (isOk && data.AddrMode_IsValid(mode)) data.set_dst_addr_mode((OstProto::Ip6::AddrMode) mode); else isOk = false; break; } case ip6_dstAddrCount: { uint count = value.toUInt(&isOk); if (isOk) data.set_dst_addr_count(count); break; } case ip6_dstAddrPrefix: { uint prefix = value.toUInt(&isOk); if (isOk) data.set_dst_addr_prefix(prefix); break; } default: qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, index); break; } _exit: return isOk; } int Ip6Protocol::protocolFrameVariableCount() const { int count = AbstractProtocol::protocolFrameVariableCount(); if (data.src_addr_mode() != OstProto::Ip6::kFixed) count = AbstractProtocol::lcm(count, data.src_addr_count()); if (data.dst_addr_mode() != OstProto::Ip6::kFixed) count = AbstractProtocol::lcm(count, data.dst_addr_count()); return count; } quint32 Ip6Protocol::protocolFrameCksum(int streamIndex, CksumType cksumType) const { if (cksumType == CksumIpPseudo) { quint32 sum = 0; QByteArray fv = protocolFrameValue(streamIndex); const quint8 *p = (quint8*) fv.constData(); // src-ip, dst-ip for (int i = 8; i < fv.size(); i+=2) sum += *((quint16*)(p + i)); sum += *((quint16*)(p + 4)); // payload len sum += qToBigEndian((quint16) *(p + 6)); // proto while(sum>>16) sum = (sum & 0xFFFF) + (sum >> 16); return ~qFromBigEndian((quint16)sum); } return AbstractProtocol::protocolFrameCksum(streamIndex, cksumType); } ostinato-0.9/common/ip6.h000066400000000000000000000064401321315675200153720ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _IP6_H #define _IP6_H #include "abstractprotocol.h" #include "ip6.pb.h" /* IPv6 Protocol Frame Format - +-----+----------+-----------------------+ | Ver | TrfClass | FlowLabel | | (4) | (8) | (20) | +-----+-------------+---------+----------+ | Payload Length | NextHdr | HopLimit | | (16) | (8) | (8) | +-------------------+---------+----------+ | | | Source Address | | (128) | | | +-----+------+------+------+------+------+ | | | Destination Address | | (128) | | | +-----+------+------+------+------+------+ Figures in brackets represent field width in bits */ class Ip6Protocol : public AbstractProtocol { public: enum ip6field { // Frame Fields ip6_version = 0, ip6_trafficClass, ip6_flowLabel, ip6_payloadLength, ip6_nextHeader, ip6_hopLimit, ip6_srcAddress, ip6_dstAddress, // Meta Fields ip6_isOverrideVersion, ip6_isOverridePayloadLength, ip6_isOverrideNextHeader, ip6_srcAddrMode, ip6_srcAddrCount, ip6_srcAddrPrefix, ip6_dstAddrMode, ip6_dstAddrCount, ip6_dstAddrPrefix, ip6_fieldCount }; Ip6Protocol(StreamBase *stream, AbstractProtocol *parent = 0); virtual ~Ip6Protocol(); static AbstractProtocol* createInstance(StreamBase *stream, AbstractProtocol *parent = 0); virtual quint32 protocolNumber() const; virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); virtual ProtocolIdType protocolIdType() const; virtual quint32 protocolId(ProtocolIdType type) const; virtual QString name() const; virtual QString shortName() const; virtual int fieldCount() const; virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; virtual QVariant fieldData(int index, FieldAttrib attrib, int streamIndex = 0) const; virtual bool setFieldData(int index, const QVariant &value, FieldAttrib attrib = FieldValue); virtual int protocolFrameVariableCount() const; virtual quint32 protocolFrameCksum(int streamIndex = 0, CksumType cksumType = CksumIp) const; private: OstProto::Ip6 data; }; #endif ostinato-0.9/common/ip6.proto000066400000000000000000000034301321315675200163020ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ import "protocol.proto"; package OstProto; // Ip6 Protocol message Ip6 { enum AddrMode { kFixed = 0; kIncHost = 1; kDecHost = 2; kRandomHost = 3; } optional bool is_override_version = 1; optional bool is_override_payload_length = 2; optional bool is_override_next_header = 3; optional uint32 version = 4 [default = 0x6]; optional uint32 traffic_class = 5; optional uint32 flow_label = 6; optional uint32 payload_length = 7; optional uint32 next_header = 8; optional uint32 hop_limit = 9 [default = 127]; optional uint64 src_addr_hi = 10; optional uint64 src_addr_lo = 11; optional AddrMode src_addr_mode = 12 [default = kFixed]; optional uint32 src_addr_count = 13 [default = 16]; optional uint32 src_addr_prefix = 14 [default = 64]; optional uint64 dst_addr_hi = 15; optional uint64 dst_addr_lo = 16; optional AddrMode dst_addr_mode = 17 [default = kFixed]; optional uint32 dst_addr_count = 18 [default = 16]; optional uint32 dst_addr_prefix = 19 [default = 64]; } extend Protocol { optional Ip6 ip6 = 302; } ostinato-0.9/common/ip6.ui000066400000000000000000000305311321315675200155560ustar00rootroot00000000000000 Ip6 0 0 506 233 Form Version false Qt::Vertical Payload Length false Traffic Class trafficClass >HH; Next Header false HH; Flow Label flowLabel >H HH HH; Hop Limit hopLimit false Qt::Horizontal 51 20 Address Mode Count Prefix Source 1 0 Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Fixed Increment Host Decrement Host Random Host false 0 0 50 16777215 10 false 0 0 50 16777215 /009; /64 Destination 1 0 Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Fixed Increment Host Decrement Host Random Host false 0 0 50 16777215 10 false 0 0 50 16777215 /009; /64 Qt::Vertical 20 40 isVersionOverride version trafficClass flowLabel isPayloadLengthOverride payloadLength isNextHeaderOverride nextHeader hopLimit srcAddr srcAddrModeCombo srcAddrCount srcAddrPrefix dstAddr dstAddrModeCombo dstAddrCount dstAddrPrefix isVersionOverride toggled(bool) version setEnabled(bool) 67 22 195 11 isPayloadLengthOverride toggled(bool) payloadLength setEnabled(bool) 319 28 493 29 isNextHeaderOverride toggled(bool) nextHeader setEnabled(bool) 316 41 348 46 ostinato-0.9/common/ip6config.cpp000066400000000000000000000160441321315675200171140ustar00rootroot00000000000000/* Copyright (C) 2010-2014 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "ip6config.h" #include "ip6.h" #include "ipv6addressvalidator.h" #include Ip6ConfigForm::Ip6ConfigForm(QWidget *parent) : AbstractProtocolConfigForm(parent) { setupUi(this); version->setValidator(new QIntValidator(0, 0xF, this)); payloadLength->setValidator(new QIntValidator(0, 0xFFFF, this)); hopLimit->setValidator(new QIntValidator(0, 0xFF, this)); srcAddr->setValidator(new IPv6AddressValidator(this)); srcAddrCount->setValidator(new QIntValidator(this)); //srcAddrPrefix->setValidator(new QIntValidator(0, 128, this)); dstAddr->setValidator(new IPv6AddressValidator(this)); dstAddrCount->setValidator(new QIntValidator(this)); //dstAddrPrefix->setValidator(new QIntValidator(0, 128, this)); } AbstractProtocolConfigForm* Ip6ConfigForm::createInstance() { return new Ip6ConfigForm; } void Ip6ConfigForm::on_srcAddr_editingFinished() { srcAddr->setText(QHostAddress(srcAddr->text()).toString()); } void Ip6ConfigForm::on_dstAddr_editingFinished() { dstAddr->setText(QHostAddress(dstAddr->text()).toString()); } void Ip6ConfigForm::on_srcAddrModeCombo_currentIndexChanged(int index) { bool enabled = (index > 0); srcAddrCount->setEnabled(enabled); srcAddrPrefix->setEnabled(enabled); } void Ip6ConfigForm::on_dstAddrModeCombo_currentIndexChanged(int index) { bool enabled = (index > 0); dstAddrCount->setEnabled(enabled); dstAddrPrefix->setEnabled(enabled); } void Ip6ConfigForm::loadWidget(AbstractProtocol *ip6Proto) { isVersionOverride->setChecked( ip6Proto->fieldData( Ip6Protocol::ip6_isOverrideVersion, AbstractProtocol::FieldValue ).toBool()); version->setText( ip6Proto->fieldData( Ip6Protocol::ip6_version, AbstractProtocol::FieldValue ).toString()); trafficClass->setText(uintToHexStr( ip6Proto->fieldData( Ip6Protocol::ip6_trafficClass, AbstractProtocol::FieldValue ).toUInt(), 1)); flowLabel->setText(QString("%1").arg( ip6Proto->fieldData( Ip6Protocol::ip6_flowLabel, AbstractProtocol::FieldValue ).toUInt(), 5, BASE_HEX, QChar('0'))); isPayloadLengthOverride->setChecked( ip6Proto->fieldData( Ip6Protocol::ip6_isOverridePayloadLength, AbstractProtocol::FieldValue ).toBool()); payloadLength->setText( ip6Proto->fieldData( Ip6Protocol::ip6_payloadLength, AbstractProtocol::FieldValue ).toString()); isNextHeaderOverride->setChecked( ip6Proto->fieldData( Ip6Protocol::ip6_isOverrideNextHeader, AbstractProtocol::FieldValue ).toBool()); nextHeader->setText(uintToHexStr( ip6Proto->fieldData( Ip6Protocol::ip6_nextHeader, AbstractProtocol::FieldValue ).toUInt(), 1)); hopLimit->setText( ip6Proto->fieldData( Ip6Protocol::ip6_hopLimit, AbstractProtocol::FieldValue ).toString()); srcAddr->setText( ip6Proto->fieldData( Ip6Protocol::ip6_srcAddress, AbstractProtocol::FieldTextValue ).toString()); srcAddrModeCombo->setCurrentIndex( ip6Proto->fieldData( Ip6Protocol::ip6_srcAddrMode, AbstractProtocol::FieldValue ).toUInt()); srcAddrCount->setText( ip6Proto->fieldData( Ip6Protocol::ip6_srcAddrCount, AbstractProtocol::FieldValue ).toString()); srcAddrPrefix->setText( ip6Proto->fieldData( Ip6Protocol::ip6_srcAddrPrefix, AbstractProtocol::FieldValue ).toString()); dstAddr->setText( ip6Proto->fieldData( Ip6Protocol::ip6_dstAddress, AbstractProtocol::FieldTextValue ).toString()); dstAddrModeCombo->setCurrentIndex( ip6Proto->fieldData( Ip6Protocol::ip6_dstAddrMode, AbstractProtocol::FieldValue ).toUInt()); dstAddrCount->setText( ip6Proto->fieldData( Ip6Protocol::ip6_dstAddrCount, AbstractProtocol::FieldValue ).toString()); dstAddrPrefix->setText( ip6Proto->fieldData( Ip6Protocol::ip6_dstAddrPrefix, AbstractProtocol::FieldValue ).toString()); } void Ip6ConfigForm::storeWidget(AbstractProtocol *ip6Proto) { bool isOk; ip6Proto->setFieldData( Ip6Protocol::ip6_isOverrideVersion, isVersionOverride->isChecked()); ip6Proto->setFieldData( Ip6Protocol::ip6_version, version->text()); ip6Proto->setFieldData( Ip6Protocol::ip6_trafficClass, trafficClass->text().remove(QChar(' ')).toUInt(&isOk, BASE_HEX)); ip6Proto->setFieldData( Ip6Protocol::ip6_flowLabel, flowLabel->text().remove(QChar(' ')).toUInt(&isOk, BASE_HEX)); ip6Proto->setFieldData( Ip6Protocol::ip6_isOverridePayloadLength, isPayloadLengthOverride->isChecked()); ip6Proto->setFieldData( Ip6Protocol::ip6_payloadLength, payloadLength->text()); ip6Proto->setFieldData( Ip6Protocol::ip6_isOverrideNextHeader, isNextHeaderOverride->isChecked()); ip6Proto->setFieldData( Ip6Protocol::ip6_nextHeader, nextHeader->text().remove(QChar(' ')).toUInt(&isOk, BASE_HEX)); ip6Proto->setFieldData( Ip6Protocol::ip6_hopLimit, hopLimit->text()); ip6Proto->setFieldData( Ip6Protocol::ip6_srcAddress, srcAddr->text()); ip6Proto->setFieldData( Ip6Protocol::ip6_srcAddrMode, srcAddrModeCombo->currentIndex()); ip6Proto->setFieldData( Ip6Protocol::ip6_srcAddrCount, srcAddrCount->text()); ip6Proto->setFieldData( Ip6Protocol::ip6_srcAddrPrefix, srcAddrPrefix->text()); ip6Proto->setFieldData( Ip6Protocol::ip6_dstAddress, dstAddr->text()); ip6Proto->setFieldData( Ip6Protocol::ip6_dstAddrMode, dstAddrModeCombo->currentIndex()); ip6Proto->setFieldData( Ip6Protocol::ip6_dstAddrCount, dstAddrCount->text()); ip6Proto->setFieldData( Ip6Protocol::ip6_dstAddrPrefix, dstAddrPrefix->text()); } ostinato-0.9/common/ip6config.h000066400000000000000000000024561321315675200165630ustar00rootroot00000000000000/* Copyright (C) 2010-2014 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _IP6_CONFIG_H #define _IP6_CONFIG_H #include "abstractprotocolconfig.h" #include "ui_ip6.h" class Ip6ConfigForm : public AbstractProtocolConfigForm, private Ui::Ip6 { Q_OBJECT public: Ip6ConfigForm(QWidget *parent = 0); static AbstractProtocolConfigForm* createInstance(); virtual void loadWidget(AbstractProtocol *ip6Proto); virtual void storeWidget(AbstractProtocol *ip6Proto); private slots: void on_srcAddr_editingFinished(); void on_dstAddr_editingFinished(); void on_srcAddrModeCombo_currentIndexChanged(int index); void on_dstAddrModeCombo_currentIndexChanged(int index); }; #endif ostinato-0.9/common/ip6edit.h000066400000000000000000000033621321315675200162400ustar00rootroot00000000000000/* Copyright (C) 2016 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _IP6_EDIT_H #define _IP6_EDIT_H #include "ipv6addressvalidator.h" #include "uint128.h" #include #include class Ip6Edit: public QLineEdit { public: Ip6Edit(QWidget *parent = 0); UInt128 value(); quint64 valueHi64(); quint64 valueLo64(); void setValue(UInt128 val); void setValue(quint64 hi, quint64 lo); void setValue(const QString &val); }; inline Ip6Edit::Ip6Edit(QWidget *parent) : QLineEdit(parent) { setValidator(new IPv6AddressValidator(this)); } inline UInt128 Ip6Edit::value() { Q_IPV6ADDR addr = QHostAddress(text()).toIPv6Address(); return UInt128((quint8*)&addr); } inline quint64 Ip6Edit::valueHi64() { return value().hi64(); } inline quint64 Ip6Edit::valueLo64() { return value().lo64(); } inline void Ip6Edit::setValue(UInt128 val) { setText(QHostAddress(val.toArray()).toString()); } inline void Ip6Edit::setValue(quint64 hi, quint64 lo) { UInt128 ip(hi, lo); setValue(ip); } inline void Ip6Edit::setValue(const QString &val) { setText(QHostAddress(val).toString()); } #endif ostinato-0.9/common/ip6over4.h000066400000000000000000000016101321315675200163440ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _IP_6_OVER_4_H #define _IP_6_OVER_4_H #include "comboprotocol.h" #include "ip4.h" #include "ip6.h" typedef ComboProtocol Ip6over4Protocol; #endif ostinato-0.9/common/ip6over4.proto000066400000000000000000000015521321315675200172650ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ import "protocol.proto"; package OstProto; // IP Tunelling - IP 6over4 message Ip6over4 { // Empty since this is a 'combo' protocol } extend Protocol { optional Ip6over4 ip6over4 = 303; } ostinato-0.9/common/ip6over4config.h000066400000000000000000000020171321315675200175340ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _IP_6_OVER_4_CONFIG_H #define _IP_6_OVER_4_CONFIG_H #include "comboprotocolconfig.h" #include "ip4config.h" #include "ip6config.h" #include "ip4.h" #include "ip6.h" typedef ComboProtocolConfigForm < OstProto::Protocol::kIp6over4FieldNumber, Ip4ConfigForm, Ip6ConfigForm, Ip4Protocol, Ip6Protocol > Ip6over4ConfigForm; #endif ostinato-0.9/common/ip6over6.h000066400000000000000000000057251321315675200163610ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _IP_6_OVER_6_H #define _IP_6_OVER_6_H #include "ip6over6.pb.h" #include "comboprotocol.h" #include "ip6.h" typedef ComboProtocol Ip6over6Combo; class Ip6over6Protocol : public Ip6over6Combo { public: Ip6over6Protocol(StreamBase *stream, AbstractProtocol *parent = 0) : Ip6over6Combo(stream, parent) { } static Ip6over6Protocol* createInstance(StreamBase *stream, AbstractProtocol *parent) { return new Ip6over6Protocol(stream, parent); } virtual void protoDataCopyInto(OstProto::Protocol &protocol) const { OstProto::Protocol tempProto; protoA->protoDataCopyInto(tempProto); protocol.MutableExtension(OstProto::ip6over6) ->MutableExtension(OstProto::ip6_outer) ->CopyFrom(tempProto.GetExtension(OstProto::ip6)); tempProto.Clear(); protoB->protoDataCopyInto(tempProto); protocol.MutableExtension(OstProto::ip6over6) ->MutableExtension(OstProto::ip6_inner) ->CopyFrom(tempProto.GetExtension(OstProto::ip6)); protocol.mutable_protocol_id()->set_id(protocolNumber()); } virtual void protoDataCopyFrom(const OstProto::Protocol &protocol) { if (protocol.protocol_id().id() == protocolNumber() && protocol.HasExtension(OstProto::ip6over6)) { OstProto::Protocol tempProto; // NOTE: To use protoX->protoDataCopyFrom() we need to arrange // so that it sees its own protocolNumber() and its own extension // in 'protocol' tempProto.mutable_protocol_id()->set_id(protoA->protocolNumber()); tempProto.MutableExtension(OstProto::ip6)->CopyFrom( protocol.GetExtension(OstProto::ip6over6).GetExtension( OstProto::ip6_outer)); protoA->protoDataCopyFrom(tempProto); tempProto.Clear(); tempProto.mutable_protocol_id()->set_id(protoB->protocolNumber()); tempProto.MutableExtension(OstProto::ip6)->CopyFrom( protocol.GetExtension(OstProto::ip6over6).GetExtension( OstProto::ip6_inner)); protoB->protoDataCopyFrom(tempProto); } } }; #endif ostinato-0.9/common/ip6over6.proto000066400000000000000000000016741321315675200172740ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ import "protocol.proto"; import "ip6.proto"; package OstProto; // IP Tunnelling - IP 6over6 message Ip6over6 { extensions 1 to 2; } extend Ip6over6 { optional Ip6 ip6_outer = 1; optional Ip6 ip6_inner = 2; } extend Protocol { optional Ip6over6 ip6over6 = 306; } ostinato-0.9/common/ip6over6config.h000066400000000000000000000017471321315675200175470ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _IP_6_OVER_6_CONFIG_H #define _IP_6_OVER_6_CONFIG_H #include "comboprotocolconfig.h" #include "ip6config.h" #include "ip6.h" typedef ComboProtocolConfigForm < OstProto::Protocol::kIp6over6FieldNumber, Ip6ConfigForm, Ip6ConfigForm, Ip6Protocol, Ip6Protocol > Ip6over6ConfigForm; #endif ostinato-0.9/common/ip6pdml.cpp000066400000000000000000000053341321315675200166030ustar00rootroot00000000000000/* Copyright (C) 2011 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "ip6pdml.h" #include "ip6.pb.h" PdmlIp6Protocol::PdmlIp6Protocol() { ostProtoId_ = OstProto::Protocol::kIp6FieldNumber; fieldMap_.insert("ipv6.version", OstProto::Ip6::kVersionFieldNumber); // Tshark 1.x uses .class while 2.x uses .tclass - we'll use either fieldMap_.insert("ipv6.class", OstProto::Ip6::kTrafficClassFieldNumber); fieldMap_.insert("ipv6.tclass", OstProto::Ip6::kTrafficClassFieldNumber); fieldMap_.insert("ipv6.flow", OstProto::Ip6::kFlowLabelFieldNumber); fieldMap_.insert("ipv6.plen", OstProto::Ip6::kPayloadLengthFieldNumber); fieldMap_.insert("ipv6.nxt", OstProto::Ip6::kNextHeaderFieldNumber); fieldMap_.insert("ipv6.hlim", OstProto::Ip6::kHopLimitFieldNumber); // ipv6.src and ipv6.dst handled as unknown fields } PdmlProtocol* PdmlIp6Protocol::createInstance() { return new PdmlIp6Protocol(); } void PdmlIp6Protocol::unknownFieldHandler(QString name, int /*pos*/, int /*size*/, const QXmlStreamAttributes &attributes, OstProto::Protocol *pbProto, OstProto::Stream* /*stream*/) { bool isOk; if (name == "ipv6.src") { OstProto::Ip6 *ip6 = pbProto->MutableExtension(OstProto::ip6); QString addrHexStr = attributes.value("value").toString(); ip6->set_src_addr_hi(addrHexStr.left(16).toULongLong(&isOk, kBaseHex)); ip6->set_src_addr_lo(addrHexStr.right(16).toULongLong(&isOk, kBaseHex)); } else if (name == "ipv6.dst") { OstProto::Ip6 *ip6 = pbProto->MutableExtension(OstProto::ip6); QString addrHexStr = attributes.value("value").toString(); ip6->set_dst_addr_hi(addrHexStr.left(16).toULongLong(&isOk, kBaseHex)); ip6->set_dst_addr_lo(addrHexStr.right(16).toULongLong(&isOk, kBaseHex)); } } void PdmlIp6Protocol::postProtocolHandler(OstProto::Protocol *pbProto, OstProto::Stream* /*stream*/) { OstProto::Ip6 *ip6 = pbProto->MutableExtension(OstProto::ip6); ip6->set_is_override_version(true); ip6->set_is_override_payload_length(true); ip6->set_is_override_next_header(true); } ostinato-0.9/common/ip6pdml.h000066400000000000000000000022361321315675200162460ustar00rootroot00000000000000/* Copyright (C) 2011 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _IP6_PDML_H #define _IP6_PDML_H #include "pdmlprotocol.h" class PdmlIp6Protocol : public PdmlProtocol { public: static PdmlProtocol* createInstance(); virtual void unknownFieldHandler(QString name, int pos, int size, const QXmlStreamAttributes &attributes, OstProto::Protocol *pbProto, OstProto::Stream *stream); virtual void postProtocolHandler(OstProto::Protocol *pbProto, OstProto::Stream *stream); protected: PdmlIp6Protocol(); }; #endif ostinato-0.9/common/iputils.h000066400000000000000000000063641321315675200163720ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _IP_UTILS_H #define _IP_UTILS_H namespace ipUtils { enum AddrMode { kFixed = 0, kIncrement = 1, kDecrement = 2, kRandom = 3 }; quint32 inline ipAddress(quint32 baseIp, int prefix, AddrMode mode, int count, int index) { int u; quint32 mask = ((1< 64) { p = 64; q = prefix - 64; } else { p = prefix; q = 0; } if (p > 0) maskHi = ~((quint64(1) << p) - 1); if (q > 0) maskLo = ~((quint64(1) << q) - 1); prefixHi = baseIpHi & maskHi; prefixLo = baseIpLo & maskLo; if (mode == kIncrement) { hostHi = ((baseIpHi & ~maskHi) + 0) & ~maskHi; hostLo = ((baseIpLo & ~maskLo) + u) & ~maskLo; } else if (mode == kDecrement) { hostHi = ((baseIpHi & ~maskHi) - 0) & ~maskHi; hostLo = ((baseIpLo & ~maskLo) - u) & ~maskLo; } else if (mode==kRandom) { hostHi = qrand() & ~maskHi; hostLo = qrand() & ~maskLo; } ipHi = prefixHi | hostHi; ipLo = prefixLo | hostLo; break; default: qWarning("Unhandled mode = %d", mode); } } } // namespace ipUtils #endif ostinato-0.9/common/ipv4addressdelegate.h000066400000000000000000000030671321315675200206210ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _IPV4_ADDRESS_DELEGATE #define _IPV4_ADDRESS_DELEGATE #include #include class IPv4AddressDelegate : public QItemDelegate { Q_OBJECT public: IPv4AddressDelegate(QObject *parent = 0); ~IPv4AddressDelegate(); QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const; }; inline IPv4AddressDelegate::IPv4AddressDelegate(QObject *parent) : QItemDelegate(parent) { } inline IPv4AddressDelegate::~IPv4AddressDelegate() { } inline QWidget* IPv4AddressDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const { QLineEdit *ipEdit; ipEdit = static_cast(QItemDelegate::createEditor( parent, option, index)); ipEdit->setInputMask("009.009.009.009;"); // FIXME: use validator return ipEdit; } #endif ostinato-0.9/common/ipv6addressdelegate.h000066400000000000000000000031201321315675200206110ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _IPV4_ADDRESS_DELEGATE #define _IPV4_ADDRESS_DELEGATE #include "ipv6addressvalidator.h" #include #include class IPv6AddressDelegate : public QItemDelegate { Q_OBJECT public: IPv6AddressDelegate(QObject *parent = 0); ~IPv6AddressDelegate(); QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const; }; inline IPv6AddressDelegate::IPv6AddressDelegate(QObject *parent) : QItemDelegate(parent) { } inline IPv6AddressDelegate::~IPv6AddressDelegate() { } inline QWidget* IPv6AddressDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const { QLineEdit *ipEdit; ipEdit = static_cast(QItemDelegate::createEditor( parent, option, index)); ipEdit->setValidator(new IPv6AddressValidator(ipEdit)); return ipEdit; } #endif ostinato-0.9/common/ipv6addressvalidator.h000066400000000000000000000041441321315675200210330ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _IPV6_ADDRESS_VALIDATOR_H #define _IPV6_ADDRESS_VALIDATOR_H #include #include class IPv6AddressValidator : public QValidator { public: IPv6AddressValidator(QObject *parent = 0) : QValidator(parent) { _ip6ValidChars.setPattern("[0-9a-fA-F]{0,4}(:[0-9a-fA-F]{0,4}){0,7}"); } ~IPv6AddressValidator() {} virtual QValidator::State validate(QString &input, int& /*pos*/) const { QValidator::State state; QHostAddress addr(input); //qDebug("%s: %s (%d)", __FUNCTION__, input.toAscii().constData(), pos); if (addr.protocol() == QAbstractSocket::IPv6Protocol) state = Acceptable; else if (_ip6ValidChars.exactMatch(input)) state = Intermediate; else state = Invalid; //qDebug("%s(%d): %s (%d), ", __FUNCTION__, state, //input.toAscii().constData(), pos); return state; } virtual void fixup(QString &input) const { input.append("::"); QHostAddress addr(input); int len = input.size(); //qDebug("%s: %s", __FUNCTION__, input.toAscii().constData()); while (addr.protocol() != QAbstractSocket::IPv6Protocol) { len--; Q_ASSERT(len >= 0); addr.setAddress(input.left(len)); } input = addr.toString(); } private: QRegExp _ip6ValidChars; }; #endif ostinato-0.9/common/llc.cpp000066400000000000000000000152201321315675200157750ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "llc.h" LlcProtocol::LlcProtocol(StreamBase *stream, AbstractProtocol *parent) : AbstractProtocol(stream, parent) { } LlcProtocol::~LlcProtocol() { } AbstractProtocol* LlcProtocol::createInstance(StreamBase *stream, AbstractProtocol *parent) { return new LlcProtocol(stream, parent); } quint32 LlcProtocol::protocolNumber() const { return OstProto::Protocol::kLlcFieldNumber; } void LlcProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const { protocol.MutableExtension(OstProto::llc)->CopyFrom(data); protocol.mutable_protocol_id()->set_id(protocolNumber()); } void LlcProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) { if (protocol.protocol_id().id() == protocolNumber() && protocol.HasExtension(OstProto::llc)) data.MergeFrom(protocol.GetExtension(OstProto::llc)); } QString LlcProtocol::name() const { return QString("802.3 Logical Link Control"); } QString LlcProtocol::shortName() const { return QString("LLC"); } AbstractProtocol::ProtocolIdType LlcProtocol::protocolIdType() const { return ProtocolIdLlc; } int LlcProtocol::fieldCount() const { return llc_fieldCount; } AbstractProtocol::FieldFlags LlcProtocol::fieldFlags(int index) const { AbstractProtocol::FieldFlags flags; flags = AbstractProtocol::fieldFlags(index); switch (index) { case llc_dsap: case llc_ssap: case llc_ctl: break; case llc_is_override_dsap: case llc_is_override_ssap: case llc_is_override_ctl: flags &= ~FrameField; flags |= MetaField; break; default: break; } return flags; } QVariant LlcProtocol::fieldData(int index, FieldAttrib attrib, int streamIndex) const { quint32 id; quint8 dsap, ssap, ctl; id = payloadProtocolId(ProtocolIdLlc); dsap = data.is_override_dsap() ? data.dsap() : (id >> 16) & 0xFF; ssap = data.is_override_ssap() ? data.ssap() : (id >> 8) & 0xFF; ctl = data.is_override_ctl() ? data.ctl() : (id >> 0) & 0xFF; switch (index) { case llc_dsap: switch(attrib) { case FieldName: return QString("DSAP"); case FieldValue: return dsap; case FieldTextValue: return QString("%1").arg(dsap, 2, BASE_HEX, QChar('0')); case FieldFrameValue: return QByteArray(1, (char)(dsap)); default: break; } break; case llc_ssap: switch(attrib) { case FieldName: return QString("SSAP"); case FieldValue: return ssap; case FieldTextValue: return QString("%1").arg(ssap, 2, BASE_HEX, QChar('0')); case FieldFrameValue: return QByteArray(1, (char)(ssap)); default: break; } break; case llc_ctl: switch(attrib) { case FieldName: return QString("Control"); case FieldValue: return ctl; case FieldTextValue: return QString("%1").arg(ctl, 2, BASE_HEX, QChar('0')); case FieldFrameValue: return QByteArray(1, (char)(ctl)); default: break; } break; // Meta fields case llc_is_override_dsap: { switch(attrib) { case FieldValue: return data.is_override_dsap(); default: break; } break; } case llc_is_override_ssap: { switch(attrib) { case FieldValue: return data.is_override_ssap(); default: break; } break; } case llc_is_override_ctl: { switch(attrib) { case FieldValue: return data.is_override_ctl(); default: break; } break; } default: qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, index); break; } return AbstractProtocol::fieldData(index, attrib, streamIndex); } bool LlcProtocol::setFieldData(int index, const QVariant &value, FieldAttrib attrib) { bool isOk = false; if (attrib != FieldValue) return false; switch (index) { case llc_dsap: { uint dsap = value.toUInt(&isOk) & 0xFF; if (isOk) data.set_dsap(dsap); break; } case llc_ssap: { uint ssap = value.toUInt(&isOk) & 0xFF; if (isOk) data.set_ssap(ssap); break; } case llc_ctl: { uint ctl = value.toUInt(&isOk) & 0xFF; if (isOk) data.set_ctl(ctl); break; } case llc_is_override_dsap: { bool ovr = value.toBool(); data.set_is_override_dsap(ovr); isOk = true; break; } case llc_is_override_ssap: { bool ovr = value.toBool(); data.set_is_override_ssap(ovr); isOk = true; break; } case llc_is_override_ctl: { bool ovr = value.toBool(); data.set_is_override_ctl(ovr); isOk = true; break; } default: qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, index); break; } return isOk; } ostinato-0.9/common/llc.h000066400000000000000000000036001321315675200154410ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _LLC_H #define _LLC_H #include "abstractprotocol.h" #include "llc.pb.h" class LlcProtocol : public AbstractProtocol { public: enum llcfield { llc_dsap = 0, llc_ssap, llc_ctl, // Meta fields llc_is_override_dsap, llc_is_override_ssap, llc_is_override_ctl, llc_fieldCount }; LlcProtocol(StreamBase *stream, AbstractProtocol *parent = 0); virtual ~LlcProtocol(); static AbstractProtocol* createInstance(StreamBase *stream, AbstractProtocol *parent = 0); virtual quint32 protocolNumber() const; virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); virtual QString name() const; virtual QString shortName() const; virtual ProtocolIdType protocolIdType() const; virtual int fieldCount() const; virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; virtual QVariant fieldData(int index, FieldAttrib attrib, int streamIndex = 0) const; virtual bool setFieldData(int index, const QVariant &value, FieldAttrib attrib = FieldValue); private: OstProto::Llc data; }; #endif ostinato-0.9/common/llc.proto000066400000000000000000000017411321315675200163610ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ import "protocol.proto"; package OstProto; message Llc { optional bool is_override_dsap = 4; optional bool is_override_ssap = 5; optional bool is_override_ctl = 6; optional uint32 dsap = 1; optional uint32 ssap = 2; optional uint32 ctl = 3; } extend Protocol { optional Llc llc = 202; } ostinato-0.9/common/llc.ui000066400000000000000000000072511321315675200156350ustar00rootroot00000000000000 llc 0 0 396 98 0 0 Form LLC DSAP false >HH; SSAP false >HH; Control false >HH; Qt::Horizontal 20 20 Qt::Vertical 20 40 cbOverrideDsap toggled(bool) leDsap setEnabled(bool) 54 34 92 33 cbOverrideSsap toggled(bool) leSsap setEnabled(bool) 167 34 192 33 cbOverrideControl toggled(bool) leControl setEnabled(bool) 285 34 310 33 ostinato-0.9/common/llcconfig.cpp000066400000000000000000000054561321315675200171750ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "llcconfig.h" #include "llc.h" LlcConfigForm::LlcConfigForm(QWidget *parent) : AbstractProtocolConfigForm(parent) { setupUi(this); } LlcConfigForm::~LlcConfigForm() { } LlcConfigForm* LlcConfigForm::createInstance() { return new LlcConfigForm; } void LlcConfigForm::loadWidget(AbstractProtocol *proto) { cbOverrideDsap->setChecked( proto->fieldData( LlcProtocol::llc_is_override_dsap, AbstractProtocol::FieldValue ).toBool()); leDsap->setText(uintToHexStr( proto->fieldData( LlcProtocol::llc_dsap, AbstractProtocol::FieldValue ).toUInt(), 1)); cbOverrideSsap->setChecked( proto->fieldData( LlcProtocol::llc_is_override_ssap, AbstractProtocol::FieldValue ).toBool()); leSsap->setText(uintToHexStr( proto->fieldData( LlcProtocol::llc_ssap, AbstractProtocol::FieldValue ).toUInt(), 1)); cbOverrideControl->setChecked( proto->fieldData( LlcProtocol::llc_is_override_ctl, AbstractProtocol::FieldValue ).toBool()); leControl->setText(uintToHexStr( proto->fieldData( LlcProtocol::llc_ctl, AbstractProtocol::FieldValue ).toUInt(), 1)); } void LlcConfigForm::storeWidget(AbstractProtocol *proto) { bool isOk; proto->setFieldData( LlcProtocol::llc_is_override_dsap, cbOverrideDsap->isChecked()); proto->setFieldData( LlcProtocol::llc_dsap, leDsap->text().toUInt(&isOk, BASE_HEX)); proto->setFieldData( LlcProtocol::llc_is_override_ssap, cbOverrideSsap->isChecked()); proto->setFieldData( LlcProtocol::llc_ssap, leSsap->text().toUInt(&isOk, BASE_HEX)); proto->setFieldData( LlcProtocol::llc_is_override_ctl, cbOverrideControl->isChecked()); proto->setFieldData( LlcProtocol::llc_ctl, leControl->text().toUInt(&isOk, BASE_HEX)); } ostinato-0.9/common/llcconfig.h000066400000000000000000000021361321315675200166320ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _LLC_CONFIG_H #define _LLC_CONFIG_H #include "abstractprotocolconfig.h" #include "ui_llc.h" class LlcConfigForm : public AbstractProtocolConfigForm, private Ui::llc { Q_OBJECT public: LlcConfigForm(QWidget *parent = 0); virtual ~LlcConfigForm(); static LlcConfigForm* createInstance(); virtual void loadWidget(AbstractProtocol *proto); virtual void storeWidget(AbstractProtocol *proto); }; #endif ostinato-0.9/common/llcpdml.cpp000066400000000000000000000046551321315675200166640ustar00rootroot00000000000000/* Copyright (C) 2011 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "llcpdml.h" #include "llc.pb.h" #include "snap.pb.h" #include PdmlLlcProtocol::PdmlLlcProtocol() { ostProtoId_ = OstProto::Protocol::kLlcFieldNumber; fieldMap_.insert("llc.dsap", OstProto::Llc::kDsapFieldNumber); fieldMap_.insert("llc.ssap", OstProto::Llc::kSsapFieldNumber); fieldMap_.insert("llc.control", OstProto::Llc::kCtlFieldNumber); } PdmlProtocol* PdmlLlcProtocol::createInstance() { return new PdmlLlcProtocol(); } void PdmlLlcProtocol::unknownFieldHandler(QString name, int /*pos*/, int /*size*/, const QXmlStreamAttributes &attributes, OstProto::Protocol* /*pbProto*/, OstProto::Stream *stream) { if (name == "llc.oui") { OstProto::Protocol *proto = stream->add_protocol(); proto->mutable_protocol_id()->set_id( OstProto::Protocol::kSnapFieldNumber); OstProto::Snap *snap = proto->MutableExtension(OstProto::snap); bool isOk; snap->set_oui(attributes.value("value").toString() .toUInt(&isOk, kBaseHex)); snap->set_is_override_oui(true); } else if ((name == "llc.type") || (name.contains(QRegExp("llc\\..*pid")))) { OstProto::Snap *snap = stream->mutable_protocol( stream->protocol_size()-1)->MutableExtension(OstProto::snap); bool isOk; snap->set_type(attributes.value("value").toString() .toUInt(&isOk, kBaseHex)); snap->set_is_override_type(true); } } void PdmlLlcProtocol::postProtocolHandler(OstProto::Protocol *pbProto, OstProto::Stream* /*stream*/) { OstProto::Llc *llc = pbProto->MutableExtension(OstProto::llc); llc->set_is_override_dsap(true); llc->set_is_override_ssap(true); llc->set_is_override_ctl(true); } ostinato-0.9/common/llcpdml.h000066400000000000000000000022361321315675200163220ustar00rootroot00000000000000/* Copyright (C) 2011 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _LLC_PDML_H #define _LLC_PDML_H #include "pdmlprotocol.h" class PdmlLlcProtocol : public PdmlProtocol { public: static PdmlProtocol* createInstance(); virtual void unknownFieldHandler(QString name, int pos, int size, const QXmlStreamAttributes &attributes, OstProto::Protocol *pbProto, OstProto::Stream *stream); virtual void postProtocolHandler(OstProto::Protocol *pbProto, OstProto::Stream *stream); protected: PdmlLlcProtocol(); }; #endif ostinato-0.9/common/mac.cpp000066400000000000000000000241401321315675200157640ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "mac.h" #include "../common/streambase.h" #include #define uintToMacStr(num) \ QString("%1").arg(num, 6*2, BASE_HEX, QChar('0')) \ .replace(QRegExp("([0-9a-fA-F]{2}\\B)"), "\\1:").toUpper() MacProtocol::MacProtocol(StreamBase *stream, AbstractProtocol *parent) : AbstractProtocol(stream, parent) { forResolve_ = false; } MacProtocol::~MacProtocol() { } AbstractProtocol* MacProtocol::createInstance(StreamBase *stream , AbstractProtocol *parent) { return new MacProtocol(stream, parent); } quint32 MacProtocol::protocolNumber() const { return OstProto::Protocol::kMacFieldNumber; } void MacProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const { protocol.MutableExtension(OstProto::mac)->CopyFrom(data); protocol.mutable_protocol_id()->set_id(protocolNumber()); } void MacProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) { if (protocol.protocol_id().id() == protocolNumber() && protocol.HasExtension(OstProto::mac)) data.MergeFrom(protocol.GetExtension(OstProto::mac)); } QString MacProtocol::name() const { return QString("Media Access Protocol"); } QString MacProtocol::shortName() const { return QString("MAC"); } int MacProtocol::fieldCount() const { return mac_fieldCount; } AbstractProtocol::FieldFlags MacProtocol::fieldFlags(int index) const { AbstractProtocol::FieldFlags flags; flags = AbstractProtocol::fieldFlags(index); switch (index) { case mac_dstAddr: case mac_srcAddr: break; case mac_dstMacMode: case mac_dstMacCount: case mac_dstMacStep: case mac_srcMacMode: case mac_srcMacCount: case mac_srcMacStep: flags &= ~FrameField; flags |= MetaField; break; } return flags; } QVariant MacProtocol::fieldData(int index, FieldAttrib attrib, int streamIndex) const { switch (index) { case mac_dstAddr: { int u; quint64 dstMac = 0; switch (data.dst_mac_mode()) { case OstProto::Mac::e_mm_fixed: dstMac = data.dst_mac(); break; case OstProto::Mac::e_mm_inc: u = (streamIndex % data.dst_mac_count()) * data.dst_mac_step(); dstMac = data.dst_mac() + u; break; case OstProto::Mac::e_mm_dec: u = (streamIndex % data.dst_mac_count()) * data.dst_mac_step(); dstMac = data.dst_mac() - u; break; case OstProto::Mac::e_mm_resolve: if (forResolve_) dstMac = 0; else { forResolve_ = true; dstMac = mpStream->neighborMacAddress(streamIndex); forResolve_ = false; } break; default: qWarning("Unhandled dstMac_mode %d", data.dst_mac_mode()); } switch(attrib) { case FieldName: return QString("Destination"); case FieldValue: return dstMac; case FieldTextValue: return uintToMacStr(dstMac); case FieldFrameValue: { QByteArray fv; fv.resize(8); qToBigEndian(dstMac, (uchar*) fv.data()); fv.remove(0, 2); return fv; } default: break; } break; } case mac_srcAddr: { int u; quint64 srcMac = 0; switch (data.src_mac_mode()) { case OstProto::Mac::e_mm_fixed: srcMac = data.src_mac(); break; case OstProto::Mac::e_mm_inc: u = (streamIndex % data.src_mac_count()) * data.src_mac_step(); srcMac = data.src_mac() + u; break; case OstProto::Mac::e_mm_dec: u = (streamIndex % data.src_mac_count()) * data.src_mac_step(); srcMac = data.src_mac() - u; break; case OstProto::Mac::e_mm_resolve: if (forResolve_) srcMac = 0; else { forResolve_ = true; srcMac = mpStream->deviceMacAddress(streamIndex); forResolve_ = false; } break; default: qWarning("Unhandled srcMac_mode %d", data.src_mac_mode()); } switch(attrib) { case FieldName: return QString("Source"); case FieldValue: return srcMac; case FieldTextValue: return uintToMacStr(srcMac); case FieldFrameValue: { QByteArray fv; fv.resize(8); qToBigEndian(srcMac, (uchar*) fv.data()); fv.remove(0, 2); return fv; } default: break; } break; } // Meta fields case mac_dstMacMode: switch(attrib) { case FieldValue: return data.dst_mac_mode(); default: break; } break; case mac_dstMacCount: switch(attrib) { case FieldValue: return data.dst_mac_count(); default: break; } break; case mac_dstMacStep: switch(attrib) { case FieldValue: return data.dst_mac_step(); default: break; } break; case mac_srcMacMode: switch(attrib) { case FieldValue: return data.src_mac_mode(); default: break; } break; case mac_srcMacCount: switch(attrib) { case FieldValue: return data.src_mac_count(); default: break; } break; case mac_srcMacStep: switch(attrib) { case FieldValue: return data.src_mac_step(); default: break; } break; default: break; } return AbstractProtocol::fieldData(index, attrib, streamIndex); } bool MacProtocol::setFieldData(int index, const QVariant &value, FieldAttrib attrib) { bool isOk = false; if (attrib != FieldValue) goto _exit; switch (index) { case mac_dstAddr: { quint64 mac = value.toULongLong(); data.set_dst_mac(mac); break; } case mac_srcAddr: { quint64 mac = value.toULongLong(); data.set_src_mac(mac); break; } // Meta-Fields case mac_dstMacMode: { uint mode = value.toUInt(&isOk); if (isOk && data.MacAddrMode_IsValid(mode)) data.set_dst_mac_mode((OstProto::Mac::MacAddrMode) mode); else isOk = false; break; } case mac_dstMacCount: { uint count = value.toUInt(&isOk); if (isOk) data.set_dst_mac_count(count); break; } case mac_dstMacStep: { uint step = value.toUInt(&isOk); if (isOk) data.set_dst_mac_step(step); break; } case mac_srcMacMode: { uint mode = value.toUInt(&isOk); if (isOk && data.MacAddrMode_IsValid(mode)) data.set_src_mac_mode((OstProto::Mac::MacAddrMode) mode); else isOk = false; break; } case mac_srcMacCount: { uint count = value.toUInt(&isOk); if (isOk) data.set_src_mac_count(count); break; } case mac_srcMacStep: { uint step = value.toUInt(&isOk); if (isOk) data.set_src_mac_step(step); break; } default: qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, index); break; } _exit: return isOk; } int MacProtocol::protocolFrameVariableCount() const { int count = AbstractProtocol::protocolFrameVariableCount(); switch (data.dst_mac_mode()) { case OstProto::Mac::e_mm_inc: case OstProto::Mac::e_mm_dec: count = AbstractProtocol::lcm(count, data.dst_mac_count()); break; default: break; } switch (data.src_mac_mode()) { case OstProto::Mac::e_mm_inc: case OstProto::Mac::e_mm_dec: count = AbstractProtocol::lcm(count, data.src_mac_count()); break; default: break; } return count; } ostinato-0.9/common/mac.h000066400000000000000000000036751321315675200154430ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _MAC_H #define _MAC_H #include "abstractprotocol.h" #include "mac.pb.h" class MacProtocol : public AbstractProtocol { public: enum macfield { mac_dstAddr = 0, mac_srcAddr, mac_dstMacMode, mac_dstMacCount, mac_dstMacStep, mac_srcMacMode, mac_srcMacCount, mac_srcMacStep, mac_fieldCount }; MacProtocol(StreamBase *stream, AbstractProtocol *parent = 0); virtual ~MacProtocol(); static AbstractProtocol* createInstance(StreamBase *stream, AbstractProtocol *parent = 0); virtual quint32 protocolNumber() const; virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); virtual QString name() const; virtual QString shortName() const; virtual int fieldCount() const; virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; virtual QVariant fieldData(int index, FieldAttrib attrib, int streamIndex = 0) const; virtual bool setFieldData(int index, const QVariant &value, FieldAttrib attrib = FieldValue); virtual int protocolFrameVariableCount() const; private: OstProto::Mac data; mutable bool forResolve_; }; #endif ostinato-0.9/common/mac.proto000066400000000000000000000027141321315675200163500ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ import "protocol.proto"; package OstProto; // Ethernet message Mac { enum MacAddrMode { e_mm_fixed = 0; e_mm_inc = 1; e_mm_dec = 2; e_mm_resolve = 3; // dst: resolve neighbor; src: from device config } // Dst Mac optional uint64 dst_mac = 1; optional MacAddrMode dst_mac_mode = 2 [default = e_mm_fixed]; optional uint32 dst_mac_count = 3 [default = 16]; optional uint32 dst_mac_step = 4 [default = 1]; // Src Mac optional uint64 src_mac = 5; optional MacAddrMode src_mac_mode = 6 [default = e_mm_fixed]; optional uint32 src_mac_count = 7 [default = 16]; optional uint32 src_mac_step = 8 [default = 1]; } extend Protocol { optional Mac mac = 100; } ostinato-0.9/common/mac.ui000066400000000000000000000107161321315675200156230ustar00rootroot00000000000000 mac 0 0 400 200 Form Address Mode Count Step Destination 120 0 Fixed Increment Decrement Resolve false false Source Fixed Increment Decrement Resolve false false Please ensure that a corresponding device is configured on the port to enable source/destination mac address resolution. A corresponding device is one which has VLANs and source/gateway IP corresponding to this stream. true Qt::Vertical 20 40 IntEdit QSpinBox
intedit.h
MacEdit QLineEdit
macedit.h
ostinato-0.9/common/macconfig.cpp000066400000000000000000000123301321315675200171500ustar00rootroot00000000000000/* Copyright (C) 2010,2013-2014 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "macconfig.h" #include "mac.h" MacConfigForm::MacConfigForm(QWidget *parent) : AbstractProtocolConfigForm(parent) { setupUi(this); resolveInfo->hide(); #if 0 // not working for some reason resolveInfo->setPixmap(resolveInfo->style()->standardIcon( QStyle::SP_MessageBoxInformation).pixmap(128)); #endif leDstMacCount->setMinimum(1); leSrcMacCount->setMinimum(1); leDstMacStep->setMinimum(0); leSrcMacStep->setMinimum(0); } MacConfigForm::~MacConfigForm() { } MacConfigForm* MacConfigForm::createInstance() { MacConfigForm *f = new MacConfigForm; return f; } void MacConfigForm::on_cmbDstMacMode_currentIndexChanged(int index) { switch (index) { case OstProto::Mac::e_mm_resolve: leDstMac->setEnabled(false); leDstMacCount->setEnabled(false); leDstMacStep->setEnabled(false); break; case OstProto::Mac::e_mm_fixed: leDstMac->setEnabled(true); leDstMacCount->setEnabled(false); leDstMacStep->setEnabled(false); break; default: leDstMac->setEnabled(true); leDstMacCount->setEnabled(true); leDstMacStep->setEnabled(true); break; } resolveInfo->setVisible( cmbDstMacMode->currentIndex() == OstProto::Mac::e_mm_resolve || cmbSrcMacMode->currentIndex() == OstProto::Mac::e_mm_resolve); } void MacConfigForm::on_cmbSrcMacMode_currentIndexChanged(int index) { switch (index) { case OstProto::Mac::e_mm_resolve: leSrcMac->setEnabled(false); leSrcMacCount->setEnabled(false); leSrcMacStep->setEnabled(false); break; case OstProto::Mac::e_mm_fixed: leSrcMac->setEnabled(true); leSrcMacCount->setEnabled(false); leSrcMacStep->setEnabled(false); break; default: leSrcMac->setEnabled(true); leSrcMacCount->setEnabled(true); leSrcMacStep->setEnabled(true); break; } resolveInfo->setVisible( cmbDstMacMode->currentIndex() == OstProto::Mac::e_mm_resolve || cmbSrcMacMode->currentIndex() == OstProto::Mac::e_mm_resolve); } void MacConfigForm::loadWidget(AbstractProtocol *proto) { leDstMac->setValue( proto->fieldData( MacProtocol::mac_dstAddr, AbstractProtocol::FieldValue ).toULongLong()); cmbDstMacMode->setCurrentIndex( proto->fieldData( MacProtocol::mac_dstMacMode, AbstractProtocol::FieldValue ).toUInt()); leDstMacCount->setValue( proto->fieldData( MacProtocol::mac_dstMacCount, AbstractProtocol::FieldValue ).toUInt()); leDstMacStep->setValue( proto->fieldData( MacProtocol::mac_dstMacStep, AbstractProtocol::FieldValue ).toUInt()); leSrcMac->setValue( proto->fieldData( MacProtocol::mac_srcAddr, AbstractProtocol::FieldValue ).toULongLong()); cmbSrcMacMode->setCurrentIndex( proto->fieldData( MacProtocol::mac_srcMacMode, AbstractProtocol::FieldValue ).toUInt()); leSrcMacCount->setValue( proto->fieldData( MacProtocol::mac_srcMacCount, AbstractProtocol::FieldValue ).toUInt()); leSrcMacStep->setValue( proto->fieldData( MacProtocol::mac_srcMacStep, AbstractProtocol::FieldValue ).toUInt()); } void MacConfigForm::storeWidget(AbstractProtocol *proto) { proto->setFieldData( MacProtocol::mac_dstAddr, leDstMac->value()); proto->setFieldData( MacProtocol::mac_dstMacMode, cmbDstMacMode->currentIndex()); proto->setFieldData( MacProtocol::mac_dstMacCount, leDstMacCount->value()); proto->setFieldData( MacProtocol::mac_dstMacStep, leDstMacStep->value()); proto->setFieldData( MacProtocol::mac_srcAddr, leSrcMac->value()); proto->setFieldData( MacProtocol::mac_srcMacMode, cmbSrcMacMode->currentIndex()); proto->setFieldData( MacProtocol::mac_srcMacCount, leSrcMacCount->value()); proto->setFieldData( MacProtocol::mac_srcMacStep, leSrcMacStep->value()); } ostinato-0.9/common/macconfig.h000066400000000000000000000023461321315675200166230ustar00rootroot00000000000000/* Copyright (C) 2010-2012 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _MAC_CONFIG_H #define _MAC_CONFIG_H #include "abstractprotocolconfig.h" #include "ui_mac.h" class MacConfigForm : public AbstractProtocolConfigForm, private Ui::mac { Q_OBJECT public: MacConfigForm(QWidget *parent = 0); virtual ~MacConfigForm(); static MacConfigForm* createInstance(); virtual void loadWidget(AbstractProtocol *proto); virtual void storeWidget(AbstractProtocol *proto); private slots: void on_cmbDstMacMode_currentIndexChanged(int index); void on_cmbSrcMacMode_currentIndexChanged(int index); }; #endif ostinato-0.9/common/macedit.h000066400000000000000000000024771321315675200163100ustar00rootroot00000000000000/* Copyright (C) 2016 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _MAC_EDIT_H #define _MAC_EDIT_H #include class MacEdit: public QLineEdit { public: MacEdit(QWidget *parent = 0); quint64 value(); void setValue(quint64 val); }; inline MacEdit::MacEdit(QWidget *parent) : QLineEdit(parent) { QRegExp reMac("([0-9,a-f,A-F]{2,2}[:-]){5,5}[0-9,a-f,A-F]{2,2}"); setValidator(new QRegExpValidator(reMac, this)); } inline quint64 MacEdit::value() { return text().remove(QChar(':')).toULongLong(NULL, 16); } inline void MacEdit::setValue(quint64 val) { setText(QString("%1").arg(val, 6*2, 16, QChar('0')) .replace(QRegExp("([0-9a-fA-F]{2}\\B)"), "\\1:").toUpper()); } #endif ostinato-0.9/common/mld.cpp000066400000000000000000000421641321315675200160060ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "mld.h" #include "iputils.h" #include #include MldProtocol::MldProtocol(StreamBase *stream, AbstractProtocol *parent) : GmpProtocol(stream, parent) { _hasPayload = false; data.set_type(kMldV1Query); } MldProtocol::~MldProtocol() { } AbstractProtocol* MldProtocol::createInstance(StreamBase *stream, AbstractProtocol *parent) { return new MldProtocol(stream, parent); } quint32 MldProtocol::protocolNumber() const { return OstProto::Protocol::kMldFieldNumber; } void MldProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const { protocol.MutableExtension(OstProto::mld)->CopyFrom(data); protocol.mutable_protocol_id()->set_id(protocolNumber()); } void MldProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) { if (protocol.protocol_id().id() == protocolNumber() && protocol.HasExtension(OstProto::mld)) data.MergeFrom(protocol.GetExtension(OstProto::mld)); } QString MldProtocol::name() const { return QString("Multicast Listener Discovery"); } QString MldProtocol::shortName() const { return QString("MLD"); } quint32 MldProtocol::protocolId(ProtocolIdType type) const { switch(type) { case ProtocolIdIp: return 0x3a; default:break; } return AbstractProtocol::protocolId(type); } AbstractProtocol::FieldFlags MldProtocol::fieldFlags(int index) const { AbstractProtocol::FieldFlags flags; flags = GmpProtocol::fieldFlags(index); switch(index) { case kMldMrt: case kMldRsvd: if (msgType() != kMldV2Report) flags |= FrameField; break; default: break; } return flags; } QVariant MldProtocol::fieldData(int index, FieldAttrib attrib, int streamIndex) const { switch (index) { case kRsvdMrtCode: { switch(attrib) { case FieldName: return QString("Code"); default: break; } break; } case kMldMrt: { quint16 mrt = 0, mrcode = 0; if (msgType() == kMldV2Query) { mrt = data.max_response_time(); mrcode = mrc(mrt); } else if (msgType() == kMldV1Query) mrcode = mrt = data.max_response_time() & 0xFFFF; switch(attrib) { case FieldName: if (isQuery()) return QString("Max Response Time"); return QString("Reserved"); case FieldValue: return mrt; case FieldTextValue: return QString("%1 ms").arg(mrt); case FieldFrameValue: { QByteArray fv; fv.resize(2); qToBigEndian(mrcode, (uchar*) fv.data()); return fv; } default: break; } break; } case kMldRsvd: { quint16 rsvd = 0; switch(attrib) { case FieldName: return QString("Reserved"); case FieldValue: return rsvd; case FieldTextValue: return QString("%1").arg(rsvd); case FieldFrameValue: { QByteArray fv; fv.resize(2); qToBigEndian(rsvd, (uchar*) fv.data()); return fv; } default: break; } break; } case kGroupAddress: { quint64 grpHi = 0, grpLo = 0; ipUtils::ipAddress( data.group_address().v6_hi(), data.group_address().v6_lo(), data.group_prefix(), ipUtils::AddrMode(data.group_mode()), data.group_count(), streamIndex, grpHi, grpLo); switch(attrib) { case FieldName: return QString("Group Address"); case FieldValue: case FieldTextValue: case FieldFrameValue: { QByteArray fv; fv.resize(16); qToBigEndian(grpHi, (uchar*) fv.data()); qToBigEndian(grpLo, (uchar*) (fv.data() + 8)); if (attrib == FieldFrameValue) return fv; else return QHostAddress((quint8*)fv.constData()).toString(); } default: break; } break; } case kSources: { switch(attrib) { case FieldName: return QString("Source List"); case FieldValue: { QStringList list; QByteArray fv; fv.resize(16); for (int i = 0; i < data.sources_size(); i++) { qToBigEndian(data.sources(i).v6_hi(), (uchar*)fv.data()); qToBigEndian(data.sources(i).v6_lo(), (uchar*)fv.data()+8); list << QHostAddress((quint8*)fv.constData()).toString(); } return list; } case FieldFrameValue: { QByteArray fv; fv.resize(16 * data.sources_size()); for (int i = 0; i < data.sources_size(); i++) { qToBigEndian(data.sources(i).v6_hi(), (uchar*)(fv.data() + i*16)); qToBigEndian(data.sources(i).v6_lo(), (uchar*)(fv.data() + i*16 + 8)); } return fv; } case FieldTextValue: { QStringList list; QByteArray fv; fv.resize(16); for (int i = 0; i < data.sources_size(); i++) { qToBigEndian(data.sources(i).v6_hi(), (uchar*)fv.data()); qToBigEndian(data.sources(i).v6_lo(), (uchar*)fv.data()+8); list << QHostAddress((quint8*)fv.constData()).toString(); } return list.join(", "); } default: break; } break; } case kGroupRecords: { switch(attrib) { case FieldValue: { QVariantList grpRecords = GmpProtocol::fieldData( index, attrib, streamIndex).toList(); QByteArray ip; ip.resize(16); for (int i = 0; i < data.group_records_size(); i++) { QVariantMap grpRec = grpRecords.at(i).toMap(); OstProto::Gmp::GroupRecord rec = data.group_records(i); qToBigEndian(quint64(rec.group_address().v6_hi()), (uchar*)(ip.data())); qToBigEndian(quint64(rec.group_address().v6_lo()), (uchar*)(ip.data() + 8)); grpRec["groupRecordAddress"] = QHostAddress( (quint8*)ip.constData()).toString(); QStringList sl; for (int j = 0; j < rec.sources_size(); j++) { qToBigEndian(rec.sources(j).v6_hi(), (uchar*)(ip.data())); qToBigEndian(rec.sources(j).v6_lo(), (uchar*)(ip.data() + 8)); sl.append(QHostAddress( (quint8*)ip.constData()).toString()); } grpRec["groupRecordSourceList"] = sl; grpRecords.replace(i, grpRec); } return grpRecords; } case FieldFrameValue: { QVariantList list = GmpProtocol::fieldData( index, attrib, streamIndex).toList(); QByteArray fv; QByteArray ip; ip.resize(16); for (int i = 0; i < data.group_records_size(); i++) { OstProto::Gmp::GroupRecord rec = data.group_records(i); QByteArray rv = list.at(i).toByteArray(); rv.insert(4, QByteArray(16+16*rec.sources_size(), char(0))); qToBigEndian(rec.group_address().v6_hi(), (uchar*)(rv.data()+4)); qToBigEndian(rec.group_address().v6_lo(), (uchar*)(rv.data()+4+8)); for (int j = 0; j < rec.sources_size(); j++) { qToBigEndian(rec.sources(j).v6_hi(), (uchar*)(rv.data()+20+16*j)); qToBigEndian(rec.sources(j).v6_lo(), (uchar*)(rv.data()+20+16*j+8)); } fv.append(rv); } return fv; } case FieldTextValue: { QStringList list = GmpProtocol::fieldData( index, attrib, streamIndex).toStringList(); QByteArray ip; ip.resize(16); for (int i = 0; i < data.group_records_size(); i++) { OstProto::Gmp::GroupRecord rec = data.group_records(i); QString recStr = list.at(i); QString str; qToBigEndian(rec.group_address().v6_hi(), (uchar*)(ip.data())); qToBigEndian(rec.group_address().v6_lo(), (uchar*)(ip.data() + 8)); str.append(QString("Group: %1").arg( QHostAddress((quint8*)ip.constData()).toString())); str.append("; Sources: "); QStringList sl; for (int j = 0; j < rec.sources_size(); j++) { qToBigEndian(rec.sources(j).v6_hi(), (uchar*)(ip.data())); qToBigEndian(rec.sources(j).v6_lo(), (uchar*)(ip.data() + 8)); sl.append(QHostAddress( (quint8*)ip.constData()).toString()); } str.append(sl.join(", ")); recStr.replace("XXX", str); list.replace(i, recStr); } return list.join("\n").insert(0, "\n"); } default: break; } break; } default: break; } return GmpProtocol::fieldData(index, attrib, streamIndex); } bool MldProtocol::setFieldData(int index, const QVariant &value, FieldAttrib attrib) { bool isOk = false; if (attrib != FieldValue) goto _exit; switch (index) { case kGroupAddress: { Q_IPV6ADDR addr = QHostAddress(value.toString()).toIPv6Address(); quint64 x; x = (quint64(addr[0]) << 56) | (quint64(addr[1]) << 48) | (quint64(addr[2]) << 40) | (quint64(addr[3]) << 32) | (quint64(addr[4]) << 24) | (quint64(addr[5]) << 16) | (quint64(addr[6]) << 8) | (quint64(addr[7]) << 0); data.mutable_group_address()->set_v6_hi(x); x = (quint64(addr[ 8]) << 56) | (quint64(addr[ 9]) << 48) | (quint64(addr[10]) << 40) | (quint64(addr[11]) << 32) | (quint64(addr[12]) << 24) | (quint64(addr[13]) << 16) | (quint64(addr[14]) << 8) | (quint64(addr[15]) << 0); data.mutable_group_address()->set_v6_lo(x); break; } case kSources: { QStringList list = value.toStringList(); data.clear_sources(); foreach(QString str, list) { OstProto::Gmp::IpAddress *src = data.add_sources(); Q_IPV6ADDR addr = QHostAddress(str).toIPv6Address(); quint64 x; x = (quint64(addr[0]) << 56) | (quint64(addr[1]) << 48) | (quint64(addr[2]) << 40) | (quint64(addr[3]) << 32) | (quint64(addr[4]) << 24) | (quint64(addr[5]) << 16) | (quint64(addr[6]) << 8) | (quint64(addr[7]) << 0); src->set_v6_hi(x); x = (quint64(addr[ 8]) << 56) | (quint64(addr[ 9]) << 48) | (quint64(addr[10]) << 40) | (quint64(addr[11]) << 32) | (quint64(addr[12]) << 24) | (quint64(addr[13]) << 16) | (quint64(addr[14]) << 8) | (quint64(addr[15]) << 0); src->set_v6_lo(x); } break; } case kGroupRecords: { GmpProtocol::setFieldData(index, value, attrib); QVariantList list = value.toList(); for (int i = 0; i < list.count(); i++) { QVariantMap grpRec = list.at(i).toMap(); OstProto::Gmp::GroupRecord *rec = data.mutable_group_records(i); Q_IPV6ADDR addr = QHostAddress( grpRec["groupRecordAddress"].toString()) .toIPv6Address(); quint64 x; x = (quint64(addr[0]) << 56) | (quint64(addr[1]) << 48) | (quint64(addr[2]) << 40) | (quint64(addr[3]) << 32) | (quint64(addr[4]) << 24) | (quint64(addr[5]) << 16) | (quint64(addr[6]) << 8) | (quint64(addr[7]) << 0); rec->mutable_group_address()->set_v6_hi(x); x = (quint64(addr[ 8]) << 56) | (quint64(addr[ 9]) << 48) | (quint64(addr[10]) << 40) | (quint64(addr[11]) << 32) | (quint64(addr[12]) << 24) | (quint64(addr[13]) << 16) | (quint64(addr[14]) << 8) | (quint64(addr[15]) << 0); rec->mutable_group_address()->set_v6_lo(x); QStringList srcList = grpRec["groupRecordSourceList"] .toStringList(); rec->clear_sources(); foreach (QString str, srcList) { OstProto::Gmp::IpAddress *src = rec->add_sources(); Q_IPV6ADDR addr = QHostAddress(str).toIPv6Address(); quint64 x; x = (quint64(addr[0]) << 56) | (quint64(addr[1]) << 48) | (quint64(addr[2]) << 40) | (quint64(addr[3]) << 32) | (quint64(addr[4]) << 24) | (quint64(addr[5]) << 16) | (quint64(addr[6]) << 8) | (quint64(addr[7]) << 0); src->set_v6_hi(x); x = (quint64(addr[ 8]) << 56) | (quint64(addr[ 9]) << 48) | (quint64(addr[10]) << 40) | (quint64(addr[11]) << 32) | (quint64(addr[12]) << 24) | (quint64(addr[13]) << 16) | (quint64(addr[14]) << 8) | (quint64(addr[15]) << 0); src->set_v6_lo(x); } } break; } default: isOk = GmpProtocol::setFieldData(index, value, attrib); break; } _exit: return isOk; } quint16 MldProtocol::checksum(int streamIndex) const { return AbstractProtocol::protocolFrameCksum(streamIndex, CksumTcpUdp); } ostinato-0.9/common/mld.h000066400000000000000000000050631321315675200154500ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _MLD_H #define _MLD_H #include "gmp.h" #include "mld.pb.h" // MLD uses the same msg type value for 'Query' messages across // versions despite the fields being different. To distinguish // Query messages of different versions, we use an additional // upper byte enum MldMsgType { kMldV1Query = 0x82, kMldV1Report = 0x83, kMldV1Done = 0x84, kMldV2Query = 0xFF82, kMldV2Report = 0x8F }; class MldProtocol : public GmpProtocol { public: MldProtocol(StreamBase *stream, AbstractProtocol *parent = 0); virtual ~MldProtocol(); static AbstractProtocol* createInstance(StreamBase *stream, AbstractProtocol *parent = 0); virtual quint32 protocolNumber() const; virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); virtual quint32 protocolId(ProtocolIdType type) const; virtual QString name() const; virtual QString shortName() const; virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; virtual QVariant fieldData(int index, FieldAttrib attrib, int streamIndex = 0) const; virtual bool setFieldData(int index, const QVariant &value, FieldAttrib attrib = FieldValue); protected: virtual bool isSsmReport() const; virtual bool isQuery() const; virtual bool isSsmQuery() const; virtual quint16 checksum(int streamIndex) const; private: int mrc(int value) const; }; inline bool MldProtocol::isSsmReport() const { return (msgType() == kMldV2Report); } inline bool MldProtocol::isQuery() const { return ((msgType() == kMldV1Query) || (msgType() == kMldV2Query)); } inline bool MldProtocol::isSsmQuery() const { return (msgType() == kMldV2Query); } inline int MldProtocol::mrc(int value) const { return quint16(value); // TODO: if value > 128, convert to mantissa/exp form } #endif ostinato-0.9/common/mld.proto000077500000000000000000000014231321315675200163630ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ import "protocol.proto"; import "gmp.proto"; package OstProto; extend Protocol { optional Gmp mld = 404; } ostinato-0.9/common/mldconfig.cpp000066400000000000000000000055011321315675200171660ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "mldconfig.h" #include "mld.h" #include "ipv6addressdelegate.h" #include "ipv6addressvalidator.h" MldConfigForm::MldConfigForm(QWidget *parent) : GmpConfigForm(parent) { connect(msgTypeCombo, SIGNAL(currentIndexChanged(int)), SLOT(on_msgTypeCombo_currentIndexChanged(int))); msgTypeCombo->setValueMask(0xFF); msgTypeCombo->addItem(kMldV1Query, "MLDv1 Query"); msgTypeCombo->addItem(kMldV1Report, "MLDv1 Report"); msgTypeCombo->addItem(kMldV1Done, "MLDv1 Done"); msgTypeCombo->addItem(kMldV2Query, "MLDv2 Query"); msgTypeCombo->addItem(kMldV2Report, "MLDv2 Report"); _defaultGroupIp = "::"; _defaultSourceIp = "::"; groupAddress->setValidator(new IPv6AddressValidator(this)); groupRecordAddress->setValidator(new IPv6AddressValidator(this)); sourceList->setItemDelegate(new IPv6AddressDelegate(this)); groupRecordSourceList->setItemDelegate(new IPv6AddressDelegate(this)); } MldConfigForm::~MldConfigForm() { } MldConfigForm* MldConfigForm::createInstance() { return new MldConfigForm; } void MldConfigForm::loadWidget(AbstractProtocol *proto) { GmpConfigForm::loadWidget(proto); maxResponseTime->setText( proto->fieldData( MldProtocol::kMldMrt, AbstractProtocol::FieldValue ).toString()); } void MldConfigForm::storeWidget(AbstractProtocol *proto) { GmpConfigForm::storeWidget(proto); proto->setFieldData( MldProtocol::kMldMrt, maxResponseTime->text()); } // // -- private slots // void MldConfigForm::on_msgTypeCombo_currentIndexChanged(int /*index*/) { switch(msgTypeCombo->currentValue()) { case kMldV1Query: case kMldV1Report: case kMldV1Done: asmGroup->show(); ssmWidget->hide(); break; case kMldV2Query: asmGroup->show(); ssmWidget->setCurrentIndex(kSsmQueryPage); ssmWidget->show(); break; case kMldV2Report: asmGroup->hide(); ssmWidget->setCurrentIndex(kSsmReportPage); ssmWidget->show(); break; default: asmGroup->hide(); ssmWidget->hide(); break; } } ostinato-0.9/common/mldconfig.h000066400000000000000000000021371321315675200166350ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _MLD_CONFIG_H #define _MLD_CONFIG_H #include "gmpconfig.h" class MldConfigForm : public GmpConfigForm { Q_OBJECT public: MldConfigForm(QWidget *parent = 0); virtual ~MldConfigForm(); static MldConfigForm* createInstance(); virtual void loadWidget(AbstractProtocol *proto); virtual void storeWidget(AbstractProtocol *proto); private slots: void on_msgTypeCombo_currentIndexChanged(int index); }; #endif ostinato-0.9/common/mldpdml.cpp000066400000000000000000000117111321315675200166550ustar00rootroot00000000000000/* Copyright (C) 2011 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "mldpdml.h" #include "mld.pb.h" PdmlMldProtocol::PdmlMldProtocol() { ostProtoId_ = OstProto::Protocol::kMldFieldNumber; fieldMap_.insert("icmpv6.code", OstProto::Gmp::kRsvdCodeFieldNumber); fieldMap_.insert("icmpv6.checksum", OstProto::Gmp::kChecksumFieldNumber); fieldMap_.insert("icmpv6.mld.maximum_response_delay", OstProto::Gmp::kMaxResponseTimeFieldNumber); // FIXME fieldMap_.insert("icmpv6.mld.flag.s", OstProto::Gmp::kSFlagFieldNumber); fieldMap_.insert("icmpv6.mld.flag.qrv", OstProto::Gmp::kQrvFieldNumber); fieldMap_.insert("icmpv6.mld.qqi", OstProto::Gmp::kQqiFieldNumber); // FIXME fieldMap_.insert("icmpv6.mld.nb_sources", OstProto::Gmp::kSourceCountFieldNumber); fieldMap_.insert("icmpv6.mldr.nb_mcast_records", OstProto::Gmp::kGroupRecordCountFieldNumber); } PdmlProtocol* PdmlMldProtocol::createInstance() { return new PdmlMldProtocol(); } void PdmlMldProtocol::preProtocolHandler(QString /*name*/, const QXmlStreamAttributes &attributes, int /*expectedPos*/, OstProto::Protocol *pbProto, OstProto::Stream* /*stream*/) { bool isOk; OstProto::Gmp *mld = pbProto->MutableExtension(OstProto::mld); mld->set_is_override_rsvd_code(true); mld->set_is_override_checksum(true); mld->set_is_override_source_count(true); mld->set_is_override_group_record_count(true); protoSize_ = attributes.value("size").toString().toUInt(&isOk); } void PdmlMldProtocol::unknownFieldHandler(QString name, int /*pos*/, int /*size*/, const QXmlStreamAttributes &attributes, OstProto::Protocol *pbProto, OstProto::Stream* /*stream*/) { bool isOk; OstProto::Gmp *mld = pbProto->MutableExtension(OstProto::mld); QString valueHexStr = attributes.value("value").toString(); if (name == "icmpv6.type") { uint type = valueHexStr.toUInt(&isOk, kBaseHex); if ((type == kMldQuery) && (protoSize_ >= 28)) type = kMldV2Query; mld->set_type(type); } else if (name == "icmpv6.mld.multicast_address") { mld->mutable_group_address()->set_v6_hi( valueHexStr.left(16).toULongLong(&isOk, kBaseHex)); mld->mutable_group_address()->set_v6_lo( valueHexStr.right(16).toULongLong(&isOk, kBaseHex)); } else if (name == "icmpv6.mld.source_address") { OstProto::Gmp::IpAddress *ip = mld->add_sources(); ip->set_v6_hi(valueHexStr.left(16).toULongLong(&isOk, kBaseHex)); ip->set_v6_lo(valueHexStr.right(16).toULongLong(&isOk, kBaseHex)); } else if (name == "icmpv6.mldr.mar.record_type") { OstProto::Gmp::GroupRecord *rec = mld->add_group_records(); rec->set_type(OstProto::Gmp::GroupRecord::RecordType( valueHexStr.toUInt(&isOk, kBaseHex))); rec->set_is_override_source_count(true); rec->set_is_override_aux_data_length(true); } else if (name == "icmpv6.mldr.mar.aux_data_len") { mld->mutable_group_records(mld->group_records_size() - 1)-> set_aux_data_length(valueHexStr.toUInt(&isOk, kBaseHex)); } else if (name == "icmpv6.mldr.mar.nb_sources") { mld->mutable_group_records(mld->group_records_size() - 1)-> set_source_count(valueHexStr.toUInt(&isOk, kBaseHex)); } else if (name == "icmpv6.mldr.mar.multicast_address") { OstProto::Gmp::IpAddress *ip = mld->mutable_group_records( mld->group_records_size() - 1)->mutable_group_address(); ip->set_v6_hi(valueHexStr.left(16).toULongLong(&isOk, kBaseHex)); ip->set_v6_lo(valueHexStr.right(16).toULongLong(&isOk, kBaseHex)); } else if (name == "icmpv6.mldr.mar.source_address") { OstProto::Gmp::IpAddress *ip = mld->mutable_group_records( mld->group_records_size() - 1)->add_sources(); ip->set_v6_hi(valueHexStr.left(16).toULongLong(&isOk, kBaseHex)); ip->set_v6_lo(valueHexStr.right(16).toULongLong(&isOk, kBaseHex)); } else if (name == "icmpv6.mldr.mar.auxiliary_data") { QByteArray ba = QByteArray::fromHex( attributes.value("value").toString().toUtf8()); mld->mutable_group_records(mld->group_records_size() - 1)-> set_aux_data(ba.constData(), ba.size()); } } ostinato-0.9/common/mldpdml.h000066400000000000000000000026631321315675200163300ustar00rootroot00000000000000/* Copyright (C) 2011 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _MLD_PDML_H #define _MLD_PDML_H #include "pdmlprotocol.h" class PdmlMldProtocol : public PdmlProtocol { friend class PdmlIcmp6Protocol; public: static PdmlProtocol* createInstance(); virtual void preProtocolHandler(QString name, const QXmlStreamAttributes &attributes, int expectedPos, OstProto::Protocol *pbProto, OstProto::Stream *stream); virtual void unknownFieldHandler(QString name, int pos, int size, const QXmlStreamAttributes &attributes, OstProto::Protocol *pbProto, OstProto::Stream *stream); protected: PdmlMldProtocol(); private: static const uint kMldQuery = 0x82; static const uint kMldV1Query = 0x82; static const uint kMldV2Query = 0xFF82; uint protoSize_; }; #endif ostinato-0.9/common/nativefileformat.cpp000066400000000000000000000416371321315675200205750ustar00rootroot00000000000000/* Copyright (C) 2010, 2016 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "nativefileformat.h" #include "crc32c.h" #include #include #include #define tr(str) QObject::tr(str) const char* NativeFileFormat::kFileMagicValue = "\xa7\xb7OSTINATO"; static const int kBaseHex = 16; static QString fileTypeStr(OstProto::FileType fileType) { switch (fileType) { case OstProto::kReservedFileType: return QString("Reserved"); case OstProto::kStreamsFileType: return QString("Streams"); case OstProto::kSessionFileType: return QString("Streams"); default: Q_ASSERT(false); } return QString("Unknown"); } NativeFileFormat::NativeFileFormat() { /* * We don't have any "real" work to do here in the constructor. * What we do is run some "assert" tests so that these get caught * at init itself instead of while saving/restoring when a user * might lose some data! */ OstProto::FileMagic magic; OstProto::FileChecksum cksum; magic.set_value(kFileMagicValue); cksum.set_value(quint32(0)); // TODO: convert Q_ASSERT to something that will run in RELEASE mode also Q_ASSERT(magic.IsInitialized()); Q_ASSERT(cksum.IsInitialized()); Q_ASSERT(magic.ByteSize() == kFileMagicSize); Q_ASSERT(cksum.ByteSize() == kFileChecksumSize); } bool NativeFileFormat::open( const QString fileName, OstProto::FileType fileType, OstProto::FileMeta &meta, OstProto::FileContent &content, QString &error) { QFile file(fileName); QByteArray buf; int size, contentOffset, contentSize; quint32 calcCksum; OstProto::FileMagic magic; OstProto::FileChecksum cksum, zeroCksum; if (!file.open(QIODevice::ReadOnly)) goto _open_fail; if (file.size() < kFileMagicSize) goto _magic_missing; if (file.size() < kFileMinSize) goto _checksum_missing; buf.resize(file.size()); size = file.read(buf.data(), buf.size()); if (size < 0) goto _read_fail; Q_ASSERT(file.atEnd()); file.close(); qDebug("%s: file.size() = %lld", __FUNCTION__, file.size()); qDebug("%s: size = %d", __FUNCTION__, size); //qDebug("Read %d bytes", buf.size()); //qDebug("%s", QString(buf.toHex()).toAscii().constData()); // Parse and verify magic if (!magic.ParseFromArray( (void*)(buf.constData() + kFileMagicOffset), kFileMagicSize)) { goto _magic_parse_fail; } if (magic.value() != kFileMagicValue) goto _magic_match_fail; // Parse and verify checksum if (!cksum.ParseFromArray( (void*)(buf.constData() + size - kFileChecksumSize), kFileChecksumSize)) { goto _cksum_parse_fail; } zeroCksum.set_value(0); if (!zeroCksum.SerializeToArray( (void*) (buf.data() + size - kFileChecksumSize), kFileChecksumSize)) { goto _zero_cksum_serialize_fail; } calcCksum = checksumCrc32C((quint8*) buf.constData(), size); qDebug("checksum \nExpected:%x Actual:%x", calcCksum, cksum.value()); if (cksum.value() != calcCksum) goto _cksum_verify_fail; // Parse the metadata first before we parse the full contents if (!meta.ParseFromArray( (void*)(buf.constData() + kFileMetaDataOffset), fileMetaSize((quint8*)buf.constData(), size))) { goto _metadata_parse_fail; } qDebug("%s: File MetaData (INFORMATION) - \n%s", __FUNCTION__, QString().fromStdString(meta.DebugString()).toAscii().constData()); qDebug("%s: END MetaData", __FUNCTION__); // MetaData Validation(s) if (meta.data().file_type() != fileType) goto _unexpected_file_type; if (meta.data().format_version_major() != kFileFormatVersionMajor) goto _incompatible_file_version; if (meta.data().format_version_minor() > kFileFormatVersionMinor) goto _incompatible_file_version; if (meta.data().format_version_minor() < kFileFormatVersionMinor) { // TODO: need to modify 'buf' such that we can parse successfully // assuming the native minor version } if (meta.data().format_version_revision() > kFileFormatVersionRevision) { error = QString(tr("%1 was created using a newer version of Ostinato." " New features/protocols will not be available.")).arg(fileName); } Q_ASSERT(meta.data().format_version_major() == kFileFormatVersionMajor); // ByteSize() does not include the Tag/Key, so we add 2 for that contentOffset = kFileMetaDataOffset + meta.data().ByteSize() + 2; contentSize = size - contentOffset - kFileChecksumSize; qDebug("%s: content offset/size = %d/%d", __FUNCTION__, contentOffset, contentSize); // Parse full contents if (!content.ParseFromArray( (void*)(buf.constData() + contentOffset), contentSize)) { goto _content_parse_fail; } return true; _content_parse_fail: error = QString(tr("Failed parsing %1 contents")).arg(fileName); qDebug("Error: %s", QString().fromStdString( content.InitializationErrorString()) .toAscii().constData()); qDebug("Debug: %s", QString().fromStdString( content.DebugString()).toAscii().constData()); goto _fail; _incompatible_file_version: error = QString(tr("%1 is in an incompatible format version - %2.%3.%4" " (Native version is %5.%6.%7)")) .arg(fileName) .arg(meta.data().format_version_major()) .arg(meta.data().format_version_minor()) .arg(meta.data().format_version_revision()) .arg(kFileFormatVersionMajor) .arg(kFileFormatVersionMinor) .arg(kFileFormatVersionRevision); goto _fail; _unexpected_file_type: error = QString(tr("%1 is not a %2 file")) .arg(fileName) .arg(fileTypeStr(fileType)); goto _fail; _metadata_parse_fail: error = QString(tr("Failed parsing %1 meta data")).arg(fileName); qDebug("Error: %s", QString().fromStdString( meta.data().InitializationErrorString()) .toAscii().constData()); goto _fail; _cksum_verify_fail: error = QString(tr("%1 checksum validation failed!\nExpected:%2 Actual:%3")) .arg(fileName) .arg(calcCksum, 0, kBaseHex) .arg(cksum.value(), 0, kBaseHex); goto _fail; _zero_cksum_serialize_fail: error = QString(tr("Internal Error: Zero Checksum Serialize failed!\n" "Error: %1\nDebug: %2")) .arg(QString().fromStdString( cksum.InitializationErrorString())) .arg(QString().fromStdString(cksum.DebugString())); goto _fail; _cksum_parse_fail: error = QString(tr("Failed parsing %1 checksum")).arg(fileName); qDebug("Error: %s", QString().fromStdString( cksum.InitializationErrorString()) .toAscii().constData()); goto _fail; _magic_match_fail: error = QString(tr("%1 is not an Ostinato file")).arg(fileName); goto _fail; _magic_parse_fail: error = QString(tr("%1 does not look like an Ostinato file")).arg(fileName); qDebug("Error: %s", QString().fromStdString( magic.InitializationErrorString()) .toAscii().constData()); goto _fail; _read_fail: error = QString(tr("Error reading from %1")).arg(fileName); goto _fail; _checksum_missing: error = QString(tr("%1 is too small (missing checksum)")).arg(fileName); goto _fail; _magic_missing: error = QString(tr("%1 is too small (missing magic value)")) .arg(fileName); goto _fail; _open_fail: error = QString(tr("Error opening %1")).arg(fileName); goto _fail; _fail: qDebug("%s", error.toAscii().constData()); return false; } bool NativeFileFormat::save( OstProto::FileType fileType, const OstProto::FileContent &content, const QString fileName, QString &error) { OstProto::FileMagic magic; OstProto::FileMeta meta; OstProto::FileChecksum cksum; QFile file(fileName); int metaSize, contentSize; int contentOffset, cksumOffset; QByteArray buf; quint32 calcCksum; magic.set_value(kFileMagicValue); Q_ASSERT(magic.IsInitialized()); cksum.set_value(0); Q_ASSERT(cksum.IsInitialized()); initFileMetaData(*(meta.mutable_data())); meta.mutable_data()->set_file_type(fileType); Q_ASSERT(meta.IsInitialized()); if (!content.IsInitialized()) goto _content_not_init; Q_ASSERT(content.IsInitialized()); metaSize = meta.ByteSize(); contentSize = content.ByteSize(); contentOffset = kFileMetaDataOffset + metaSize; cksumOffset = contentOffset + contentSize; Q_ASSERT(magic.ByteSize() == kFileMagicSize); Q_ASSERT(cksum.ByteSize() == kFileChecksumSize); buf.resize(kFileMagicSize + metaSize + contentSize + kFileChecksumSize); // Serialize everything if (!magic.SerializeToArray((void*) (buf.data() + kFileMagicOffset), kFileMagicSize)) { goto _magic_serialize_fail; } if (!meta.SerializeToArray((void*) (buf.data() + kFileMetaDataOffset), metaSize)) { goto _meta_serialize_fail; } if (!content.SerializeToArray((void*) (buf.data() + contentOffset), contentSize)) { goto _content_serialize_fail; } if (!cksum.SerializeToArray((void*) (buf.data() + cksumOffset), kFileChecksumSize)) { goto _zero_cksum_serialize_fail; } // TODO: emit status("Calculating checksum..."); // Calculate and write checksum calcCksum = checksumCrc32C((quint8*)buf.constData(), buf.size()); cksum.set_value(calcCksum); if (!cksum.SerializeToArray( (void*) (buf.data() + cksumOffset), kFileChecksumSize)) { goto _cksum_serialize_fail; } qDebug("Writing %d bytes", buf.size()); //qDebug("%s", QString(buf.toHex()).toAscii().constData()); // TODO: emit status("Writing to disk..."); if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) goto _open_fail; if (file.write(buf) < 0) goto _write_fail; file.close(); return true; _write_fail: error = QString(tr("Error writing to %1")).arg(fileName); goto _fail; _open_fail: error = QString(tr("Error opening %1 (Error Code = %2)")) .arg(fileName) .arg(file.error()); goto _fail; _cksum_serialize_fail: error = QString(tr("Internal Error: Checksum Serialize failed\n%1\n%2")) .arg(QString().fromStdString( cksum.InitializationErrorString())) .arg(QString().fromStdString(cksum.DebugString())); goto _fail; _zero_cksum_serialize_fail: error = QString(tr("Internal Eror: Zero Checksum Serialize failed\n%1\n%2")) .arg(QString().fromStdString( cksum.InitializationErrorString())) .arg(QString().fromStdString(cksum.DebugString())); goto _fail; _content_serialize_fail: error = QString(tr("Internal Error: Content Serialize failed\n%1\n%2")) .arg(QString().fromStdString( content.InitializationErrorString())) .arg(QString().fromStdString(content.DebugString())); goto _fail; _meta_serialize_fail: error = QString(tr("Internal Error: Meta Data Serialize failed\n%1\n%2")) .arg(QString().fromStdString( meta.InitializationErrorString())) .arg(QString().fromStdString(meta.DebugString())); goto _fail; _magic_serialize_fail: error = QString(tr("Internal Error: Magic Serialize failed\n%1\n%2")) .arg(QString().fromStdString( magic.InitializationErrorString())) .arg(QString().fromStdString(magic.DebugString())); goto _fail; _content_not_init: error = QString(tr("Internal Error: Content not initialized\n%1\n%2")) .arg(QString().fromStdString( content.InitializationErrorString())) .arg(QString().fromStdString(content.DebugString())); goto _fail; _fail: qDebug("%s", error.toAscii().constData()); return false; } bool NativeFileFormat::isNativeFileFormat( const QString fileName, OstProto::FileType fileType) { bool ret = false; QFile file(fileName); QByteArray buf; OstProto::FileMagic magic; if (!file.open(QIODevice::ReadOnly)) goto _exit; // Assume tag/length for MetaData will fit in 8 bytes buf = file.peek(kFileMagicOffset + kFileMagicSize + 8); if (!magic.ParseFromArray((void*)(buf.constData() + kFileMagicOffset), kFileMagicSize)) goto _close_exit; if (magic.value() == kFileMagicValue) { OstProto::FileMeta meta; int metaSize = fileMetaSize((quint8*)buf.constData(), buf.size()); buf = file.peek(kFileMagicOffset + kFileMagicSize + metaSize); if (!meta.ParseFromArray( (void*)(buf.constData() + kFileMetaDataOffset), metaSize)) { qDebug("%s: File MetaData\n%s", __FUNCTION__, QString().fromStdString(meta.DebugString()).toAscii().constData()); goto _close_exit; } if (meta.data().file_type() == fileType) ret = true; } _close_exit: file.close(); _exit: return ret; } void NativeFileFormat::initFileMetaData(OstProto::FileMetaData &metaData) { // Fill in the "native" file format version metaData.set_format_version_major(kFileFormatVersionMajor); metaData.set_format_version_minor(kFileFormatVersionMinor); metaData.set_format_version_revision(kFileFormatVersionRevision); metaData.set_generator_name( qApp->applicationName().toUtf8().constData()); metaData.set_generator_version( qApp->property("version").toString().toUtf8().constData()); metaData.set_generator_revision( qApp->property("revision").toString().toUtf8().constData()); } int NativeFileFormat::fileMetaSize(const quint8* file, int size) { int i = kFileMetaDataOffset; uint result, shift; const int kWireTypeLengthDelimited = 2; // An embedded Message field is encoded as // // See Protobuf Encoding for more details // Decode 'Key' varint result = 0; shift = 0; while (i < size) { quint8 byte = file[i++]; result |= (byte & 0x7f) << shift; if (!(byte & 0x80)) // MSB == 0? break; shift += 7; } if (i >= size) return 0; Q_ASSERT(result == ((OstProto::File::kMetaDataFieldNumber << 3) | kWireTypeLengthDelimited)); // Decode 'Length' varint result = 0; shift = 0; while (i < size) { quint8 byte = file[i++]; result |= (byte & 0x7f) << shift; if (!(byte & 0x80)) // MSB == 0? break; shift += 7; } if (i >= size) return 0; return int(result+(i-kFileMetaDataOffset)); } #pragma GCC diagnostic ignored "-Wdeprecated-declarations" /*! Fixup content to what is expected in the native version */ void NativeFileFormat::postParseFixup(OstProto::FileMetaData metaData, OstProto::FileContent &content) { Q_ASSERT(metaData.format_version_major() == kFileFormatVersionMajor); // Do fixups from oldest to newest versions switch (metaData.format_version_minor()) { case 1: { int n = content.matter().streams().stream_size(); for (int i = 0; i < n; i++) { OstProto::StreamControl *sctl = content.mutable_matter()->mutable_streams()->mutable_stream(i)->mutable_control(); sctl->set_packets_per_sec(sctl->obsolete_packets_per_sec()); sctl->set_bursts_per_sec(sctl->obsolete_bursts_per_sec()); } // fall-through to next higher version until native version } case kFileFormatVersionMinor: // native version break; case 0: default: qWarning("%s: minor version %u unhandled", __FUNCTION__, metaData.format_version_minor()); Q_ASSERT_X(false, "postParseFixup", "unhandled minor version"); } } #pragma GCC diagnostic warning "-Wdeprecated-declarations" ostinato-0.9/common/nativefileformat.h000066400000000000000000000046221321315675200202330ustar00rootroot00000000000000/* Copyright (C) 2010, 2016 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _NATIVE_FILE_FORMAT_H #define _NATIVE_FILE_FORMAT_H /* * This file contains helper functions for the native file format * defined in fileformat.proto * * The actual file format classes - (Ostm)FileFormat and OssnFileFormat * use multiple inheritance from the abstract interface class and this * helper class * * The primary reason for the existence of this class is to have a common * code for dealing with native file formats */ #include "fileformat.pb.h" #include class NativeFileFormat { public: NativeFileFormat(); bool open(const QString fileName, OstProto::FileType fileType, OstProto::FileMeta &meta, OstProto::FileContent &content, QString &error); bool save(OstProto::FileType fileType, const OstProto::FileContent &content, const QString fileName, QString &error); bool isNativeFileFormat(const QString fileName, OstProto::FileType fileType); void postParseFixup(OstProto::FileMetaData metaData, OstProto::FileContent &content); private: void initFileMetaData(OstProto::FileMetaData &metaData); int fileMetaSize(const quint8* file, int size); static const int kFileMagicSize = 12; static const int kFileChecksumSize = 5; static const int kFileMinSize = kFileMagicSize + kFileChecksumSize; static const int kFileMagicOffset = 0; static const int kFileMetaDataOffset = kFileMagicSize; static const char* kFileMagicValue; // Native file format version static const uint kFileFormatVersionMajor = 0; static const uint kFileFormatVersionMinor = 2; static const uint kFileFormatVersionRevision = 4; }; #endif ostinato-0.9/common/ossnfileformat.cpp000066400000000000000000000051051321315675200202570ustar00rootroot00000000000000/* Copyright (C) 2016 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "ossnfileformat.h" OssnFileFormat ossnFileFormat; OssnFileFormat::OssnFileFormat() : SessionFileFormat(), NativeFileFormat() { // Do Nothing } bool OssnFileFormat::open(const QString fileName, OstProto::SessionContent &session, QString &error) { OstProto::FileMeta meta; OstProto::FileContent content; bool ret = NativeFileFormat::open(fileName, OstProto::kSessionFileType, meta, content, error); if (!ret) goto _exit; if (!content.matter().has_session()) goto _missing_session; postParseFixup(meta.data(), content); session.CopyFrom(content.matter().session()); return true; _missing_session: error = QString(tr("%1 does not contain a session")).arg(fileName); goto _fail; _fail: qDebug("%s", error.toAscii().constData()); _exit: return false; } bool OssnFileFormat::save(const OstProto::SessionContent &session, const QString fileName, QString &error) { OstProto::FileContent content; if (!session.IsInitialized()) goto _session_not_init; content.mutable_matter()->mutable_session()->CopyFrom(session); Q_ASSERT(content.IsInitialized()); return NativeFileFormat::save(OstProto::kSessionFileType, content, fileName, error); _session_not_init: error = QString(tr("Internal Error: Session not initialized\n%1\n%2")) .arg(QString().fromStdString( session.InitializationErrorString())) .arg(QString().fromStdString(session.DebugString())); goto _fail; _fail: qDebug("%s", error.toAscii().constData()); return false; } bool OssnFileFormat::isMyFileFormat(const QString fileName) { return isNativeFileFormat(fileName, OstProto::kSessionFileType); } bool OssnFileFormat::isMyFileType(const QString fileType) { return fileType.contains("(*.ossn)") ? true : false; } ostinato-0.9/common/ossnfileformat.h000066400000000000000000000024111321315675200177210ustar00rootroot00000000000000/* Copyright (C) 2016 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _OSSN_FILE_FORMAT_H #define _OSSN_FILE_FORMAT_H #include "nativefileformat.h" #include "sessionfileformat.h" class OssnFileFormat : public SessionFileFormat, public NativeFileFormat { public: OssnFileFormat(); virtual bool open(const QString fileName, OstProto::SessionContent &session, QString &error); virtual bool save(const OstProto::SessionContent &session, const QString fileName, QString &error); virtual bool isMyFileFormat(const QString fileName); virtual bool isMyFileType(const QString fileType); }; extern OssnFileFormat ossnFileFormat; #endif ostinato-0.9/common/ostmfileformat.cpp000066400000000000000000000051461321315675200202640ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "ostmfileformat.h" OstmFileFormat fileFormat; OstmFileFormat::OstmFileFormat() : StreamFileFormat(), NativeFileFormat() { // Do Nothing! } bool OstmFileFormat::open(const QString fileName, OstProto::StreamConfigList &streams, QString &error) { OstProto::FileMeta meta; OstProto::FileContent content; bool ret = NativeFileFormat::open(fileName, OstProto::kStreamsFileType, meta, content, error); if (!ret) goto _fail; if (!content.matter().has_streams()) goto _missing_streams; postParseFixup(meta.data(), content); streams.CopyFrom(content.matter().streams()); return true; _missing_streams: error = QString(tr("%1 does not contain any streams")).arg(fileName); goto _fail; _fail: qDebug("%s", error.toAscii().constData()); return false; } bool OstmFileFormat::save(const OstProto::StreamConfigList streams, const QString fileName, QString &error) { OstProto::FileContent content; if (!streams.IsInitialized()) goto _stream_not_init; content.mutable_matter()->mutable_streams()->CopyFrom(streams); Q_ASSERT(content.IsInitialized()); return NativeFileFormat::save(OstProto::kStreamsFileType, content, fileName, error); _stream_not_init: error = QString(tr("Internal Error: Streams not initialized\n%1\n%2")) .arg(QString().fromStdString( streams.InitializationErrorString())) .arg(QString().fromStdString(streams.DebugString())); goto _fail; _fail: qDebug("%s", error.toAscii().constData()); return false; } bool OstmFileFormat::isMyFileFormat(const QString fileName) { return isNativeFileFormat(fileName, OstProto::kStreamsFileType); } bool OstmFileFormat::isMyFileType(const QString fileType) { if (fileType.startsWith("Ostinato")) return true; else return false; } ostinato-0.9/common/ostmfileformat.h000066400000000000000000000024201321315675200177210ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _OSTM_FILE_FORMAT_H #define _OSTM_FILE_FORMAT_H #include "nativefileformat.h" #include "streamfileformat.h" #include "fileformat.pb.h" class OstmFileFormat : public StreamFileFormat, public NativeFileFormat { public: OstmFileFormat(); virtual bool open(const QString fileName, OstProto::StreamConfigList &streams, QString &error); virtual bool save(const OstProto::StreamConfigList streams, const QString fileName, QString &error); bool isMyFileFormat(const QString fileName); bool isMyFileType(const QString fileType); }; extern OstmFileFormat fileFormat; #endif ostinato-0.9/common/ostproto.pro000066400000000000000000000037221321315675200171360ustar00rootroot00000000000000TEMPLATE = lib CONFIG += qt staticlib QT -= gui QT += network script xml LIBS += \ -lprotobuf PROTOS = \ protocol.proto \ emulproto.proto PROTOS += \ mac.proto \ payload.proto \ eth2.proto \ dot3.proto \ llc.proto \ snap.proto \ dot2llc.proto \ dot2snap.proto \ vlan.proto \ svlan.proto \ vlanstack.proto \ stp.proto \ arp.proto \ ip4.proto \ ip6.proto \ ip6over4.proto \ ip4over6.proto \ ip4over4.proto \ ip6over6.proto \ icmp.proto \ gmp.proto \ igmp.proto \ mld.proto \ tcp.proto \ udp.proto \ textproto.proto \ userscript.proto \ hexdump.proto \ sample.proto \ sign.proto HEADERS = \ abstractprotocol.h \ comboprotocol.h \ protocolmanager.h \ protocollist.h \ protocollistiterator.h \ streambase.h \ updater.h \ HEADERS += \ mac.h \ vlan.h \ svlan.h \ vlanstack.h \ eth2.h \ dot3.h \ llc.h \ dot2llc.h \ snap.h \ dot2snap.h \ arp.h \ ip4.h \ ip6.h \ ip4over4.h \ ip4over6.h \ ip6over4.h \ ip6over6.h \ gmp.h \ icmp.h \ igmp.h \ mld.h \ tcp.h \ udp.h \ textproto.h \ hexdump.h \ payload.h \ sample.h \ sign.h \ userscript.h SOURCES = \ abstractprotocol.cpp \ crc32c.cpp \ protocolmanager.cpp \ protocollist.cpp \ protocollistiterator.cpp \ streambase.cpp \ updater.cpp \ SOURCES += \ mac.cpp \ vlan.cpp \ svlan.cpp \ eth2.cpp \ dot3.cpp \ llc.cpp \ snap.cpp \ stp.cpp \ arp.cpp \ ip4.cpp \ ip6.cpp \ gmp.cpp \ icmp.cpp \ igmp.cpp \ mld.cpp \ tcp.cpp \ udp.cpp \ textproto.cpp \ hexdump.cpp \ payload.cpp \ sample.cpp \ sign.cpp \ userscript.cpp QMAKE_DISTCLEAN += object_script.* #binding.depends = compiler_protobuf_py_make_all #QMAKE_EXTRA_TARGETS += binding include(../protobuf.pri) ostinato-0.9/common/ostprotogui.pro000066400000000000000000000053651321315675200176500ustar00rootroot00000000000000TEMPLATE = lib CONFIG += qt staticlib QT += network xml script INCLUDEPATH += "../extra/qhexedit2/src" LIBS += \ -lprotobuf FORMS = \ pcapfileimport.ui \ FORMS += \ mac.ui \ vlan.ui \ eth2.ui \ dot3.ui \ llc.ui \ snap.ui \ stp.ui \ arp.ui \ ip4.ui \ ip6.ui \ gmp.ui \ icmp.ui \ tcp.ui \ udp.ui \ textproto.ui \ hexdump.ui \ payload.ui \ sample.ui \ sign.ui \ userscript.ui PROTOS = \ fileformat.proto # TODO: Move fileformat related stuff into a different library - why? HEADERS = \ ostprotolib.h \ ipv4addressdelegate.h \ ipv6addressdelegate.h \ nativefileformat.h \ ossnfileformat.h \ ostmfileformat.h \ pcapfileformat.h \ pdmlfileformat.h \ pythonfileformat.h \ pdmlprotocol.h \ pdmlprotocols.h \ pdmlreader.h \ sessionfileformat.h \ streamfileformat.h \ spinboxdelegate.h HEADERS += \ abstractprotocolconfig.h \ comboprotocolconfig.h \ protocolwidgetfactory.h \ macconfig.h \ vlanconfig.h \ svlanconfig.h \ vlanstackconfig.h \ eth2config.h \ dot3config.h \ llcconfig.h \ dot2llcconfig.h \ snapconfig.h \ dot2snapconfig.h \ stpconfig.h \ arpconfig.h \ ip4config.h \ ip6config.h \ ip4over4config.h \ gmpconfig.h \ icmpconfig.h \ igmpconfig.h \ mldconfig.h \ tcpconfig.h \ udpconfig.h \ textprotoconfig.h \ hexdumpconfig.h \ payloadconfig.h \ sampleconfig.h \ signconfig.h \ userscriptconfig.h SOURCES += \ ostprotolib.cpp \ nativefileformat.cpp \ ossnfileformat.cpp \ ostmfileformat.cpp \ pcapfileformat.cpp \ pdmlfileformat.cpp \ pythonfileformat.cpp \ pdmlprotocol.cpp \ pdmlprotocols.cpp \ pdmlreader.cpp \ sessionfileformat.cpp \ streamfileformat.cpp \ spinboxdelegate.cpp SOURCES += \ protocolwidgetfactory.cpp \ macconfig.cpp \ vlanconfig.cpp \ eth2config.cpp \ dot3config.cpp \ llcconfig.cpp \ snapconfig.cpp \ stpconfig.cpp \ arpconfig.cpp \ ip4config.cpp \ ip6config.cpp \ gmpconfig.cpp \ icmpconfig.cpp \ igmpconfig.cpp \ mldconfig.cpp \ tcpconfig.cpp \ udpconfig.cpp \ textprotoconfig.cpp \ hexdumpconfig.cpp \ payloadconfig.cpp \ sampleconfig.cpp \ signconfig.cpp \ userscriptconfig.cpp SOURCES += \ vlanpdml.cpp \ svlanpdml.cpp \ stppdml.cpp \ eth2pdml.cpp \ llcpdml.cpp \ arppdml.cpp \ ip4pdml.cpp \ ip6pdml.cpp \ icmppdml.cpp \ icmp6pdml.cpp \ igmppdml.cpp \ mldpdml.cpp \ tcppdml.cpp \ udppdml.cpp \ textprotopdml.cpp \ samplepdml.cpp QMAKE_DISTCLEAN += object_script.* include(../protobuf.pri) ostinato-0.9/common/ostprotolib.cpp000066400000000000000000000025101321315675200176010ustar00rootroot00000000000000/* Copyright (C) 2011 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "ostprotolib.h" QString OstProtoLib::tsharkPath_; QString OstProtoLib::gzipPath_; QString OstProtoLib::diffPath_; QString OstProtoLib::awkPath_; // TODO: one set method for each external app void OstProtoLib::setExternalApplicationPaths(QString tsharkPath, QString gzipPath, QString diffPath, QString awkPath) { tsharkPath_ = tsharkPath; gzipPath_ = gzipPath; diffPath_ = diffPath; awkPath_ = awkPath; } QString OstProtoLib::tsharkPath() { return tsharkPath_; } QString OstProtoLib::gzipPath() { return gzipPath_; } QString OstProtoLib::diffPath() { return diffPath_; } QString OstProtoLib::awkPath() { return awkPath_; } ostinato-0.9/common/ostprotolib.h000066400000000000000000000022221321315675200172460ustar00rootroot00000000000000/* Copyright (C) 2011 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _OST_PROTO_LIB_H #define _OST_PROTO_LIB_H #include class OstProtoLib { public: static void setExternalApplicationPaths(QString tsharkPath, QString gzipPath, QString diffPath, QString awkPath); static QString tsharkPath(); static QString gzipPath(); static QString diffPath(); static QString awkPath(); private: static QString tsharkPath_; static QString gzipPath_; static QString diffPath_; static QString awkPath_; }; #endif ostinato-0.9/common/payload.cpp000066400000000000000000000162171321315675200166630ustar00rootroot00000000000000/* Copyright (C) 2010, 2013-2014 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "payload.h" #include "streambase.h" PayloadProtocol::PayloadProtocol(StreamBase *stream, AbstractProtocol *parent) : AbstractProtocol(stream, parent) { } PayloadProtocol::~PayloadProtocol() { } AbstractProtocol* PayloadProtocol::createInstance(StreamBase *stream, AbstractProtocol *parent) { return new PayloadProtocol(stream, parent); } quint32 PayloadProtocol::protocolNumber() const { return OstProto::Protocol::kPayloadFieldNumber; } void PayloadProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const { protocol.MutableExtension(OstProto::payload)->CopyFrom(data); protocol.mutable_protocol_id()->set_id(protocolNumber()); } void PayloadProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) { if (protocol.protocol_id().id() == protocolNumber() && protocol.HasExtension(OstProto::payload)) data.MergeFrom(protocol.GetExtension(OstProto::payload)); } QString PayloadProtocol::name() const { return QString("Payload Data"); } QString PayloadProtocol::shortName() const { return QString("DATA"); } int PayloadProtocol::protocolFrameSize(int streamIndex) const { int len; len = mpStream->frameLen(streamIndex) - protocolFrameOffset(streamIndex) - protocolFramePayloadSize(streamIndex) - kFcsSize; if (len < 0) len = 0; qDebug("%s: this = %p, streamIndex = %d, len = %d", __FUNCTION__, this, streamIndex, len); return len; } int PayloadProtocol::fieldCount() const { return payload_fieldCount; } AbstractProtocol::FieldFlags PayloadProtocol::fieldFlags(int index) const { AbstractProtocol::FieldFlags flags; flags = AbstractProtocol::fieldFlags(index); switch (index) { case payload_dataPattern: break; // Meta fields case payload_dataPatternMode: flags &= ~FrameField; flags |= MetaField; break; } return flags; } QVariant PayloadProtocol::fieldData(int index, FieldAttrib attrib, int streamIndex) const { switch (index) { case payload_dataPattern: switch(attrib) { case FieldName: return QString("Data"); case FieldValue: return data.pattern(); case FieldTextValue: return QString(fieldData(index, FieldFrameValue, streamIndex).toByteArray().toHex()); case FieldFrameValue: { QByteArray fv; int dataLen; dataLen = protocolFrameSize(streamIndex); // FIXME: Hack! Bad! Bad! Very Bad!!! if (dataLen <= 0) dataLen = 1; fv.resize(dataLen+4); switch(data.pattern_mode()) { case OstProto::Payload::e_dp_fixed_word: for (int i = 0; i < (dataLen/4)+1; i++) qToBigEndian((quint32) data.pattern(), (uchar*)(fv.data()+(i*4)) ); break; case OstProto::Payload::e_dp_inc_byte: for (int i = 0; i < dataLen; i++) fv[i] = i % (0xFF + 1); break; case OstProto::Payload::e_dp_dec_byte: for (int i = 0; i < dataLen; i++) fv[i] = 0xFF - (i % (0xFF + 1)); break; case OstProto::Payload::e_dp_random: //! \todo (HIGH) cksum is incorrect for random pattern for (int i = 0; i < dataLen; i++) fv[i] = qrand() % (0xFF + 1); break; default: qWarning("Unhandled data pattern %d", data.pattern_mode()); } fv.resize(dataLen); return fv; } default: break; } break; // Meta fields case payload_dataPatternMode: switch(attrib) { case FieldValue: return data.pattern_mode(); default: break; } break; default: break; } return AbstractProtocol::fieldData(index, attrib, streamIndex); } bool PayloadProtocol::setFieldData(int index, const QVariant &value, FieldAttrib attrib) { bool isOk = false; if (attrib != FieldValue) return false; switch (index) { case payload_dataPattern: { uint pattern = value.toUInt(&isOk); if (isOk) data.set_pattern(pattern); break; } case payload_dataPatternMode: { uint mode = value.toUInt(&isOk); if (isOk && data.DataPatternMode_IsValid(mode)) data.set_pattern_mode(OstProto::Payload::DataPatternMode(mode)); else isOk = false; break; } default: qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, index); break; } return isOk; } bool PayloadProtocol::isProtocolFrameValueVariable() const { return (AbstractProtocol::isProtocolFrameSizeVariable() || isProtocolFrameSizeVariable()); } bool PayloadProtocol::isProtocolFrameSizeVariable() const { if (mpStream->lenMode() == StreamBase::e_fl_fixed) return false; else return true; } int PayloadProtocol::protocolFrameVariableCount() const { int count = AbstractProtocol::protocolFrameVariableCount(); if (data.pattern_mode() == OstProto::Payload::e_dp_random) { switch(mpStream->sendUnit()) { case OstProto::StreamControl::e_su_packets: return mpStream->numPackets(); case OstProto::StreamControl::e_su_bursts: return int(mpStream->numBursts() * mpStream->burstSize() * mpStream->burstRate()); } } if (mpStream->lenMode() != StreamBase::e_fl_fixed) { count = AbstractProtocol::lcm(count, mpStream->frameLenMax() - mpStream->frameLenMin() + 1); } return count; } ostinato-0.9/common/payload.h000066400000000000000000000040201321315675200163150ustar00rootroot00000000000000/* Copyright (C) 2010-2014 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _PAYLOAD_H #define _PAYLOAD_H #include "abstractprotocol.h" #include "payload.pb.h" class PayloadProtocol : public AbstractProtocol { public: enum payloadfield { payload_dataPattern, // Meta fields payload_dataPatternMode, payload_fieldCount }; PayloadProtocol(StreamBase *stream, AbstractProtocol *parent = 0); virtual ~PayloadProtocol(); static AbstractProtocol* createInstance(StreamBase *stream, AbstractProtocol *parent = 0); virtual quint32 protocolNumber() const; virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); virtual QString name() const; virtual QString shortName() const; virtual int protocolFrameSize(int streamIndex = 0) const; virtual int fieldCount() const; virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; virtual QVariant fieldData(int index, FieldAttrib attrib, int streamIndex = 0) const; virtual bool setFieldData(int index, const QVariant &value, FieldAttrib attrib = FieldValue); virtual bool isProtocolFrameValueVariable() const; virtual bool isProtocolFrameSizeVariable() const; virtual int protocolFrameVariableCount() const; private: OstProto::Payload data; }; #endif ostinato-0.9/common/payload.proto000066400000000000000000000021331321315675200172340ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ import "protocol.proto"; package OstProto; message Payload { enum DataPatternMode { e_dp_fixed_word = 0; e_dp_inc_byte = 1; e_dp_dec_byte = 2; e_dp_random = 3; } // Data Pattern optional DataPatternMode pattern_mode = 1; optional uint32 pattern = 2; //optional uint32 data_start_ofs = 13; } extend Protocol { optional Payload payload = 101; } ostinato-0.9/common/payload.ui000066400000000000000000000046541321315675200165200ustar00rootroot00000000000000 payload 0 0 299 114 Form Type cmbPatternMode Fixed Word Increment Byte Decrement Byte Random Qt::Horizontal 40 20 Pattern lePattern >HH HH HH HH; 11 Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Qt::Vertical 20 40 ostinato-0.9/common/payloadconfig.cpp000066400000000000000000000044161321315675200200470ustar00rootroot00000000000000/* Copyright (C) 2010-2014 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "payloadconfig.h" #include "payload.h" PayloadConfigForm::PayloadConfigForm(QWidget *parent) : AbstractProtocolConfigForm(parent) { setupUi(this); } PayloadConfigForm::~PayloadConfigForm() { } AbstractProtocolConfigForm* PayloadConfigForm::createInstance() { return new PayloadConfigForm; } void PayloadConfigForm::loadWidget(AbstractProtocol *proto) { cmbPatternMode->setCurrentIndex( proto->fieldData( PayloadProtocol::payload_dataPatternMode, AbstractProtocol::FieldValue ).toUInt()); lePattern->setText(uintToHexStr( proto->fieldData( PayloadProtocol::payload_dataPattern, AbstractProtocol::FieldValue ).toUInt(), 4)); } void PayloadConfigForm::storeWidget(AbstractProtocol *proto) { bool isOk; proto->setFieldData( PayloadProtocol::payload_dataPatternMode, cmbPatternMode->currentIndex()); proto->setFieldData( PayloadProtocol::payload_dataPattern, lePattern->text().remove(QChar(' ')).toUInt(&isOk, BASE_HEX)); } void PayloadConfigForm::on_cmbPatternMode_currentIndexChanged(int index) { switch(index) { case OstProto::Payload::e_dp_fixed_word: lePattern->setEnabled(true); break; case OstProto::Payload::e_dp_inc_byte: case OstProto::Payload::e_dp_dec_byte: case OstProto::Payload::e_dp_random: lePattern->setDisabled(true); break; default: qWarning("Unhandled/Unknown PatternMode = %d",index); } } ostinato-0.9/common/payloadconfig.h000066400000000000000000000023361321315675200175130ustar00rootroot00000000000000/* Copyright (C) 2010-2014 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _PAYLOAD_CONFIG_H #define _PAYLOAD_CONFIG_H #include "abstractprotocolconfig.h" #include "ui_payload.h" class PayloadConfigForm : public AbstractProtocolConfigForm, private Ui::payload { Q_OBJECT public: PayloadConfigForm(QWidget *parent = 0); virtual ~PayloadConfigForm(); static AbstractProtocolConfigForm* createInstance(); virtual void loadWidget(AbstractProtocol *proto); virtual void storeWidget(AbstractProtocol *proto); private slots: void on_cmbPatternMode_currentIndexChanged(int index); }; #endif ostinato-0.9/common/pcapfileformat.cpp000066400000000000000000000440171321315675200202250ustar00rootroot00000000000000/* Copyright (C) 2011 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "pcapfileformat.h" #include "pdmlreader.h" #include "ostprotolib.h" #include "streambase.h" #include "hexdump.pb.h" #include #include #include #include #include #include const quint32 kPcapFileMagic = 0xa1b2c3d4; const quint32 kPcapFileMagicSwapped = 0xd4c3b2a1; const quint16 kPcapFileVersionMajor = 2; const quint16 kPcapFileVersionMinor = 4; const quint32 kMaxSnapLen = 65535; const quint32 kDltEthernet = 1; PcapFileFormat pcapFileFormat; PcapImportOptionsDialog::PcapImportOptionsDialog(QVariantMap *options) : QDialog(NULL) { setupUi(this); options_ = options; viaPdml->setChecked(options_->value("ViaPdml").toBool()); doDiff->setChecked(options_->value("DoDiff").toBool()); connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); } PcapImportOptionsDialog::~PcapImportOptionsDialog() { } void PcapImportOptionsDialog::accept() { options_->insert("ViaPdml", viaPdml->isChecked()); options_->insert("DoDiff", doDiff->isChecked()); QDialog::accept(); } PcapFileFormat::PcapFileFormat() { importOptions_.insert("ViaPdml", true); importOptions_.insert("DoDiff", true); importDialog_ = NULL; } PcapFileFormat::~PcapFileFormat() { delete importDialog_; } bool PcapFileFormat::open(const QString fileName, OstProto::StreamConfigList &streams, QString &error) { bool isOk = false; QFile file(fileName); QTemporaryFile file2; quint32 magic; uchar gzipMagic[2]; int len; PcapFileHeader fileHdr; PcapPacketHeader pktHdr; OstProto::Stream *prevStream = NULL; uint lastUsec = 0; int pktCount; qint64 byteCount = 0; qint64 byteTotal; QByteArray pktBuf; if (!file.open(QIODevice::ReadOnly)) goto _err_open; len = file.peek((char*)gzipMagic, sizeof(gzipMagic)); if (len < int(sizeof(gzipMagic))) goto _err_reading_magic; if ((gzipMagic[0] == 0x1f) && (gzipMagic[1] == 0x8b)) { QProcess gzip; emit status("Decompressing..."); emit target(0); if (!file2.open()) { error.append("Unable to open temporary file to uncompress .gz\n"); goto _err_unzip_fail; } qDebug("decompressing to %s", file2.fileName().toAscii().constData()); gzip.setStandardOutputFile(file2.fileName()); gzip.start(OstProtoLib::gzipPath(), QStringList() << "-d" << "-c" << fileName); if (!gzip.waitForStarted(-1)) { error.append(QString("Unable to start gzip. Check path in Preferences.\n")); goto _err_unzip_fail; } if (!gzip.waitForFinished(-1)) { error.append(QString("Error running gzip\n")); goto _err_unzip_fail; } file2.seek(0); fd_.setDevice(&file2); } else { fd_.setDevice(&file); } byteTotal = fd_.device()->size() - sizeof(fileHdr); emit status("Reading File Header..."); emit target(0); fd_ >> magic; qDebug("magic = %08x", magic); if (magic == kPcapFileMagicSwapped) { // Toggle Byte order if (fd_.byteOrder() == QDataStream::BigEndian) fd_.setByteOrder(QDataStream::LittleEndian); else fd_.setByteOrder(QDataStream::BigEndian); } else if (magic != kPcapFileMagic) goto _err_bad_magic; fd_ >> fileHdr.versionMajor; fd_ >> fileHdr.versionMinor; fd_ >> fileHdr.thisZone; fd_ >> fileHdr.sigfigs; fd_ >> fileHdr.snapLen; fd_ >> fileHdr.network; if ((fileHdr.versionMajor != kPcapFileVersionMajor) || (fileHdr.versionMinor != kPcapFileVersionMinor)) goto _err_unsupported_version; #if 1 // XXX: we support only Ethernet, for now if (fileHdr.network != kDltEthernet) goto _err_unsupported_encap; #endif pktBuf.resize(fileHdr.snapLen); if (importOptions_.value("ViaPdml").toBool()) { QProcess tshark; QTemporaryFile pdmlFile; PdmlReader reader(&streams); if (!pdmlFile.open()) { error.append("Unable to open temporary file to create PDML\n"); goto _non_pdml; } qDebug("generating PDML %s", pdmlFile.fileName().toAscii().constData()); emit status("Generating PDML..."); emit target(0); tshark.setStandardOutputFile(pdmlFile.fileName()); tshark.start(OstProtoLib::tsharkPath(), QStringList() << QString("-r%1").arg(fileName) << "-otcp.desegment_tcp_streams:FALSE" << "-Tpdml"); if (!tshark.waitForStarted(-1)) { error.append(QString("Unable to start tshark. Check path in preferences.\n")); goto _non_pdml; } if (!tshark.waitForFinished(-1)) { error.append(QString("Error running tshark\n")); goto _non_pdml; } connect(&reader, SIGNAL(progress(int)), this, SIGNAL(progress(int))); emit status("Reading PDML packets..."); emit target(100); // in percentage isOk = reader.read(&pdmlFile, this, &stop_); if (stop_) goto _user_cancel; if (!isOk) { error.append(QString("Error processing PDML (%1, %2): %3\n") .arg(reader.lineNumber()) .arg(reader.columnNumber()) .arg(reader.errorString())); goto _exit; } if (!importOptions_.value("DoDiff").toBool()) goto _exit; // !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-! // Let's do the diff ... // !-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-! QProcess awk; QProcess diff; QTemporaryFile originalTextFile; QTemporaryFile importedPcapFile; QTemporaryFile importedTextFile; QTemporaryFile diffFile; const QString kAwkFilter = "/^[^0]/ { " "printf \" %s \", $1;" "for (i=4; i %s", originalTextFile.fileName().toAscii().constData(), importedTextFile.fileName().toAscii().constData(), diffFile.fileName().toAscii().constData()); emit status("Taking diff..."); emit target(0); diff.setStandardOutputFile(diffFile.fileName()); diff.start(OstProtoLib::diffPath(), QStringList() << "-u" << "-F^ [1-9]" << QString("--label=%1 (actual)") .arg(QFileInfo(fileName).fileName()) << QString("--label=%1 (imported)") .arg(QFileInfo(fileName).fileName()) << originalTextFile.fileName() << importedTextFile.fileName()); if (!diff.waitForStarted(-1)) { error.append(QString("Unable to start diff. Check path in Preferences.\n") .arg(diff.exitCode())); goto _diff_fail; } if (!diff.waitForFinished(-1)) { error.append(QString("Error running diff\n")); goto _diff_fail; } diffFile.close(); if (diffFile.size()) { error.append("There is a diff between the original and imported streams. See details for diff.\n\n\n\n"); diffFile.open(); diffFile.seek(0); error.append(QString(diffFile.readAll())); } goto _exit; } _non_pdml: emit status("Reading Packets..."); emit target(100); // in percentage pktCount = 1; while (!fd_.atEnd()) { OstProto::Stream *stream = streams.add_stream(); OstProto::Protocol *proto = stream->add_protocol(); OstProto::HexDump *hexDump = proto->MutableExtension(OstProto::hexDump); proto->mutable_protocol_id()->set_id( OstProto::Protocol::kHexDumpFieldNumber); readPacket(pktHdr, pktBuf); // validations on inclLen <= origLen && inclLen <= snapLen Q_ASSERT(pktHdr.inclLen <= fileHdr.snapLen); // TODO: convert to if hexDump->set_content(pktBuf.data(), pktHdr.inclLen); hexDump->set_pad_until_end(false); stream->mutable_stream_id()->set_id(pktCount); stream->mutable_core()->set_is_enabled(true); stream->mutable_core()->set_frame_len(pktHdr.inclLen+4); // FCS // setup packet rate to the timing in pcap (as close as possible) const uint kUsecsInSec = uint(1e6); uint usec = (pktHdr.tsSec*kUsecsInSec + pktHdr.tsUsec); uint delta = usec - lastUsec; if ((pktCount != 1) && delta) stream->mutable_control()->set_packets_per_sec(kUsecsInSec/delta); if (prevStream) prevStream->mutable_control()->CopyFrom(stream->control()); lastUsec = usec; prevStream = stream; pktCount++; qDebug("pktCount = %d", pktCount); byteCount += pktHdr.inclLen + sizeof(pktHdr); emit progress(int(byteCount*100/byteTotal)); // in percentage if (stop_) goto _user_cancel; } isOk = true; goto _exit; _user_cancel: isOk = true; goto _exit; _diff_fail: goto _exit; _err_unsupported_encap: error = QString(tr("%1 has non-ethernet encapsulation (%2) which is " "not supported - Sorry!")) .arg(QFileInfo(fileName).fileName()).arg(fileHdr.network); goto _exit; _err_unsupported_version: error = QString(tr("%1 is in PCAP version %2.%3 format which is " "not supported - Sorry!")) .arg(fileName).arg(fileHdr.versionMajor).arg(fileHdr.versionMinor); goto _exit; _err_bad_magic: error = QString(tr("%1 is not a valid PCAP file")).arg(fileName); goto _exit; #if 0 _err_truncated: error = QString(tr("%1 is too short")).arg(fileName); goto _exit; #endif _err_unzip_fail: goto _exit; _err_reading_magic: error = QString(tr("Unable to read magic from %1")).arg(fileName); goto _exit; _err_open: error = QString(tr("Unable to open file: %1")).arg(fileName); goto _exit; _exit: file.close(); return isOk; } /*! Reads packet meta data into pktHdr and packet content into buf. Returns true if packet is read successfully, false otherwise. */ bool PcapFileFormat::readPacket(PcapPacketHeader &pktHdr, QByteArray &pktBuf) { quint32 len; // TODO: chk fd_.status() // read PcapPacketHeader fd_ >> pktHdr.tsSec; fd_ >> pktHdr.tsUsec; fd_ >> pktHdr.inclLen; fd_ >> pktHdr.origLen; // TODO: chk fd_.status() // XXX: should never be required, but we play safe if (quint32(pktBuf.size()) < pktHdr.inclLen) pktBuf.resize(pktHdr.inclLen); // read Pkt contents len = fd_.readRawData(pktBuf.data(), pktHdr.inclLen); // TODO: use while? Q_ASSERT(len == pktHdr.inclLen); // TODO: remove assert pktBuf.resize(len); return true; } bool PcapFileFormat::save(const OstProto::StreamConfigList streams, const QString fileName, QString &error) { bool isOk = false; QFile file(fileName); PcapFileHeader fileHdr; PcapPacketHeader pktHdr; QByteArray pktBuf; if (!file.open(QIODevice::WriteOnly)) goto _err_open; fd_.setDevice(&file); fileHdr.magicNumber = kPcapFileMagic; fileHdr.versionMajor = kPcapFileVersionMajor; fileHdr.versionMinor = kPcapFileVersionMinor; fileHdr.thisZone = 0; fileHdr.sigfigs = 0; fileHdr.snapLen = kMaxSnapLen; fileHdr.network = kDltEthernet; fd_ << fileHdr.magicNumber; fd_ << fileHdr.versionMajor; fd_ << fileHdr.versionMinor; fd_ << fileHdr.thisZone; fd_ << fileHdr.sigfigs; fd_ << fileHdr.snapLen; fd_ << fileHdr.network; pktBuf.resize(kMaxSnapLen); emit status("Writing Packets..."); emit target(streams.stream_size()); pktHdr.tsSec = 0; pktHdr.tsUsec = 0; for (int i = 0; i < streams.stream_size(); i++) { StreamBase s; s.setId(i); s.protoDataCopyFrom(streams.stream(i)); // TODO: expand frameIndex for each stream s.frameValue((uchar*)pktBuf.data(), pktBuf.size(), 0); pktHdr.inclLen = s.frameProtocolLength(0); // FIXME: stream index = 0 pktHdr.origLen = s.frameLen() - 4; // FCS; FIXME: Hardcoding qDebug("savepcap i=%d, incl/orig len = %d/%d", i, pktHdr.inclLen, pktHdr.origLen); if (pktHdr.inclLen > fileHdr.snapLen) pktHdr.inclLen = fileHdr.snapLen; fd_ << pktHdr.tsSec; fd_ << pktHdr.tsUsec; fd_ << pktHdr.inclLen; fd_ << pktHdr.origLen; fd_.writeRawData(pktBuf.data(), pktHdr.inclLen); if (s.packetRate()) pktHdr.tsUsec += quint32(1e6/s.packetRate()); if (pktHdr.tsUsec >= 1000000) { pktHdr.tsSec++; pktHdr.tsUsec -= 1000000; } emit progress(i); } file.close(); isOk = true; goto _exit; _err_open: error = QString(tr("Unable to open file: %1")).arg(fileName); goto _exit; _exit: return isOk; } QDialog* PcapFileFormat::openOptionsDialog() { if (!importDialog_) importDialog_ = new PcapImportOptionsDialog(&importOptions_); return importDialog_; } bool PcapFileFormat::isMyFileFormat(const QString /*fileName*/) { // TODO return true; } bool PcapFileFormat::isMyFileType(const QString fileType) { if (fileType.startsWith("PCAP")) return true; else return false; } ostinato-0.9/common/pcapfileformat.h000066400000000000000000000050151321315675200176650ustar00rootroot00000000000000/* Copyright (C) 2011 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _PCAP_FILE_FORMAT_H #define _PCAP_FILE_FORMAT_H #include "streamfileformat.h" #include "ui_pcapfileimport.h" #include #include class PcapImportOptionsDialog: public QDialog, public Ui::PcapFileImport { public: PcapImportOptionsDialog(QVariantMap *options); ~PcapImportOptionsDialog(); private slots: void accept(); private: QVariantMap *options_; }; class PdmlReader; class PcapFileFormat : public StreamFileFormat { friend class PdmlReader; public: PcapFileFormat(); ~PcapFileFormat(); bool open(const QString fileName, OstProto::StreamConfigList &streams, QString &error); bool save(const OstProto::StreamConfigList streams, const QString fileName, QString &error); virtual QDialog* openOptionsDialog(); bool isMyFileFormat(const QString fileName); bool isMyFileType(const QString fileType); private: typedef struct { quint32 magicNumber; /* magic number */ quint16 versionMajor; /* major version number */ quint16 versionMinor; /* minor version number */ qint32 thisZone; /* GMT to local correction */ quint32 sigfigs; /* accuracy of timestamps */ quint32 snapLen; /* max length of captured packets, in octets */ quint32 network; /* data link type */ } PcapFileHeader; typedef struct { quint32 tsSec; /* timestamp seconds */ quint32 tsUsec; /* timestamp microseconds */ quint32 inclLen; /* number of octets of packet saved in file */ quint32 origLen; /* actual length of packet */ } PcapPacketHeader; bool readPacket(PcapPacketHeader &pktHdr, QByteArray &pktBuf); QDataStream fd_; QVariantMap importOptions_; PcapImportOptionsDialog *importDialog_; }; extern PcapFileFormat pcapFileFormat; #endif ostinato-0.9/common/pcapfileimport.ui000066400000000000000000000057531321315675200201060ustar00rootroot00000000000000 PcapFileImport 0 0 326 93 PCAP import options Intelligent Import (via PDML) 0 0 16 16 false Do a diff after import Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::NoButton|QDialogButtonBox::Ok buttonBox accepted() PcapFileImport accept() 249 81 157 90 buttonBox rejected() PcapFileImport reject() 249 81 258 90 viaPdml toggled(bool) doDiff setEnabled(bool) 15 16 37 42 viaPdml toggled(bool) doDiff setChecked(bool) 151 14 150 34 ostinato-0.9/common/pdmlfileformat.cpp000066400000000000000000000100011321315675200202200ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "pdmlfileformat.h" #include "ostprotolib.h" #include "pdmlreader.h" #include #include PdmlFileFormat pdmlFileFormat; PdmlFileFormat::PdmlFileFormat() { } PdmlFileFormat::~PdmlFileFormat() { } bool PdmlFileFormat::open(const QString fileName, OstProto::StreamConfigList &streams, QString &error) { bool isOk = false; QFile file(fileName); PdmlReader *reader = new PdmlReader(&streams); if (!file.open(QIODevice::ReadOnly)) goto _open_fail; connect(reader, SIGNAL(progress(int)), this, SIGNAL(progress(int))); emit status("Reading PDML packets..."); emit target(100); // in percentage isOk = reader->read(&file, NULL, &stop_); if (stop_) goto _user_cancel; if (!isOk) { error.append(QString("Error processing PDML (%1, %2): %3\n") .arg(reader->lineNumber()) .arg(reader->columnNumber()) .arg(reader->errorString())); goto _exit; } goto _exit; _open_fail: isOk = false; _user_cancel: _exit: delete reader; return isOk; } bool PdmlFileFormat::save(const OstProto::StreamConfigList streams, const QString fileName, QString &error) { bool isOk = false; QTemporaryFile pcapFile; StreamFileFormat *fmt = StreamFileFormat::fileFormatFromType("PCAP"); QProcess tshark; Q_ASSERT(fmt); if (!pcapFile.open()) { error.append("Unable to open temporary file to create PCAP\n"); goto _fail; } qDebug("intermediate PCAP %s", pcapFile.fileName().toAscii().constData()); connect(fmt, SIGNAL(target(int)), this, SIGNAL(target(int))); connect(fmt, SIGNAL(progress(int)), this, SIGNAL(progress(int))); emit status("Writing intermediate PCAP file..."); isOk = fmt->save(streams, pcapFile.fileName(), error); qDebug("generating PDML %s", fileName.toAscii().constData()); emit status("Converting PCAP to PDML..."); emit target(0); tshark.setStandardOutputFile(fileName); tshark.start(OstProtoLib::tsharkPath(), QStringList() << QString("-r%1").arg(pcapFile.fileName()) << "-Tpdml"); if (!tshark.waitForStarted(-1)) { error.append(QString("Unable to start tshark. Check path in preferences.\n")); goto _fail; } if (!tshark.waitForFinished(-1)) { error.append(QString("Error running tshark\n")); goto _fail; } isOk = true; _fail: return isOk; } bool PdmlFileFormat::isMyFileFormat(const QString fileName) { bool ret = false; QFile file(fileName); QByteArray buf; QXmlStreamReader xml; if (!file.open(QIODevice::ReadOnly)) goto _exit; xml.setDevice(&file); xml.readNext(); if (xml.hasError() || !xml.isStartDocument()) goto _close_exit; // skip everything until the start of the first element while (!xml.isStartElement()) { xml.readNext(); if (xml.hasError()) goto _close_exit; } if (!xml.hasError() && xml.isStartElement() && (xml.name() == "pdml")) ret = true; else ret = false; _close_exit: xml.clear(); file.close(); _exit: return ret; } bool PdmlFileFormat::isMyFileType(const QString fileType) { if (fileType.startsWith("PDML")) return true; else return false; } ostinato-0.9/common/pdmlfileformat.h000066400000000000000000000023311321315675200176740ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _PDML_FILE_FORMAT_H #define _PDML_FILE_FORMAT_H #include "streamfileformat.h" class PdmlFileFormat : public StreamFileFormat { public: PdmlFileFormat(); ~PdmlFileFormat(); virtual bool open(const QString fileName, OstProto::StreamConfigList &streams, QString &error); virtual bool save(const OstProto::StreamConfigList streams, const QString fileName, QString &error); bool isMyFileFormat(const QString fileName); bool isMyFileType(const QString fileType); }; extern PdmlFileFormat pdmlFileFormat; #endif ostinato-0.9/common/pdmlprotocol.cpp000066400000000000000000000161771321315675200177550ustar00rootroot00000000000000/* Copyright (C) 2011 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "pdmlprotocol.h" /*! \class PdmlProtocol PdmlProtocol is the base class which provides the interface for all PDML decode helper protocols All Pdml helper classes derived from PdmlProtocol MUST register themselves with PdmlReader. When PdmlReader encounters a 'proto' tag in the PDML during parsing, it instantiates the corresponding helper PdmlProtocol class and calls its methods to decode the protocol. A subclass MUST initialize the following inherited protected variables in its constructor - - ostProtoId_ - fieldMap_ A subclass typically needs to reimplement the following methods - - createInstance() Depending on certain conditions, subclasses may need to reimplement the following additional methods - - unknownFieldHandler() - preProtocolHandler() - postProtocolHandler() See the description of the methods for more information. Use the SamplePdmlProtocol implementation as boilerplate code and for guidelines and tips */ /*! Constructs the PdmlProtocol */ PdmlProtocol::PdmlProtocol() { ostProtoId_ = -1; } /*! Destroys the PdmlProtocol */ PdmlProtocol::~PdmlProtocol() { } /*! Allocates and returns a new instance of the class Caller is responsible for freeing up after use. Subclasses MUST implement this function and register it with PdmlReader */ PdmlProtocol* PdmlProtocol::createInstance() { return new PdmlProtocol(); } /*! Returns the protocol's field number as defined in message 'Protocol', enum 'k' (file: protocol.proto) */ int PdmlProtocol::ostProtoId() const { return ostProtoId_; } /*! Returns true if name is a 'known' field that can be directly mapped to the protobuf field */ bool PdmlProtocol::hasField(QString name) const { return fieldMap_.contains(name); } /*! Returns the protocol's protobuf field number corresponding to name */ int PdmlProtocol::fieldId(QString name) const { return fieldMap_.value(name); } /*! This method is called by PdmlReader before any fields within the protocol are processed. All attributes associated with the 'proto' tag in the PDML are passed to this method Use this method to do any special handling that may be required for preprocessing */ void PdmlProtocol::preProtocolHandler(QString /*name*/, const QXmlStreamAttributes& /*attributes*/, int /*expectedPos*/, OstProto::Protocol* /*pbProto*/, OstProto::Stream* /*stream*/) { return; // do nothing! } /*! This method is called by PdmlReader when it encounters a nested protocol in the PDML i.e. a protocol within a protocol or a protocol within a field This is a notification to the protocol that protocol processing will be ending prematurely. postProtocolHandler() will still be called in such cases. */ void PdmlProtocol::prematureEndHandler(int /*pos*/, OstProto::Protocol* /*pbProto*/, OstProto::Stream* /*stream*/) { return; // do nothing! } /*! This method is called by PdmlReader after all fields within the protocol are processed. Use this method to do any special handling that may be required for postprocessing */ void PdmlProtocol::postProtocolHandler(OstProto::Protocol* /*pbProto*/, OstProto::Stream* /*stream*/) { return; // do nothing! } /*! This method is called by PdmlReader for each field in the protocol Depending on whether it is a known or unknown field, the virtual methods knownFieldHandler() and unknownFieldHandler() are invoked */ void PdmlProtocol::fieldHandler(QString name, const QXmlStreamAttributes &attributes, OstProto::Protocol *pbProto, OstProto::Stream *stream) { if (hasField(name)) { QString valueHexStr = attributes.value("value").toString(); qDebug("\t(KNOWN) fieldName:%s, value:%s", name.toAscii().constData(), valueHexStr.toAscii().constData()); knownFieldHandler(name, valueHexStr, pbProto); } else { int pos = -1; int size = -1; if (!attributes.value("pos").isEmpty()) pos = attributes.value("pos").toString().toInt(); if (!attributes.value("size").isEmpty()) size = attributes.value("size").toString().toInt(); qDebug("\t(UNKNOWN) fieldName:%s, pos:%d, size:%d", name.toAscii().constData(), pos, size); unknownFieldHandler(name, pos, size, attributes, pbProto, stream); } } /*! Handles a 'known' field Uses protobuf reflection interface to set the protobuf field name to valueHexStr as per the field's datatype */ void PdmlProtocol::knownFieldHandler(QString name, QString valueHexStr, OstProto::Protocol *pbProto) { const google::protobuf::Reflection *protoRefl = pbProto->GetReflection(); const google::protobuf::FieldDescriptor *extDesc = protoRefl->FindKnownExtensionByNumber(ostProtoId()); google::protobuf::Message *msg = protoRefl->MutableMessage(pbProto,extDesc); const google::protobuf::Reflection *msgRefl = msg->GetReflection(); const google::protobuf::FieldDescriptor *fieldDesc = msg->GetDescriptor()->FindFieldByNumber(fieldId(name)); bool isOk; Q_ASSERT(fieldDesc != NULL); switch(fieldDesc->cpp_type()) { case google::protobuf::FieldDescriptor::CPPTYPE_BOOL: msgRefl->SetBool(msg, fieldDesc, bool(valueHexStr.toUInt(&isOk))); break; case google::protobuf::FieldDescriptor::CPPTYPE_ENUM: // TODO case google::protobuf::FieldDescriptor::CPPTYPE_UINT32: msgRefl->SetUInt32(msg, fieldDesc, valueHexStr.toUInt(&isOk, kBaseHex)); break; case google::protobuf::FieldDescriptor::CPPTYPE_UINT64: msgRefl->SetUInt64(msg, fieldDesc, valueHexStr.toULongLong(&isOk, kBaseHex)); break; case google::protobuf::FieldDescriptor::CPPTYPE_STRING: { QByteArray hexVal = QByteArray::fromHex(valueHexStr.toUtf8()); std::string str(hexVal.constData(), hexVal.size()); msgRefl->SetString(msg, fieldDesc, str); break; } default: qDebug("%s: unhandled cpptype = %d", __FUNCTION__, fieldDesc->cpp_type()); } } /*! Handles a 'unknown' field The default implementation does nothing. Subclasses may need to implement this if the protocol contains 'unknown' fields. */ void PdmlProtocol::unknownFieldHandler(QString /*name*/, int /*pos*/, int /*size*/, const QXmlStreamAttributes& /*attributes*/, OstProto::Protocol* /*pbProto*/, OstProto::Stream* /*stream*/) { return; // do nothing! } ostinato-0.9/common/pdmlprotocol.h000066400000000000000000000042251321315675200174110ustar00rootroot00000000000000/* Copyright (C) 2011 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _PDML_PROTOCOL_H #define _PDML_PROTOCOL_H #include "protocol.pb.h" #include #include #include #include const int kBaseHex = 16; class PdmlProtocol { public: virtual ~PdmlProtocol(); static PdmlProtocol* createInstance(); int ostProtoId() const; bool hasField(QString name) const; int fieldId(QString name) const; virtual void preProtocolHandler(QString name, const QXmlStreamAttributes &attributes, int expectedPos, OstProto::Protocol *pbProto, OstProto::Stream *stream); virtual void prematureEndHandler(int pos, OstProto::Protocol *pbProto, OstProto::Stream *stream); virtual void postProtocolHandler(OstProto::Protocol *pbProto, OstProto::Stream *stream); void fieldHandler(QString name, const QXmlStreamAttributes &attributes, OstProto::Protocol *pbProto, OstProto::Stream *stream); void knownFieldHandler(QString name, QString valueHexStr, OstProto::Protocol *pbProto); virtual void unknownFieldHandler(QString name, int pos, int size, const QXmlStreamAttributes &attributes, OstProto::Protocol *pbProto, OstProto::Stream *stream); protected: PdmlProtocol(); //!< Protocol's field number as defined in message 'Protocol', enum 'k' int ostProtoId_; //!< Map of PDML field names to protobuf field numbers for 'known' fields QMap fieldMap_; }; #endif ostinato-0.9/common/pdmlprotocols.cpp000066400000000000000000000136071321315675200201330ustar00rootroot00000000000000/* Copyright (C) 2011 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "pdmlprotocols.h" #include "hexdump.pb.h" // ---------------------------------------------------------- // // PdmlUnknownProtocol // // ---------------------------------------------------------- // PdmlUnknownProtocol::PdmlUnknownProtocol() { ostProtoId_ = OstProto::Protocol::kHexDumpFieldNumber; endPos_ = expPos_ = -1; } PdmlProtocol* PdmlUnknownProtocol::createInstance() { return new PdmlUnknownProtocol(); } void PdmlUnknownProtocol::preProtocolHandler(QString /*name*/, const QXmlStreamAttributes &attributes, int expectedPos, OstProto::Protocol* /*pbProto*/, OstProto::Stream *stream) { bool isOk; int size; int pos = attributes.value("pos").toString().toUInt(&isOk); if (!isOk) { if (expectedPos >= 0) expPos_ = pos = expectedPos; else goto _skip_pos_size_proc; } size = attributes.value("size").toString().toUInt(&isOk); if (!isOk) goto _skip_pos_size_proc; // If pos+size goes beyond the frame length, this is a "reassembled" // protocol and should be skipped if ((pos + size) > int(stream->core().frame_len())) goto _skip_pos_size_proc; expPos_ = pos; endPos_ = expPos_ + size; _skip_pos_size_proc: OstProto::HexDump *hexDump = stream->mutable_protocol( stream->protocol_size()-1)->MutableExtension(OstProto::hexDump); hexDump->set_pad_until_end(false); } void PdmlUnknownProtocol::prematureEndHandler(int pos, OstProto::Protocol* /*pbProto*/, OstProto::Stream* /*stream*/) { endPos_ = pos; } void PdmlUnknownProtocol::postProtocolHandler(OstProto::Protocol *pbProto, OstProto::Stream *stream) { OstProto::HexDump *hexDump = pbProto->MutableExtension(OstProto::hexDump); // Skipped field(s) at end? Pad with zero! if (endPos_ > expPos_) { QByteArray hexVal(endPos_ - expPos_, char(0)); hexDump->mutable_content()->append(hexVal.constData(), hexVal.size()); expPos_ += hexVal.size(); } qDebug(" hexdump: expPos_ = %d, endPos_ = %d\n", expPos_, endPos_); // If empty for some reason, remove the protocol if (hexDump->content().size() == 0) stream->mutable_protocol()->RemoveLast(); endPos_ = expPos_ = -1; } void PdmlUnknownProtocol::unknownFieldHandler(QString name, int pos, int /*size*/, const QXmlStreamAttributes &attributes, OstProto::Protocol *pbProto, OstProto::Stream* /*stream*/) { OstProto::HexDump *hexDump = pbProto->MutableExtension(OstProto::hexDump); qDebug(" hexdump: %s, pos = %d, expPos_ = %d, endPos_ = %d\n", name.toAscii().constData(), pos, expPos_, endPos_); // Skipped field? Pad with zero! if ((pos > expPos_) && (expPos_ < endPos_)) { QByteArray hexVal(pos - expPos_, char(0)); hexDump->mutable_content()->append(hexVal.constData(), hexVal.size()); expPos_ += hexVal.size(); } if (pos == expPos_) { QByteArray hexVal = attributes.value("unmaskedvalue").isEmpty() ? QByteArray::fromHex(attributes.value("value").toString().toUtf8()) : QByteArray::fromHex(attributes.value("unmaskedvalue").toString().toUtf8()); hexDump->mutable_content()->append(hexVal.constData(), hexVal.size()); expPos_ += hexVal.size(); } } // ---------------------------------------------------------- // // PdmlGenInfoProtocol // // ---------------------------------------------------------- // PdmlGenInfoProtocol::PdmlGenInfoProtocol() { } PdmlProtocol* PdmlGenInfoProtocol::createInstance() { return new PdmlGenInfoProtocol(); } // ---------------------------------------------------------- // // PdmlFrameProtocol // // ---------------------------------------------------------- // PdmlFrameProtocol::PdmlFrameProtocol() { } PdmlProtocol* PdmlFrameProtocol::createInstance() { return new PdmlFrameProtocol(); } void PdmlFrameProtocol::unknownFieldHandler(QString name, int /*pos*/, int /*size*/, const QXmlStreamAttributes &attributes, OstProto::Protocol* /*pbProto*/, OstProto::Stream *stream) { if (name == "frame.len") { int len = -1; if (!attributes.value("show").isEmpty()) len = attributes.value("show").toString().toInt(); stream->mutable_core()->set_frame_len(len+4); // TODO:check FCS } else if (name == "frame.time_delta") { if (!attributes.value("show").isEmpty()) { QString delta = attributes.value("show").toString(); int decimal = delta.indexOf('.'); if (decimal >= 0) { const uint kNsecsInSec = 1000000000; uint sec = delta.left(decimal).toUInt(); uint nsec = delta.mid(decimal+1).toUInt(); uint ipg = sec*kNsecsInSec + nsec; if (ipg) { stream->mutable_control()->set_packets_per_sec( kNsecsInSec/ipg); } qDebug("sec.nsec = %u.%u, ipg = %u", sec, nsec, ipg); } } } } ostinato-0.9/common/pdmlprotocols.h000066400000000000000000000037531321315675200176010ustar00rootroot00000000000000/* Copyright (C) 2011 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _PDML_PROTOCOLS_H #define _PDML_PROTOCOLS_H #include "pdmlprotocol.h" class PdmlUnknownProtocol : public PdmlProtocol { public: static PdmlProtocol* createInstance(); virtual void preProtocolHandler(QString name, const QXmlStreamAttributes &attributes, int expectedPos, OstProto::Protocol *pbProto, OstProto::Stream *stream); virtual void prematureEndHandler(int pos, OstProto::Protocol *pbProto, OstProto::Stream *stream); virtual void postProtocolHandler(OstProto::Protocol *pbProto, OstProto::Stream *stream); virtual void unknownFieldHandler(QString name, int pos, int size, const QXmlStreamAttributes &attributes, OstProto::Protocol *pbProto, OstProto::Stream *stream); protected: PdmlUnknownProtocol(); private: int endPos_; int expPos_; }; class PdmlGenInfoProtocol : public PdmlProtocol { public: static PdmlProtocol* createInstance(); protected: PdmlGenInfoProtocol(); }; class PdmlFrameProtocol : public PdmlProtocol { public: static PdmlProtocol* createInstance(); virtual void unknownFieldHandler(QString name, int pos, int size, const QXmlStreamAttributes &attributes, OstProto::Protocol *pbProto, OstProto::Stream *stream); protected: PdmlFrameProtocol(); }; #endif ostinato-0.9/common/pdmlreader.cpp000066400000000000000000000373461321315675200173570ustar00rootroot00000000000000/* Copyright (C) 2011 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "pdmlreader.h" #include "abstractprotocol.h" #include "hexdump.pb.h" #include "pcapfileformat.h" #include "streambase.h" #include "pdmlprotocols.h" #include "arppdml.h" #include "eth2pdml.h" #include "llcpdml.h" #include "icmppdml.h" #include "icmp6pdml.h" #include "igmppdml.h" #include "ip4pdml.h" #include "ip6pdml.h" #include "mldpdml.h" #include "stppdml.h" #include "svlanpdml.h" #include "tcppdml.h" #include "textprotopdml.h" #include "udppdml.h" #include "vlanpdml.h" PdmlReader::PdmlReader(OstProto::StreamConfigList *streams) { //gPdmlReader = this; pcap_ = NULL; streams_ = streams; currentStream_ = NULL; prevStream_ = NULL; stop_ = NULL; factory_.insert("hexdump", PdmlUnknownProtocol::createInstance); factory_.insert("geninfo", PdmlGenInfoProtocol::createInstance); factory_.insert("frame", PdmlFrameProtocol::createInstance); factory_.insert("arp", PdmlArpProtocol::createInstance); factory_.insert("eth", PdmlEthProtocol::createInstance); factory_.insert("http", PdmlTextProtocol::createInstance); factory_.insert("icmp", PdmlIcmpProtocol::createInstance); factory_.insert("icmpv6", PdmlIcmp6Protocol::createInstance); factory_.insert("igmp", PdmlIgmpProtocol::createInstance); factory_.insert("ieee8021ad", PdmlSvlanProtocol::createInstance); factory_.insert("imap", PdmlTextProtocol::createInstance); factory_.insert("ip", PdmlIp4Protocol::createInstance); factory_.insert("ipv6", PdmlIp6Protocol::createInstance); factory_.insert("llc", PdmlLlcProtocol::createInstance); factory_.insert("nntp", PdmlTextProtocol::createInstance); factory_.insert("pop", PdmlTextProtocol::createInstance); factory_.insert("rtsp", PdmlTextProtocol::createInstance); factory_.insert("sdp", PdmlTextProtocol::createInstance); factory_.insert("sip", PdmlTextProtocol::createInstance); factory_.insert("smtp", PdmlTextProtocol::createInstance); factory_.insert("stp", PdmlStpProtocol::createInstance); factory_.insert("tcp", PdmlTcpProtocol::createInstance); factory_.insert("udp", PdmlUdpProtocol::createInstance); factory_.insert("udplite", PdmlUdpProtocol::createInstance); factory_.insert("vlan", PdmlVlanProtocol::createInstance); } PdmlReader::~PdmlReader() { } bool PdmlReader::read(QIODevice *device, PcapFileFormat *pcap, bool *stop) { setDevice(device); pcap_ = pcap; stop_ = stop; while (!atEnd()) { readNext(); if (isStartElement()) { if (name() == "pdml") readPdml(); else raiseError("Not a pdml file!"); } } if (error() && (errorString() != "USER-CANCEL")) { qDebug("Line %lld", lineNumber()); qDebug("Col %lld", columnNumber()); qDebug("%s", errorString().toAscii().constData()); return false; } return true; } // TODO: use a temp pool to avoid a lot of new/delete PdmlProtocol* PdmlReader::allocPdmlProtocol(QString protoName) { // If protoName is not known, we use a hexdump if (!factory_.contains(protoName)) protoName = "hexdump"; // If MLD is not supported by the creator of the PDML, we interpret // ICMPv6 as ICMP since our implementation of the ICMPv6 PDML protocol // exists just to distinguish between MLD and ICMP. Non MLD ICMPv6 is // also handled by ICMP only if (!isMldSupport_ && (protoName == "icmpv6")) protoName = "icmp"; return (*(factory_.value(protoName)))(); } void PdmlReader::freePdmlProtocol(PdmlProtocol *proto) { delete proto; } bool PdmlReader::isDontCareProto() { Q_ASSERT(isStartElement() && name() == "proto"); QStringRef protoName = attributes().value("name"); if (protoName.isEmpty() || (protoName == "expert")) return true; return false; } void PdmlReader::skipElement() { Q_ASSERT(isStartElement()); qDebug("skipping element - <%s>", name().toString().toAscii().constData()); while (!atEnd()) { readNext(); if (isEndElement()) break; if (isStartElement()) skipElement(); } } void PdmlReader::readPdml() { QStringList creator; Q_ASSERT(isStartElement() && name() == "pdml"); isMldSupport_ = true; creator = attributes().value("creator").toString().split('/'); if ((creator.size() >= 2) && (creator.at(0) == "wireshark")) { QList minMldVer; minMldVer << 1 << 5 << 0; QStringList version = creator.at(1).split('.'); for (int i = 0; i < qMin(version.size(), minMldVer.size()); i++) { if (version.at(i).toUInt() < minMldVer.at(i)) { isMldSupport_ = false; break; } } } packetCount_ = 1; while (!atEnd()) { readNext(); if (isEndElement()) break; if (isStartElement()) { if (name() == "packet") readPacket(); else skipElement(); } } } void PdmlReader::readPacket() { PcapFileFormat::PcapPacketHeader pktHdr; Q_ASSERT(isStartElement() && name() == "packet"); qDebug("%s: packetNum = %d", __FUNCTION__, packetCount_); skipUntilEnd_ = false; // XXX: we play dumb and convert each packet to a stream, for now prevStream_ = currentStream_; currentStream_ = streams_->add_stream(); currentStream_->mutable_stream_id()->set_id(packetCount_); currentStream_->mutable_core()->set_is_enabled(true); // Set to a high number; will get reset to correct value during parse currentStream_->mutable_core()->set_frame_len(16384); // FIXME: Hard coding! expPos_ = 0; if (pcap_) pcap_->readPacket(pktHdr, pktBuf_); while (!atEnd()) { readNext(); if (isEndElement()) break; if (isStartElement()) { if (skipUntilEnd_) skipElement(); else if (name() == "proto") readProto(); else if (name() == "field") readField(NULL, NULL); // TODO: top level field!!!! else skipElement(); } } currentStream_->mutable_core()->set_name(""); // FIXME // If trailing bytes are missing, add those from the pcap if ((expPos_ < pktBuf_.size()) && pcap_) { OstProto::Protocol *proto = currentStream_->add_protocol(); OstProto::HexDump *hexDump = proto->MutableExtension( OstProto::hexDump); proto->mutable_protocol_id()->set_id( OstProto::Protocol::kHexDumpFieldNumber); qDebug("adding trailing %d bytes starting from %d", pktBuf_.size() - expPos_, expPos_); hexDump->set_content(pktBuf_.constData() + expPos_, pktBuf_.size() - expPos_); hexDump->set_pad_until_end(false); } packetCount_++; emit progress(int(characterOffset()*100/device()->size())); // in % if (prevStream_) prevStream_->mutable_control()->CopyFrom(currentStream_->control()); if (stop_ && *stop_) raiseError("USER-CANCEL"); } void PdmlReader::readProto() { PdmlProtocol *pdmlProto = NULL; OstProto::Protocol *pbProto = NULL; Q_ASSERT(isStartElement() && name() == "proto"); QString protoName; int pos = -1; int size = -1; if (!attributes().value("name").isEmpty()) protoName = attributes().value("name").toString(); if (!attributes().value("pos").isEmpty()) pos = attributes().value("pos").toString().toInt(); if (!attributes().value("size").isEmpty()) size = attributes().value("size").toString().toInt(); qDebug("proto: %s, pos = %d, expPos_ = %d, size = %d", protoName.toAscii().constData(), pos, expPos_, size); // This is a heuristic to skip protocols which are not part of // this frame, but of a reassembled segment spanning several frames // 1. Proto starting pos is 0, but we've already seen some protocols // 2. Protocol Size exceeds frame length if (((pos == 0) && (currentStream_->protocol_size() > 0)) || ((pos + size) > int(currentStream_->core().frame_len()))) { skipElement(); return; } if (isDontCareProto()) { skipElement(); return; } // if we detect a gap between subsequent protocols, we "fill-in" // with a "hexdump" from the pcap if (pos > expPos_ && pcap_) { appendHexDumpProto(expPos_, pos - expPos_); expPos_ = pos; } // for unknown protocol, read a hexdump from the pcap if (!factory_.contains(protoName) && pcap_) { int size = -1; if (!attributes().value("size").isEmpty()) size = attributes().value("size").toString().toInt(); // Check if this proto is a subset of previous proto - if so, do nothing if ((pos >= 0) && (size > 0) && ((pos + size) <= expPos_)) { qDebug("subset proto"); skipElement(); return; } if (pos >= 0 && size > 0 && ((pos + size) <= pktBuf_.size())) { appendHexDumpProto(pos, size); expPos_ += size; skipElement(); return; } } pdmlProto = appendPdmlProto(protoName, &pbProto); qDebug("%s: preProtocolHandler(expPos = %d)", protoName.toAscii().constData(), expPos_); pdmlProto->preProtocolHandler(protoName, attributes(), expPos_, pbProto, currentStream_); while (!atEnd()) { readNext(); if (isEndElement()) break; if (isStartElement()) { if (name() == "proto") { // an embedded proto qDebug("embedded proto: %s\n", attributes().value("name") .toString().toAscii().constData()); if (isDontCareProto()) { skipElement(); continue; } // if we are in the midst of processing a protocol, we // end it prematurely before we start processing the // embedded protocol // // XXX: pdmlProto may be NULL for a sequence of embedded protos if (pdmlProto) { int endPos = -1; if (!attributes().value("pos").isEmpty()) endPos = attributes().value("pos").toString().toInt(); pdmlProto->prematureEndHandler(endPos, pbProto, currentStream_); pdmlProto->postProtocolHandler(pbProto, currentStream_); StreamBase s; s.protoDataCopyFrom(*currentStream_); expPos_ = s.frameProtocolLength(0); } readProto(); pdmlProto = NULL; pbProto = NULL; } else if (name() == "field") { if ((protoName == "fake-field-wrapper") && (attributes().value("name") == "tcp.segments")) { skipElement(); qDebug("[skipping reassembled tcp segments]"); skipUntilEnd_ = true; continue; } if (pdmlProto == NULL) { pdmlProto = appendPdmlProto(protoName, &pbProto); qDebug("%s: preProtocolHandler(expPos = %d)", protoName.toAscii().constData(), expPos_); pdmlProto->preProtocolHandler(protoName, attributes(), expPos_, pbProto, currentStream_); } readField(pdmlProto, pbProto); } else skipElement(); } } // Close-off current protocol if (pdmlProto) { pdmlProto->postProtocolHandler(pbProto, currentStream_); freePdmlProtocol(pdmlProto); StreamBase s; s.protoDataCopyFrom(*currentStream_); expPos_ = s.frameProtocolLength(0); } } void PdmlReader::readField(PdmlProtocol *pdmlProto, OstProto::Protocol *pbProto) { Q_ASSERT(isStartElement() && name() == "field"); // fields with "hide='yes'" are informational and should be skipped if (attributes().value("hide") == "yes") { skipElement(); return; } QString fieldName = attributes().value("name").toString(); qDebug(" fieldName:%s", fieldName.toAscii().constData()); pdmlProto->fieldHandler(fieldName, attributes(), pbProto, currentStream_); while (!atEnd()) { readNext(); if (isEndElement()) break; if (isStartElement()) { if (name() == "proto") { // Since we are in the midst of processing a protocol, we // end it prematurely before we start processing the // embedded protocol // int endPos = -1; if (!attributes().value("pos").isEmpty()) endPos = attributes().value("pos").toString().toInt(); pdmlProto->prematureEndHandler(endPos, pbProto, currentStream_); pdmlProto->postProtocolHandler(pbProto, currentStream_); StreamBase s; s.protoDataCopyFrom(*currentStream_); expPos_ = s.frameProtocolLength(0); readProto(); } else if (name() == "field") readField(pdmlProto, pbProto); else skipElement(); } } } void PdmlReader::appendHexDumpProto(int offset, int size) { OstProto::Protocol *proto = currentStream_->add_protocol(); OstProto::HexDump *hexDump = proto->MutableExtension(OstProto::hexDump); proto->mutable_protocol_id()->set_id( OstProto::Protocol::kHexDumpFieldNumber); qDebug("filling in gap of %d bytes starting from %d", size, offset); hexDump->set_content(pktBuf_.constData() + offset, size); hexDump->set_pad_until_end(false); } PdmlProtocol* PdmlReader::appendPdmlProto(const QString &protoName, OstProto::Protocol **pbProto) { PdmlProtocol* pdmlProto = allocPdmlProtocol(protoName); Q_ASSERT(pdmlProto != NULL); int protoId = pdmlProto->ostProtoId(); if (protoId > 0) // Non-Base Class { OstProto::Protocol *proto = currentStream_->add_protocol(); proto->mutable_protocol_id()->set_id(protoId); const google::protobuf::Reflection *msgRefl = proto->GetReflection(); const google::protobuf::FieldDescriptor *fieldDesc = msgRefl->FindKnownExtensionByNumber(protoId); // TODO: if !fDesc // init default values of all fields in protocol msgRefl->MutableMessage(proto, fieldDesc); *pbProto = proto; qDebug("%s: name = %s", __FUNCTION__, protoName.toAscii().constData()); } else *pbProto = NULL; return pdmlProto; } ostinato-0.9/common/pdmlreader.h000066400000000000000000000036501321315675200170130ustar00rootroot00000000000000/* Copyright (C) 2011 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _PDML_READER_H #define _PDML_READER_H #include "pdmlprotocol.h" #include #include class PcapFileFormat; class PdmlReader : public QObject, public QXmlStreamReader { Q_OBJECT public: PdmlReader(OstProto::StreamConfigList *streams); ~PdmlReader(); bool read(QIODevice *device, PcapFileFormat *pcap = NULL, bool *stop = NULL); signals: void progress(int value); private: PdmlProtocol* allocPdmlProtocol(QString protoName); void freePdmlProtocol(PdmlProtocol *proto); bool isDontCareProto(); void skipElement(); void readPdml(); void readPacket(); void readProto(); void readField(PdmlProtocol *pdmlProto, OstProto::Protocol *pbProto); void appendHexDumpProto(int offset, int size); PdmlProtocol* appendPdmlProto(const QString &protoName, OstProto::Protocol **pbProto); typedef PdmlProtocol* (*FactoryMethod)(); QMap factory_; bool *stop_; OstProto::StreamConfigList *streams_; PcapFileFormat *pcap_; QByteArray pktBuf_; bool isMldSupport_; int packetCount_; int expPos_; bool skipUntilEnd_; OstProto::Stream *prevStream_; OstProto::Stream *currentStream_; }; #endif ostinato-0.9/common/protocol.proto000066400000000000000000000232021321315675200174440ustar00rootroot00000000000000/* Copyright (C) 2010-2015 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ package OstProto; option cc_generic_services = true; option py_generic_services = true; message VersionInfo { required string version = 1; optional string client_name = 2; } message VersionCompatibility { enum Compatibility { kIncompatible = 0; kCompatible = 1; } required Compatibility result = 1; optional string notes = 2; } message StreamId { required uint32 id = 1; } message StreamCore { enum FrameLengthMode { e_fl_fixed = 0; e_fl_inc = 1; e_fl_dec = 2; e_fl_random = 3; } // Basics optional string name = 1; optional bool is_enabled = 2; optional uint32 ordinal = 3; optional FrameLengthMode len_mode = 14 [default = e_fl_fixed]; /// Frame Length (includes CRC) optional uint32 frame_len = 15 [default = 64]; optional uint32 frame_len_min = 16 [default = 64]; optional uint32 frame_len_max = 17 [default = 1518]; } message StreamControl { enum SendUnit { e_su_packets = 0; e_su_bursts = 1; } enum SendMode { e_sm_fixed = 0; e_sm_continuous = 1; } enum NextWhat { e_nw_stop = 0; e_nw_goto_next = 1; e_nw_goto_id = 2; } optional SendUnit unit = 1 [default = e_su_packets]; optional SendMode mode = 2 [default = e_sm_fixed]; optional uint32 num_packets = 3 [default = 10]; optional uint32 num_bursts = 4 [default = 1]; optional uint32 packets_per_burst = 5 [default = 10]; optional NextWhat next = 6 [default = e_nw_goto_next]; optional uint32 OBSOLETE_packets_per_sec = 7 [default = 1, deprecated=true]; optional uint32 OBSOLETE_bursts_per_sec = 8 [default = 1, deprecated=true]; optional double packets_per_sec = 9 [default = 1]; optional double bursts_per_sec = 10 [default = 1]; } message ProtocolId { required uint32 id = 1; } message VariableField { enum Type { kCounter8 = 0; kCounter16 = 1; kCounter32 = 2; } enum Mode { kIncrement = 0; kDecrement = 1; kRandom = 2; } optional Type type = 1 [default = kCounter8]; optional uint32 offset = 2; optional fixed32 mask = 3 [default = 0xffffffff]; optional uint32 value = 4; optional Mode mode = 5 [default = kIncrement]; optional uint32 count = 6 [default = 16]; optional uint32 step = 7 [default = 1]; } message Protocol { required ProtocolId protocol_id = 1; repeated VariableField variable_field = 2; extensions 100 to 199; // Reserved for Ostinato Use extensions 200 to 500; // Available for use by protocols enum k { kMacFieldNumber = 100; kPayloadFieldNumber = 101; kSampleFieldNumber = 102; kUserScriptFieldNumber = 103; kHexDumpFieldNumber = 104; kSignFieldNumber = 105; kEth2FieldNumber = 200; kDot3FieldNumber = 201; kLlcFieldNumber = 202; kSnapFieldNumber = 203; kSvlanFieldNumber = 204; kVlanFieldNumber = 205; kDot2LlcFieldNumber = 206; kDot2SnapFieldNumber = 207; kVlanStackFieldNumber = 208; kStpFieldNumber = 209; kArpFieldNumber = 300; kIp4FieldNumber = 301; kIp6FieldNumber = 302; kIp6over4FieldNumber = 303; kIp4over6FieldNumber = 304; kIp4over4FieldNumber = 305; kIp6over6FieldNumber = 306; kTcpFieldNumber = 400; kUdpFieldNumber = 401; kIcmpFieldNumber = 402; kIgmpFieldNumber = 403; kMldFieldNumber = 404; kTextProtocolFieldNumber = 500; } } message Stream { required StreamId stream_id = 1; optional StreamCore core = 2; optional StreamControl control = 3; repeated Protocol protocol = 4; } message Void { // nothing! } message Ack { //! \todo (LOW) do we need any fields in 'Ack' } message PortId { required uint32 id = 1; } message PortIdList { repeated PortId port_id = 1; } message StreamIdList { required PortId port_id = 1; repeated StreamId stream_id = 2; } enum TransmitMode { kSequentialTransmit = 0; kInterleavedTransmit = 1; } message Port { required PortId port_id = 1; optional string name = 2; optional string description = 3; optional string notes = 4; optional bool is_enabled = 5; optional bool is_exclusive_control = 6; optional TransmitMode transmit_mode = 7 [default = kSequentialTransmit]; optional string user_name = 8; optional bool is_tracking_stream_stats = 9; } message PortConfigList { repeated Port port = 1; } message StreamConfigList { required PortId port_id = 1; repeated Stream stream = 2; } message CaptureBuffer { //! \todo (HIGH) define CaptureBuffer } message CaptureBufferList { repeated CaptureBuffer list = 1; } enum LinkState { LinkStateUnknown = 0; LinkStateDown = 1; LinkStateUp = 2; } message PortState { optional LinkState link_state = 1 [default = LinkStateUnknown]; optional bool is_transmit_on = 2 [default = false]; optional bool is_capture_on = 3 [default = false]; } message PortStats { required PortId port_id = 1; optional PortState state = 2; optional uint64 rx_pkts = 11; optional uint64 rx_bytes = 12; optional uint64 rx_pkts_nic = 13; optional uint64 rx_bytes_nic = 14; optional uint64 rx_pps = 15; optional uint64 rx_bps = 16; optional uint64 tx_pkts = 21; optional uint64 tx_bytes = 22; optional uint64 tx_pkts_nic = 23; optional uint64 tx_bytes_nic = 24; optional uint64 tx_pps = 25; optional uint64 tx_bps = 26; optional uint64 rx_drops = 100; optional uint64 rx_errors = 101; optional uint64 rx_fifo_errors = 102; optional uint64 rx_frame_errors = 103; } message PortStatsList { repeated PortStats port_stats = 1; } message StreamGuid { required uint32 id = 1; } message StreamGuidList { required PortIdList port_id_list = 1; repeated StreamGuid stream_guid = 2; } message StreamStats { required PortId port_id = 1; required StreamGuid stream_guid = 2; optional uint64 rx_pkts = 11; optional uint64 rx_bytes = 12; optional uint64 tx_pkts = 13; optional uint64 tx_bytes = 14; } message StreamStatsList { repeated StreamStats stream_stats = 1; } enum NotifType { portConfigChanged = 1; } message Notification { required NotifType notif_type = 1; optional PortIdList port_id_list = 6; } /* * Protocol Emulation */ message DeviceGroupId { required uint32 id = 1; } message DeviceGroupCore { optional string name = 1; } message DeviceGroupIdList { required PortId port_id = 1; repeated DeviceGroupId device_group_id = 2; } message EncapEmulation { // Encap Protocols implemented as extensions extensions 1000 to 1999; } message DeviceGroup { required DeviceGroupId device_group_id = 1; optional DeviceGroupCore core = 2; optional EncapEmulation encap = 3; optional uint32 device_count = 4 [default = 1]; // per-encap // Device Protocols implemented as extensions extensions 2000 to 5999; } message DeviceGroupConfigList { required PortId port_id = 1; repeated DeviceGroup device_group = 2; } message PortDeviceList { required PortId port_id = 1; extensions 100 to 199; } message PortNeighborList { required PortId port_id = 1; extensions 100 to 199; } service OstService { rpc getPortIdList(Void) returns (PortIdList); rpc getPortConfig(PortIdList) returns (PortConfigList); rpc modifyPort(PortConfigList) returns (Ack); rpc getStreamIdList(PortId) returns (StreamIdList); rpc getStreamConfig(StreamIdList) returns (StreamConfigList); rpc addStream(StreamIdList) returns (Ack); rpc deleteStream(StreamIdList) returns (Ack); rpc modifyStream(StreamConfigList) returns (Ack); rpc startTransmit(PortIdList) returns (Ack); rpc stopTransmit(PortIdList) returns (Ack); rpc startCapture(PortIdList) returns (Ack); rpc stopCapture(PortIdList) returns (Ack); rpc getCaptureBuffer(PortId) returns (CaptureBuffer); rpc getStats(PortIdList) returns (PortStatsList); rpc clearStats(PortIdList) returns (Ack); rpc checkVersion(VersionInfo) returns (VersionCompatibility); // Device Emulation rpc getDeviceGroupIdList(PortId) returns (DeviceGroupIdList); rpc getDeviceGroupConfig(DeviceGroupIdList) returns (DeviceGroupConfigList); rpc addDeviceGroup(DeviceGroupIdList) returns (Ack); rpc deleteDeviceGroup(DeviceGroupIdList) returns (Ack); rpc modifyDeviceGroup(DeviceGroupConfigList) returns (Ack); rpc getDeviceList(PortId) returns (PortDeviceList); rpc resolveDeviceNeighbors(PortIdList) returns (Ack); rpc clearDeviceNeighbors(PortIdList) returns (Ack); rpc getDeviceNeighbors(PortId) returns (PortNeighborList); // Stream Stats rpc getStreamStats(StreamGuidList) returns (StreamStatsList); rpc clearStreamStats(StreamGuidList) returns (Ack); // XXX: Add new RPCs at the end only to preserve backward compatibility } ostinato-0.9/common/protocollist.cpp000066400000000000000000000014611321315675200177620ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "protocollist.h" #include "abstractprotocol.h" void ProtocolList::destroy() { while (!isEmpty()) delete takeFirst(); } ostinato-0.9/common/protocollist.h000066400000000000000000000014571321315675200174340ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include class AbstractProtocol; class ProtocolList : public QLinkedList { public: void destroy(); }; ostinato-0.9/common/protocollistiterator.cpp000066400000000000000000000060441321315675200215360ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "protocollistiterator.h" #include "protocollist.h" #include "abstractprotocol.h" ProtocolListIterator::ProtocolListIterator(ProtocolList &list) { _iter = new QMutableLinkedListIterator(list); } ProtocolListIterator::~ProtocolListIterator() { delete _iter; } bool ProtocolListIterator::findNext(const AbstractProtocol* value) const { return _iter->findNext(const_cast(value)); } bool ProtocolListIterator::findPrevious(const AbstractProtocol* value) { return _iter->findPrevious(const_cast(value)); } bool ProtocolListIterator::hasNext() const { return _iter->hasNext(); } bool ProtocolListIterator::hasPrevious() const { return _iter->hasPrevious(); } void ProtocolListIterator::insert(AbstractProtocol* value) { if (_iter->hasPrevious()) { value->prev = _iter->peekPrevious(); value->prev->next = value; } else value->prev = NULL; if (_iter->hasNext()) { value->next = _iter->peekNext(); value->next->prev = value; } else value->next = NULL; _iter->insert(const_cast(value)); } AbstractProtocol* ProtocolListIterator::next() { return _iter->next(); } AbstractProtocol* ProtocolListIterator::peekNext() const { return _iter->peekNext(); } AbstractProtocol* ProtocolListIterator::peekPrevious() const { return _iter->peekPrevious(); } AbstractProtocol* ProtocolListIterator::previous() { return _iter->previous(); } void ProtocolListIterator::remove() { if (_iter->value()->prev) _iter->value()->prev->next = _iter->value()->next; if (_iter->value()->next) _iter->value()->next->prev = _iter->value()->prev; _iter->remove(); } void ProtocolListIterator::setValue(AbstractProtocol* value) const { if (_iter->value()->prev) _iter->value()->prev->next = value; if (_iter->value()->next) _iter->value()->next->prev = value; value->prev = _iter->value()->prev; value->next = _iter->value()->next; _iter->setValue(const_cast(value)); } void ProtocolListIterator::toBack() { _iter->toBack(); } void ProtocolListIterator::toFront() { _iter->toFront(); } const AbstractProtocol* ProtocolListIterator::value() const { return _iter->value(); } AbstractProtocol* ProtocolListIterator::value() { return _iter->value(); } ostinato-0.9/common/protocollistiterator.h000066400000000000000000000027101321315675200211770ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include class AbstractProtocol; class ProtocolList; class ProtocolListIterator { private: QMutableLinkedListIterator *_iter; public: ProtocolListIterator(ProtocolList &list); ~ProtocolListIterator(); bool findNext(const AbstractProtocol* value) const; bool findPrevious(const AbstractProtocol* value); bool hasNext() const; bool hasPrevious() const; void insert(AbstractProtocol* value); AbstractProtocol* next(); AbstractProtocol* peekNext() const; AbstractProtocol* peekPrevious() const; AbstractProtocol* previous(); void remove(); void setValue(AbstractProtocol* value) const; void toBack(); void toFront(); const AbstractProtocol* value() const; AbstractProtocol* value(); }; ostinato-0.9/common/protocolmanager.cpp000066400000000000000000000167021321315675200204250ustar00rootroot00000000000000/* Copyright (C) 2010, 2014 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "protocolmanager.h" #include "abstractprotocol.h" #include "protocol.pb.h" #include "mac.h" #include "vlan.h" #include "svlan.h" #include "vlanstack.h" // L2 Protos #include "dot3.h" #include "llc.h" #include "dot2llc.h" #include "snap.h" #include "dot2snap.h" #include "eth2.h" #include "stp.h" // L3 Protos #include "arp.h" #include "ip4.h" #include "ip6.h" #include "ip4over4.h" #include "ip4over6.h" #include "ip6over4.h" #include "ip6over6.h" // L4 Protos #include "icmp.h" #include "igmp.h" #include "mld.h" #include "tcp.h" #include "udp.h" // L5 Protos #include "textproto.h" // Special Protos #include "hexdump.h" #include "payload.h" #include "sample.h" #include "sign.h" #include "userscript.h" ProtocolManager *OstProtocolManager; ProtocolManager::ProtocolManager() { /*! \todo (LOW) calls to registerProtocol() should be done by the protocols themselves (once this is done remove the #includes for all the protocols) */ registerProtocol(OstProto::Protocol::kMacFieldNumber, (void*) MacProtocol::createInstance); registerProtocol(OstProto::Protocol::kVlanFieldNumber, (void*) VlanProtocol::createInstance); registerProtocol(OstProto::Protocol::kSvlanFieldNumber, (void*) SVlanProtocol::createInstance); registerProtocol(OstProto::Protocol::kVlanStackFieldNumber, (void*) VlanStackProtocol::createInstance); registerProtocol(OstProto::Protocol::kEth2FieldNumber, (void*) Eth2Protocol::createInstance); registerProtocol(OstProto::Protocol::kDot3FieldNumber, (void*) Dot3Protocol::createInstance); registerProtocol(OstProto::Protocol::kLlcFieldNumber, (void*) LlcProtocol::createInstance); registerProtocol(OstProto::Protocol::kDot2LlcFieldNumber, (void*) Dot2LlcProtocol::createInstance); registerProtocol(OstProto::Protocol::kSnapFieldNumber, (void*) SnapProtocol::createInstance); registerProtocol(OstProto::Protocol::kDot2SnapFieldNumber, (void*) Dot2SnapProtocol::createInstance); registerProtocol(OstProto::Protocol::kStpFieldNumber, (void*) StpProtocol::createInstance); // Layer 3 Protocols registerProtocol(OstProto::Protocol::kArpFieldNumber, (void*) ArpProtocol::createInstance); registerProtocol(OstProto::Protocol::kIp4FieldNumber, (void*) Ip4Protocol::createInstance); registerProtocol(OstProto::Protocol::kIp6FieldNumber, (void*) Ip6Protocol::createInstance); registerProtocol(OstProto::Protocol::kIp4over4FieldNumber, (void*) Ip4over4Protocol::createInstance); registerProtocol(OstProto::Protocol::kIp4over6FieldNumber, (void*) Ip4over6Protocol::createInstance); registerProtocol(OstProto::Protocol::kIp6over4FieldNumber, (void*) Ip6over4Protocol::createInstance); registerProtocol(OstProto::Protocol::kIp6over6FieldNumber, (void*) Ip6over6Protocol::createInstance); // Layer 4 Protocols registerProtocol(OstProto::Protocol::kIcmpFieldNumber, (void*) IcmpProtocol::createInstance); registerProtocol(OstProto::Protocol::kIgmpFieldNumber, (void*) IgmpProtocol::createInstance); registerProtocol(OstProto::Protocol::kMldFieldNumber, (void*) MldProtocol::createInstance); registerProtocol(OstProto::Protocol::kTcpFieldNumber, (void*) TcpProtocol::createInstance); registerProtocol(OstProto::Protocol::kUdpFieldNumber, (void*) UdpProtocol::createInstance); // Layer 5 Protocols registerProtocol(OstProto::Protocol::kTextProtocolFieldNumber, (void*) TextProtocol::createInstance); // Special Protocols registerProtocol(OstProto::Protocol::kHexDumpFieldNumber, (void*) HexDumpProtocol::createInstance); registerProtocol(OstProto::Protocol::kPayloadFieldNumber, (void*) PayloadProtocol::createInstance); registerProtocol(OstProto::Protocol::kSampleFieldNumber, (void*) SampleProtocol::createInstance); registerProtocol(OstProto::Protocol::kSignFieldNumber, (void*) SignProtocol::createInstance); registerProtocol(OstProto::Protocol::kUserScriptFieldNumber, (void*) UserScriptProtocol::createInstance); populateNeighbourProtocols(); } ProtocolManager::~ProtocolManager() { numberToNameMap.clear(); nameToNumberMap.clear(); neighbourProtocols.clear(); factory.clear(); QList pl = protocolList.values(); while (!pl.isEmpty()) delete pl.takeFirst(); } void ProtocolManager::registerProtocol(int protoNumber, void *protoInstanceCreator) { AbstractProtocol *p; Q_ASSERT(!factory.contains(protoNumber)); factory.insert(protoNumber, protoInstanceCreator); p = createProtocol(protoNumber, NULL); protocolList.insert(protoNumber, p); numberToNameMap.insert(protoNumber, p->shortName()); nameToNumberMap.insert(p->shortName(), protoNumber); } void ProtocolManager::populateNeighbourProtocols() { neighbourProtocols.clear(); foreach(AbstractProtocol *p, protocolList) { if (p->protocolIdType() != AbstractProtocol::ProtocolIdNone) { foreach(AbstractProtocol *q, protocolList) { if (q->protocolId(p->protocolIdType())) neighbourProtocols.insert( p->protocolNumber(), q->protocolNumber()); } } } } bool ProtocolManager::isRegisteredProtocol(int protoNumber) { return factory.contains(protoNumber); } AbstractProtocol* ProtocolManager::createProtocol(int protoNumber, StreamBase *stream, AbstractProtocol *parent) { AbstractProtocol* (*pc)(StreamBase*, AbstractProtocol*); AbstractProtocol* p; pc = (AbstractProtocol* (*)(StreamBase*, AbstractProtocol*)) factory.value(protoNumber); Q_ASSERT_X(pc != NULL, __FUNCTION__, QString("No Protocol Creator registered for protocol %1") .arg(protoNumber).toAscii().constData()); p = (*pc)(stream, parent); return p; } AbstractProtocol* ProtocolManager::createProtocol(QString protoName, StreamBase *stream, AbstractProtocol *parent) { return createProtocol(nameToNumberMap.value(protoName), stream, parent); } bool ProtocolManager::isValidNeighbour(int protoPrefix, int protoSuffix) { if (neighbourProtocols.contains(protoPrefix, protoSuffix)) return true; else return false; } bool ProtocolManager::protocolHasPayload(int protoNumber) { Q_ASSERT(protocolList.contains(protoNumber)); return protocolList.value(protoNumber)->protocolHasPayload(); } QStringList ProtocolManager::protocolDatabase() { return numberToNameMap.values(); } ostinato-0.9/common/protocolmanager.h000066400000000000000000000032371321315675200200710ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _PROTOCOL_MANAGER_H #define _PROTOCOL_MANAGER_H #include #include class AbstractProtocol; class StreamBase; class ProtocolManager { QMap numberToNameMap; QMap nameToNumberMap; QMultiMap neighbourProtocols; QMap factory; QMap protocolList; void populateNeighbourProtocols(); public: ProtocolManager(); ~ProtocolManager(); // TODO: make registerProtocol static void registerProtocol(int protoNumber, void *protoInstanceCreator); bool isRegisteredProtocol(int protoNumber); AbstractProtocol* createProtocol(int protoNumber, StreamBase *stream, AbstractProtocol *parent = 0); AbstractProtocol* createProtocol(QString protoName, StreamBase *stream, AbstractProtocol *parent = 0); bool isValidNeighbour(int protoPrefix, int protoSuffix); bool protocolHasPayload(int protoNumber); QStringList protocolDatabase(); }; #endif ostinato-0.9/common/protocolwidgetfactory.cpp000066400000000000000000000170051321315675200216630ustar00rootroot00000000000000/* Copyright (C) 2014 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "protocolwidgetfactory.h" #include "macconfig.h" #include "vlanconfig.h" #include "svlanconfig.h" #include "vlanstackconfig.h" // L2 Protocol Widgets #include "eth2config.h" #include "dot3config.h" #include "llcconfig.h" #include "dot2llcconfig.h" #include "snapconfig.h" #include "dot2snapconfig.h" #include "stpconfig.h" // L3 Protocol Widgets #include "arpconfig.h" #include "ip4config.h" #include "ip6config.h" #include "ip4over4config.h" #include "ip4over6config.h" #include "ip6over4config.h" #include "ip6over6config.h" // L4 Protocol Widgets #include "icmpconfig.h" #include "igmpconfig.h" #include "mldconfig.h" #include "tcpconfig.h" #include "udpconfig.h" // L5 Protocol Widgets #include "textprotoconfig.h" // Special Protocol Widgets #include "hexdumpconfig.h" #include "payloadconfig.h" #include "sampleconfig.h" #include "signconfig.h" #include "userscriptconfig.h" ProtocolWidgetFactory *OstProtocolWidgetFactory; QMap ProtocolWidgetFactory::configWidgetFactory; ProtocolWidgetFactory::ProtocolWidgetFactory() { /*! * Ideally Protocol Widgets should register themselves * with the Factory */ OstProtocolWidgetFactory->registerProtocolConfigWidget( OstProto::Protocol::kMacFieldNumber, (void*) MacConfigForm::createInstance); OstProtocolWidgetFactory->registerProtocolConfigWidget( OstProto::Protocol::kVlanFieldNumber, (void*) VlanConfigForm::createInstance); OstProtocolWidgetFactory->registerProtocolConfigWidget( OstProto::Protocol::kSvlanFieldNumber, (void*) SVlanConfigForm::createInstance); OstProtocolWidgetFactory->registerProtocolConfigWidget( OstProto::Protocol::kVlanStackFieldNumber, (void*) VlanStackConfigForm::createInstance); OstProtocolWidgetFactory->registerProtocolConfigWidget( OstProto::Protocol::kEth2FieldNumber, (void*) Eth2ConfigForm::createInstance); OstProtocolWidgetFactory->registerProtocolConfigWidget( OstProto::Protocol::kDot3FieldNumber, (void*) Dot3ConfigForm::createInstance); OstProtocolWidgetFactory->registerProtocolConfigWidget( OstProto::Protocol::kLlcFieldNumber, (void*) LlcConfigForm::createInstance); OstProtocolWidgetFactory->registerProtocolConfigWidget( OstProto::Protocol::kDot2LlcFieldNumber, (void*) Dot2LlcConfigForm::createInstance); OstProtocolWidgetFactory->registerProtocolConfigWidget( OstProto::Protocol::kSnapFieldNumber, (void*) SnapConfigForm::createInstance); OstProtocolWidgetFactory->registerProtocolConfigWidget( OstProto::Protocol::kDot2SnapFieldNumber, (void*) Dot2SnapConfigForm::createInstance); OstProtocolWidgetFactory->registerProtocolConfigWidget( OstProto::Protocol::kStpFieldNumber, (void*) StpConfigForm::createInstance); // Layer 3 Protocols OstProtocolWidgetFactory->registerProtocolConfigWidget( OstProto::Protocol::kArpFieldNumber, (void*) ArpConfigForm::createInstance); OstProtocolWidgetFactory->registerProtocolConfigWidget( OstProto::Protocol::kIp4FieldNumber, (void*) Ip4ConfigForm::createInstance); OstProtocolWidgetFactory->registerProtocolConfigWidget( OstProto::Protocol::kIp6FieldNumber, (void*) Ip6ConfigForm::createInstance); OstProtocolWidgetFactory->registerProtocolConfigWidget( OstProto::Protocol::kIp4over4FieldNumber, (void*) Ip4over4ConfigForm::createInstance); OstProtocolWidgetFactory->registerProtocolConfigWidget( OstProto::Protocol::kIp4over6FieldNumber, (void*) Ip4over6ConfigForm::createInstance); OstProtocolWidgetFactory->registerProtocolConfigWidget( OstProto::Protocol::kIp6over4FieldNumber, (void*) Ip6over4ConfigForm::createInstance); OstProtocolWidgetFactory->registerProtocolConfigWidget( OstProto::Protocol::kIp6over6FieldNumber, (void*) Ip6over6ConfigForm::createInstance); // Layer 4 Protocols OstProtocolWidgetFactory->registerProtocolConfigWidget( OstProto::Protocol::kIcmpFieldNumber, (void*) IcmpConfigForm::createInstance); OstProtocolWidgetFactory->registerProtocolConfigWidget( OstProto::Protocol::kIgmpFieldNumber, (void*) IgmpConfigForm::createInstance); OstProtocolWidgetFactory->registerProtocolConfigWidget( OstProto::Protocol::kMldFieldNumber, (void*) MldConfigForm::createInstance); OstProtocolWidgetFactory->registerProtocolConfigWidget( OstProto::Protocol::kTcpFieldNumber, (void*) TcpConfigForm::createInstance); OstProtocolWidgetFactory->registerProtocolConfigWidget( OstProto::Protocol::kUdpFieldNumber, (void*) UdpConfigForm::createInstance); // Layer 5 Protocols OstProtocolWidgetFactory->registerProtocolConfigWidget( OstProto::Protocol::kTextProtocolFieldNumber, (void*) TextProtocolConfigForm::createInstance); // Special Protocols OstProtocolWidgetFactory->registerProtocolConfigWidget( OstProto::Protocol::kHexDumpFieldNumber, (void*) HexDumpConfigForm::createInstance); OstProtocolWidgetFactory->registerProtocolConfigWidget( OstProto::Protocol::kPayloadFieldNumber, (void*) PayloadConfigForm::createInstance); OstProtocolWidgetFactory->registerProtocolConfigWidget( OstProto::Protocol::kSampleFieldNumber, (void*) SampleConfigForm::createInstance); OstProtocolWidgetFactory->registerProtocolConfigWidget( OstProto::Protocol::kSignFieldNumber, (void*) SignConfigForm::createInstance); OstProtocolWidgetFactory->registerProtocolConfigWidget( OstProto::Protocol::kUserScriptFieldNumber, (void*) UserScriptConfigForm::createInstance); } ProtocolWidgetFactory::~ProtocolWidgetFactory() { configWidgetFactory.clear(); } void ProtocolWidgetFactory::registerProtocolConfigWidget(int protoNumber, void *protoConfigWidgetInstanceCreator) { Q_ASSERT(!configWidgetFactory.contains(protoNumber)); configWidgetFactory.insert(protoNumber, protoConfigWidgetInstanceCreator); } AbstractProtocolConfigForm* ProtocolWidgetFactory::createConfigWidget( int protoNumber) { AbstractProtocolConfigForm* (*pc)(); AbstractProtocolConfigForm* p; pc = (AbstractProtocolConfigForm* (*)()) configWidgetFactory.value(protoNumber); Q_ASSERT_X(pc != NULL, __FUNCTION__, QString(protoNumber).toAscii().constData()); p = (*pc)(); return p; } void ProtocolWidgetFactory::deleteConfigWidget( AbstractProtocolConfigForm *configWidget) { delete configWidget; } ostinato-0.9/common/protocolwidgetfactory.h000066400000000000000000000025771321315675200213400ustar00rootroot00000000000000/* Copyright (C) 2014 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _PROTOCOL_WIDGET_FACTORY_H #define _PROTOCOL_WIDGET_FACTORY_H #include class AbstractProtocolConfigForm; // Singleton class class ProtocolWidgetFactory { static QMap configWidgetFactory; public: ProtocolWidgetFactory(); ~ProtocolWidgetFactory(); // TODO: make registerProtocolConfigWidget static?? // TODO: define a function pointer prototype instead of void* for // protoConfigWidgetInstanceCreator static void registerProtocolConfigWidget(int protoNumber, void *protoConfigWidgetInstanceCreator); AbstractProtocolConfigForm* createConfigWidget(int protoNumber); void deleteConfigWidget(AbstractProtocolConfigForm *configWidget); }; #endif ostinato-0.9/common/pythonfileformat.cpp000066400000000000000000000501721321315675200206220ustar00rootroot00000000000000/* Copyright (C) 2015 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "pythonfileformat.h" #include #include #include #include #include using google::protobuf::Message; using google::protobuf::Reflection; using google::protobuf::FieldDescriptor; PythonFileFormat pythonFileFormat; extern char *version; extern char *revision; PythonFileFormat::PythonFileFormat() { // Nothing to do } PythonFileFormat::~PythonFileFormat() { // Nothing to do } bool PythonFileFormat::open(const QString /*fileName*/, OstProto::StreamConfigList &/*streams*/, QString &/*error*/) { // NOT SUPPORTED! return false; } bool PythonFileFormat::save(const OstProto::StreamConfigList streams, const QString fileName, QString &error) { QFile file(fileName); QTextStream out(&file); QSet imports; if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) goto _open_fail; // import standard modules emit status("Writing imports ..."); emit target(0); writeStandardImports(out); emit target(streams.stream_size()); // import protocols from respective modules // build the import list using a QSet to eliminate duplicates for (int i = 0; i < streams.stream_size(); i++) { const OstProto::Stream &stream = streams.stream(i); for (int j = 0 ; j < stream.protocol_size(); j++) { const OstProto::Protocol &protocol = stream.protocol(j); const Reflection *refl = protocol.GetReflection(); std::vector fields; refl->ListFields(protocol, &fields); for (uint k = 0; k < fields.size(); k++) { // skip non extension fields if (!fields.at(k)->is_extension()) continue; if (fields.at(k)->file()->name() != fields.at(k)->message_type()->file()->name()) { imports.insert( QString("%1 import %2").arg( QString(fields.at(k)->message_type() ->file()->name().c_str()) .replace(".proto", "_pb2"), fields.at(k)->message_type()->name().c_str())); imports.insert( QString("%1 import %2").arg( QString(fields.at(k) ->file()->name().c_str()) .replace(".proto", "_pb2"), fields.at(k)->name().c_str())); } else { imports.insert( QString("%1 import %2, %3").arg( QString(fields.at(k)->file()->name().c_str()) .replace(".proto", "_pb2"), fields.at(k)->message_type()->name().c_str(), fields.at(k)->name().c_str())); } } } emit progress(i); } // write the import statements out << "# import ostinato modules\n"; out << "from ostinato.core import DroneProxy, ost_pb\n"; foreach (QString str, imports) out << "from ostinato.protocols." << str << "\n"; out << "\n"; // start of script - init, connect to drone etc. emit status("Writing prologue ..."); emit target(0); writePrologue(out); // Add streams emit status("Writing stream adds ..."); emit target(streams.stream_size()); out << " # ------------#\n"; out << " # add streams #\n"; out << " # ------------#\n"; out << " stream_id = ost_pb.StreamIdList()\n"; out << " stream_id.port_id.id = tx_port_number\n"; for (int i = 0; i < streams.stream_size(); i++) { out << " stream_id.stream_id.add().id = " << streams.stream(i).stream_id().id() << "\n"; emit progress(i); } out << " drone.addStream(stream_id)\n"; out << "\n"; // Configure streams with actual values emit status("Writing stream configuration ..."); emit target(streams.stream_size()); out << " # ------------------#\n"; out << " # configure streams #\n"; out << " # ------------------#\n"; out << " stream_cfg = ost_pb.StreamConfigList()\n"; out << " stream_cfg.port_id.id = tx_port_number\n"; for (int i = 0; i < streams.stream_size(); i++) { const OstProto::Stream &stream = streams.stream(i); const Reflection *refl; std::vector fields; out << "\n"; out << " # stream " << stream.stream_id().id() << " " << stream.core().name().c_str() << "\n"; out << " s = stream_cfg.stream.add()\n"; out << " s.stream_id.id = " << stream.stream_id().id() << "\n"; // Stream Core values refl = stream.core().GetReflection(); refl->ListFields(stream.core(), &fields); for (uint j = 0; j < fields.size(); j++) { writeFieldAssignment(out, QString(" s.core.") .append(fields.at(j)->name().c_str()), stream.core(), refl, fields.at(j)); } // Stream Control values refl = stream.control().GetReflection(); refl->ListFields(stream.control(), &fields); for (uint j = 0; j < fields.size(); j++) { writeFieldAssignment(out, QString(" s.control.") .append(fields.at(j)->name().c_str()), stream.control(), refl, fields.at(j)); } // Protocols for (int j = 0 ; j < stream.protocol_size(); j++) { const OstProto::Protocol &protocol = stream.protocol(j); out << "\n" << " p = s.protocol.add()\n" << " p.protocol_id.id = " << QString(OstProto::Protocol_k_descriptor() ->FindValueByNumber(protocol.protocol_id().id()) ->full_name().c_str()) .replace("OstProto", "ost_pb"); out << "\n"; refl = protocol.GetReflection(); refl->ListFields(protocol, &fields); for (uint k = 0; k < fields.size(); k++) { // skip protocol_id field if (fields.at(k)->number() == OstProto::Protocol::kProtocolIdFieldNumber) continue; QString pfx(" p.Extensions[X]"); pfx.replace(fields.at(k)->is_extension()? "X": "Extensions[X]", fields.at(k)->name().c_str()); writeFieldAssignment(out, pfx, protocol, refl, fields.at(k)); } } emit progress(i); } out << "\n"; out << " drone.modifyStream(stream_cfg)\n"; // end of script - transmit streams, disconnect from drone etc. emit status("Writing epilogue ..."); emit target(0); writeEpilogue(out); out.flush(); file.close(); return true; _open_fail: error = QString(tr("Error opening %1 (Error Code = %2)")) .arg(fileName) .arg(file.error()); return false; } bool PythonFileFormat::isMyFileFormat(const QString /*fileName*/) { // isMyFileFormat() is used for file open case to detect // file format - Open not supported for Python Scripts return false; } bool PythonFileFormat::isMyFileType(const QString fileType) { if (fileType.startsWith("PythonScript")) return true; else return false; } // // Private Member Functions // void PythonFileFormat::writeStandardImports(QTextStream &out) { out << "#! /usr/bin/env python\n"; out << "\n"; out << "# This script was programmatically generated\n" << "# by Ostinato version " << version << " revision " << revision << "\n" << "# The script should work out of the box mostly,\n" << "# but occassionally might need minor tweaking\n" << "# Please report any bugs at http://ostinato.org\n"; out << "\n"; out << "# standard modules\n"; out << "import logging\n"; out << "import os\n"; out << "import sys\n"; out << "import time\n"; out << "\n"; } void PythonFileFormat::writePrologue(QTextStream &out) { out << "# initialize the below variables appropriately " << "to avoid manual input\n"; out << "host_name = ''\n"; out << "tx_port_number = -1\n"; out << "\n"; out << "# setup logging\n"; out << "log = logging.getLogger(__name__)\n"; out << "logging.basicConfig(level=logging.INFO)\n"; out << "\n"; out << "# get inputs, if required\n"; out << "while len(host_name) == 0:\n"; out << " host_name = raw_input('Drone\\'s Hostname/IP: ')\n"; out << "while tx_port_number < 0:\n"; out << " tx_port_number = int(raw_input('Tx Port Number: '))\n"; out << "\n"; out << "drone = DroneProxy(host_name)\n"; out << "\n"; out << "try:\n"; out << " # connect to drone\n"; out << " log.info('connecting to drone(%s:%d)' \n"; out << " % (drone.hostName(), drone.portNumber()))\n"; out << " drone.connect()\n"; out << "\n"; out << " # setup tx port list\n"; out << " tx_port = ost_pb.PortIdList()\n"; out << " tx_port.port_id.add().id = tx_port_number;\n"; out << "\n"; } void PythonFileFormat::writeEpilogue(QTextStream &out) { out << " # clear tx/rx stats\n"; out << " log.info('clearing tx stats')\n"; out << " drone.clearStats(tx_port)\n"; out << "\n"; out << " log.info('starting transmit')\n"; out << " drone.startTransmit(tx_port)\n"; out << "\n"; out << " # wait for transmit to finish\n"; out << " log.info('waiting for transmit to finish ...')\n"; out << " while True:\n"; out << " try:\n"; out << " time.sleep(5)\n"; out << " tx_stats = drone.getStats(tx_port)\n"; out << " if tx_stats.port_stats[0].state.is_transmit_on" " == False:\n"; out << " break\n"; out << " except KeyboardInterrupt:\n"; out << " log.info('transmit interrupted by user')\n"; out << " break\n"; out << "\n"; out << " # stop transmit and capture\n"; out << " log.info('stopping transmit')\n"; out << " drone.stopTransmit(tx_port)\n"; out << "\n"; out << " # get tx stats\n"; out << " log.info('retreiving stats')\n"; out << " tx_stats = drone.getStats(tx_port)\n"; out << "\n"; out << " log.info('tx pkts = %d' % (tx_stats.port_stats[0].tx_pkts))\n"; out << "\n"; out << " # delete streams\n"; out << " log.info('deleting tx_streams')\n"; out << " drone.deleteStream(stream_id)\n"; out << "\n"; out << " # bye for now\n"; out << " drone.disconnect()\n"; out << "\n"; out << "except Exception as ex:\n"; out << " log.exception(ex)\n"; out << " sys.exit(1)\n"; } void PythonFileFormat::writeFieldAssignment( QTextStream &out, QString fieldName, const Message &msg, const Reflection *refl, const FieldDescriptor *fieldDesc, int index) { // for a repeated field, // if index < 0 => we are writing a repeated aggregate // if index >= 0 => we are writing a repeated element if (fieldDesc->is_repeated() && (index < 0)) { int n = refl->FieldSize(msg, fieldDesc); QString var = singularize(fieldDesc->name().c_str()); for (int i = 0; i < n; i++) { out << " " << var << " = " << fieldName.trimmed() << ".add()\n"; writeFieldAssignment(out, QString(" ").append(var), msg, refl, fieldDesc, i); } return; } // Ideally fields should not be set if they have the same // value as the default value - but currently protocols don't // check this when setting values in the protobuf data object // so here we check that explicitly for each field and if true // we don't output anything switch(fieldDesc->cpp_type()) { case FieldDescriptor::CPPTYPE_INT32: { qint32 val = fieldDesc->is_repeated() ? refl->GetRepeatedInt32(msg, fieldDesc, index) : refl->GetInt32(msg, fieldDesc); if (val != fieldDesc->default_value_int32()) out << fieldName << " = " << val << "\n"; break; } case FieldDescriptor::CPPTYPE_INT64: { qint64 val = fieldDesc->is_repeated() ? refl->GetRepeatedInt64(msg, fieldDesc, index) : refl->GetInt64(msg, fieldDesc); if (val != fieldDesc->default_value_int64()) out << fieldName << " = " << val << "\n"; break; } case FieldDescriptor::CPPTYPE_UINT32: { quint32 val = fieldDesc->is_repeated() ? refl->GetRepeatedUInt32(msg, fieldDesc, index) : refl->GetUInt32(msg, fieldDesc); QString valStr; if (useDecimalBase(fieldName)) valStr.setNum(val); else valStr.setNum(val, 16).prepend("0x"); if (val != fieldDesc->default_value_uint32()) out << fieldName << " = " << valStr << "\n"; break; } case FieldDescriptor::CPPTYPE_UINT64: { quint64 val = fieldDesc->is_repeated() ? refl->GetRepeatedUInt64(msg, fieldDesc, index) : refl->GetUInt64(msg, fieldDesc); QString valStr; if (useDecimalBase(fieldName)) valStr.setNum(val); else valStr.setNum(val, 16).prepend("0x"); if (val != fieldDesc->default_value_uint64()) out << fieldName << " = " << valStr << "\n"; break; } case FieldDescriptor::CPPTYPE_DOUBLE: { double val = fieldDesc->is_repeated() ? refl->GetRepeatedDouble(msg, fieldDesc, index) : refl->GetDouble(msg, fieldDesc); if (val != fieldDesc->default_value_double()) out << fieldName << " = " << val << "\n"; break; } case FieldDescriptor::CPPTYPE_FLOAT: { float val = fieldDesc->is_repeated() ? refl->GetRepeatedFloat(msg, fieldDesc, index) : refl->GetFloat(msg, fieldDesc); if (val != fieldDesc->default_value_float()) out << fieldName << " = " << val << "\n"; break; } case FieldDescriptor::CPPTYPE_BOOL: { bool val = fieldDesc->is_repeated() ? refl->GetRepeatedBool(msg, fieldDesc, index) : refl->GetBool(msg, fieldDesc); if (val != fieldDesc->default_value_bool()) out << fieldName << " = " << (refl->GetBool(msg, fieldDesc) ? "True" : "False") << "\n"; break; } case FieldDescriptor::CPPTYPE_STRING: { std::string val = fieldDesc->is_repeated() ? refl->GetRepeatedStringReference(msg, fieldDesc, index, &val) : refl->GetStringReference(msg, fieldDesc, &val); QString escVal = escapeString(QString::fromStdString(val)); if (val != fieldDesc->default_value_string()) out << fieldName << " = '" << escVal << "'\n"; break; } case FieldDescriptor::CPPTYPE_ENUM: { // Fields defined in protocol.proto are within ost_pb scope QString module = fieldDesc->file()->name() == "protocol.proto" ? "ost_pb." : ""; std::string val = fieldDesc->is_repeated() ? refl->GetRepeatedEnum(msg, fieldDesc, index)->full_name() : refl->GetEnum(msg, fieldDesc)->full_name(); if (val != fieldDesc->default_value_enum()->full_name()) out << fieldName << " = " << QString::fromStdString(val) .replace("OstProto.", module) << "\n"; break; } case FieldDescriptor::CPPTYPE_MESSAGE: { QString pfxStr(fieldName); const Message &msg2 = fieldDesc->is_repeated() ? refl->GetRepeatedMessage(msg, fieldDesc, index) : refl->GetMessage(msg, fieldDesc); const Reflection *refl2 = msg2.GetReflection(); std::vector fields2; QList autoFields; refl2->ListFields(msg2, &fields2); // Unfortunately, auto-calculated fields such as cksum, length // and protocol-type etc. may be set in the protobuf even if // they are not being overridden; // Intelligence regarding them is inside the respective protocol // implementation, not inside the protobuf objects - the latter // is all we have available here to work with; // We attempt a crude hack here to detect such fields and avoid // writing assignment statements for them for (uint i = 0; i < fields2.size(); i++) { std::string name = fields2.at(i)->name(); if ((fields2.at(i)->cpp_type() == FieldDescriptor::CPPTYPE_BOOL) && (name.find("is_override_") == 0) && (refl2->GetBool(msg2, fields2.at(i)) == false)) { name.erase(0, sizeof("is_override_") - 1); autoFields.append(name); } } for (uint i = 0 ; i < fields2.size(); i++) { // skip auto fields that are not overridden if (autoFields.contains(fields2.at(i)->name())) continue; writeFieldAssignment(out, QString("%1.%2").arg(pfxStr, fields2.at(i)->name().c_str()), msg2, refl2, fields2.at(i)); } break; } default: qWarning("unable to write field of unsupported type %d", fieldDesc->cpp_type()); } } QString PythonFileFormat::singularize(QString plural) { QString singular = plural; // Apply some heuristics if (plural.endsWith("ies")) singular.replace(singular.length()-3, 3, "y"); else if (plural.endsWith("ses")) singular.chop(2); else if (plural.endsWith("s")) singular.chop(1); return singular; } QString PythonFileFormat::escapeString(QString str) { QString escStr = ""; for (int i=0; i < str.length(); i++) { uchar c = str[i].cell(); if ((c < 128) && isprint(c)) { if (c == '\'') escStr.append("\\'"); else escStr.append(str[i]); } else escStr.append(QString("\\x%1").arg(int(c), 2, 16, QChar('0'))); } return escStr; } bool PythonFileFormat::useDecimalBase(QString fieldName) { // Heuristic - use Hex base for all except for the following return fieldName.endsWith("count") || fieldName.endsWith("length") || fieldName.endsWith("len") || fieldName.endsWith("time"); } ostinato-0.9/common/pythonfileformat.h000066400000000000000000000034401321315675200202630ustar00rootroot00000000000000/* Copyright (C) 2015 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _PYTHON_FILE_FORMAT_H #define _PYTHON_FILE_FORMAT_H #include "streamfileformat.h" #include class PythonFileFormat : public StreamFileFormat { public: PythonFileFormat(); ~PythonFileFormat(); virtual bool open(const QString fileName, OstProto::StreamConfigList &streams, QString &error); virtual bool save(const OstProto::StreamConfigList streams, const QString fileName, QString &error); bool isMyFileFormat(const QString fileName); bool isMyFileType(const QString fileType); private: void writeStandardImports(QTextStream &out); void writePrologue(QTextStream &out); void writeEpilogue(QTextStream &out); void writeFieldAssignment(QTextStream &out, QString fieldName, const google::protobuf::Message &msg, const google::protobuf::Reflection *refl, const google::protobuf::FieldDescriptor *fieldDesc, int index = -1); QString singularize(QString plural); QString escapeString(QString str); bool useDecimalBase(QString fieldName); }; extern PythonFileFormat pythonFileFormat; #endif ostinato-0.9/common/sample.cpp000066400000000000000000000313631321315675200165120ustar00rootroot00000000000000/* Copyright (C) 2010, 2014 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "sample.h" SampleProtocol::SampleProtocol(StreamBase *stream, AbstractProtocol *parent) : AbstractProtocol(stream, parent) { } SampleProtocol::~SampleProtocol() { } AbstractProtocol* SampleProtocol::createInstance(StreamBase *stream, AbstractProtocol *parent) { return new SampleProtocol(stream, parent); } quint32 SampleProtocol::protocolNumber() const { return OstProto::Protocol::kSampleFieldNumber; } void SampleProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const { protocol.MutableExtension(OstProto::sample)->CopyFrom(data); protocol.mutable_protocol_id()->set_id(protocolNumber()); } void SampleProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) { if (protocol.protocol_id().id() == protocolNumber() && protocol.HasExtension(OstProto::sample)) data.MergeFrom(protocol.GetExtension(OstProto::sample)); } QString SampleProtocol::name() const { return QString("Sample Protocol"); } QString SampleProtocol::shortName() const { return QString("SAMPLE"); } /*! TODO Return the ProtocolIdType for your protocol \n If your protocol doesn't have a protocolId field, you don't need to reimplement this method - the base class implementation will do the right thing */ AbstractProtocol::ProtocolIdType SampleProtocol::protocolIdType() const { return ProtocolIdIp; } /*! TODO Return the protocolId for your protoocol based on the 'type' requested \n If not all types are valid for your protocol, handle the valid type(s) and for the remaining fallback to the base class implementation; if your protocol doesn't have a protocolId at all, you don't need to reimplement this method - the base class will do the right thing */ quint32 SampleProtocol::protocolId(ProtocolIdType type) const { switch(type) { case ProtocolIdLlc: return 0xFFFFFF; case ProtocolIdEth: return 0xFFFF; case ProtocolIdIp: return 0xFF; default:break; } return AbstractProtocol::protocolId(type); } int SampleProtocol::fieldCount() const { return sample_fieldCount; } /*! TODO Return the number of frame fields for your protocol. A frame field is a field which has the FrameField flag set \n If your protocol has different sets of fields based on a OpCode/Type field (e.g. icmp), you MUST re-implement this function; however, if your protocol has a fixed set of frame fields always, you don't need to reimplement this method - the base class implementation will do the right thing */ int SampleProtocol::frameFieldCount() const { return AbstractProtocol::frameFieldCount(); } /*! TODO Edit this function to return the appropriate flags for each field \n See AbstractProtocol::FieldFlags for more info */ AbstractProtocol::FieldFlags SampleProtocol::fieldFlags(int index) const { AbstractProtocol::FieldFlags flags; flags = AbstractProtocol::fieldFlags(index); switch (index) { case sample_a: case sample_b: case sample_payloadLength: break; case sample_checksum: flags |= CksumField; break; case sample_x: case sample_y: break; case sample_is_override_checksum: flags &= ~FrameField; flags |= MetaField; break; default: qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, index); break; } return flags; } /*! TODO: Edit this function to return the data for each field See AbstractProtocol::fieldData() for more info */ QVariant SampleProtocol::fieldData(int index, FieldAttrib attrib, int streamIndex) const { switch (index) { case sample_a: { int a = data.ab() >> 13; switch(attrib) { case FieldName: return QString("A"); case FieldValue: return a; case FieldTextValue: return QString("%1").arg(a); case FieldFrameValue: return QByteArray(1, (char) a); case FieldBitSize: return 3; default: break; } break; } case sample_b: { int b = data.ab() & 0x1FFF; switch(attrib) { case FieldName: return QString("B"); case FieldValue: return b; case FieldTextValue: return QString("%1").arg(b, 4, BASE_HEX, QChar('0')); case FieldFrameValue: { QByteArray fv; fv.resize(2); qToBigEndian((quint16) b, (uchar*) fv.data()); return fv; } case FieldBitSize: return 13; default: break; } break; } case sample_payloadLength: { switch(attrib) { case FieldName: return QString("Payload Length"); case FieldValue: return protocolFramePayloadSize(streamIndex); case FieldFrameValue: { QByteArray fv; int totlen; totlen = protocolFramePayloadSize(streamIndex); fv.resize(2); qToBigEndian((quint16) totlen, (uchar*) fv.data()); return fv; } case FieldTextValue: return QString("%1").arg( protocolFramePayloadSize(streamIndex)); case FieldBitSize: return 16; default: break; } break; } case sample_checksum: { quint16 cksum; switch(attrib) { case FieldValue: case FieldFrameValue: case FieldTextValue: if (data.is_override_checksum()) cksum = data.checksum(); else cksum = protocolFrameCksum(streamIndex, CksumIp); break; default: cksum = 0; // avoid the 'maybe used unitialized' warning break; } switch(attrib) { case FieldName: return QString("Checksum"); case FieldValue: return cksum; case FieldFrameValue: { QByteArray fv; fv.resize(2); qToBigEndian(cksum, (uchar*) fv.data()); return fv; } case FieldTextValue: return QString("0x%1").arg( cksum, 4, BASE_HEX, QChar('0'));; case FieldBitSize: return 16; default: break; } break; } case sample_x: { switch(attrib) { case FieldName: return QString("X"); case FieldValue: return data.x(); case FieldTextValue: // Use the following line for display in decimal return QString("%1").arg(data.x()); // Use the following line for display in hexa-decimal //return QString("%1").arg(data.x(), 8, BASE_HEX, QChar('0')); case FieldFrameValue: { QByteArray fv; fv.resize(4); qToBigEndian((quint32) data.x(), (uchar*) fv.data()); return fv; } default: break; } break; } case sample_y: { switch(attrib) { case FieldName: return QString("Y"); case FieldValue: return data.y(); case FieldTextValue: // Use the following line for display in decimal //return QString("%1").arg(data.y()); // Use the following line for display in hexa-decimal return QString("%1").arg(data.y(), 4, BASE_HEX, QChar('0')); case FieldFrameValue: { QByteArray fv; fv.resize(2); qToBigEndian((quint16) data.y(), (uchar*) fv.data()); return fv; } default: break; } break; } // Meta fields case sample_is_override_checksum: { switch(attrib) { case FieldValue: return data.is_override_checksum(); default: break; } break; } default: qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, index); break; } return AbstractProtocol::fieldData(index, attrib, streamIndex); } /*! TODO: Edit this function to set the data for each field See AbstractProtocol::setFieldData() for more info */ bool SampleProtocol::setFieldData(int index, const QVariant &value, FieldAttrib attrib) { bool isOk = false; if (attrib != FieldValue) goto _exit; switch (index) { case sample_a: { uint a = value.toUInt(&isOk); if (isOk) data.set_ab((data.ab() & 0x1FFF) | ((a & 0x07) << 13)); break; } case sample_b: { uint b = value.toUInt(&isOk); if (isOk) data.set_ab((data.ab() & 0xe000) | (b & 0x1FFF)); break; } case sample_payloadLength: { uint len = value.toUInt(&isOk); if (isOk) data.set_payload_length(len); break; } case sample_checksum: { uint csum = value.toUInt(&isOk); if (isOk) data.set_checksum(csum); break; } case sample_x: { uint x = value.toUInt(&isOk); if (isOk) data.set_x(x); break; } case sample_y: { uint y = value.toUInt(&isOk); if (isOk) data.set_y(y); break; } case sample_is_override_checksum: { bool ovr = value.toBool(); data.set_is_override_checksum(ovr); isOk = true; break; } default: qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, index); break; } _exit: return isOk; } /*! TODO: Return the protocol frame size in bytes\n If your protocol has a fixed size - you don't need to reimplement this; the base class implementation is good enough */ int SampleProtocol::protocolFrameSize(int streamIndex) const { return AbstractProtocol::protocolFrameSize(streamIndex); } /*! TODO: If your protocol frame size can vary across pkts of the same stream, return true \n Otherwise you don't need to reimplement this method - the base class always returns false */ bool SampleProtocol::isProtocolFrameSizeVariable() const { return false; } /*! TODO: If your protocol frame has any variable fields or has a variable size, return the minimum number of frames required to vary the fields \n See AbstractProtocol::protocolFrameVariableCount() for more info */ int SampleProtocol::protocolFrameVariableCount() const { return AbstractProtocol::protocolFrameVariableCount(); } ostinato-0.9/common/sample.h000066400000000000000000000047641321315675200161640ustar00rootroot00000000000000/* Copyright (C) 2010, 2014 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _SAMPLE_H #define _SAMPLE_H #include "abstractprotocol.h" #include "sample.pb.h" /* Sample Protocol Frame Format - +-----+------+------+------+------+------+ | A | B | LEN | CSUM | X | Y | | (3) | (13) | (16) | (16) | (32) | (32) | +-----+------+------+------+------+------+ Figures in brackets represent field width in bits */ class SampleProtocol : public AbstractProtocol { public: enum samplefield { // Frame Fields sample_a = 0, sample_b, sample_payloadLength, sample_checksum, sample_x, sample_y, // Meta Fields sample_is_override_checksum, sample_fieldCount }; SampleProtocol(StreamBase *stream, AbstractProtocol *parent = 0); virtual ~SampleProtocol(); static AbstractProtocol* createInstance(StreamBase *stream, AbstractProtocol *parent = 0); virtual quint32 protocolNumber() const; virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); virtual ProtocolIdType protocolIdType() const; virtual quint32 protocolId(ProtocolIdType type) const; virtual QString name() const; virtual QString shortName() const; virtual int fieldCount() const; virtual int frameFieldCount() const; virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; virtual QVariant fieldData(int index, FieldAttrib attrib, int streamIndex = 0) const; virtual bool setFieldData(int index, const QVariant &value, FieldAttrib attrib = FieldValue); virtual int protocolFrameSize(int streamIndex = 0) const; virtual bool isProtocolFrameSizeVariable() const; virtual int protocolFrameVariableCount() const; private: OstProto::Sample data; }; #endif ostinato-0.9/common/sample.proto000066400000000000000000000020321321315675200170620ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ import "protocol.proto"; package OstProto; // Sample Protocol message Sample { optional bool is_override_checksum = 1; optional uint32 ab = 2; optional uint32 payload_length = 3; optional uint32 checksum = 4; optional uint32 x = 5 [default = 1234]; optional uint32 y = 6; } extend Protocol { optional Sample sample = 102; } ostinato-0.9/common/sample.ui000066400000000000000000000113661321315675200163460ustar00rootroot00000000000000 Sample 0 0 263 116 Form Field A Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter sampleA >HH; Checksum false >HH HH; Field B Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter sampleB >HH HH; Field X Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter sampleX Qt::Horizontal 40 20 Length Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter samplePayloadLength false Field Y Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter sampleY Qt::Vertical 20 40 sampleA sampleB samplePayloadLength isChecksumOverride sampleChecksum sampleX sampleY isChecksumOverride toggled(bool) sampleChecksum setEnabled(bool) 345 122 406 122 ostinato-0.9/common/sampleconfig.cpp000066400000000000000000000062251321315675200176770ustar00rootroot00000000000000/* Copyright (C) 2010, 2014 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "sampleconfig.h" #include "sample.h" SampleConfigForm::SampleConfigForm(QWidget *parent) : AbstractProtocolConfigForm(parent) { setupUi(this); } SampleConfigForm::~SampleConfigForm() { } SampleConfigForm* SampleConfigForm::createInstance() { return new SampleConfigForm; } /*! TODO: Edit this function to load each field's data into the config Widget See AbstractProtocolConfigForm::loadWidget() for more info */ void SampleConfigForm::loadWidget(AbstractProtocol *proto) { sampleA->setText( proto->fieldData( SampleProtocol::sample_a, AbstractProtocol::FieldValue ).toString()); sampleB->setText( proto->fieldData( SampleProtocol::sample_b, AbstractProtocol::FieldValue ).toString()); samplePayloadLength->setText( proto->fieldData( SampleProtocol::sample_payloadLength, AbstractProtocol::FieldValue ).toString()); isChecksumOverride->setChecked( proto->fieldData( SampleProtocol::sample_is_override_checksum, AbstractProtocol::FieldValue ).toBool()); sampleChecksum->setText(uintToHexStr( proto->fieldData( SampleProtocol::sample_checksum, AbstractProtocol::FieldValue ).toUInt(), 2)); sampleX->setText( proto->fieldData( SampleProtocol::sample_x, AbstractProtocol::FieldValue ).toString()); sampleY->setText( proto->fieldData( SampleProtocol::sample_y, AbstractProtocol::FieldValue ).toString()); } /*! TODO: Edit this function to store each field's data from the config Widget See AbstractProtocolConfigForm::storeWidget() for more info */ void SampleConfigForm::storeWidget(AbstractProtocol *proto) { proto->setFieldData( SampleProtocol::sample_a, sampleA->text()); proto->setFieldData( SampleProtocol::sample_b, sampleB->text()); proto->setFieldData( SampleProtocol::sample_payloadLength, samplePayloadLength->text()); proto->setFieldData( SampleProtocol::sample_is_override_checksum, isChecksumOverride->isChecked()); proto->setFieldData( SampleProtocol::sample_checksum, hexStrToUInt(sampleChecksum->text())); proto->setFieldData( SampleProtocol::sample_x, sampleX->text()); proto->setFieldData( SampleProtocol::sample_y, sampleY->text()); } ostinato-0.9/common/sampleconfig.h000066400000000000000000000022141321315675200173360ustar00rootroot00000000000000/* Copyright (C) 2010, 2014 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _SAMPLE_CONFIG_H #define _SAMPLE_CONFIG_H #include "abstractprotocolconfig.h" #include "ui_sample.h" class SampleConfigForm : public AbstractProtocolConfigForm, private Ui::Sample { Q_OBJECT public: SampleConfigForm(QWidget *parent = 0); virtual ~SampleConfigForm(); static SampleConfigForm* createInstance(); virtual void loadWidget(AbstractProtocol *proto); virtual void storeWidget(AbstractProtocol *proto); private slots: }; #endif ostinato-0.9/common/samplepdml.cpp000066400000000000000000000077431321315675200173740ustar00rootroot00000000000000/* Copyright (C) 2014 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "samplepdml.h" #include "sample.pb.h" /*! TODO : Initialize the following inherited protected members - - ostProtoId_ - fieldMap_ ostProtoId_ is the protocol's protobuf field number as defined in message 'Protocol' enum 'k' in file protocol.proto fieldMap_ is a mapping of the protocol's field names as they appear in the PDML to the protobuf field numbers for the protocol. All such fields are classified as 'known' fields and the base class will take care of decoding these without any help from the subclass. Note that the PDML field names are same as the field names used in Wireshark display filters. The full reference for these is available at - http://www.wireshark.org/docs/dfref/ */ PdmlSampleProtocol::PdmlSampleProtocol() { ostProtoId_ = OstProto::Protocol::kSampleFieldNumber; fieldMap_.insert("sample.checksum", OstProto::Sample::kChecksumFieldNumber); fieldMap_.insert("sample.x", OstProto::Sample::kXFieldNumber); fieldMap_.insert("sample.y", OstProto::Sample::kYFieldNumber); } PdmlSampleProtocol::~PdmlSampleProtocol() { } PdmlSampleProtocol* PdmlSampleProtocol::createInstance() { return new PdmlSampleProtocol(); } /*! TODO: Use this method to do any special handling that may be required for preprocessing a protocol before parsing/decoding the protocol's fields */ void PdmlSampleProtocol::preProtocolHandler(QString /*name*/, const QXmlStreamAttributes& /*attributes*/, int /*expectedPos*/, OstProto::Protocol* /*pbProto*/, OstProto::Stream* /*stream*/) { return; } /*! TODO: Use this method to do any special handling or cleanup that may be required when a protocol decode is ending prematurely */ void PdmlSampleProtocol::prematureEndHandler(int /*pos*/, OstProto::Protocol* /*pbProto*/, OstProto::Stream* /*stream*/) { return; } /*! TODO: Use this method to do any special handling that may be required for postprocessing a protocol after parsing/decoding all the protocol fields If your protocol's protobuf has some meta-fields that should be set to their non default values, this is a good place to do that. e.g. derived fields such as length, checksum etc. may be correct or incorrect in the PCAP/PDML - to retain the same value as in the PCAP/PDML and not let Ostinato recalculate these, you can set the is_override_length, is_override_cksum meta-fields to true here */ void PdmlSampleProtocol::postProtocolHandler(OstProto::Protocol* /*pbProto*/, OstProto::Stream* /*stream*/) { return; } /*! TODO: Handle all 'unknown' fields using this method You need to typically only handle frame fields or fields actually present in the protocol on the wire. So you can safely ignore meta-fields such as Good/Bad Checksum. Some fields may not have a 'name' attribute, so cannot be classified as a 'known' field. Use this method to identify such fields using other attributes such as 'show' or 'showname' and populate the corresponding protobuf field. If the PDML protocol contains some fields that are not supported by Ostinato, use a HexDump protocol as a replacement to store these bytes */ void PdmlSampleProtocol::unknownFieldHandler(QString /*name*/, int /*pos*/, int /*size*/, const QXmlStreamAttributes& /*attributes*/, OstProto::Protocol* /*pbProto*/, OstProto::Stream* /*stream*/) { return; } ostinato-0.9/common/samplepdml.h000066400000000000000000000032271321315675200170320ustar00rootroot00000000000000/* Copyright (C) 2014 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _SAMPLE_PDML_H #define _SAMPLE_PDML_H #include "pdmlprotocol.h" class PdmlSampleProtocol : public PdmlProtocol { public: virtual ~PdmlSampleProtocol(); static PdmlSampleProtocol* createInstance(); virtual void preProtocolHandler(QString name, const QXmlStreamAttributes &attributes, int expectedPos, OstProto::Protocol *pbProto, OstProto::Stream *stream); virtual void prematureEndHandler(int pos, OstProto::Protocol *pbProto, OstProto::Stream *stream); virtual void postProtocolHandler(OstProto::Protocol *pbProto, OstProto::Stream *stream); void fieldHandler(QString name, const QXmlStreamAttributes &attributes, OstProto::Protocol *pbProto, OstProto::Stream *stream); virtual void unknownFieldHandler(QString name, int pos, int size, const QXmlStreamAttributes &attributes, OstProto::Protocol *pbProto, OstProto::Stream *stream); protected: PdmlSampleProtocol(); }; #endif ostinato-0.9/common/sessionfileformat.cpp000066400000000000000000000046421321315675200207650ustar00rootroot00000000000000/* Copyright (C) 2016 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "sessionfileformat.h" #include "ossnfileformat.h" #include SessionFileFormat::SessionFileFormat() { stop_ = false; } SessionFileFormat::~SessionFileFormat() { } QDialog* SessionFileFormat::openOptionsDialog() { return NULL; } QDialog* SessionFileFormat::saveOptionsDialog() { return NULL; } QStringList SessionFileFormat::supportedFileTypes(Operation op) { QStringList fileTypes; fileTypes << "Ostinato Session (*.ossn)"; if (op == kOpenFile) fileTypes << "All files (*)"; return fileTypes; } void SessionFileFormat::openAsync(const QString fileName, OstProto::SessionContent &session, QString &error) { fileName_ = fileName; openSession_ = &session; error_ = &error; op_ = kOpenFile; stop_ = false; start(); } void SessionFileFormat::saveAsync( const OstProto::SessionContent &session, const QString fileName, QString &error) { saveSession_ = &session; fileName_ = fileName; error_ = &error; op_ = kSaveFile; stop_ = false; start(); } bool SessionFileFormat::result() { return result_; } SessionFileFormat* SessionFileFormat::fileFormatFromFile( const QString fileName) { if (ossnFileFormat.isMyFileFormat(fileName)) return &ossnFileFormat; return NULL; } SessionFileFormat* SessionFileFormat::fileFormatFromType( const QString fileType) { if (ossnFileFormat.isMyFileType(fileType)) return &ossnFileFormat; return NULL; } void SessionFileFormat::cancel() { stop_ = true; } void SessionFileFormat::run() { if (op_ == kOpenFile) result_ = open(fileName_, *openSession_, *error_); else if (op_ == kSaveFile) result_ = save(*saveSession_, fileName_, *error_); } ostinato-0.9/common/sessionfileformat.h000066400000000000000000000043471321315675200204340ustar00rootroot00000000000000/* Copyright (C) 2016 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _SESSION_FILE_FORMAT_H #define _SESSION_FILE_FORMAT_H #include "fileformat.pb.h" #include "protocol.pb.h" #include #include class QDialog; class SessionFileFormat : public QThread { Q_OBJECT public: enum Operation { kOpenFile, kSaveFile }; SessionFileFormat(); virtual ~SessionFileFormat(); virtual bool open(const QString fileName, OstProto::SessionContent &session, QString &error) = 0; virtual bool save(const OstProto::SessionContent &session, const QString fileName, QString &error) = 0; virtual QDialog* openOptionsDialog(); virtual QDialog* saveOptionsDialog(); void openAsync(const QString fileName, OstProto::SessionContent &session, QString &error); void saveAsync(const OstProto::SessionContent &session, const QString fileName, QString &error); bool result(); static QStringList supportedFileTypes(Operation op); static SessionFileFormat* fileFormatFromFile(const QString fileName); static SessionFileFormat* fileFormatFromType(const QString fileType); virtual bool isMyFileFormat(const QString fileName) = 0; virtual bool isMyFileType(const QString fileType) = 0; signals: void status(QString text); void target(int value); void progress(int value); public slots: void cancel(); protected: void run(); bool stop_; private: QString fileName_; OstProto::SessionContent *openSession_; const OstProto::SessionContent *saveSession_; QString *error_; Operation op_; bool result_; }; #endif ostinato-0.9/common/sign.cpp000066400000000000000000000127621321315675200161730ustar00rootroot00000000000000/* Copyright (C) 2016 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "sign.h" SignProtocol::SignProtocol(StreamBase *stream, AbstractProtocol *parent) : AbstractProtocol(stream, parent) { } SignProtocol::~SignProtocol() { } AbstractProtocol* SignProtocol::createInstance(StreamBase *stream, AbstractProtocol *parent) { return new SignProtocol(stream, parent); } quint32 SignProtocol::protocolNumber() const { return OstProto::Protocol::kSignFieldNumber; } void SignProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const { protocol.MutableExtension(OstProto::sign)->CopyFrom(data); protocol.mutable_protocol_id()->set_id(protocolNumber()); } void SignProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) { if (protocol.protocol_id().id() == protocolNumber() && protocol.HasExtension(OstProto::sign)) data.MergeFrom(protocol.GetExtension(OstProto::sign)); } QString SignProtocol::name() const { return QString("Signature"); } QString SignProtocol::shortName() const { return QString("SIGN"); } int SignProtocol::fieldCount() const { return sign_fieldCount; } AbstractProtocol::FieldFlags SignProtocol::fieldFlags(int index) const { AbstractProtocol::FieldFlags flags; flags = AbstractProtocol::fieldFlags(index); switch (index) { case sign_magic: case sign_tlv_guid: case sign_tlv_end: break; default: qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, index); break; } return flags; } QVariant SignProtocol::fieldData(int index, FieldAttrib attrib, int streamIndex) const { switch (index) { case sign_magic: { switch(attrib) { case FieldName: return QString("Magic"); case FieldValue: return kSignMagic; case FieldTextValue: return QString("%1").arg(kSignMagic); case FieldFrameValue: { QByteArray fv; fv.resize(4); qToBigEndian(kSignMagic, (uchar*) fv.data()); return fv; } default: break; } break; } case sign_tlv_guid: { quint32 guid = data.stream_guid() & 0xFFFFFF; switch(attrib) { case FieldName: return QString("Stream GUID"); case FieldValue: return guid; case FieldTextValue: return QString("%1").arg(guid); case FieldFrameValue: { QByteArray fv; fv.resize(4); fv[0] = (guid >> 16) & 0xff; fv[1] = (guid >> 8) & 0xff; fv[2] = (guid >> 0) & 0xff; fv[3] = kTypeLenGuid; return fv; } default: break; } break; } case sign_tlv_end: { switch(attrib) { case FieldName: return QString("End TLV"); case FieldValue: return 0; case FieldTextValue: return QString("NA"); case FieldFrameValue: return QByteArray(1, kTypeLenEnd); default: break; } break; } default: qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, index); break; } return AbstractProtocol::fieldData(index, attrib, streamIndex); } bool SignProtocol::setFieldData(int index, const QVariant &value, FieldAttrib attrib) { bool isOk = false; if (attrib != FieldValue) goto _exit; switch (index) { case sign_tlv_guid: { uint guid = value.toUInt(&isOk); if (isOk) data.set_stream_guid(guid & 0xFFFFFF); break; } default: qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, index); break; } _exit: return isOk; } quint32 SignProtocol::magic() { return kSignMagic; } bool SignProtocol::packetGuid(const uchar *pkt, int pktLen, uint *guid) { const uchar *p = pkt + pktLen - sizeof(kSignMagic); quint32 magic = qFromBigEndian(p); if (magic != kSignMagic) return false; p--; while (*p != kTypeLenEnd) { if (*p == kTypeLenGuid) { *guid = qFromBigEndian(p - 3) >> 8; return true; } p -= 1 + (*p >> 5); // move to next TLV } return false; } ostinato-0.9/common/sign.h000066400000000000000000000051321321315675200156310ustar00rootroot00000000000000/* Copyright (C) 2016 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _SIGN_H #define _SIGN_H #include "abstractprotocol.h" #include "sign.pb.h" /* Sign Protocol is expected at the end of the frame (just before the Eth FCS) ---+--------+-------+ . . .| TLV(s) | Magic | | (8+) | (32) | ---+--------+-------+ Figures in brackets represent field width in bits TLVs are encoded as +-------+-----+------+ | Value | Len | Type | | (...) | (3) | (5) | +-------+-----+------+ Len does NOT include the one byte of TypeLen Size of the value field varies between 0 to 7 bytes Defined TLVs Type = 0, Len = 0 (0x00): End of TLVs Type = 1, Len = 3 (0x61): Stream GUID */ class SignProtocol : public AbstractProtocol { public: enum samplefield { // Frame Fields sign_tlv_end = 0, sign_tlv_guid, sign_magic, // Meta Fields // - None sign_fieldCount }; SignProtocol(StreamBase *stream, AbstractProtocol *parent = 0); virtual ~SignProtocol(); static AbstractProtocol* createInstance(StreamBase *stream, AbstractProtocol *parent = 0); virtual quint32 protocolNumber() const; virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); virtual QString name() const; virtual QString shortName() const; virtual int fieldCount() const; virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; virtual QVariant fieldData(int index, FieldAttrib attrib, int streamIndex = 0) const; virtual bool setFieldData(int index, const QVariant &value, FieldAttrib attrib = FieldValue); static quint32 magic(); static bool packetGuid(const uchar *pkt, int pktLen, uint *guid); private: static const quint32 kSignMagic = 0x1d10c0da; // coda! (unicode - 0x1d10c) static const quint8 kTypeLenEnd = 0x00; static const quint8 kTypeLenGuid = 0x61; OstProto::Sign data; }; #endif ostinato-0.9/common/sign.proto000066400000000000000000000015111321315675200165420ustar00rootroot00000000000000/* Copyright (C) 2016 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ import "protocol.proto"; package OstProto; // Sign Protocol message Sign { optional uint32 stream_guid = 1; } extend Protocol { optional Sign sign = 105; } ostinato-0.9/common/sign.ui000066400000000000000000000024101321315675200160130ustar00rootroot00000000000000 Sign 0 0 400 64 Form Stream GUID guid Qt::Horizontal 40 20 Qt::Vertical 20 40 ostinato-0.9/common/signconfig.cpp000066400000000000000000000024721321315675200173560ustar00rootroot00000000000000/* Copyright (C) 2016 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "signconfig.h" #include "sign.h" SignConfigForm::SignConfigForm(QWidget *parent) : AbstractProtocolConfigForm(parent) { setupUi(this); } SignConfigForm::~SignConfigForm() { } SignConfigForm* SignConfigForm::createInstance() { return new SignConfigForm; } void SignConfigForm::loadWidget(AbstractProtocol *proto) { guid->setText( proto->fieldData( SignProtocol::sign_tlv_guid, AbstractProtocol::FieldValue ).toString()); } void SignConfigForm::storeWidget(AbstractProtocol *proto) { proto->setFieldData( SignProtocol::sign_tlv_guid, guid->text()); } ostinato-0.9/common/signconfig.h000066400000000000000000000021641321315675200170210ustar00rootroot00000000000000/* Copyright (C) 2016 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _SIGN_CONFIG_H #define _SIGN_CONFIG_H #include "abstractprotocolconfig.h" #include "ui_sign.h" class SignConfigForm : public AbstractProtocolConfigForm, private Ui::Sign { Q_OBJECT public: SignConfigForm(QWidget *parent = 0); virtual ~SignConfigForm(); static SignConfigForm* createInstance(); virtual void loadWidget(AbstractProtocol *proto); virtual void storeWidget(AbstractProtocol *proto); private slots: }; #endif ostinato-0.9/common/snap.cpp000066400000000000000000000147371321315675200162000ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "snap.h" quint32 kStdOui = 0x000000; SnapProtocol::SnapProtocol(StreamBase *stream, AbstractProtocol *parent) : AbstractProtocol(stream, parent) { } SnapProtocol::~SnapProtocol() { } AbstractProtocol* SnapProtocol::createInstance(StreamBase *stream, AbstractProtocol *parent) { return new SnapProtocol(stream, parent); } quint32 SnapProtocol::protocolNumber() const { return OstProto::Protocol::kSnapFieldNumber; } void SnapProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const { protocol.MutableExtension(OstProto::snap)->CopyFrom(data); protocol.mutable_protocol_id()->set_id(protocolNumber()); } void SnapProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) { if (protocol.protocol_id().id() == protocolNumber() && protocol.HasExtension(OstProto::snap)) data.MergeFrom(protocol.GetExtension(OstProto::snap)); } QString SnapProtocol::name() const { return QString("SubNetwork Access Protocol"); } QString SnapProtocol::shortName() const { return QString("SNAP"); } AbstractProtocol::ProtocolIdType SnapProtocol::protocolIdType() const { return ProtocolIdEth; } quint32 SnapProtocol::protocolId(ProtocolIdType type) const { switch(type) { case ProtocolIdLlc: return 0xAAAA03; default: break; } return AbstractProtocol::protocolId(type); } int SnapProtocol::fieldCount() const { return snap_fieldCount; } AbstractProtocol::FieldFlags SnapProtocol::fieldFlags(int index) const { AbstractProtocol::FieldFlags flags; flags = AbstractProtocol::fieldFlags(index); switch (index) { case snap_oui: case snap_type: break; case snap_is_override_oui: case snap_is_override_type: flags &= ~FrameField; flags |= MetaField; break; default: break; } return flags; } QVariant SnapProtocol::fieldData(int index, FieldAttrib attrib, int streamIndex) const { switch (index) { case snap_oui: switch(attrib) { case FieldName: return QString("OUI"); case FieldValue: { quint32 oui = data.is_override_oui() ? data.oui() : kStdOui; return oui; } case FieldTextValue: { quint32 oui = data.is_override_oui() ? data.oui() : kStdOui; return QString("%1").arg(oui, 6, BASE_HEX, QChar('0')); } case FieldFrameValue: { quint32 oui = data.is_override_oui() ? data.oui() : kStdOui; QByteArray fv; fv.resize(4); qToBigEndian(oui, (uchar*) fv.data()); fv.remove(0, 1); return fv; } default: break; } break; case snap_type: { quint16 type; switch(attrib) { case FieldName: return QString("Type"); case FieldValue: type = data.is_override_type() ? data.type() : payloadProtocolId(ProtocolIdEth); return type; case FieldTextValue: type = data.is_override_type() ? data.type() : payloadProtocolId(ProtocolIdEth); return QString("%1").arg(type, 4, BASE_HEX, QChar('0')); case FieldFrameValue: { QByteArray fv; fv.resize(2); type = data.is_override_type() ? data.type() : payloadProtocolId(ProtocolIdEth); qToBigEndian(type, (uchar*) fv.data()); return fv; } default: break; } break; } // Meta fields case snap_is_override_oui: { switch(attrib) { case FieldValue: return data.is_override_oui(); default: break; } break; } case snap_is_override_type: { switch(attrib) { case FieldValue: return data.is_override_type(); default: break; } break; } default: qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, index); break; } return AbstractProtocol::fieldData(index, attrib, streamIndex); } bool SnapProtocol::setFieldData(int index, const QVariant &value, FieldAttrib attrib) { bool isOk = false; if (attrib != FieldValue) return false; switch (index) { case snap_oui: { uint oui = value.toUInt(&isOk); if (isOk) data.set_oui(oui); break; } case snap_type: { uint type = value.toUInt(&isOk); if (isOk) data.set_type(type); break; } case snap_is_override_oui: { bool ovr = value.toBool(); data.set_is_override_oui(ovr); isOk = true; break; } case snap_is_override_type: { bool ovr = value.toBool(); data.set_is_override_type(ovr); isOk = true; break; } default: qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, index); break; } return isOk; } ostinato-0.9/common/snap.h000066400000000000000000000036331321315675200156360ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _SNAP_H #define _SNAP_H #include "abstractprotocol.h" #include "snap.pb.h" class SnapProtocol : public AbstractProtocol { public: enum snapfield { snap_oui = 0, snap_type, // Meta fields snap_is_override_oui, snap_is_override_type, snap_fieldCount }; SnapProtocol(StreamBase *stream, AbstractProtocol *parent = 0); virtual ~SnapProtocol(); static AbstractProtocol* createInstance(StreamBase *stream, AbstractProtocol *parent = 0); virtual quint32 protocolNumber() const; virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); virtual QString name() const; virtual QString shortName() const; virtual ProtocolIdType protocolIdType() const; virtual quint32 protocolId(ProtocolIdType type) const; virtual int fieldCount() const; virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; virtual QVariant fieldData(int index, FieldAttrib attrib, int streamIndex = 0) const; virtual bool setFieldData(int index, const QVariant &value, FieldAttrib attrib = FieldValue); private: OstProto::Snap data; }; #endif ostinato-0.9/common/snap.proto000066400000000000000000000016361321315675200165530ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ import "protocol.proto"; package OstProto; message Snap { optional bool is_override_oui = 3; optional bool is_override_type = 4; optional uint32 oui = 1; optional uint32 type = 2; } extend Protocol { optional Snap snap = 203; } ostinato-0.9/common/snap.ui000066400000000000000000000053221321315675200160210ustar00rootroot00000000000000 snap 0 0 268 98 Form SNAP OUI false >HH HH HH; Type false >HH HH; Qt::Horizontal 40 20 Qt::Vertical 20 40 cbOverrideOui toggled(bool) leOui setEnabled(bool) 49 42 68 43 cbOverrideType toggled(bool) leType setEnabled(bool) 161 34 183 33 ostinato-0.9/common/snapconfig.cpp000066400000000000000000000043121321315675200173520ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "snapconfig.h" #include "snap.h" SnapConfigForm::SnapConfigForm(QWidget *parent) : AbstractProtocolConfigForm(parent) { setupUi(this); } SnapConfigForm::~SnapConfigForm() { } SnapConfigForm* SnapConfigForm::createInstance() { return new SnapConfigForm; } void SnapConfigForm::loadWidget(AbstractProtocol *proto) { cbOverrideOui->setChecked( proto->fieldData( SnapProtocol::snap_is_override_oui, AbstractProtocol::FieldValue ).toBool()); leOui->setText(uintToHexStr( proto->fieldData( SnapProtocol::snap_oui, AbstractProtocol::FieldValue ).toUInt(), 3)); cbOverrideType->setChecked( proto->fieldData( SnapProtocol::snap_is_override_type, AbstractProtocol::FieldValue ).toBool()); leType->setText(uintToHexStr( proto->fieldData( SnapProtocol::snap_type, AbstractProtocol::FieldValue ).toUInt(), 2)); } void SnapConfigForm::storeWidget(AbstractProtocol *proto) { proto->setFieldData( SnapProtocol::snap_is_override_oui, cbOverrideOui->isChecked()); proto->setFieldData( SnapProtocol::snap_oui, hexStrToUInt(leOui->text())); proto->setFieldData( SnapProtocol::snap_is_override_type, cbOverrideType->isChecked()); proto->setFieldData( SnapProtocol::snap_type, hexStrToUInt(leType->text())); } ostinato-0.9/common/snapconfig.h000066400000000000000000000021451321315675200170210ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _SNAP_CONFIG_H #define _SNAP_CONFIG_H #include "abstractprotocolconfig.h" #include "ui_snap.h" class SnapConfigForm : public AbstractProtocolConfigForm, private Ui::snap { Q_OBJECT public: SnapConfigForm(QWidget *parent = 0); virtual ~SnapConfigForm(); static SnapConfigForm* createInstance(); virtual void loadWidget(AbstractProtocol *proto); virtual void storeWidget(AbstractProtocol *proto); }; #endif ostinato-0.9/common/spinboxdelegate.cpp000066400000000000000000000077251321315675200204130ustar00rootroot00000000000000/* Copyright (C) 2016 Srivats P. This file is part of "Ostinato" This 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 3 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, see This file incorporates work covered by the following copyright and permission notice: Copyright (C) 2015 The Qt Company Ltd. Contact: http://www.qt.io/licensing/ This file is part of the examples of the Qt Toolkit. $QT_BEGIN_LICENSE:BSD$ You may use this file under the terms of the BSD license as follows: "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 Qt Company Ltd 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." $QT_END_LICENSE$ */ //#include #include "spinboxdelegate.h" #include SpinBoxDelegate::SpinBoxDelegate(QObject *parent) : QItemDelegate(parent) { } QWidget *SpinBoxDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &/* option */, const QModelIndex &index) const { QSpinBox *editor = new QSpinBox(parent); editor->setMinimum(colMin_.contains(index.column()) ? colMin_.value(index.column()) : 0); editor->setMaximum(colMax_.contains(index.column()) ? colMax_.value(index.column()) : INT_MAX); return editor; } void SpinBoxDelegate::setColumnRange(int col, int min, int max) { colMin_.insert(col, min); colMax_.insert(col, max); } void SpinBoxDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const { int value = index.model()->data(index, Qt::EditRole).toInt(); QSpinBox *spinBox = static_cast(editor); spinBox->setValue(value); } void SpinBoxDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const { QSpinBox *spinBox = static_cast(editor); spinBox->interpretText(); int value = spinBox->value(); model->setData(index, value, Qt::EditRole); } void SpinBoxDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &/* index */) const { editor->setGeometry(option.rect); } ostinato-0.9/common/spinboxdelegate.h000066400000000000000000000065421321315675200200540ustar00rootroot00000000000000/* Copyright (C) 2016 Srivats P. This file is part of "Ostinato" This 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 3 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, see This file incorporates work covered by the following copyright and permission notice: Copyright (C) 2015 The Qt Company Ltd. Contact: http://www.qt.io/licensing/ This file is part of the examples of the Qt Toolkit. $QT_BEGIN_LICENSE:BSD$ You may use this file under the terms of the BSD license as follows: "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 Qt Company Ltd 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." $QT_END_LICENSE$ */ #ifndef _SPIN_BOX_DELEGATE_H #define _SPIN_BOX_DELEGATE_H #include #include #include #include #include #include class SpinBoxDelegate : public QItemDelegate { Q_OBJECT public: SpinBoxDelegate(QObject *parent = 0); void setColumnRange(int col, int min, int max); QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const; void setEditorData(QWidget *editor, const QModelIndex &index) const; void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const; void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const; private: QHash colMin_; QHash colMax_; }; #endif ostinato-0.9/common/stp.cpp000066400000000000000000000405521321315675200160370ustar00rootroot00000000000000/* Copyright (C) 2014 PLVision. This file is part of "Ostinato" This 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 3 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, see This module is developed by PLVision */ #include "stp.h" #include #define uintToMacStr(num) \ QString("%1").arg(num, 6 * 2, BASE_HEX, QChar('0')) \ .replace(QRegExp("([0-9a-fA-F]{2}\\B)"), "\\1:").toUpper() #define ONE_BIT(pos) ((unsigned int)(1 << (pos))) #define BITS(bit) (bit) #define BYTES(byte) (byte) #define BYTES_TO_BITS(byte) (byte * 8) #define STP_LLC 0x424203 StpProtocol::StpProtocol(StreamBase *stream, AbstractProtocol *parent) : AbstractProtocol(stream, parent) { } StpProtocol::~StpProtocol() { } AbstractProtocol* StpProtocol::createInstance(StreamBase *stream, AbstractProtocol *parent) { return new StpProtocol(stream, parent); } quint32 StpProtocol::protocolNumber() const { return OstProto::Protocol::kStpFieldNumber; } void StpProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const { protocol.MutableExtension(OstProto::stp)->CopyFrom(data_); protocol.mutable_protocol_id()->set_id(protocolNumber()); } void StpProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) { if (protocol.protocol_id().id() == protocolNumber() && protocol.HasExtension(OstProto::stp)) data_.MergeFrom(protocol.GetExtension(OstProto::stp)); } QString StpProtocol::name() const { return QString("Spanning Tree Protocol"); } QString StpProtocol::shortName() const { return QString("STP"); } quint32 StpProtocol::protocolId(ProtocolIdType type) const { switch(type) { case ProtocolIdLlc: return STP_LLC; default: break; } return AbstractProtocol::protocolId(type); } int StpProtocol::fieldCount() const { return stp_fieldCount; } AbstractProtocol::FieldFlags StpProtocol::fieldFlags(int index) const { AbstractProtocol::FieldFlags flags; flags = AbstractProtocol::fieldFlags(index); switch (index) { case stp_protocol_id: case stp_version_id: case stp_bpdu_type: case stp_flags: case stp_root_id: case stp_root_path_cost: case stp_bridge_id: case stp_port_id: case stp_message_age: case stp_max_age: case stp_hello_time: case stp_forward_delay: break; default: qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, index); break; } return flags; } QVariant StpProtocol::fieldData(int index, FieldAttrib attrib, int streamIndex) const { QString str[] = {"Topology Change", "Topology Change Acknowledgment"}; switch (index) { case stp_protocol_id: { switch (attrib) { case FieldName: return QString("Protocol Identifier"); case FieldValue: return data_.protocol_id(); case FieldTextValue: return QString("0x%1").arg(data_.protocol_id(), 4, BASE_HEX, QChar('0')); case FieldFrameValue: { QByteArray fv; fv.resize(BYTES(2)); qToBigEndian((quint16)data_.protocol_id(), (uchar*)fv.data()); return fv; } case FieldBitSize: return BYTES_TO_BITS(2); default: break; } break; } case stp_version_id: { switch (attrib) { case FieldName: return QString("Version Identifier"); case FieldValue: return data_.protocol_version_id(); case FieldTextValue: return QString("%1").arg( data_.protocol_version_id()); case FieldFrameValue: return QByteArray(1, (char)data_.protocol_version_id()); case FieldBitSize: return BYTES_TO_BITS(1); default: break; } break; } case stp_bpdu_type: { switch (attrib) { case FieldName: return QString("BPDU Type"); case FieldValue: return data_.bpdu_type(); case FieldTextValue: return QString("0x%1").arg(data_.bpdu_type(), 2, BASE_HEX, QChar('0')); case FieldFrameValue: return QByteArray(1, (char)data_.bpdu_type()); case FieldBitSize: return BYTES_TO_BITS(1); default: break; } break; } case stp_flags: { switch (attrib) { case FieldName: return QString("BPDU Flags"); case FieldValue: return data_.flags(); case FieldTextValue: { QString strTemp = "("; if((data_.flags() & ONE_BIT(0))) strTemp += str[0] + ", "; if((data_.flags() & ONE_BIT(7))) strTemp += str[1] + ", "; strTemp += ")"; strTemp.replace(", )", ")"); return strTemp; } case FieldFrameValue: return QByteArray(1, (char)data_.flags()); case FieldBitSize: return BYTES_TO_BITS(1); default: break; } break; } case stp_root_id: { switch (attrib) { case FieldName: return QString("Root Identifier"); case FieldValue: return (quint64) data_.root_id(); case FieldTextValue: { // Root ID contain two value: // Root ID Priority(first 2 bytes) // and Root ID MAC (last 6 bytes). (IEEE802.1D-2008) quint16 priority = ( data_.root_id() & 0xFFFF000000000000ULL) >> (BYTES_TO_BITS(6)); quint64 mac = data_.root_id() & 0x0000FFFFFFFFFFFFULL; return QString("Priority: %1 / MAC: %2") .arg(QString::number(priority), uintToMacStr(mac)); } case FieldFrameValue: { QByteArray fv; fv.resize(BYTES(8)); qToBigEndian((quint64)data_.root_id(), (uchar*)fv.data()); return fv; } case FieldBitSize: return BYTES_TO_BITS(8); default: break; } break; } case stp_root_path_cost: { switch (attrib) { case FieldName: return QString("Root Path Cost"); case FieldValue: return data_.root_path_cost(); case FieldTextValue: return QString("%1").arg(data_.root_path_cost()); case FieldFrameValue: { QByteArray fv; fv.resize(BYTES(4)); qToBigEndian(data_.root_path_cost(), (uchar*)fv.data()); return fv; } case FieldBitSize: return BYTES_TO_BITS(4); default: break; } break; } case stp_bridge_id: { switch (attrib) { case FieldName: return QString("Bridge Identifier"); case FieldValue: return (quint64) data_.bridge_id(); case FieldTextValue: { // Bridge ID contain two value: // Bridge ID Priority(first 2 bytes) // and Bridge ID MAC (last 6 bytes). (IEEE802.1D-2008) quint16 priority = (data_.bridge_id() & 0xFFFF000000000000ULL ) >> (BYTES_TO_BITS(6)); quint64 mac = data_.bridge_id() & 0x0000FFFFFFFFFFFFULL; return QString("Priority: %1 / MAC: %2").arg(QString::number(priority), uintToMacStr(mac)); } case FieldFrameValue: { QByteArray fv; fv.resize(BYTES(8)); qToBigEndian((quint64)data_.bridge_id(), (uchar*)fv.data()); return fv; } case FieldBitSize: return BYTES_TO_BITS(8); default: break; } break; } case stp_port_id: { switch (attrib) { case FieldName: return QString("Port Identifier"); case FieldValue: return data_.port_id(); case FieldTextValue: return QString("0x%1").arg(data_.port_id(), 4, BASE_HEX, QChar('0')); case FieldFrameValue: { QByteArray fv; fv.resize(BYTES(2)); qToBigEndian((quint16)data_.port_id(), (uchar*)fv.data()); return fv; } case FieldBitSize: return BYTES_TO_BITS(2); default: break; } break; } case stp_message_age: { switch (attrib) { case FieldName: return QString("Message Age"); case FieldValue: return data_.message_age(); case FieldTextValue: return QString("%1").arg(data_.message_age()); case FieldFrameValue: { QByteArray fv; fv.resize(BYTES(2)); qToBigEndian((quint16)(data_.message_age()), (uchar*)fv.data()); return fv; } case FieldBitSize: return BYTES_TO_BITS(2); default: break; } break; } case stp_max_age: { switch (attrib) { case FieldName: return QString("Max Age"); case FieldValue: return data_.max_age(); case FieldTextValue: return QString("%1").arg(data_.max_age()); case FieldFrameValue: { QByteArray fv; fv.resize(BYTES(2)); qToBigEndian((quint16)data_.max_age(), (uchar*)fv.data()); return fv; } case FieldBitSize: return BYTES_TO_BITS(2); default: break; } break; } case stp_hello_time: { switch (attrib) { case FieldName: return QString("Hello Time"); case FieldValue: return data_.hello_time(); case FieldTextValue: return QString("%1").arg(data_.hello_time()); case FieldFrameValue: { QByteArray fv; fv.resize(BYTES(2)); qToBigEndian((quint16)data_.hello_time(), (uchar*)fv.data()); return fv; } case FieldBitSize: return BYTES_TO_BITS(2); default: break; } break; } case stp_forward_delay: { switch (attrib) { case FieldName: return QString("Forward Delay"); case FieldValue: return data_.forward_delay(); case FieldTextValue: return QString("%1").arg(data_.forward_delay()); case FieldFrameValue: { QByteArray fv; fv.resize(BYTES(2)); qToBigEndian((quint16)data_.forward_delay(), (uchar*)fv.data()); return fv; } case FieldBitSize: return BYTES_TO_BITS(2); default: break; } break; } default: break; } return AbstractProtocol::fieldData(index, attrib, streamIndex); } bool StpProtocol::setFieldData(int index, const QVariant &value, FieldAttrib attrib) { bool isOk = false; if (attrib != FieldValue) return isOk; switch (index) { case stp_protocol_id: { quint16 protoId = value.toUInt(&isOk); if (isOk) data_.set_protocol_id(protoId); break; } case stp_version_id: { quint8 versionId = value.toUInt(&isOk); if (isOk) data_.set_protocol_version_id(versionId); break; } case stp_bpdu_type: { quint8 bpdu = value.toUInt(&isOk); if (isOk) data_.set_bpdu_type(bpdu); break; } case stp_flags: { quint8 flags = value.toUInt(&isOk); if (isOk) data_.set_flags(flags); break; } case stp_root_id: { quint64 rootId = value.toULongLong(&isOk); if (isOk) data_.set_root_id(rootId); break; } case stp_root_path_cost: { quint32 pathCost = value.toUInt(&isOk); if (isOk) data_.set_root_path_cost(pathCost); break; } case stp_bridge_id: { quint64 bridgeId = value.toULongLong(&isOk); if (isOk) data_.set_bridge_id(bridgeId); break; } case stp_port_id: { quint32 port_id = value.toUInt(&isOk); if (isOk) data_.set_port_id(port_id); break; } case stp_message_age: { quint32 messageAge = value.toUInt(&isOk); if (isOk) data_.set_message_age(messageAge); break; } case stp_max_age: { quint32 maxAge = value.toUInt(&isOk); if (isOk) data_.set_max_age(maxAge); break; } case stp_hello_time: { quint32 helloTime = value.toUInt(&isOk); if (isOk) data_.set_hello_time(helloTime); break; } case stp_forward_delay: { quint32 forwardDelay = value.toUInt(&isOk); if (isOk) data_.set_forward_delay(forwardDelay); break; } default: break; } return isOk; } ostinato-0.9/common/stp.h000066400000000000000000000105031321315675200154750ustar00rootroot00000000000000/* Copyright (C) 2014 PLVision. This file is part of "Ostinato" This 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 3 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, see This module is developed by PLVision */ #ifndef _STP_H #define _STP_H #include "abstractprotocol.h" #include "stp.pb.h" /* Stp Protocol Frame Format - +------------------------------------+------------------+------------------+ | Protocol ID | Protocol VID | BPDU type | | (16) | (8) | (8) | +------------------+-----------------+------------------+------------------+ | Flags | Root Identifier ->| | (8) | (64) ->| +------------------+-------------------------------------------------------+ |-> Root Identifier ->| |-> (64) ->| +------------------+-------------------------------------------------------+ |-> Root Identifier| Root Path Cost ->| |-> (8) | (32) ->| +------------------+-------------------------------------------------------+ |-> Root Path Cost | Bridge Identifier ->| |-> (32) | (64) ->| +------------------+-------------------------------------------------------+ |-> Bridge Identifier ->| |-> (64) ->| +------------------+--------------------------------- --+------------------+ |-> Bridge Identif.| Port Identifier | Message Age ->| |-> (64) | (16) | (16) ->| +------------------+------------------------------------+------------------+ |-> Message Age | Max Age | Hello Time ->| |-> (16) | (16) | (16) ->| +------------------+------------------------------------+------------------+ |-> Hello Time | Forward delay | |-> (16) | (16) | +------------------+------------------------------------+ Figures in brackets represent field width in bits */ class StpProtocol : public AbstractProtocol { public: enum stpfield { stp_protocol_id = 0, stp_version_id, stp_bpdu_type, stp_flags, stp_root_id, stp_root_path_cost, stp_bridge_id, stp_port_id, stp_message_age, stp_max_age, stp_hello_time, stp_forward_delay, stp_fieldCount }; StpProtocol(StreamBase *stream, AbstractProtocol *parent = 0); virtual ~StpProtocol(); static AbstractProtocol* createInstance(StreamBase *stream, AbstractProtocol *parent = 0); virtual quint32 protocolNumber() const; virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); virtual quint32 protocolId(ProtocolIdType type) const; virtual QString name() const; virtual QString shortName() const; virtual int fieldCount() const; virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; virtual QVariant fieldData(int index, FieldAttrib attrib, int streamIndex = 0) const; virtual bool setFieldData(int index, const QVariant &value, FieldAttrib attrib = FieldValue); private: OstProto::Stp data_; }; #endif ostinato-0.9/common/stp.proto000066400000000000000000000025671321315675200164240ustar00rootroot00000000000000/* Copyright (C) 2014 PLVision. This file is part of "Ostinato" This 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 3 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, see This module is developed by PLVision */ import "protocol.proto"; package OstProto; // Spanning Tree Protocol message Stp { optional uint32 protocol_id = 1 [default = 0x0000]; optional uint32 protocol_version_id = 2 [default = 0x00]; optional uint32 bpdu_type = 3 [default = 0x00]; optional uint32 flags = 4; optional uint64 root_id = 5; optional uint32 root_path_cost = 6; optional uint64 bridge_id = 7; optional uint32 port_id = 8; optional uint32 message_age = 9; optional uint32 max_age = 10; optional uint32 hello_time = 11; optional uint32 forward_delay = 12; } extend Protocol { optional Stp stp = 209; } ostinato-0.9/common/stp.ui000066400000000000000000000354211321315675200156710ustar00rootroot00000000000000 Stp 0 0 615 378 Form 0 Protocol Identifier Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter true 5 0 Version Identifier Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter ui_version_id true 3 0 3 BPDU Type Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter 0 0 TC TCA Qt::LeftToRight BPDU Flags Root Identifier 9 9 0 0 >HH HH HH HH HH HH; 17 Priority 5 MAC Address Path Cost 0 0 10 0 0 Bridge Identifier 0 0 >HH HH HH HH HH HH; Priority MAC Address 5 Port Identifier 9 9 3 Number Priority 3 Timers Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter false 0 9 0 Message Age (1/256s) 5 Max Age (1/256s) 5 0 0 Hello Time (1/256s) 5 Forward Delay (1/256s) 5 Qt::Vertical 20 40 groupBox_4 ui_protocol_id ui_version_id ui_bpdu_type ui_flags_tc_check ui_flags_tca_check ui_root_id_priority ui_root_id ui_root_path_cost ui_bridge_id_priority ui_bridge_id ui_port_id_priority ui_port_id_number ui_message_age ui_max_age ui_hello_time ui_forward_delay ostinato-0.9/common/stpconfig.cpp000066400000000000000000000204171321315675200172230ustar00rootroot00000000000000/* Copyright (C) 2014 PLVision. This file is part of "Ostinato" This 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 3 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, see This module is developed by PLVision */ #include "stpconfig.h" #include "stp.h" #include #include #define ONE_BYTE_MAX 255 #define TWO_BYTE_MAX 65535 #define FOUR_BYTE_MAX 4294967295U #define BIT_0 0 #define BIT_7 7 #define ONE_BIT(pos) ((unsigned int)(1 << (pos))) #define BYTES(byte) (byte * sizeof(unsigned char)) #define STR_BYTES_LEN(len) (BYTES(len) * 2) class UNumberValidator : public QValidator { private: quint64 min_; quint64 max_; public: UNumberValidator(quint64 min, quint64 max, QObject * parent = 0) : QValidator(parent), min_(min), max_(max){} virtual ~UNumberValidator(){} virtual QValidator::State validate(QString& input, int& /*pos*/) const { QValidator::State state = QValidator::Acceptable; quint64 val = input.toULongLong(); if(val < min_ || val > max_) state = QValidator::Invalid; return state; } }; StpConfigForm::StpConfigForm(QWidget *parent) : AbstractProtocolConfigForm(parent) { QRegExp reMac("([0-9,a-f,A-F]{2,2}[:-]){5,5}[0-9,a-f,A-F]{2,2}"); setupUi(this); QRegExpValidator *validateMACAddress = new QRegExpValidator(reMac, this); UNumberValidator *validateByte = new UNumberValidator(0, ONE_BYTE_MAX, this); UNumberValidator *validate2Byte = new UNumberValidator(0, TWO_BYTE_MAX, this); UNumberValidator *validate4Byte = new UNumberValidator(0, FOUR_BYTE_MAX, this); ui_protocol_id->setValidator(validate2Byte); ui_version_id->setValidator(validateByte); ui_bpdu_type->setValidator(validateByte); ui_root_id_priority->setValidator(validate2Byte); ui_root_id->setValidator(validateMACAddress); ui_root_path_cost->setValidator(validate4Byte); ui_bridge_id_priority->setValidator(validate2Byte); ui_bridge_id->setValidator(validateMACAddress); ui_port_id_priority->setValidator(validateByte); ui_port_id_number->setValidator(validateByte); ui_message_age->setValidator(validate2Byte); ui_max_age->setValidator(validate2Byte); ui_hello_time->setValidator(validate2Byte); ui_forward_delay->setValidator(validate2Byte); } StpConfigForm::~StpConfigForm() { } StpConfigForm* StpConfigForm::createInstance() { return new StpConfigForm; } void StpConfigForm::loadWidget(AbstractProtocol *proto) { bool isOk; ui_protocol_id->setText( proto->fieldData( StpProtocol::stp_protocol_id, AbstractProtocol::FieldValue ).toString()); ui_version_id->setText( proto->fieldData( StpProtocol::stp_version_id, AbstractProtocol::FieldValue ).toString()); ui_bpdu_type->setText( proto->fieldData( StpProtocol::stp_bpdu_type, AbstractProtocol::FieldValue ).toString()); quint8 flags = proto->fieldData( StpProtocol::stp_flags, AbstractProtocol::FieldValue ).toUInt(); ui_flags_tc_check->setChecked(flags & ONE_BIT(BIT_0)); ui_flags_tca_check->setChecked(flags & ONE_BIT(BIT_7)); // root priority value stored as the first two bytes of stp_root_id // and the last 6 bytes are root MAC address (IEEE802.1D-2008) quint64 rootId = proto->fieldData( StpProtocol::stp_root_id, AbstractProtocol::FieldValue ).toULongLong(&isOk); ui_root_id->setText( QString::number(rootId & 0x0000FFFFFFFFFFFFULL, BASE_HEX)); ui_root_id_priority->setText(QString::number(rootId >> 48)); ui_root_path_cost->setText( proto->fieldData( StpProtocol::stp_root_path_cost, AbstractProtocol::FieldValue ).toString()); // bridge priority value stored as the first two bytes of stp_bridge_id // and the last 6 bytes are bridge MAC address (IEEE802.1D-2008) quint64 bridgeId = proto->fieldData( StpProtocol::stp_bridge_id, AbstractProtocol::FieldValue ).toULongLong(&isOk); ui_bridge_id->setText( QString::number(bridgeId & 0x0000FFFFFFFFFFFFULL, BASE_HEX)); ui_bridge_id_priority->setText(QString::number(bridgeId >> 48)); // port priority is a first byte of stp_port_id field // and port ID is a second byte (IEEE802.1D-2008) uint portId = proto->fieldData( StpProtocol::stp_port_id, AbstractProtocol::FieldValue ).toUInt(&isOk); ui_port_id_priority->setText(QString::number(portId >> 8)); ui_port_id_number->setText(QString::number(portId & ONE_BYTE_MAX)); ui_message_age->setText( proto->fieldData( StpProtocol::stp_message_age, AbstractProtocol::FieldValue ).toString()); ui_max_age->setText( proto->fieldData( StpProtocol::stp_max_age, AbstractProtocol::FieldValue ).toString()); ui_hello_time->setText( proto->fieldData( StpProtocol::stp_hello_time, AbstractProtocol::FieldValue ).toString()); ui_forward_delay->setText( proto->fieldData( StpProtocol::stp_forward_delay, AbstractProtocol::FieldValue ).toString()); } void StpConfigForm::storeWidget(AbstractProtocol *proto) { bool isOk; proto->setFieldData( StpProtocol::stp_protocol_id, QString("%1").arg( ui_protocol_id->text().toUInt(&isOk) & TWO_BYTE_MAX)); proto->setFieldData( StpProtocol::stp_version_id, ui_version_id->text()); proto->setFieldData( StpProtocol::stp_bpdu_type, ui_bpdu_type->text()); char flags = 0; if (ui_flags_tc_check->isChecked()) flags = flags | ONE_BIT(BIT_0); if (ui_flags_tca_check->isChecked()) flags = flags | ONE_BIT(BIT_7); proto->setFieldData(StpProtocol::stp_flags, flags); // root priority value stored as the first two bytes of stp_root_id // and the last 6 bytes are root MAC address (IEEE802.1D-2008) quint64 rootIdPrio = ui_root_id_priority->text() .toULongLong(&isOk) & TWO_BYTE_MAX; quint64 rootId = hexStrToUInt64( ui_root_id->text()) | rootIdPrio << 48; proto->setFieldData(StpProtocol::stp_root_id, rootId); proto->setFieldData( StpProtocol::stp_root_path_cost, ui_root_path_cost->text()); // bridge priority value stored as the first two bytes of stp_bridge_id // and the last 6 bytes are bridge MAC address (IEEE802.1D-2008) quint64 bridgeIdPrio = ui_bridge_id_priority->text().toULongLong(&isOk) & TWO_BYTE_MAX; quint64 bridgeId = hexStrToUInt64(ui_bridge_id->text()) | bridgeIdPrio << 48; proto->setFieldData(StpProtocol::stp_bridge_id, bridgeId); // port priority is a first byte of stp_port_id field // and port ID is a second byte (IEEE802.1D-2008) ushort portIdPrio = ui_port_id_priority->text().toUInt(&isOk, BASE_DEC) & ONE_BYTE_MAX; ushort portId = ui_port_id_number->text().toUInt(&isOk, BASE_DEC) & ONE_BYTE_MAX; proto->setFieldData(StpProtocol::stp_port_id, portIdPrio << 8 | portId); // timers proto->setFieldData( StpProtocol::stp_message_age, ui_message_age->text().toUInt(&isOk, BASE_DEC) & TWO_BYTE_MAX); proto->setFieldData( StpProtocol::stp_max_age, QString("%1").arg( ui_max_age->text().toUInt(&isOk, BASE_DEC) & TWO_BYTE_MAX)); proto->setFieldData( StpProtocol::stp_hello_time, QString("%1").arg( ui_hello_time->text().toUInt(&isOk, BASE_DEC) & TWO_BYTE_MAX)); proto->setFieldData( StpProtocol::stp_forward_delay, QString("%1").arg( ui_forward_delay->text().toUInt(&isOk, BASE_DEC) & TWO_BYTE_MAX)); } ostinato-0.9/common/stpconfig.h000066400000000000000000000022331321315675200166640ustar00rootroot00000000000000/* Copyright (C) 2014 PLVision. This file is part of "Ostinato" This 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 3 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, see This module is developed by PLVision */ #ifndef _STP_CONFIG_H #define _STP_CONFIG_H #include "abstractprotocolconfig.h" #include "ui_stp.h" class StpConfigForm : public AbstractProtocolConfigForm, private Ui::Stp { Q_OBJECT public: StpConfigForm(QWidget *parent = 0); virtual ~StpConfigForm(); static StpConfigForm* createInstance(); virtual void loadWidget(AbstractProtocol *proto); virtual void storeWidget(AbstractProtocol *proto); }; #endif ostinato-0.9/common/stppdml.cpp000066400000000000000000000050051321315675200167060ustar00rootroot00000000000000/* Copyright (C) 2014 PLVision. This file is part of "Ostinato" This 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 3 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, see This module is developed by PLVision */ #include "stppdml.h" #include "stp.pb.h" #define ROOT_IDENTIFIER_POS 22 #define BRIDGE_IDENTIFIER_POS 34 #define BASE_DEC 10 #define BASE_HEX 16 PdmlStpProtocol::PdmlStpProtocol() { ostProtoId_ = OstProto::Protocol::kStpFieldNumber; fieldMap_.insert("stp.protocol", OstProto::Stp::kProtocolIdFieldNumber); fieldMap_.insert("stp.version", OstProto::Stp::kProtocolVersionIdFieldNumber); fieldMap_.insert("stp.type", OstProto::Stp::kBpduTypeFieldNumber); fieldMap_.insert("stp.flags", OstProto::Stp::kFlagsFieldNumber); fieldMap_.insert("stp.root.cost", OstProto::Stp::kRootPathCostFieldNumber); fieldMap_.insert("stp.port", OstProto::Stp::kPortIdFieldNumber); fieldMap_.insert("stp.msg_age", OstProto::Stp::kMessageAgeFieldNumber); fieldMap_.insert("stp.max_age", OstProto::Stp::kMaxAgeFieldNumber); fieldMap_.insert("stp.hello", OstProto::Stp::kHelloTimeFieldNumber); fieldMap_.insert("stp.forward", OstProto::Stp::kForwardDelayFieldNumber); } PdmlStpProtocol::~PdmlStpProtocol() { } PdmlProtocol* PdmlStpProtocol::createInstance() { return new PdmlStpProtocol(); } void PdmlStpProtocol::unknownFieldHandler( QString name, int pos, int /*size*/, const QXmlStreamAttributes &attributes, OstProto::Protocol *pbProto, OstProto::Stream* /*stream*/) { bool isOk; OstProto::Stp *stp = pbProto->MutableExtension(OstProto::stp); if ((name == "") && (pos == ROOT_IDENTIFIER_POS)) { stp->set_root_id(attributes.value("value").toString(). toULongLong(&isOk, BASE_HEX)); } if ((name == "") && (pos == BRIDGE_IDENTIFIER_POS)) { stp->set_bridge_id(attributes.value("value").toString(). toULongLong(&isOk, BASE_HEX)); } } ostinato-0.9/common/stppdml.h000066400000000000000000000022561321315675200163600ustar00rootroot00000000000000/* Copyright (C) 2014 PLVision. This file is part of "Ostinato" This 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 3 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, see This module is developed by PLVision */ #ifndef _STP_PDML_H #define _STP_PDML_H #include "pdmlprotocol.h" class PdmlStpProtocol : public PdmlProtocol { public: virtual ~PdmlStpProtocol(); static PdmlProtocol *createInstance(); void unknownFieldHandler(QString name, int pos, int size, const QXmlStreamAttributes &attributes, OstProto::Protocol *pbProto, OstProto::Stream* stream); protected: PdmlStpProtocol(); }; #endif ostinato-0.9/common/streambase.cpp000066400000000000000000000372631321315675200173640ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "streambase.h" #include "abstractprotocol.h" #include "protocollist.h" #include "protocollistiterator.h" #include "protocolmanager.h" extern ProtocolManager *OstProtocolManager; extern quint64 getDeviceMacAddress(int portId, int streamId, int frameIndex); extern quint64 getNeighborMacAddress(int portId, int streamId, int frameIndex); StreamBase::StreamBase(int portId) : portId_(portId), mStreamId(new OstProto::StreamId), mCore(new OstProto::StreamCore), mControl(new OstProto::StreamControl) { AbstractProtocol *proto; ProtocolListIterator *iter; mStreamId->set_id(0xFFFFFFFF); currentFrameProtocols = new ProtocolList; iter = createProtocolListIterator(); // By default newly created streams have the mac and payload protocols proto = OstProtocolManager->createProtocol( OstProto::Protocol::kMacFieldNumber, this); iter->insert(proto); qDebug("stream: mac = %p", proto); proto = OstProtocolManager->createProtocol( OstProto::Protocol::kPayloadFieldNumber, this); iter->insert(proto); qDebug("stream: payload = %p", proto); #ifndef QT_NO_DEBUG_OUTPUT { iter->toFront(); while (iter->hasNext()) { qDebug("{{%p}}", iter->next()); // qDebug("{{%p}: %d}", iter->peekNext(), iter->next()->protocolNumber()); } iter->toFront(); while (iter->hasNext()) { qDebug("{[%d]}", iter->next()->protocolNumber()); // qDebug("{{%p}: %d}", iter->peekNext(), iter->next()->protocolNumber()); } } #endif delete iter; } StreamBase::~StreamBase() { currentFrameProtocols->destroy(); delete currentFrameProtocols; delete mControl; delete mCore; delete mStreamId; } void StreamBase::protoDataCopyFrom(const OstProto::Stream &stream) { AbstractProtocol *proto; ProtocolListIterator *iter; mStreamId->CopyFrom(stream.stream_id()); mCore->CopyFrom(stream.core()); mControl->CopyFrom(stream.control()); currentFrameProtocols->destroy(); iter = createProtocolListIterator(); for (int i=0; i < stream.protocol_size(); i++) { int protoId = stream.protocol(i).protocol_id().id(); if (!OstProtocolManager->isRegisteredProtocol(protoId)) { qWarning("Skipping unregistered protocol %d", protoId); continue; } proto = OstProtocolManager->createProtocol(protoId, this); proto->commonProtoDataCopyFrom(stream.protocol(i)); proto->protoDataCopyFrom(stream.protocol(i)); iter->insert(proto); } delete iter; } void StreamBase::protoDataCopyInto(OstProto::Stream &stream) const { stream.mutable_stream_id()->CopyFrom(*mStreamId); stream.mutable_core()->CopyFrom(*mCore); stream.mutable_control()->CopyFrom(*mControl); stream.clear_protocol(); foreach (const AbstractProtocol* proto, *currentFrameProtocols) { OstProto::Protocol *p; p = stream.add_protocol(); proto->commonProtoDataCopyInto(*p); proto->protoDataCopyInto(*p); } } #if 0 ProtocolList StreamBase::frameProtocol() { return currentFrameProtocols; } void StreamBase::setFrameProtocol(ProtocolList protocolList) { //currentFrameProtocols.destroy(); currentFrameProtocols = protocolList; } #endif bool StreamBase::hasProtocol(quint32 protocolNumber) { foreach(const AbstractProtocol *proto, *currentFrameProtocols) if (proto->protocolNumber() == protocolNumber) return true; return false; } ProtocolListIterator* StreamBase::createProtocolListIterator() const { return new ProtocolListIterator(*currentFrameProtocols); } quint32 StreamBase::id() const { return mStreamId->id(); } bool StreamBase::setId(quint32 id) { mStreamId->set_id(id); return true; } quint32 StreamBase::ordinal() { return mCore->ordinal(); } bool StreamBase::setOrdinal(quint32 ordinal) { mCore->set_ordinal(ordinal); return true; } bool StreamBase::isEnabled() const { return mCore->is_enabled(); } bool StreamBase::setEnabled(bool flag) { mCore->set_is_enabled(flag); return true; } const QString StreamBase::name() const { return QString().fromStdString(mCore->name()); } bool StreamBase::setName(QString name) { mCore->set_name(name.toStdString()); return true; } StreamBase::FrameLengthMode StreamBase::lenMode() const { return (StreamBase::FrameLengthMode) mCore->len_mode(); } bool StreamBase::setLenMode(FrameLengthMode lenMode) { mCore->set_len_mode((OstProto::StreamCore::FrameLengthMode) lenMode); return true; } quint16 StreamBase::frameLen(int streamIndex) const { int pktLen; // Decide a frame length based on length mode switch(lenMode()) { case OstProto::StreamCore::e_fl_fixed: pktLen = mCore->frame_len(); break; case OstProto::StreamCore::e_fl_inc: pktLen = frameLenMin() + (streamIndex % (frameLenMax() - frameLenMin() + 1)); break; case OstProto::StreamCore::e_fl_dec: pktLen = frameLenMax() - (streamIndex % (frameLenMax() - frameLenMin() + 1)); break; case OstProto::StreamCore::e_fl_random: //! \todo (MED) This 'random' sequence is same across iterations pktLen = 64; // to avoid the 'maybe used uninitialized' warning qsrand(reinterpret_cast(this)); for (int i = 0; i <= streamIndex; i++) pktLen = qrand(); pktLen = frameLenMin() + (pktLen % (frameLenMax() - frameLenMin() + 1)); break; default: qWarning("Unhandled len mode %d. Using default 64", lenMode()); pktLen = 64; break; } return pktLen; } bool StreamBase::setFrameLen(quint16 frameLen) { mCore->set_frame_len(frameLen); return true; } quint16 StreamBase::frameLenMin() const { return mCore->frame_len_min(); } bool StreamBase::setFrameLenMin(quint16 frameLenMin) { mCore->set_frame_len_min(frameLenMin); return true; } quint16 StreamBase::frameLenMax() const { return mCore->frame_len_max(); } bool StreamBase::setFrameLenMax(quint16 frameLenMax) { mCore->set_frame_len_max(frameLenMax); return true; } /*! Convenience Function */ quint16 StreamBase::frameLenAvg() const { quint16 avgFrameLen; if (lenMode() == e_fl_fixed) avgFrameLen = frameLen(); else avgFrameLen = (frameLenMin() + frameLenMax())/2; return avgFrameLen; } StreamBase::SendUnit StreamBase::sendUnit() const { return (StreamBase::SendUnit) mControl->unit(); } bool StreamBase::setSendUnit(SendUnit sendUnit) { mControl->set_unit((OstProto::StreamControl::SendUnit) sendUnit); return true; } StreamBase::SendMode StreamBase::sendMode() const { return (StreamBase::SendMode) mControl->mode(); } bool StreamBase::setSendMode(SendMode sendMode) { mControl->set_mode( (OstProto::StreamControl::SendMode) sendMode); return true; } StreamBase::NextWhat StreamBase::nextWhat() const { return (StreamBase::NextWhat) mControl->next(); } bool StreamBase::setNextWhat(NextWhat nextWhat) { mControl->set_next((OstProto::StreamControl::NextWhat) nextWhat); return true; } quint32 StreamBase::numPackets() const { return (quint32) mControl->num_packets(); } bool StreamBase::setNumPackets(quint32 numPackets) { mControl->set_num_packets(numPackets); return true; } quint32 StreamBase::numBursts() const { return (quint32) mControl->num_bursts(); } bool StreamBase::setNumBursts(quint32 numBursts) { mControl->set_num_bursts(numBursts); return true; } quint32 StreamBase::burstSize() const { return (quint32) mControl->packets_per_burst(); } bool StreamBase::setBurstSize(quint32 packetsPerBurst) { mControl->set_packets_per_burst(packetsPerBurst); return true; } double StreamBase::packetRate() const { return (double) mControl->packets_per_sec(); } bool StreamBase::setPacketRate(double packetsPerSec) { mControl->set_packets_per_sec(packetsPerSec); return true; } double StreamBase::burstRate() const { return (double) mControl->bursts_per_sec(); } bool StreamBase::setBurstRate(double burstsPerSec) { mControl->set_bursts_per_sec(burstsPerSec); return true; } /*! Convenience Function */ double StreamBase::averagePacketRate() const { double avgPacketRate = 0; switch (sendUnit()) { case e_su_bursts: avgPacketRate = burstRate() * burstSize(); break; case e_su_packets: avgPacketRate = packetRate(); break; default: Q_ASSERT(false); // Unreachable!! } return avgPacketRate; } /*! Convenience Function */ bool StreamBase::setAveragePacketRate(double packetsPerSec) { switch (sendUnit()) { case e_su_bursts: setBurstRate(packetsPerSec/double(burstSize())); break; case e_su_packets: setPacketRate(packetsPerSec); break; default: Q_ASSERT(false); // Unreachable!! } return true; } bool StreamBase::isFrameVariable() const { ProtocolListIterator *iter; iter = createProtocolListIterator(); while (iter->hasNext()) { AbstractProtocol *proto; proto = iter->next(); if (proto->isProtocolFrameValueVariable()) goto _exit; } delete iter; return false; _exit: delete iter; return true; } bool StreamBase::isFrameSizeVariable() const { ProtocolListIterator *iter; iter = createProtocolListIterator(); while (iter->hasNext()) { AbstractProtocol *proto; proto = iter->next(); if (proto->isProtocolFrameSizeVariable()) goto _exit; } delete iter; return false; _exit: delete iter; return true; } int StreamBase::frameSizeVariableCount() const { int count = 1; switch(lenMode()) { case OstProto::StreamCore::e_fl_fixed: break; case OstProto::StreamCore::e_fl_inc: case OstProto::StreamCore::e_fl_dec: case OstProto::StreamCore::e_fl_random: count = frameLenMax() - frameLenMin() + 1; break; default: qWarning("%s: Unhandled len mode %d", __FUNCTION__, lenMode()); break; } return count; } int StreamBase::frameVariableCount() const { ProtocolListIterator *iter; quint64 frameCount = 1; iter = createProtocolListIterator(); while (iter->hasNext()) { AbstractProtocol *proto; int count; proto = iter->next(); count = proto->protocolFrameVariableCount(); // correct count for mis-behaving protocols if (count <= 0) count = 1; frameCount = AbstractProtocol::lcm(frameCount, count); } delete iter; return AbstractProtocol::lcm(frameCount, frameSizeVariableCount()); } // frameProtocolLength() returns the sum of all the individual protocol sizes // which may be different from frameLen() int StreamBase::frameProtocolLength(int frameIndex) const { int len = 0; ProtocolListIterator *iter = createProtocolListIterator(); while (iter->hasNext()) { AbstractProtocol *proto = iter->next(); len += proto->protocolFrameSize(frameIndex); } delete iter; return len; } int StreamBase::frameCount() const { int count = 0; switch (sendUnit()) { case e_su_packets: count = numPackets(); break; case e_su_bursts: count = numBursts() * burstSize(); break; default: Q_ASSERT(false); // unreachable } return count; } // Returns packet length - if bufMaxSize < frameLen(), returns truncated // length i.e. bufMaxSize int StreamBase::frameValue(uchar *buf, int bufMaxSize, int frameIndex) const { int maxSize, size, pktLen, len = 0; pktLen = frameLen(frameIndex); // pktLen is adjusted for CRC/FCS which will be added by the NIC pktLen -= kFcsSize; if (pktLen <= 0) return 0; maxSize = qMin(pktLen, bufMaxSize); ProtocolListIterator *iter; iter = createProtocolListIterator(); while (iter->hasNext()) { AbstractProtocol *proto; QByteArray ba; proto = iter->next(); ba = proto->protocolFrameValue(frameIndex); size = qMin(ba.size(), maxSize-len); memcpy(buf+len, ba.constData(), size); len += size; if (len == maxSize) break; } delete iter; // Pad with zero, if required and if we have space if (len < maxSize) { size = maxSize-len; memset(buf+len, 0, size); len += size; } return len; } quint64 StreamBase::deviceMacAddress(int frameIndex) const { return getDeviceMacAddress(portId_, int(mStreamId->id()), frameIndex); } quint64 StreamBase::neighborMacAddress(int frameIndex) const { return getNeighborMacAddress(portId_, int(mStreamId->id()), frameIndex); } /*! Checks for any potential errors with the packets generated by this stream. Returns true if no problems are found, false otherwise. Details of the error(s) are available in the INOUT param result All errors found are returned. However, each type of error is reported only once, even if multiple packets may have that error. */ bool StreamBase::preflightCheck(QStringList &result) const { bool pass = true; bool chkTrunc = true; bool chkJumbo = true; int count = isFrameSizeVariable() ? frameCount() : 1; for (int i = 0; i < count; i++) { int pktLen = frameLen(i); if (chkTrunc && (pktLen < (frameProtocolLength(i) + kFcsSize))) { result << QObject::tr("One or more frames may be truncated - " "frame length should be at least %1") .arg(frameProtocolLength(i) + kFcsSize); chkTrunc = false; pass = false; } if (chkJumbo && (pktLen > 1522)) { result << QObject::tr("Jumbo frames may be truncated or dropped " "if not supported by the hardware"); pass = false; } // Break out of loop if we've seen at least one instance of all // the above errors if (!chkTrunc && !chkJumbo) break; } if (frameCount() <= averagePacketRate() && nextWhat() != e_nw_goto_id) { result << QObject::tr("Only %L1 frames at the rate of " "%L2 frames/sec are configured to be transmitted. " "Transmission will last for only %L3 second - " "to transmit for a longer duration, " "increase the number of packets (bursts) and/or " "set the 'After this stream' action as 'Goto First'") .arg(frameCount()) .arg(averagePacketRate(), 0, 'f', 2) .arg(frameCount()/averagePacketRate(), 0, 'f'); pass = false; } return pass; } bool StreamBase::StreamLessThan(StreamBase* stream1, StreamBase* stream2) { return stream1->ordinal() < stream2->ordinal() ? true : false; } ostinato-0.9/common/streambase.h000066400000000000000000000074451321315675200170300ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _STREAM_BASE_H #define _STREAM_BASE_H #include #include #include "protocol.pb.h" const int kFcsSize = 4; class AbstractProtocol; class ProtocolList; class ProtocolListIterator; class StreamBase { public: StreamBase(int portId = -1); ~StreamBase(); void protoDataCopyFrom(const OstProto::Stream &stream); void protoDataCopyInto(OstProto::Stream &stream) const; bool hasProtocol(quint32 protocolNumber); ProtocolListIterator* createProtocolListIterator() const; //! \todo (LOW) should we have a copy constructor?? public: enum FrameLengthMode { e_fl_fixed, e_fl_inc, e_fl_dec, e_fl_random }; enum SendUnit { e_su_packets, e_su_bursts }; enum SendMode { e_sm_fixed, e_sm_continuous }; enum NextWhat { e_nw_stop, e_nw_goto_next, e_nw_goto_id }; quint32 id() const; bool setId(quint32 id); #if 0 // FIXME(HI): needed? quint32 portId() { return mCore->port_id();} bool setPortId(quint32 id) { mCore->set_port_id(id); return true;} #endif quint32 ordinal(); bool setOrdinal(quint32 ordinal); bool isEnabled() const; bool setEnabled(bool flag); const QString name() const ; bool setName(QString name) ; // Frame Length (includes FCS); FrameLengthMode lenMode() const; bool setLenMode(FrameLengthMode lenMode); quint16 frameLen(int streamIndex = 0) const; bool setFrameLen(quint16 frameLen); quint16 frameLenMin() const; bool setFrameLenMin(quint16 frameLenMin); quint16 frameLenMax() const; bool setFrameLenMax(quint16 frameLenMax); quint16 frameLenAvg() const; SendUnit sendUnit() const; bool setSendUnit(SendUnit sendUnit); SendMode sendMode() const; bool setSendMode(SendMode sendMode); NextWhat nextWhat() const; bool setNextWhat(NextWhat nextWhat); quint32 numPackets() const; bool setNumPackets(quint32 numPackets); quint32 numBursts() const; bool setNumBursts(quint32 numBursts); quint32 burstSize() const; bool setBurstSize(quint32 packetsPerBurst); double packetRate() const; bool setPacketRate(double packetsPerSec); double burstRate() const; bool setBurstRate(double burstsPerSec); double averagePacketRate() const; bool setAveragePacketRate(double packetsPerSec); bool isFrameVariable() const; bool isFrameSizeVariable() const; int frameSizeVariableCount() const; int frameVariableCount() const; int frameProtocolLength(int frameIndex) const; int frameCount() const; int frameValue(uchar *buf, int bufMaxSize, int frameIndex) const; quint64 deviceMacAddress(int frameIndex) const; quint64 neighborMacAddress(int frameIndex) const; bool preflightCheck(QStringList &result) const; static bool StreamLessThan(StreamBase* stream1, StreamBase* stream2); private: int portId_; OstProto::StreamId *mStreamId; OstProto::StreamCore *mCore; OstProto::StreamControl *mControl; ProtocolList *currentFrameProtocols; }; #endif ostinato-0.9/common/streamfileformat.cpp000066400000000000000000000057611321315675200206000ustar00rootroot00000000000000/* Copyright (C) 2011 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "streamfileformat.h" #include "ostmfileformat.h" #include "pcapfileformat.h" #include "pdmlfileformat.h" #include "pythonfileformat.h" #include StreamFileFormat::StreamFileFormat() { stop_ = false; } StreamFileFormat::~StreamFileFormat() { } QDialog* StreamFileFormat::openOptionsDialog() { return NULL; } QDialog* StreamFileFormat::saveOptionsDialog() { return NULL; } QStringList StreamFileFormat::supportedFileTypes(Operation op) { QStringList fileTypes; fileTypes << "Ostinato (*.ostm)" << "PCAP (*.pcap)" << "PDML (*.pdml)"; if (op == kSaveFile) fileTypes << "PythonScript (*.py)"; else if (op == kOpenFile) fileTypes << "All files (*)"; return fileTypes; } void StreamFileFormat::openAsync(const QString fileName, OstProto::StreamConfigList &streams, QString &error) { fileName_ = fileName; openStreams_ = &streams; error_ = &error; op_ = kOpenFile; stop_ = false; start(); } void StreamFileFormat::saveAsync( const OstProto::StreamConfigList streams, const QString fileName, QString &error) { saveStreams_ = streams; fileName_ = fileName; error_ = &error; op_ = kSaveFile; stop_ = false; start(); } bool StreamFileFormat::result() { return result_; } StreamFileFormat* StreamFileFormat::fileFormatFromFile( const QString fileName) { if (fileFormat.isMyFileFormat(fileName)) return &fileFormat; if (pdmlFileFormat.isMyFileFormat(fileName)) return &pdmlFileFormat; if (pcapFileFormat.isMyFileFormat(fileName)) return &pcapFileFormat; return NULL; } StreamFileFormat* StreamFileFormat::fileFormatFromType( const QString fileType) { if (fileFormat.isMyFileType(fileType)) return &fileFormat; if (pdmlFileFormat.isMyFileType(fileType)) return &pdmlFileFormat; if (pcapFileFormat.isMyFileType(fileType)) return &pcapFileFormat; if (pythonFileFormat.isMyFileType(fileType)) return &pythonFileFormat; return NULL; } void StreamFileFormat::cancel() { stop_ = true; } void StreamFileFormat::run() { if (op_ == kOpenFile) result_ = open(fileName_, *openStreams_, *error_); else if (op_ == kSaveFile) result_ = save(saveStreams_, fileName_, *error_); } ostinato-0.9/common/streamfileformat.h000066400000000000000000000043051321315675200202360ustar00rootroot00000000000000/* Copyright (C) 2011 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _STREAM_FILE_FORMAT_H #define _STREAM_FILE_FORMAT_H #include "protocol.pb.h" #include #include class QDialog; class StreamFileFormat : public QThread { Q_OBJECT public: enum Operation { kOpenFile, kSaveFile }; StreamFileFormat(); virtual ~StreamFileFormat(); virtual bool open(const QString fileName, OstProto::StreamConfigList &streams, QString &error) = 0; virtual bool save(const OstProto::StreamConfigList streams, const QString fileName, QString &error) = 0; virtual QDialog* openOptionsDialog(); virtual QDialog* saveOptionsDialog(); void openAsync(const QString fileName, OstProto::StreamConfigList &streams, QString &error); void saveAsync(const OstProto::StreamConfigList streams, const QString fileName, QString &error); bool result(); static QStringList supportedFileTypes(Operation op); static StreamFileFormat* fileFormatFromFile(const QString fileName); static StreamFileFormat* fileFormatFromType(const QString fileType); #if 0 bool isMyFileFormat(const QString fileName) = 0; bool isMyFileType(const QString fileType) = 0; #endif signals: void status(QString text); void target(int value); void progress(int value); public slots: void cancel(); protected: void run(); bool stop_; private: QString fileName_; OstProto::StreamConfigList *openStreams_; OstProto::StreamConfigList saveStreams_; QString *error_; Operation op_; bool result_; }; #endif ostinato-0.9/common/svlan.cpp000066400000000000000000000033711321315675200163520ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "svlan.h" #include "svlan.pb.h" SVlanProtocol::SVlanProtocol(StreamBase *stream, AbstractProtocol *parent) : VlanProtocol(stream, parent) { data.set_tpid(0x88a8); data.set_is_override_tpid(true); } SVlanProtocol::~SVlanProtocol() { } AbstractProtocol* SVlanProtocol::createInstance(StreamBase *stream, AbstractProtocol *parent) { return new SVlanProtocol(stream, parent); } quint32 SVlanProtocol::protocolNumber() const { return OstProto::Protocol::kSvlanFieldNumber; } void SVlanProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const { protocol.MutableExtension(OstProto::svlan)->CopyFrom(data); protocol.mutable_protocol_id()->set_id(protocolNumber()); } void SVlanProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) { if (protocol.protocol_id().id() == protocolNumber() && protocol.HasExtension(OstProto::svlan)) data.MergeFrom(protocol.GetExtension(OstProto::svlan)); } QString SVlanProtocol::name() const { return QString("SVlan"); } QString SVlanProtocol::shortName() const { return QString("SVlan"); } ostinato-0.9/common/svlan.h000066400000000000000000000023511321315675200160140ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _SVLAN_H #define _SVLAN_H #include "vlan.h" class SVlanProtocol : public VlanProtocol { public: SVlanProtocol(StreamBase *stream, AbstractProtocol *parent = 0); virtual ~SVlanProtocol(); static AbstractProtocol* createInstance(StreamBase *stream, AbstractProtocol *parent = 0); virtual quint32 protocolNumber() const; virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); virtual QString name() const; virtual QString shortName() const; }; #endif ostinato-0.9/common/svlan.proto000066400000000000000000000014271321315675200167330ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ import "protocol.proto"; import "vlan.proto"; package OstProto; extend Protocol { optional Vlan svlan = 204; } ostinato-0.9/common/svlanconfig.h000066400000000000000000000014351321315675200172040ustar00rootroot00000000000000/* Copyright (C) 2014 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _SVLAN_CONFIG_H #define _SVLAN_CONFIG_H #include "vlanconfig.h" typedef VlanConfigForm SVlanConfigForm; #endif ostinato-0.9/common/svlanpdml.cpp000066400000000000000000000074211321315675200172270ustar00rootroot00000000000000/* Copyright (C) 2011 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "svlanpdml.h" #include "eth2.pb.h" #include "svlan.pb.h" PdmlSvlanProtocol::PdmlSvlanProtocol() { ostProtoId_ = OstProto::Protocol::kSvlanFieldNumber; } PdmlProtocol* PdmlSvlanProtocol::createInstance() { return new PdmlSvlanProtocol(); } void PdmlSvlanProtocol::preProtocolHandler(QString /*name*/, const QXmlStreamAttributes& /*attributes*/, int /*expectedPos*/, OstProto::Protocol *pbProto, OstProto::Stream *stream) { OstProto::Vlan *svlan = pbProto->MutableExtension(OstProto::svlan); svlan->set_tpid(0x88a8); svlan->set_is_override_tpid(true); // If a eth2 protocol precedes svlan, we remove the eth2 protocol // 'coz the eth2.etherType is actually the svlan.tpid // // We assume that the current protocol is the last in the stream int index = stream->protocol_size() - 1; if ((index > 1) && (stream->protocol(index).protocol_id().id() == OstProto::Protocol::kSvlanFieldNumber) && (stream->protocol(index - 1).protocol_id().id() == OstProto::Protocol::kEth2FieldNumber)) { stream->mutable_protocol()->SwapElements(index, index - 1); Q_ASSERT(stream->protocol(index).protocol_id().id() == OstProto::Protocol::kEth2FieldNumber); stream->mutable_protocol()->RemoveLast(); } } void PdmlSvlanProtocol::unknownFieldHandler(QString name, int /*pos*/, int /*size*/, const QXmlStreamAttributes &attributes, OstProto::Protocol *pbProto, OstProto::Stream *stream) { if ((name == "ieee8021ad.id") || (name == "ieee8021ad.svid")) { bool isOk; OstProto::Vlan *svlan = pbProto->MutableExtension(OstProto::svlan); uint tag = attributes.value("unmaskedvalue").isEmpty() ? attributes.value("value").toString().toUInt(&isOk, kBaseHex) : attributes.value("unmaskedvalue").toString().toUInt(&isOk,kBaseHex); svlan->set_vlan_tag(tag); } else if (name == "ieee8021ad.cvid") { OstProto::Protocol *proto = stream->add_protocol(); proto->mutable_protocol_id()->set_id( OstProto::Protocol::kSvlanFieldNumber); OstProto::Vlan *svlan = proto->MutableExtension(OstProto::svlan); svlan->set_tpid(0x88a8); svlan->set_is_override_tpid(true); bool isOk; uint tag = attributes.value("unmaskedvalue").isEmpty() ? attributes.value("value").toString().toUInt(&isOk, kBaseHex) : attributes.value("unmaskedvalue").toString().toUInt(&isOk,kBaseHex); svlan->set_vlan_tag(tag); } else if (name == "ieee8021ah.etype") // yes 'ah' not 'ad' - not a typo! { OstProto::Protocol *proto = stream->add_protocol(); proto->mutable_protocol_id()->set_id( OstProto::Protocol::kEth2FieldNumber); bool isOk; OstProto::Eth2 *eth2 = proto->MutableExtension(OstProto::eth2); eth2->set_type(attributes.value("value") .toString().toUInt(&isOk, kBaseHex)); eth2->set_is_override_type(true); } } ostinato-0.9/common/svlanpdml.h000066400000000000000000000023721321315675200166740ustar00rootroot00000000000000/* Copyright (C) 2011 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _SVLAN_PDML_H #define _SVLAN_PDML_H #include "pdmlprotocol.h" class PdmlSvlanProtocol : public PdmlProtocol { public: static PdmlProtocol* createInstance(); virtual void preProtocolHandler(QString name, const QXmlStreamAttributes &attributes, int expectedPos, OstProto::Protocol *pbProto, OstProto::Stream *stream); virtual void unknownFieldHandler(QString name, int pos, int size, const QXmlStreamAttributes &attributes, OstProto::Protocol *pbProto, OstProto::Stream *stream); protected: PdmlSvlanProtocol(); }; #endif ostinato-0.9/common/tcp.cpp000066400000000000000000000415611321315675200160200ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "tcp.h" TcpProtocol::TcpProtocol(StreamBase *stream, AbstractProtocol *parent) : AbstractProtocol(stream, parent) { } TcpProtocol::~TcpProtocol() { } AbstractProtocol* TcpProtocol::createInstance(StreamBase *stream, AbstractProtocol *parent) { return new TcpProtocol(stream, parent); } quint32 TcpProtocol::protocolNumber() const { return OstProto::Protocol::kTcpFieldNumber; } void TcpProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const { protocol.MutableExtension(OstProto::tcp)->CopyFrom(data); protocol.mutable_protocol_id()->set_id(protocolNumber()); } void TcpProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) { if (protocol.protocol_id().id() == protocolNumber() && protocol.HasExtension(OstProto::tcp)) data.MergeFrom(protocol.GetExtension(OstProto::tcp)); } QString TcpProtocol::name() const { return QString("Transmission Control Protocol (state less)"); } QString TcpProtocol::shortName() const { return QString("TCP"); } AbstractProtocol::ProtocolIdType TcpProtocol::protocolIdType() const { return ProtocolIdTcpUdp; } quint32 TcpProtocol::protocolId(ProtocolIdType type) const { switch(type) { case ProtocolIdIp: return 0x06; default: break; } return AbstractProtocol::protocolId(type); } int TcpProtocol::fieldCount() const { return tcp_fieldCount; } AbstractProtocol::FieldFlags TcpProtocol::fieldFlags(int index) const { AbstractProtocol::FieldFlags flags; flags = AbstractProtocol::fieldFlags(index); switch (index) { case tcp_src_port: case tcp_dst_port: case tcp_seq_num: case tcp_ack_num: case tcp_hdrlen: case tcp_rsvd: case tcp_flags: case tcp_window: break; case tcp_cksum: flags |= CksumField; break; case tcp_urg_ptr: break; case tcp_is_override_src_port: case tcp_is_override_dst_port: case tcp_is_override_hdrlen: case tcp_is_override_cksum: flags &= ~FrameField; flags |= MetaField; break; default: qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, index); break; } return flags; } QVariant TcpProtocol::fieldData(int index, FieldAttrib attrib, int streamIndex) const { switch (index) { case tcp_src_port: { quint16 srcPort; switch(attrib) { case FieldValue: case FieldFrameValue: case FieldTextValue: if (data.is_override_src_port()) srcPort = data.src_port(); else srcPort = payloadProtocolId(ProtocolIdTcpUdp); break; default: srcPort = 0; // avoid the 'maybe used unitialized' warning break; } switch(attrib) { case FieldName: return QString("Source Port"); case FieldValue: return srcPort; case FieldTextValue: return QString("%1").arg(srcPort); case FieldFrameValue: { QByteArray fv; fv.resize(2); qToBigEndian(srcPort, (uchar*) fv.data()); return fv; } default: break; } break; } case tcp_dst_port: { quint16 dstPort; switch(attrib) { case FieldValue: case FieldFrameValue: case FieldTextValue: if (data.is_override_dst_port()) dstPort = data.dst_port(); else dstPort = payloadProtocolId(ProtocolIdTcpUdp); break; default: dstPort = 0; // avoid the 'maybe used unitialized' warning break; } switch(attrib) { case FieldName: return QString("Destination Port"); case FieldValue: return dstPort; case FieldTextValue: return QString("%1").arg(dstPort); case FieldFrameValue: { QByteArray fv; fv.resize(2); qToBigEndian(dstPort, (uchar*) fv.data()); return fv; } default: break; } break; } case tcp_seq_num: switch(attrib) { case FieldName: return QString("Sequence Number"); case FieldValue: return data.seq_num(); case FieldTextValue: return QString("%1").arg(data.seq_num()); case FieldFrameValue: { QByteArray fv; fv.resize(4); qToBigEndian((quint32) data.seq_num(), (uchar*) fv.data()); return fv; } default: break; } break; case tcp_ack_num: switch(attrib) { case FieldName: return QString("Acknowledgement Number"); case FieldValue: return data.ack_num(); case FieldTextValue: return QString("%1").arg(data.ack_num()); case FieldFrameValue: { QByteArray fv; fv.resize(4); qToBigEndian((quint32) data.ack_num(), (uchar*) fv.data()); return fv; } default: break; } break; case tcp_hdrlen: switch(attrib) { case FieldName: return QString("Header Length"); case FieldValue: if (data.is_override_hdrlen()) return ((data.hdrlen_rsvd() >> 4) & 0x0F); else return 5; case FieldTextValue: if (data.is_override_hdrlen()) return QString("%1 bytes").arg( 4 * ((data.hdrlen_rsvd() >> 4) & 0x0F)); else return QString("20 bytes"); case FieldFrameValue: if (data.is_override_hdrlen()) return QByteArray(1, (char)((data.hdrlen_rsvd() >> 4) & 0x0F)); else return QByteArray(1, (char) 0x05); case FieldBitSize: return 4; default: break; } break; case tcp_rsvd: switch(attrib) { case FieldName: return QString("Reserved"); case FieldValue: return (data.hdrlen_rsvd() & 0x0F); case FieldTextValue: return QString("%1").arg(data.hdrlen_rsvd() & 0x0F); case FieldFrameValue: return QByteArray(1, (char)(data.hdrlen_rsvd() & 0x0F)); case FieldBitSize: return 4; default: break; } break; case tcp_flags: switch(attrib) { case FieldName: return QString("Flags"); case FieldValue: return (data.flags()); case FieldTextValue: { QString s; s.append("URG: "); s.append(data.flags() & TCP_FLAG_URG ? "1" : "0"); s.append(" ACK: "); s.append(data.flags() & TCP_FLAG_ACK ? "1" : "0"); s.append(" PSH: "); s.append(data.flags() & TCP_FLAG_PSH ? "1" : "0"); s.append(" RST: "); s.append(data.flags() & TCP_FLAG_RST ? "1" : "0"); s.append(" SYN: "); s.append(data.flags() & TCP_FLAG_SYN ? "1" : "0"); s.append(" FIN: "); s.append(data.flags() & TCP_FLAG_FIN ? "1" : "0"); return s; } case FieldFrameValue: return QByteArray(1, (char)(data.flags() & 0x3F)); default: break; } break; case tcp_window: switch(attrib) { case FieldName: return QString("Window Size"); case FieldValue: return data.window(); case FieldTextValue: return QString("%1").arg(data.window()); case FieldFrameValue: { QByteArray fv; fv.resize(2); qToBigEndian((quint16) data.window(), (uchar*) fv.data()); return fv; } default: break; } break; case tcp_cksum: switch(attrib) { case FieldName: return QString("Checksum"); case FieldValue: { quint16 cksum; if (data.is_override_cksum()) cksum = data.cksum(); else cksum = protocolFrameCksum(streamIndex, CksumTcpUdp); return cksum; } case FieldTextValue: { quint16 cksum; if (data.is_override_cksum()) cksum = data.cksum(); else cksum = protocolFrameCksum(streamIndex, CksumTcpUdp); return QString("0x%1").arg(cksum, 4, BASE_HEX, QChar('0')); } case FieldFrameValue: { quint16 cksum; if (data.is_override_cksum()) cksum = data.cksum(); else cksum = protocolFrameCksum(streamIndex, CksumTcpUdp); QByteArray fv; fv.resize(2); qToBigEndian(cksum, (uchar*) fv.data()); return fv; } case FieldBitSize: return 16; default: break; } break; case tcp_urg_ptr: switch(attrib) { case FieldName: return QString("Urgent Pointer"); case FieldValue: return data.urg_ptr(); case FieldTextValue: return QString("%1").arg(data.urg_ptr()); case FieldFrameValue: { QByteArray fv; fv.resize(2); qToBigEndian((quint16) data.urg_ptr(), (uchar*) fv.data()); return fv; } default: break; } break; // Meta fields case tcp_is_override_src_port: { switch(attrib) { case FieldValue: return data.is_override_src_port(); default: break; } break; } case tcp_is_override_dst_port: { switch(attrib) { case FieldValue: return data.is_override_dst_port(); default: break; } break; } case tcp_is_override_hdrlen: { switch(attrib) { case FieldValue: return data.is_override_hdrlen(); default: break; } break; } case tcp_is_override_cksum: { switch(attrib) { case FieldValue: return data.is_override_cksum(); default: break; } break; } default: qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, index); break; } return AbstractProtocol::fieldData(index, attrib, streamIndex); } bool TcpProtocol::setFieldData(int index, const QVariant &value, FieldAttrib attrib) { bool isOk = false; if (attrib != FieldValue) goto _exit; switch (index) { case tcp_src_port: { uint srcPort = value.toUInt(&isOk); if (isOk) data.set_src_port(srcPort); break; } case tcp_dst_port: { uint dstPort = value.toUInt(&isOk); if (isOk) data.set_dst_port(dstPort); break; } case tcp_seq_num: { uint seqNum = value.toUInt(&isOk); if (isOk) data.set_seq_num(seqNum); break; } case tcp_ack_num: { uint ackNum = value.toUInt(&isOk); if (isOk) data.set_ack_num(ackNum); break; } case tcp_hdrlen: { uint hdrLen = value.toUInt(&isOk); if (isOk) data.set_hdrlen_rsvd( (data.hdrlen_rsvd() & 0x0F) | (hdrLen << 4)); break; } case tcp_rsvd: { uint rsvd = value.toUInt(&isOk); if (isOk) data.set_hdrlen_rsvd( (data.hdrlen_rsvd() & 0xF0) | (rsvd & 0x0F)); break; } case tcp_flags: { uint flags = value.toUInt(&isOk); if (isOk) data.set_flags(flags); break; } case tcp_window: { uint window = value.toUInt(&isOk); if (isOk) data.set_window(window); break; } case tcp_cksum: { uint cksum = value.toUInt(&isOk); if (isOk) data.set_cksum(cksum); break; } case tcp_urg_ptr: { uint urgPtr = value.toUInt(&isOk); if (isOk) data.set_urg_ptr(urgPtr); break; } case tcp_is_override_src_port: { data.set_is_override_src_port(value.toBool()); isOk = true; break; } case tcp_is_override_dst_port: { data.set_is_override_dst_port(value.toBool()); isOk = true; break; } case tcp_is_override_hdrlen: { data.set_is_override_hdrlen(value.toBool()); isOk = true; break; } case tcp_is_override_cksum: { data.set_is_override_cksum(value.toBool()); isOk = true; break; } default: qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, index); break; } _exit: return isOk; } int TcpProtocol::protocolFrameVariableCount() const { int count = AbstractProtocol::protocolFrameVariableCount(); if (!data.is_override_cksum()) count = AbstractProtocol::lcm(count, protocolFramePayloadVariableCount()); return count; } ostinato-0.9/common/tcp.h000066400000000000000000000045131321315675200154610ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _TCP_H #define _TCP_H #include "abstractprotocol.h" #include "tcp.pb.h" #define TCP_FLAG_URG 0x20 #define TCP_FLAG_ACK 0x10 #define TCP_FLAG_PSH 0x08 #define TCP_FLAG_RST 0x04 #define TCP_FLAG_SYN 0x02 #define TCP_FLAG_FIN 0x01 class TcpProtocol : public AbstractProtocol { public: enum tcpfield { tcp_src_port = 0, tcp_dst_port, tcp_seq_num, tcp_ack_num, tcp_hdrlen, tcp_rsvd, tcp_flags, tcp_window, tcp_cksum, tcp_urg_ptr, tcp_is_override_src_port, tcp_is_override_dst_port, tcp_is_override_hdrlen, tcp_is_override_cksum, tcp_fieldCount }; TcpProtocol(StreamBase *stream, AbstractProtocol *parent = 0); virtual ~TcpProtocol(); static AbstractProtocol* createInstance(StreamBase *stream, AbstractProtocol *parent = 0); virtual quint32 protocolNumber() const; virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); virtual QString name() const; virtual QString shortName() const; virtual ProtocolIdType protocolIdType() const; virtual quint32 protocolId(ProtocolIdType type) const; virtual int fieldCount() const; virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; virtual QVariant fieldData(int index, FieldAttrib attrib, int streamIndex = 0) const; virtual bool setFieldData(int index, const QVariant &value, FieldAttrib attrib = FieldValue); virtual int protocolFrameVariableCount() const; private: OstProto::Tcp data; }; #endif ostinato-0.9/common/tcp.proto000066400000000000000000000025251321315675200163760ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ import "protocol.proto"; package OstProto; // Tcp message Tcp { optional bool is_override_src_port = 1; optional bool is_override_dst_port = 2; optional bool is_override_hdrlen = 3; optional bool is_override_cksum = 4; optional uint32 src_port = 5 [default = 49152]; optional uint32 dst_port = 6 [default = 49153]; optional uint32 seq_num = 7 [default = 129018]; optional uint32 ack_num = 8; optional uint32 hdrlen_rsvd = 9 [default = 0x50]; optional uint32 flags = 10; optional uint32 window = 11 [default = 1024]; optional uint32 cksum = 12; optional uint32 urg_ptr = 13; } extend Protocol { optional Tcp tcp = 400; } ostinato-0.9/common/tcp.ui000066400000000000000000000164501321315675200156520ustar00rootroot00000000000000 tcp 0 0 447 194 Form Override Source Port false Qt::Vertical Override Checksum false >HH HH; Override Destination Port false Urgent Pointer Sequence Number Flags URG ACK PSH RST SYN FIN Qt::Horizontal 21 20 Acknowledgement Number Override Header Length (x4) false Window Qt::Vertical 20 40 <i>Note: Ostinato is stateless- it cannot establish a TCP connection and generate seq/ack numbers accordingly</i> true Qt::Vertical 20 40 cbTcpHdrLenOverride toggled(bool) leTcpHdrLen setEnabled(bool) 141 123 187 123 cbTcpCksumOverride toggled(bool) leTcpCksum setEnabled(bool) 316 14 384 17 cbTcpSrcPortOverride toggled(bool) leTcpSrcPort setEnabled(bool) 159 16 178 18 cbTcpDstPortOverride toggled(bool) leTcpDstPort setEnabled(bool) 147 45 180 44 ostinato-0.9/common/tcpconfig.cpp000066400000000000000000000125611321315675200172040ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "tcpconfig.h" #include "tcp.h" TcpConfigForm::TcpConfigForm(QWidget *parent) : AbstractProtocolConfigForm(parent) { setupUi(this); } TcpConfigForm::~TcpConfigForm() { } TcpConfigForm* TcpConfigForm::createInstance() { return new TcpConfigForm; } void TcpConfigForm::loadWidget(AbstractProtocol *proto) { leTcpSrcPort->setText( proto->fieldData( TcpProtocol::tcp_src_port, AbstractProtocol::FieldValue ).toString()); cbTcpSrcPortOverride->setChecked( proto->fieldData( TcpProtocol::tcp_is_override_src_port, AbstractProtocol::FieldValue ).toBool()); leTcpDstPort->setText( proto->fieldData( TcpProtocol::tcp_dst_port, AbstractProtocol::FieldValue ).toString()); cbTcpDstPortOverride->setChecked( proto->fieldData( TcpProtocol::tcp_is_override_dst_port, AbstractProtocol::FieldValue ).toBool()); leTcpSeqNum->setText( proto->fieldData( TcpProtocol::tcp_seq_num, AbstractProtocol::FieldValue ).toString()); leTcpAckNum->setText( proto->fieldData( TcpProtocol::tcp_ack_num, AbstractProtocol::FieldValue ).toString()); leTcpHdrLen->setText( proto->fieldData( TcpProtocol::tcp_hdrlen, AbstractProtocol::FieldValue ).toString()); cbTcpHdrLenOverride->setChecked( proto->fieldData( TcpProtocol::tcp_is_override_hdrlen, AbstractProtocol::FieldValue ).toBool()); leTcpWindow->setText( proto->fieldData( TcpProtocol::tcp_window, AbstractProtocol::FieldValue ).toString()); leTcpCksum->setText(uintToHexStr( proto->fieldData( TcpProtocol::tcp_cksum, AbstractProtocol::FieldValue ).toUInt(), 2)); cbTcpCksumOverride->setChecked( proto->fieldData( TcpProtocol::tcp_is_override_cksum, AbstractProtocol::FieldValue ).toBool()); leTcpUrgentPointer->setText( proto->fieldData( TcpProtocol::tcp_urg_ptr, AbstractProtocol::FieldValue ).toString()); uint flags = proto->fieldData( TcpProtocol::tcp_flags, AbstractProtocol::FieldValue ).toUInt(); cbTcpFlagsUrg->setChecked((flags & TCP_FLAG_URG) > 0); cbTcpFlagsAck->setChecked((flags & TCP_FLAG_ACK) > 0); cbTcpFlagsPsh->setChecked((flags & TCP_FLAG_PSH) > 0); cbTcpFlagsRst->setChecked((flags & TCP_FLAG_RST) > 0); cbTcpFlagsSyn->setChecked((flags & TCP_FLAG_SYN) > 0); cbTcpFlagsFin->setChecked((flags & TCP_FLAG_FIN) > 0); } void TcpConfigForm::storeWidget(AbstractProtocol *proto) { int ff = 0; proto->setFieldData( TcpProtocol::tcp_src_port, leTcpSrcPort->text()); proto->setFieldData( TcpProtocol::tcp_is_override_src_port, cbTcpSrcPortOverride->isChecked()); proto->setFieldData( TcpProtocol::tcp_dst_port, leTcpDstPort->text()); proto->setFieldData( TcpProtocol::tcp_is_override_dst_port, cbTcpDstPortOverride->isChecked()); proto->setFieldData( TcpProtocol::tcp_seq_num, leTcpSeqNum->text()); proto->setFieldData( TcpProtocol::tcp_ack_num, leTcpAckNum->text()); proto->setFieldData( TcpProtocol::tcp_hdrlen, leTcpHdrLen->text()); proto->setFieldData( TcpProtocol::tcp_is_override_hdrlen, cbTcpHdrLenOverride->isChecked()); proto->setFieldData( TcpProtocol::tcp_window, leTcpWindow->text()); proto->setFieldData( TcpProtocol::tcp_cksum, hexStrToUInt(leTcpCksum->text())); proto->setFieldData( TcpProtocol::tcp_is_override_cksum, cbTcpCksumOverride->isChecked()); proto->setFieldData( TcpProtocol::tcp_urg_ptr, leTcpUrgentPointer->text()); if (cbTcpFlagsUrg->isChecked()) ff |= TCP_FLAG_URG; if (cbTcpFlagsAck->isChecked()) ff |= TCP_FLAG_ACK; if (cbTcpFlagsPsh->isChecked()) ff |= TCP_FLAG_PSH; if (cbTcpFlagsRst->isChecked()) ff |= TCP_FLAG_RST; if (cbTcpFlagsSyn->isChecked()) ff |= TCP_FLAG_SYN; if (cbTcpFlagsFin->isChecked()) ff |= TCP_FLAG_FIN; proto->setFieldData(TcpProtocol::tcp_flags, ff); } ostinato-0.9/common/tcpconfig.h000066400000000000000000000021351321315675200166450ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _TCP_CONFIG_H #define _TCP_CONFIG_H #include "abstractprotocolconfig.h" #include "ui_tcp.h" class TcpConfigForm : public AbstractProtocolConfigForm, private Ui::tcp { Q_OBJECT public: TcpConfigForm(QWidget *parent = 0); virtual ~TcpConfigForm(); static TcpConfigForm* createInstance(); virtual void loadWidget(AbstractProtocol *proto); virtual void storeWidget(AbstractProtocol *proto); }; #endif ostinato-0.9/common/tcppdml.cpp000066400000000000000000000065221321315675200166730ustar00rootroot00000000000000/* Copyright (C) 2011 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "tcppdml.h" #include "hexdump.pb.h" #include "tcp.pb.h" PdmlTcpProtocol::PdmlTcpProtocol() { ostProtoId_ = OstProto::Protocol::kTcpFieldNumber; fieldMap_.insert("tcp.srcport", OstProto::Tcp::kSrcPortFieldNumber); fieldMap_.insert("tcp.dstport", OstProto::Tcp::kDstPortFieldNumber); fieldMap_.insert("tcp.seq", OstProto::Tcp::kSeqNumFieldNumber); fieldMap_.insert("tcp.ack", OstProto::Tcp::kAckNumFieldNumber); fieldMap_.insert("tcp.hdr_len", OstProto::Tcp::kHdrlenRsvdFieldNumber); fieldMap_.insert("tcp.flags", OstProto::Tcp::kFlagsFieldNumber); fieldMap_.insert("tcp.window_size", OstProto::Tcp::kWindowFieldNumber); fieldMap_.insert("tcp.checksum", OstProto::Tcp::kCksumFieldNumber); fieldMap_.insert("tcp.urgent_pointer", OstProto::Tcp::kUrgPtrFieldNumber); } PdmlProtocol* PdmlTcpProtocol::createInstance() { return new PdmlTcpProtocol(); } void PdmlTcpProtocol::unknownFieldHandler(QString name, int /*pos*/, int /*size*/, const QXmlStreamAttributes &attributes, OstProto::Protocol *pbProto, OstProto::Stream* /*stream*/) { if (name == "tcp.options") options_ = QByteArray::fromHex(attributes.value("value").toString().toUtf8()); else if (name == "") { if (attributes.value("show").toString().startsWith("Acknowledgement number")) { bool isOk; OstProto::Tcp *tcp = pbProto->MutableExtension(OstProto::tcp); tcp->set_ack_num(attributes.value("value").toString().toUInt(&isOk, kBaseHex)); } #if 0 else if (attributes.value("show").toString().startsWith("TCP segment data")) { segmentData_ = QByteArray::fromHex(attributes.value("value").toString().toUtf8()); stream->mutable_core()->mutable_name()->insert(0, segmentData_.constData(), segmentData_.size()); } #endif } } void PdmlTcpProtocol::postProtocolHandler(OstProto::Protocol *pbProto, OstProto::Stream *stream) { OstProto::Tcp *tcp = pbProto->MutableExtension(OstProto::tcp); qDebug("Tcp: post\n"); tcp->set_is_override_src_port(true); tcp->set_is_override_dst_port(true); tcp->set_is_override_hdrlen(true); tcp->set_is_override_cksum(true); if (options_.size()) { OstProto::Protocol *proto = stream->add_protocol(); proto->mutable_protocol_id()->set_id( OstProto::Protocol::kHexDumpFieldNumber); OstProto::HexDump *hexDump = proto->MutableExtension(OstProto::hexDump); hexDump->mutable_content()->append(options_.constData(), options_.size()); hexDump->set_pad_until_end(false); options_.resize(0); } } ostinato-0.9/common/tcppdml.h000066400000000000000000000023351321315675200163360ustar00rootroot00000000000000/* Copyright (C) 2011 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _TCP_PDML_H #define _TCP_PDML_H #include "pdmlprotocol.h" class PdmlTcpProtocol : public PdmlProtocol { public: static PdmlProtocol* createInstance(); virtual void unknownFieldHandler(QString name, int pos, int size, const QXmlStreamAttributes &attributes, OstProto::Protocol *pbProto, OstProto::Stream *stream); virtual void postProtocolHandler(OstProto::Protocol *pbProto, OstProto::Stream *stream); protected: PdmlTcpProtocol(); private: QByteArray options_; QByteArray segmentData_; }; #endif ostinato-0.9/common/textproto.cpp000066400000000000000000000137461321315675200173060ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "textproto.h" TextProtocol::TextProtocol(StreamBase *stream, AbstractProtocol *parent) : AbstractProtocol(stream, parent) { } TextProtocol::~TextProtocol() { } AbstractProtocol* TextProtocol::createInstance(StreamBase *stream, AbstractProtocol *parent) { return new TextProtocol(stream, parent); } quint32 TextProtocol::protocolNumber() const { return OstProto::Protocol::kTextProtocolFieldNumber; } void TextProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const { protocol.MutableExtension(OstProto::textProtocol)->CopyFrom(data); protocol.mutable_protocol_id()->set_id(protocolNumber()); } void TextProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) { if (protocol.protocol_id().id() == protocolNumber() && protocol.HasExtension(OstProto::textProtocol)) data.MergeFrom(protocol.GetExtension(OstProto::textProtocol)); } QString TextProtocol::name() const { return QString("Text Protocol"); } QString TextProtocol::shortName() const { return QString("TEXT"); } quint32 TextProtocol::protocolId(ProtocolIdType type) const { switch(type) { case ProtocolIdTcpUdp: return data.port_num(); default:break; } return AbstractProtocol::protocolId(type); } int TextProtocol::fieldCount() const { return textProto_fieldCount; } AbstractProtocol::FieldFlags TextProtocol::fieldFlags(int index) const { AbstractProtocol::FieldFlags flags; flags = AbstractProtocol::fieldFlags(index); switch (index) { case textProto_text: break; case textProto_portNum: case textProto_eol: case textProto_encoding: flags &= ~FrameField; flags |= MetaField; break; default: qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, index); break; } return flags; } QVariant TextProtocol::fieldData(int index, FieldAttrib attrib, int streamIndex) const { switch (index) { case textProto_text: { switch(attrib) { case FieldName: return QString("Text"); case FieldValue: case FieldTextValue: return QString().fromStdString(data.text()); case FieldFrameValue: { QString text; Q_ASSERT(data.encoding() == OstProto::TextProtocol::kUtf8); text = QString().fromStdString(data.text()); if (data.eol() == OstProto::TextProtocol::kCrLf) text.replace('\n', "\r\n"); else if (data.eol() == OstProto::TextProtocol::kCr) text.replace('\n', '\r'); return text.toUtf8(); } default: break; } break; } // Meta fields case textProto_portNum: { switch(attrib) { case FieldValue: return data.port_num(); default: break; } break; } case textProto_eol: { switch(attrib) { case FieldValue: return data.eol(); default: break; } break; } case textProto_encoding: { switch(attrib) { case FieldValue: return data.encoding(); default: break; } break; } default: qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, index); break; } return AbstractProtocol::fieldData(index, attrib, streamIndex); } bool TextProtocol::setFieldData(int index, const QVariant &value, FieldAttrib attrib) { bool isOk = false; if (attrib != FieldValue) goto _exit; switch (index) { case textProto_text: { data.set_text(value.toString().toUtf8()); isOk = true; break; } case textProto_portNum: { uint portNum = value.toUInt(&isOk); if (isOk) data.set_port_num(portNum); break; } case textProto_eol: { uint eol = value.toUInt(&isOk); if (isOk && data.EndOfLine_IsValid(eol)) data.set_eol((OstProto::TextProtocol::EndOfLine) eol); else isOk = false; break; } case textProto_encoding: { uint enc = value.toUInt(&isOk); if (isOk && data.TextEncoding_IsValid(enc)) data.set_encoding((OstProto::TextProtocol::TextEncoding) enc); else isOk = false; break; } default: qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, index); break; } _exit: return isOk; } int TextProtocol::protocolFrameSize(int streamIndex) const { return fieldData(textProto_text, FieldFrameValue, streamIndex) .toByteArray().size() ; } ostinato-0.9/common/textproto.h000066400000000000000000000041701321315675200167420ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _TEXT_PROTOCOL_H #define _TEXT_PROTOCOL_H #include "abstractprotocol.h" #include "textproto.pb.h" /* TextProtocol Protocol Frame Format - specified text with the specified line ending and encoded with the specified encoding */ class TextProtocol : public AbstractProtocol { public: enum textProtocolField { // Frame Fields textProto_text = 0, // Meta Fields textProto_portNum, textProto_eol, textProto_encoding, textProto_fieldCount }; TextProtocol(StreamBase *stream, AbstractProtocol *parent = 0); virtual ~TextProtocol(); static AbstractProtocol* createInstance(StreamBase *stream, AbstractProtocol *parent = 0); virtual quint32 protocolNumber() const; virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); virtual quint32 protocolId(ProtocolIdType type) const; virtual QString name() const; virtual QString shortName() const; virtual int fieldCount() const; virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; virtual QVariant fieldData(int index, FieldAttrib attrib, int streamIndex = 0) const; virtual bool setFieldData(int index, const QVariant &value, FieldAttrib attrib = FieldValue); virtual int protocolFrameSize(int streamIndex = 0) const; private: OstProto::TextProtocol data; }; #endif ostinato-0.9/common/textproto.proto000066400000000000000000000022071321315675200176550ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ import "protocol.proto"; package OstProto; // Any Text based protocol message TextProtocol { enum TextEncoding { kUtf8 = 0; } enum EndOfLine { kCr = 0; kLf = 1; kCrLf = 2; } optional uint32 port_num = 1 [default = 80]; optional TextEncoding encoding = 2 [default = kUtf8]; optional string text = 3; optional EndOfLine eol = 4 [default = kLf]; } extend Protocol { optional TextProtocol textProtocol = 500; } ostinato-0.9/common/textproto.ui000066400000000000000000000051751321315675200171360ustar00rootroot00000000000000 TextProto 0 0 535 300 Form TCP/UDP Port Number (Protocol) portNumCombo 2 0 Line Ending 2 CR LF CRLF Encode as encodingCombo 1 0 UTF-8 false IntComboBox QComboBox
intcombobox.h
ostinato-0.9/common/textprotoconfig.cpp000066400000000000000000000047561321315675200204750ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "textprotoconfig.h" #include "textproto.h" TextProtocolConfigForm::TextProtocolConfigForm(QWidget *parent) : AbstractProtocolConfigForm(parent) { setupUi(this); portNumCombo->setValidator(new QIntValidator(0, 0xFFFF, this)); portNumCombo->addItem(0, "Reserved"); portNumCombo->addItem(80, "HTTP"); portNumCombo->addItem(554, "RTSP"); portNumCombo->addItem(5060, "SIP"); } TextProtocolConfigForm::~TextProtocolConfigForm() { } TextProtocolConfigForm* TextProtocolConfigForm::createInstance() { return new TextProtocolConfigForm; } void TextProtocolConfigForm::loadWidget(AbstractProtocol *proto) { portNumCombo->setValue( proto->fieldData( TextProtocol::textProto_portNum, AbstractProtocol::FieldValue ).toUInt()); eolCombo->setCurrentIndex( proto->fieldData( TextProtocol::textProto_eol, AbstractProtocol::FieldValue ).toUInt()); encodingCombo->setCurrentIndex( proto->fieldData( TextProtocol::textProto_encoding, AbstractProtocol::FieldValue ).toUInt()); protoText->setText( proto->fieldData( TextProtocol::textProto_text, AbstractProtocol::FieldValue ).toString()); } void TextProtocolConfigForm::storeWidget(AbstractProtocol *proto) { proto->setFieldData( TextProtocol::textProto_portNum, portNumCombo->currentValue()); proto->setFieldData( TextProtocol::textProto_eol, eolCombo->currentIndex()); proto->setFieldData( TextProtocol::textProto_encoding, encodingCombo->currentIndex()); proto->setFieldData( TextProtocol::textProto_text, protoText->toPlainText()); } ostinato-0.9/common/textprotoconfig.h000066400000000000000000000022411321315675200201250ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _TEXT_PROTOCOL_CONFIG_H #define _TEXT_PROTOCOL_CONFIG_H #include "abstractprotocolconfig.h" #include "ui_textproto.h" class TextProtocolConfigForm : public AbstractProtocolConfigForm, private Ui::TextProto { Q_OBJECT public: TextProtocolConfigForm(QWidget *parent = 0); virtual ~TextProtocolConfigForm(); static TextProtocolConfigForm* createInstance(); virtual void loadWidget(AbstractProtocol *proto); virtual void storeWidget(AbstractProtocol *proto); }; #endif ostinato-0.9/common/textprotopdml.cpp000066400000000000000000000112661321315675200201560ustar00rootroot00000000000000/* Copyright (C) 2011 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "textprotopdml.h" #include "textproto.pb.h" PdmlTextProtocol::PdmlTextProtocol() { ostProtoId_ = OstProto::Protocol::kTextProtocolFieldNumber; } PdmlProtocol* PdmlTextProtocol::createInstance() { return new PdmlTextProtocol(); } void PdmlTextProtocol::preProtocolHandler(QString /*name*/, const QXmlStreamAttributes &attributes, int expectedPos, OstProto::Protocol *pbProto, OstProto::Stream *stream) { bool isOk; int size; int pos = attributes.value("pos").toString().toUInt(&isOk); if (!isOk) { if (expectedPos >= 0) expPos_ = pos = expectedPos; else goto _skip_pos_size_proc; } size = attributes.value("size").toString().toUInt(&isOk); if (!isOk) goto _skip_pos_size_proc; // If pos+size goes beyond the frame length, this is a "reassembled" // protocol and should be skipped if ((pos + size) > int(stream->core().frame_len())) goto _skip_pos_size_proc; expPos_ = pos; endPos_ = expPos_ + size; _skip_pos_size_proc: qDebug("expPos_ = %d, endPos_ = %d", expPos_, endPos_); OstProto::TextProtocol *text = pbProto->MutableExtension( OstProto::textProtocol); text->set_port_num(0); text->set_eol(OstProto::TextProtocol::kCrLf); // by default we assume CRLF detectEol_ = true; contentType_ = kUnknownContent; } void PdmlTextProtocol::unknownFieldHandler(QString name, int pos, int size, const QXmlStreamAttributes &attributes, OstProto::Protocol *pbProto, OstProto::Stream* /*stream*/) { _retry: switch(contentType_) { case kUnknownContent: if (name == "data") contentType_ = kOtherContent; else contentType_ = kTextContent; goto _retry; break; case kTextContent: { OstProto::TextProtocol *text = pbProto->MutableExtension( OstProto::textProtocol); if ((name == "data") || (attributes.value("show") == "HTTP chunked response")) { contentType_ = kOtherContent; goto _retry; } if (pos < expPos_) break; if ((pos + size) > endPos_) break; if (pos > expPos_) { int gap = pos - expPos_; QByteArray filler(gap, '\n'); if (text->eol() == OstProto::TextProtocol::kCrLf) { if (gap & 0x01) // Odd { filler.resize(gap/2 + 1); filler[0]=int(' '); } else // Even filler.resize(gap/2); } text->mutable_text()->append(filler.constData(), filler.size()); expPos_ += gap; } QByteArray line = QByteArray::fromHex( attributes.value("value").toString().toUtf8()); if (detectEol_) { if (line.right(2) == "\r\n") text->set_eol(OstProto::TextProtocol::kCrLf); else if (line.right(1) == "\r") text->set_eol(OstProto::TextProtocol::kCr); else if (line.right(1) == "\n") text->set_eol(OstProto::TextProtocol::kLf); detectEol_ = false; } // Convert line endings to LF only - Qt reqmt that TextProto honours line.replace("\r\n", "\n"); line.replace('\r', '\n'); text->mutable_text()->append(line.constData(), line.size()); expPos_ += size; break; } case kOtherContent: // Do nothing! break; default: Q_ASSERT(false); } } void PdmlTextProtocol::postProtocolHandler(OstProto::Protocol *pbProto, OstProto::Stream *stream) { OstProto::TextProtocol *text = pbProto->MutableExtension( OstProto::textProtocol); // Empty Text Content - remove ourselves if (text->text().length() == 0) stream->mutable_protocol()->RemoveLast(); expPos_ = endPos_ = -1; detectEol_ = true; contentType_ = kUnknownContent; } ostinato-0.9/common/textprotopdml.h000066400000000000000000000030551321315675200176200ustar00rootroot00000000000000/* Copyright (C) 2011 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _TEXT_PROTO_PDML_H #define _TEXT_PROTO_PDML_H #include "pdmlprotocol.h" class PdmlTextProtocol : public PdmlProtocol { public: static PdmlProtocol* createInstance(); virtual void preProtocolHandler(QString name, const QXmlStreamAttributes &attributes, int expectedPos, OstProto::Protocol *pbProto, OstProto::Stream *stream); virtual void unknownFieldHandler(QString name, int pos, int size, const QXmlStreamAttributes &attributes, OstProto::Protocol *pbProto, OstProto::Stream *stream); virtual void postProtocolHandler(OstProto::Protocol *pbProto, OstProto::Stream *stream); protected: PdmlTextProtocol(); private: enum ContentType { kUnknownContent, kTextContent, kOtherContent }; bool detectEol_; ContentType contentType_; int expPos_; int endPos_; }; #endif ostinato-0.9/common/udp.cpp000066400000000000000000000266671321315675200160340ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "udp.h" UdpProtocol::UdpProtocol(StreamBase *stream, AbstractProtocol *parent) : AbstractProtocol(stream, parent) { } UdpProtocol::~UdpProtocol() { } AbstractProtocol* UdpProtocol::createInstance(StreamBase *stream, AbstractProtocol *parent) { return new UdpProtocol(stream, parent); } quint32 UdpProtocol::protocolNumber() const { return OstProto::Protocol::kUdpFieldNumber; } void UdpProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const { protocol.MutableExtension(OstProto::udp)->CopyFrom(data); protocol.mutable_protocol_id()->set_id(protocolNumber()); } void UdpProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) { if (protocol.protocol_id().id() == protocolNumber() && protocol.HasExtension(OstProto::udp)) data.MergeFrom(protocol.GetExtension(OstProto::udp)); } QString UdpProtocol::name() const { return QString("User Datagram Protocol"); } QString UdpProtocol::shortName() const { return QString("UDP"); } AbstractProtocol::ProtocolIdType UdpProtocol::protocolIdType() const { return ProtocolIdTcpUdp; } quint32 UdpProtocol::protocolId(ProtocolIdType type) const { switch(type) { case ProtocolIdIp: return 0x11; default: break; } return AbstractProtocol::protocolId(type); } int UdpProtocol::fieldCount() const { return udp_fieldCount; } AbstractProtocol::FieldFlags UdpProtocol::fieldFlags(int index) const { AbstractProtocol::FieldFlags flags; flags = AbstractProtocol::fieldFlags(index); switch (index) { case udp_srcPort: case udp_dstPort: case udp_totLen: break; case udp_cksum: flags |= CksumField; break; case udp_isOverrideSrcPort: case udp_isOverrideDstPort: case udp_isOverrideTotLen: case udp_isOverrideCksum: flags &= ~FrameField; flags |= MetaField; break; default: qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, index); break; } return flags; } QVariant UdpProtocol::fieldData(int index, FieldAttrib attrib, int streamIndex) const { switch (index) { case udp_srcPort: { quint16 srcPort; switch(attrib) { case FieldValue: case FieldFrameValue: case FieldTextValue: if (data.is_override_src_port()) srcPort = data.src_port(); else srcPort = payloadProtocolId(ProtocolIdTcpUdp); break; default: srcPort = 0; // avoid the 'maybe used unitialized' warning break; } switch(attrib) { case FieldName: return QString("Source Port"); case FieldValue: return srcPort; case FieldTextValue: return QString("%1").arg(srcPort); case FieldFrameValue: { QByteArray fv; fv.resize(2); qToBigEndian(srcPort, (uchar*) fv.data()); return fv; } default: break; } break; } case udp_dstPort: { quint16 dstPort; switch(attrib) { case FieldValue: case FieldFrameValue: case FieldTextValue: if (data.is_override_dst_port()) dstPort = data.dst_port(); else dstPort = payloadProtocolId(ProtocolIdTcpUdp); break; default: dstPort = 0; // avoid the 'maybe used unitialized' warning break; } switch(attrib) { case FieldName: return QString("Destination Port"); case FieldValue: return dstPort; case FieldTextValue: return QString("%1").arg(dstPort); case FieldFrameValue: { QByteArray fv; fv.resize(2); qToBigEndian(dstPort, (uchar*) fv.data()); return fv; } default: break; } break; } case udp_totLen: { switch(attrib) { case FieldName: return QString("Datagram Length"); case FieldValue: { int totlen; totlen = data.is_override_totlen() ? data.totlen() : (protocolFramePayloadSize(streamIndex) + 8); return totlen; } case FieldFrameValue: { QByteArray fv; int totlen; totlen = data.is_override_totlen() ? data.totlen() : (protocolFramePayloadSize(streamIndex) + 8); fv.resize(2); qToBigEndian((quint16) totlen, (uchar*) fv.data()); return fv; } case FieldTextValue: { int totlen; totlen = data.is_override_totlen() ? data.totlen() : (protocolFramePayloadSize(streamIndex) + 8); return QString("%1").arg(totlen); } case FieldBitSize: return 16; default: break; } break; } case udp_cksum: { quint16 cksum; switch(attrib) { case FieldValue: case FieldFrameValue: case FieldTextValue: { if (data.is_override_cksum()) cksum = data.cksum(); else { cksum = protocolFrameCksum(streamIndex, CksumTcpUdp); if (cksum == 0) cksum = 0xFFFF; } qDebug("UDP cksum = %hu", cksum); break; } default: cksum = 0; break; } switch(attrib) { case FieldName: return QString("Checksum"); case FieldValue: return cksum; case FieldFrameValue: { QByteArray fv; fv.resize(2); qToBigEndian(cksum, (uchar*) fv.data()); return fv; } case FieldTextValue: return QString("0x%1"). arg(cksum, 4, BASE_HEX, QChar('0'));; case FieldBitSize: return 16; default: break; } break; } // Meta fields case udp_isOverrideSrcPort: { switch(attrib) { case FieldValue: return data.is_override_src_port(); default: break; } break; } case udp_isOverrideDstPort: { switch(attrib) { case FieldValue: return data.is_override_dst_port(); default: break; } break; } case udp_isOverrideTotLen: { switch(attrib) { case FieldValue: return data.is_override_totlen(); default: break; } break; } case udp_isOverrideCksum: { switch(attrib) { case FieldValue: return data.is_override_cksum(); default: break; } break; } default: qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, index); break; } return AbstractProtocol::fieldData(index, attrib, streamIndex); } bool UdpProtocol::setFieldData(int index, const QVariant& value, FieldAttrib attrib) { bool isOk = false; if (attrib != FieldValue) goto _exit; switch (index) { case udp_isOverrideSrcPort: { data.set_is_override_src_port(value.toBool()); isOk = true; break; } case udp_isOverrideDstPort: { data.set_is_override_dst_port(value.toBool()); isOk = true; break; } case udp_isOverrideTotLen: { data.set_is_override_totlen(value.toBool()); isOk = true; break; } case udp_isOverrideCksum: { data.set_is_override_cksum(value.toBool()); isOk = true; break; } case udp_srcPort: { uint srcPort = value.toUInt(&isOk); if (isOk) data.set_src_port(srcPort); break; } case udp_dstPort: { uint dstPort = value.toUInt(&isOk); if (isOk) data.set_dst_port(dstPort); break; } case udp_totLen: { uint totLen = value.toUInt(&isOk); if (isOk) data.set_totlen(totLen); break; } case udp_cksum: { uint cksum = value.toUInt(&isOk); if (isOk) data.set_cksum(cksum); break; } default: qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, index); break; } _exit: return isOk; } int UdpProtocol::protocolFrameVariableCount() const { int count; if (data.is_override_totlen() && data.is_override_cksum()) count = AbstractProtocol::protocolFrameVariableCount(); else count = AbstractProtocol::lcm( AbstractProtocol::protocolFrameVariableCount(), protocolFramePayloadVariableCount()); return count; } ostinato-0.9/common/udp.h000066400000000000000000000040271321315675200154630ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _UDP_H #define _UDP_H #include "abstractprotocol.h" #include "udp.pb.h" class UdpProtocol : public AbstractProtocol { public: enum udpfield { udp_srcPort = 0, udp_dstPort, udp_totLen, udp_cksum, udp_isOverrideSrcPort, udp_isOverrideDstPort, udp_isOverrideTotLen, udp_isOverrideCksum, udp_fieldCount }; UdpProtocol(StreamBase *stream, AbstractProtocol *parent = 0); virtual ~UdpProtocol(); static AbstractProtocol* createInstance(StreamBase *stream, AbstractProtocol *parent = 0); virtual quint32 protocolNumber() const; virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); virtual QString name() const; virtual QString shortName() const; virtual ProtocolIdType protocolIdType() const; virtual quint32 protocolId(ProtocolIdType type) const; virtual int fieldCount() const; virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; virtual QVariant fieldData(int index, FieldAttrib attrib, int streamIndex = 0) const; virtual bool setFieldData(int index, const QVariant &value, FieldAttrib attrib = FieldValue); virtual int protocolFrameVariableCount() const; private: OstProto::Udp data; }; #endif ostinato-0.9/common/udp.proto000066400000000000000000000021521321315675200163740ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ import "protocol.proto"; package OstProto; // UDP message Udp { optional bool is_override_src_port = 1; optional bool is_override_dst_port = 2; optional bool is_override_totlen = 3; optional bool is_override_cksum = 4; optional uint32 src_port = 5 [default = 49152]; optional uint32 dst_port = 6 [default = 49153]; optional uint32 totlen = 7; optional uint32 cksum = 8; } extend Protocol { optional Udp udp = 401; } ostinato-0.9/common/udp.ui000066400000000000000000000101341321315675200156450ustar00rootroot00000000000000 udp 0 0 246 144 Form Override Source Port false Override Destination Port false Override Length false Override Checksum false >HH HH; Qt::Horizontal 40 20 Qt::Vertical 20 40 cbUdpLengthOverride toggled(bool) leUdpLength setEnabled(bool) 59 63 209 81 cbUdpCksumOverride toggled(bool) leUdpCksum setEnabled(bool) 55 106 209 107 cbUdpDstPortOverride toggled(bool) leUdpDstPort setEnabled(bool) 131 43 166 46 cbUdpSrcPortOverride toggled(bool) leUdpSrcPort setEnabled(bool) 125 21 167 20 ostinato-0.9/common/udpconfig.cpp000066400000000000000000000062221321315675200172030ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "udpconfig.h" #include "udp.h" UdpConfigForm::UdpConfigForm(QWidget *parent) : AbstractProtocolConfigForm(parent) { setupUi(this); } UdpConfigForm::~UdpConfigForm() { } UdpConfigForm* UdpConfigForm::createInstance() { return new UdpConfigForm; } void UdpConfigForm::loadWidget(AbstractProtocol *proto) { leUdpSrcPort->setText( proto->fieldData( UdpProtocol::udp_srcPort, AbstractProtocol::FieldValue ).toString()); cbUdpSrcPortOverride->setChecked( proto->fieldData( UdpProtocol::udp_isOverrideSrcPort, AbstractProtocol::FieldValue ).toBool()); leUdpDstPort->setText( proto->fieldData( UdpProtocol::udp_dstPort, AbstractProtocol::FieldValue ).toString()); cbUdpDstPortOverride->setChecked( proto->fieldData( UdpProtocol::udp_isOverrideDstPort, AbstractProtocol::FieldValue ).toBool()); leUdpLength->setText( proto->fieldData( UdpProtocol::udp_totLen, AbstractProtocol::FieldValue ).toString()); cbUdpLengthOverride->setChecked( proto->fieldData( UdpProtocol::udp_isOverrideTotLen, AbstractProtocol::FieldValue ).toBool()); leUdpCksum->setText(uintToHexStr( proto->fieldData( UdpProtocol::udp_cksum, AbstractProtocol::FieldValue ).toUInt(), 2)); cbUdpCksumOverride->setChecked( proto->fieldData( UdpProtocol::udp_isOverrideCksum, AbstractProtocol::FieldValue ).toBool()); } void UdpConfigForm::storeWidget(AbstractProtocol *proto) { proto->setFieldData( UdpProtocol::udp_srcPort, leUdpSrcPort->text()); proto->setFieldData( UdpProtocol::udp_isOverrideSrcPort, cbUdpSrcPortOverride->isChecked()); proto->setFieldData( UdpProtocol::udp_dstPort, leUdpDstPort->text()); proto->setFieldData( UdpProtocol::udp_isOverrideDstPort, cbUdpDstPortOverride->isChecked()); proto->setFieldData( UdpProtocol::udp_totLen, leUdpLength->text()); proto->setFieldData( UdpProtocol::udp_isOverrideTotLen, cbUdpLengthOverride->isChecked()); proto->setFieldData( UdpProtocol::udp_cksum, hexStrToUInt(leUdpCksum->text())); proto->setFieldData( UdpProtocol::udp_isOverrideCksum, cbUdpCksumOverride->isChecked()); } ostinato-0.9/common/udpconfig.h000066400000000000000000000021361321315675200166500ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _UDP_CONFIG_H #define _UDP_CONFIG_H #include "abstractprotocolconfig.h" #include "ui_udp.h" class UdpConfigForm : public AbstractProtocolConfigForm, private Ui::udp { Q_OBJECT public: UdpConfigForm(QWidget *parent = 0); virtual ~UdpConfigForm(); static UdpConfigForm* createInstance(); virtual void loadWidget(AbstractProtocol *proto); virtual void storeWidget(AbstractProtocol *proto); }; #endif ostinato-0.9/common/udppdml.cpp000066400000000000000000000031761321315675200166770ustar00rootroot00000000000000/* Copyright (C) 2011 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "udppdml.h" #include "udp.pb.h" PdmlUdpProtocol::PdmlUdpProtocol() { ostProtoId_ = OstProto::Protocol::kUdpFieldNumber; fieldMap_.insert("udp.srcport", OstProto::Udp::kSrcPortFieldNumber); fieldMap_.insert("udp.dstport", OstProto::Udp::kDstPortFieldNumber); fieldMap_.insert("udp.length", OstProto::Udp::kTotlenFieldNumber); fieldMap_.insert("udp.checksum_coverage", OstProto::Udp::kTotlenFieldNumber); fieldMap_.insert("udp.checksum", OstProto::Udp::kCksumFieldNumber); } PdmlProtocol* PdmlUdpProtocol::createInstance() { return new PdmlUdpProtocol(); } void PdmlUdpProtocol::postProtocolHandler(OstProto::Protocol *pbProto, OstProto::Stream* /*stream*/) { OstProto::Udp *udp = pbProto->MutableExtension(OstProto::udp); qDebug("Udp: post\n"); udp->set_is_override_src_port(true); udp->set_is_override_dst_port(true); udp->set_is_override_totlen(true); udp->set_is_override_cksum(true); } ostinato-0.9/common/udppdml.h000066400000000000000000000017351321315675200163430ustar00rootroot00000000000000/* Copyright (C) 2011 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _UDP_PDML_H #define _UDP_PDML_H #include "pdmlprotocol.h" class PdmlUdpProtocol : public PdmlProtocol { public: static PdmlProtocol* createInstance(); virtual void postProtocolHandler(OstProto::Protocol *pbProto, OstProto::Stream *stream); protected: PdmlUdpProtocol(); }; #endif ostinato-0.9/common/uint128.h000066400000000000000000000101071321315675200161010ustar00rootroot00000000000000/* Copyright (C) 2015 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _UINT128_H #define _UINT128_H #include #include #include class UInt128 { public: UInt128(); UInt128(quint64 hi, quint64 lo); UInt128(quint8 *value); quint64 hi64() const; quint64 lo64() const; quint8* toArray() const; bool operator==(const UInt128 &other) const; bool operator!=(const UInt128 &other) const; UInt128 operator+(const UInt128 &other) const; UInt128 operator*(const uint &other) const; UInt128 operator<<(const int &shift) const; UInt128 operator~() const; UInt128 operator&(const UInt128 &other) const; UInt128 operator|(const UInt128 &other) const; private: quint64 hi_; quint64 lo_; quint8 array_[16]; }; inline UInt128::UInt128() { // Do nothing - value will be garbage like any other uint } inline UInt128::UInt128(quint64 hi, quint64 lo) { hi_ = hi; lo_ = lo; } inline UInt128::UInt128(quint8 *value) { hi_ = (quint64(value[0]) << 56) | (quint64(value[1]) << 48) | (quint64(value[2]) << 40) | (quint64(value[3]) << 32) | (quint64(value[4]) << 24) | (quint64(value[5]) << 16) | (quint64(value[6]) << 8) | (quint64(value[7]) << 0); lo_ = (quint64(value[ 8]) << 56) | (quint64(value[ 9]) << 48) | (quint64(value[10]) << 40) | (quint64(value[11]) << 32) | (quint64(value[12]) << 24) | (quint64(value[13]) << 16) | (quint64(value[14]) << 8) | (quint64(value[15]) << 0); } inline quint64 UInt128::hi64() const { return hi_; } inline quint64 UInt128::lo64() const { return lo_; } inline quint8* UInt128::toArray() const { qToBigEndian(hi_, const_cast(array_ + 0)); qToBigEndian(lo_, const_cast(array_ + 8)); return (quint8*)array_; } inline bool UInt128::operator==(const UInt128 &other) const { return ((hi_ == other.hi_) && (lo_ == other.lo_)); } inline bool UInt128::operator!=(const UInt128 &other) const { return ((hi_ != other.hi_) || (lo_ != other.lo_)); } inline UInt128 UInt128::operator+(const UInt128 &other) const { UInt128 sum; sum.lo_ = lo_ + other.lo_; sum.hi_ = hi_ + other.hi_ + (sum.lo_ < lo_); return sum; } inline UInt128 UInt128::operator*(const uint &other) const { UInt128 product; // FIXME product.hi_ = 0; product.lo_ = lo_ * other; return product; } inline UInt128 UInt128::operator<<(const int &shift) const { UInt128 shifted; if (shift < 64) return UInt128((hi_<>(64-shift)), lo_ << shift); return UInt128(hi_<<(shift-64), 0); } inline UInt128 UInt128::operator~() const { return UInt128(~hi_, ~lo_); } inline UInt128 UInt128::operator&(const UInt128 &other) const { return UInt128(hi_ & other.hi_, lo_ & other.lo_); } inline UInt128 UInt128::operator|(const UInt128 &other) const { return UInt128(hi_ | other.hi_, lo_ | other.lo_); } template <> inline UInt128 qFromBigEndian(const uchar *src) { quint64 hi, lo; hi = qFromBigEndian(src); lo = qFromBigEndian(src+8); return UInt128(hi, lo); } template <> inline UInt128 qToBigEndian(const UInt128 src) { quint64 hi, lo; hi = qToBigEndian(src.hi64()); lo = qToBigEndian(src.lo64()); return UInt128(hi, lo); } inline uint qHash(const UInt128 &key) { return qHash(key.hi64()) ^ qHash(key.lo64()); } #endif ostinato-0.9/common/updater.cpp000066400000000000000000000106041321315675200166700ustar00rootroot00000000000000/* Copyright (C) 2015 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "updater.h" #include #include #include #include extern const char* version; Updater::Updater() { http_ = NULL; file_ = NULL; #if 1 // Tests! Q_ASSERT(isVersionNewer("1.1", "1") == true); Q_ASSERT(isVersionNewer("10.1", "2") == true); Q_ASSERT(isVersionNewer("0.10", "0.2") == true); Q_ASSERT(isVersionNewer("1.10.1", "1.2.3") == true); Q_ASSERT(isVersionNewer("0.7.1", "0.8") == false); #endif } Updater::~Updater() { delete http_; delete file_; } void Updater::checkForNewVersion() { QString host("update.ostinato.org"); QHttpRequestHeader reqHdr("GET", "/update/pad.xml"); http_ = new QHttp(host); file_ = new QTemporaryFile(); reqHdr.setValue("Host", host); reqHdr.setValue("User-Agent", userAgent()); connect(http_, SIGNAL(responseHeaderReceived(QHttpResponseHeader)), this, SLOT(responseReceived(QHttpResponseHeader))); connect(http_, SIGNAL(requestFinished(int, bool)), this, SLOT(parseXml(int, bool))); connect(http_, SIGNAL(stateChanged(int)), this, SLOT(stateUpdate(int))); file_->open(); qDebug("Updater: PAD XML file - %s", qPrintable(file_->fileName())); http_->request(reqHdr, NULL, file_); qDebug("Updater: %s", qPrintable(http_->currentRequest().toString() .replace("\r\n", "\nUpdater: "))); } void Updater::stateUpdate(int state) { qDebug("Updater: state %d", state); } void Updater::responseReceived(QHttpResponseHeader response) { qDebug("Updater: HTTP/%d.%d %d %s", response.majorVersion(), response.minorVersion(), response.statusCode(), qPrintable(response.reasonPhrase())); } void Updater::parseXml(int /*id*/, bool error) { QXmlStreamReader xml; QString newVersion; if (error) { qDebug("Updater: %s", qPrintable(http_->errorString())); goto _exit; } // Close and reopen the file so that we read from the top file_->close(); file_->open(); xml.setDevice(file_); while (!xml.atEnd()) { xml.readNext(); if (xml.isStartElement() && (xml.name() == "Program_Version")) newVersion = xml.readElementText(); } qDebug("Updater: latest version = %s", qPrintable(newVersion)); if (!newVersion.isEmpty() && isVersionNewer(newVersion, QString(version))) emit newVersionAvailable(newVersion); _exit: // Job done, time to self-destruct deleteLater(); } bool Updater::isVersionNewer(QString newVersion, QString curVersion) { QStringList curVer = QString(curVersion).split('.'); QStringList newVer = QString(newVersion).split('.'); for (int i = 0; i < qMin(curVer.size(), newVer.size()); i++) { bool isOk; uint n = newVer.at(i).toUInt(&isOk); uint c = curVer.at(i).toUInt(&isOk); if (n > c) return true; else if (n < c) return false; } if (newVer.size() > curVer.size()) return true; return false; } QString Updater::userAgent() { QString ua = QString("Mozilla/5.0 (%1) %2/%3 (Qt/%6)") .arg(sysInfo()) .arg(QCoreApplication::instance()->applicationName()) .arg(version) .arg(qVersion()); return ua; } QString Updater::sysInfo() { #if defined(Q_OS_WIN32) return QString("Windows/0x%1").arg(QSysInfo::WindowsVersion, 0, 16); #elif defined(Q_OS_LINUX) return QString("Linux"); #elif defined(Q_OS_MAC) return QString("MacOSX/0x%1").arg(QSysInfo::MacintoshVersion, 0, 16); #elif defined(Q_OS_BSD4) return QString("BSD"); #elif defined(Q_OS_UNIX) return QString("Unix"); #else return QString("Unknown"); #endif } ostinato-0.9/common/updater.h000066400000000000000000000024321321315675200163350ustar00rootroot00000000000000/* Copyright (C) 2015 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _UPDATER_H #define _UPDATER_H #include #include class QHttp; class QTemporaryFile; class Updater : public QObject { Q_OBJECT public: Updater(); virtual ~Updater(); void checkForNewVersion(); static bool isVersionNewer(QString newVersion, QString curVersion); signals: void newVersionAvailable(QString); private slots: void stateUpdate(int state); void responseReceived(QHttpResponseHeader response); void parseXml(int id, bool error); private: QString userAgent(); QString sysInfo(); QHttp *http_; QTemporaryFile *file_; }; #endif ostinato-0.9/common/userscript.cpp000066400000000000000000000340661321315675200174370ustar00rootroot00000000000000/* Copyright (C) 2010, 2014 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "userscript.h" // // -------------------- UserScriptProtocol -------------------- // UserScriptProtocol::UserScriptProtocol(StreamBase *stream, AbstractProtocol *parent) : AbstractProtocol(stream, parent), userProtocol_(this) { isScriptValid_ = false; errorLineNumber_ = 0; userProtocolScriptValue_ = engine_.newQObject(&userProtocol_); engine_.globalObject().setProperty("protocol", userProtocolScriptValue_); QScriptValue meta = engine_.newQMetaObject(userProtocol_.metaObject()); engine_.globalObject().setProperty("Protocol", meta); } UserScriptProtocol::~UserScriptProtocol() { } AbstractProtocol* UserScriptProtocol::createInstance(StreamBase *stream, AbstractProtocol *parent) { return new UserScriptProtocol(stream, parent); } quint32 UserScriptProtocol::protocolNumber() const { return OstProto::Protocol::kUserScriptFieldNumber; } void UserScriptProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const { protocol.MutableExtension(OstProto::userScript)->CopyFrom(data); protocol.mutable_protocol_id()->set_id(protocolNumber()); } void UserScriptProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) { if (protocol.protocol_id().id() == protocolNumber() && protocol.HasExtension(OstProto::userScript)) data.MergeFrom(protocol.GetExtension(OstProto::userScript)); evaluateUserScript(); } QString UserScriptProtocol::name() const { return QString("%1:{UserScript} [EXPERIMENTAL]").arg(userProtocol_.name()); } QString UserScriptProtocol::shortName() const { return QString("%1:{Script} [EXPERIMENTAL]").arg(userProtocol_.name()); } quint32 UserScriptProtocol::protocolId(ProtocolIdType type) const { QScriptValue userFunction; QScriptValue userValue; if (!isScriptValid_) goto _do_default; userFunction = userProtocolScriptValue_.property("protocolId"); if (!userFunction.isValid()) goto _do_default; Q_ASSERT(userFunction.isFunction()); userValue = userFunction.call(QScriptValue(), QScriptValueList() << QScriptValue(&engine_, type)); Q_ASSERT(userValue.isValid()); Q_ASSERT(userValue.isNumber()); return userValue.toUInt32(); _do_default: return AbstractProtocol::protocolId(type); } int UserScriptProtocol::fieldCount() const { return userScript_fieldCount; } QVariant UserScriptProtocol::fieldData(int index, FieldAttrib attrib, int streamIndex) const { switch (index) { case userScript_program: switch(attrib) { case FieldName: return QString("UserProtocol"); case FieldValue: case FieldTextValue: return QString().fromStdString(data.program()); case FieldFrameValue: { if (!isScriptValid_) return QByteArray(); QScriptValue userFunction = userProtocolScriptValue_.property( "protocolFrameValue"); Q_ASSERT(userFunction.isValid()); Q_ASSERT(userFunction.isFunction()); QScriptValue userValue = userFunction.call(QScriptValue(), QScriptValueList() << QScriptValue(&engine_, streamIndex)); Q_ASSERT(userValue.isValid()); Q_ASSERT(userValue.isArray()); QByteArray fv; QList pktBuf; qScriptValueToSequence(userValue, pktBuf); fv.resize(pktBuf.size()); for (int i = 0; i < pktBuf.size(); i++) fv[i] = pktBuf.at(i) & 0xFF; return fv; } default: break; } break; default: qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, index); break; } return AbstractProtocol::fieldData(index, attrib, streamIndex); } bool UserScriptProtocol::setFieldData(int index, const QVariant &value, FieldAttrib attrib) { bool isOk = false; if (attrib != FieldValue) goto _exit; switch (index) { case userScript_program: { data.set_program(value.toString().toStdString()); evaluateUserScript(); break; } default: qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, index); break; } _exit: return isOk; } int UserScriptProtocol::protocolFrameSize(int streamIndex) const { if (!isScriptValid_) return 0; QScriptValue userFunction = userProtocolScriptValue_.property( "protocolFrameSize"); Q_ASSERT(userFunction.isValid()); Q_ASSERT(userFunction.isFunction()); QScriptValue userValue = userFunction.call(QScriptValue(), QScriptValueList() << QScriptValue(&engine_, streamIndex)); Q_ASSERT(userValue.isNumber()); return userValue.toInt32(); } bool UserScriptProtocol::isProtocolFrameSizeVariable() const { return userProtocol_.isProtocolFrameSizeVariable(); } int UserScriptProtocol::protocolFrameVariableCount() const { return AbstractProtocol::lcm( AbstractProtocol::protocolFrameVariableCount(), userProtocol_.protocolFrameVariableCount()); } quint32 UserScriptProtocol::protocolFrameCksum(int streamIndex, CksumType cksumType) const { QScriptValue userFunction; QScriptValue userValue; if (!isScriptValid_) goto _do_default; userFunction = userProtocolScriptValue_.property("protocolFrameCksum"); qDebug("userscript protoFrameCksum(): isValid:%d/isFunc:%d", userFunction.isValid(), userFunction.isFunction()); if (!userFunction.isValid()) goto _do_default; Q_ASSERT(userFunction.isFunction()); userValue = userFunction.call(QScriptValue(), QScriptValueList() << QScriptValue(&engine_, streamIndex) << QScriptValue(&engine_, cksumType)); Q_ASSERT(userValue.isValid()); Q_ASSERT(userValue.isNumber()); return userValue.toUInt32(); _do_default: return AbstractProtocol::protocolFrameCksum(streamIndex, cksumType); } void UserScriptProtocol::evaluateUserScript() const { QScriptValue userFunction; QScriptValue userValue; QString property; isScriptValid_ = false; errorLineNumber_ = userScriptLineCount(); // Reset all properties including the dynamic ones userProtocol_.reset(); userProtocolScriptValue_.setProperty("protocolFrameValue", QScriptValue()); userProtocolScriptValue_.setProperty("protocolFrameSize", QScriptValue()); userProtocolScriptValue_.setProperty("protocolFrameCksum", QScriptValue()); userProtocolScriptValue_.setProperty("protocolId", QScriptValue()); engine_.evaluate(fieldData(userScript_program, FieldValue).toString()); if (engine_.hasUncaughtException()) goto _error_exception; // Validate protocolFrameValue() property = QString("protocolFrameValue"); userFunction = userProtocolScriptValue_.property(property); qDebug("userscript property %s: isValid:%d/isFunc:%d", property.toAscii().constData(), userFunction.isValid(), userFunction.isFunction()); if (!userFunction.isValid()) { errorText_ = property + QString(" not set"); goto _error_exit; } if (!userFunction.isFunction()) { errorText_ = property + QString(" is not a function"); goto _error_exit; } userValue = userFunction.call(); if (engine_.hasUncaughtException()) goto _error_exception; qDebug("userscript property %s return value: isValid:%d/isArray:%d", property.toAscii().constData(), userValue.isValid(), userValue.isArray()); if (!userValue.isArray()) { errorText_ = property + QString(" does not return an array"); goto _error_exit; } // Validate protocolFrameSize() property = QString("protocolFrameSize"); userFunction = userProtocolScriptValue_.property(property); qDebug("userscript property %s: isValid:%d/isFunc:%d", property.toAscii().constData(), userFunction.isValid(), userFunction.isFunction()); if (!userFunction.isValid()) { errorText_ = property + QString(" not set"); goto _error_exit; } if (!userFunction.isFunction()) { errorText_ = property + QString(" is not a function"); goto _error_exit; } userValue = userFunction.call(); if (engine_.hasUncaughtException()) goto _error_exception; qDebug("userscript property %s return value: isValid:%d/isNumber:%d", property.toAscii().constData(), userValue.isValid(), userValue.isNumber()); if (!userValue.isNumber()) { errorText_ = property + QString(" does not return a number"); goto _error_exit; } // Validate protocolFrameCksum() [optional] property = QString("protocolFrameCksum"); userFunction = userProtocolScriptValue_.property(property); qDebug("userscript property %s: isValid:%d/isFunc:%d", property.toAscii().constData(), userFunction.isValid(), userFunction.isFunction()); if (!userFunction.isValid()) goto _skip_cksum; if (!userFunction.isFunction()) { errorText_ = property + QString(" is not a function"); goto _error_exit; } userValue = userFunction.call(); if (engine_.hasUncaughtException()) goto _error_exception; qDebug("userscript property %s return value: isValid:%d/isNumber:%d", property.toAscii().constData(), userValue.isValid(), userValue.isNumber()); if (!userValue.isNumber()) { errorText_ = property + QString(" does not return a number"); goto _error_exit; } _skip_cksum: // Validate protocolId() [optional] property = QString("protocolId"); userFunction = userProtocolScriptValue_.property(property); qDebug("userscript property %s: isValid:%d/isFunc:%d", property.toAscii().constData(), userFunction.isValid(), userFunction.isFunction()); if (!userFunction.isValid()) goto _skip_protocol_id; if (!userFunction.isFunction()) { errorText_ = property + QString(" is not a function"); goto _error_exit; } userValue = userFunction.call(); if (engine_.hasUncaughtException()) goto _error_exception; qDebug("userscript property %s return value: isValid:%d/isNumber:%d", property.toAscii().constData(), userValue.isValid(), userValue.isNumber()); if (!userValue.isNumber()) { errorText_ = property + QString(" does not return a number"); goto _error_exit; } _skip_protocol_id: errorText_ = QString(""); isScriptValid_ = true; return; _error_exception: errorLineNumber_ = engine_.uncaughtExceptionLineNumber(); errorText_ = engine_.uncaughtException().toString(); _error_exit: userProtocol_.reset(); return; } bool UserScriptProtocol::isScriptValid() const { return isScriptValid_; } int UserScriptProtocol::userScriptErrorLineNumber() const { return errorLineNumber_; } QString UserScriptProtocol::userScriptErrorText() const { return errorText_; } int UserScriptProtocol::userScriptLineCount() const { return fieldData(userScript_program, FieldValue).toString().count( QChar('\n')) + 1; } // // -------------------- UserProtocol -------------------- // UserProtocol::UserProtocol(AbstractProtocol *parent) : parent_ (parent) { reset(); } void UserProtocol::reset() { name_ = QString(); protocolFrameSizeVariable_ = false; protocolFrameVariableCount_ = 1; } QString UserProtocol::name() const { return name_; } void UserProtocol::setName(QString &name) { name_ = name; } bool UserProtocol::isProtocolFrameSizeVariable() const { return protocolFrameSizeVariable_; } void UserProtocol::setProtocolFrameSizeVariable(bool variable) { protocolFrameSizeVariable_ = variable; } int UserProtocol::protocolFrameVariableCount() const { return protocolFrameVariableCount_; } void UserProtocol::setProtocolFrameVariableCount(int count) { protocolFrameVariableCount_ = count; } quint32 UserProtocol::payloadProtocolId(UserProtocol::ProtocolIdType type) const { return parent_->payloadProtocolId( static_cast(type)); } int UserProtocol::protocolFrameOffset(int streamIndex) const { return parent_->protocolFrameOffset(streamIndex); } int UserProtocol::protocolFramePayloadSize(int streamIndex) const { return parent_->protocolFramePayloadSize(streamIndex); } bool UserProtocol::isProtocolFramePayloadValueVariable() const { return parent_->isProtocolFramePayloadValueVariable(); } bool UserProtocol::isProtocolFramePayloadSizeVariable() const { return parent_->isProtocolFramePayloadSizeVariable(); } int UserProtocol::protocolFramePayloadVariableCount() const { return parent_->protocolFramePayloadVariableCount(); } quint32 UserProtocol::protocolFrameHeaderCksum(int streamIndex, AbstractProtocol::CksumType cksumType) const { return parent_->protocolFrameHeaderCksum(streamIndex, cksumType); } quint32 UserProtocol::protocolFramePayloadCksum(int streamIndex, AbstractProtocol::CksumType cksumType) const { quint32 cksum; cksum = parent_->protocolFramePayloadCksum(streamIndex, cksumType); qDebug("UserProto:%s = %d", __FUNCTION__, cksum); return cksum; } /* vim: set shiftwidth=4 tabstop=8 softtabstop=4 expandtab: */ ostinato-0.9/common/userscript.h000066400000000000000000000110421321315675200170710ustar00rootroot00000000000000/* Copyright (C) 2010, 2014 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _USER_SCRIPT_H #define _USER_SCRIPT_H #include "abstractprotocol.h" #include "userscript.pb.h" #include #include class UserScriptProtocol; class UserProtocol : public QObject { Q_OBJECT; Q_ENUMS(ProtocolIdType); Q_ENUMS(CksumType); Q_PROPERTY(QString name READ name WRITE setName); Q_PROPERTY(bool protocolFrameSizeVariable READ isProtocolFrameSizeVariable WRITE setProtocolFrameSizeVariable); Q_PROPERTY(int protocolFrameVariableCount READ protocolFrameVariableCount WRITE setProtocolFrameVariableCount); public: enum ProtocolIdType { ProtocolIdLlc = AbstractProtocol::ProtocolIdLlc, ProtocolIdEth = AbstractProtocol::ProtocolIdEth, ProtocolIdIp = AbstractProtocol::ProtocolIdIp, ProtocolIdTcpUdp = AbstractProtocol::ProtocolIdTcpUdp }; enum CksumType { CksumIp = AbstractProtocol::CksumIp, CksumIpPseudo = AbstractProtocol::CksumIpPseudo, CksumTcpUdp = AbstractProtocol::CksumTcpUdp }; UserProtocol(AbstractProtocol *parent); public slots: void reset(); QString name() const; void setName(QString &name); bool isProtocolFrameSizeVariable() const; void setProtocolFrameSizeVariable(bool variable); int protocolFrameVariableCount() const; void setProtocolFrameVariableCount(int count); quint32 payloadProtocolId(UserProtocol::ProtocolIdType type) const; int protocolFrameOffset(int streamIndex = 0) const; int protocolFramePayloadSize(int streamIndex = 0) const; bool isProtocolFramePayloadValueVariable() const; bool isProtocolFramePayloadSizeVariable() const; int protocolFramePayloadVariableCount() const; quint32 protocolFrameHeaderCksum(int streamIndex = 0, AbstractProtocol::CksumType cksumType = AbstractProtocol::CksumIp) const; quint32 protocolFramePayloadCksum(int streamIndex = 0, AbstractProtocol::CksumType cksumType = AbstractProtocol::CksumIp) const; private: AbstractProtocol *parent_; QString name_; bool protocolFrameSizeVariable_; int protocolFrameVariableCount_; }; class UserScriptProtocol : public AbstractProtocol { public: enum userScriptfield { // Frame Fields userScript_program = 0, userScript_fieldCount }; UserScriptProtocol(StreamBase *stream, AbstractProtocol *parent = 0); virtual ~UserScriptProtocol(); static AbstractProtocol* createInstance(StreamBase *stream, AbstractProtocol *parent = 0); virtual quint32 protocolNumber() const; virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); virtual quint32 protocolId(ProtocolIdType type) const; virtual QString name() const; virtual QString shortName() const; virtual int fieldCount() const; virtual QVariant fieldData(int index, FieldAttrib attrib, int streamIndex = 0) const; virtual bool setFieldData(int index, const QVariant &value, FieldAttrib attrib = FieldValue); virtual int protocolFrameSize(int streamIndex = 0) const; virtual bool isProtocolFrameSizeVariable() const; virtual int protocolFrameVariableCount() const; virtual quint32 protocolFrameCksum(int streamIndex = 0, CksumType cksumType = CksumIp) const; void evaluateUserScript() const; bool isScriptValid() const; int userScriptErrorLineNumber() const; QString userScriptErrorText() const; private: int userScriptLineCount() const; OstProto::UserScript data; mutable QScriptEngine engine_; mutable UserProtocol userProtocol_; mutable QScriptValue userProtocolScriptValue_; mutable bool isScriptValid_; mutable int errorLineNumber_; mutable QString errorText_; }; #endif ostinato-0.9/common/userscript.proto000066400000000000000000000015361321315675200200140ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ import "protocol.proto"; package OstProto; // Sample Protocol message UserScript { optional string program = 1; } extend Protocol { optional UserScript userScript = 103; } ostinato-0.9/common/userscript.ui000066400000000000000000000033761321315675200172720ustar00rootroot00000000000000 UserScript 0 0 517 335 Form 10 0 QFrame::Panel QFrame::Sunken 4 4 4 4 4 Unknown Compile ostinato-0.9/common/userscriptconfig.cpp000066400000000000000000000057171321315675200206260ustar00rootroot00000000000000/* Copyright (C) 2010, 2014 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "userscriptconfig.h" #include "userscript.h" #include UserScriptConfigForm::UserScriptConfigForm(QWidget *parent) : AbstractProtocolConfigForm(parent) { setupUi(this); // The protocol_ (UserScriptProtocol) is a dummy protocol internal // to UserScriptConfigForm whose sole purpose is to "compile" the script // so that the configForm widget can display the compilation result. // It is *not* used for actual packet contents at any time protocol_ = new UserScriptProtocol(NULL); compileScript(); } UserScriptConfigForm::~UserScriptConfigForm() { delete protocol_; } UserScriptConfigForm* UserScriptConfigForm::createInstance() { return new UserScriptConfigForm; } void UserScriptConfigForm::loadWidget(AbstractProtocol *proto) { programEdit->setPlainText( proto->fieldData( UserScriptProtocol::userScript_program, AbstractProtocol::FieldValue ).toString()); compileScript(); } void UserScriptConfigForm::storeWidget(AbstractProtocol *proto) { proto->setFieldData( UserScriptProtocol::userScript_program, programEdit->toPlainText()); } // // ----- private methods // void UserScriptConfigForm::compileScript() { // storeWidget() will save the updated userscript into // the protocol_ which in turn triggers the protocol_ to // compile it storeWidget(protocol_); if (protocol_->isScriptValid()) { statusLabel->setText(QString("Success")); compileButton->setDisabled(true); } else { statusLabel->setText( QString("Error: %1: %2").arg( protocol_->userScriptErrorLineNumber()).arg( protocol_->userScriptErrorText())); compileButton->setEnabled(true); } } // // ----- private slots // void UserScriptConfigForm::on_programEdit_textChanged() { compileButton->setEnabled(true); } void UserScriptConfigForm::on_compileButton_clicked(bool /*checked*/) { compileScript(); if (!protocol_->isScriptValid()) { QMessageBox::critical(this, "Error", QString("%1: %2").arg( protocol_->userScriptErrorLineNumber()).arg( protocol_->userScriptErrorText())); } } ostinato-0.9/common/userscriptconfig.h000066400000000000000000000025661321315675200202720ustar00rootroot00000000000000/* Copyright (C) 2010, 2014 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _USER_SCRIPT_CONFIG_H #define _USER_SCRIPT_CONFIG_H #include "abstractprotocolconfig.h" #include "ui_userscript.h" class UserScriptProtocol; class UserScriptConfigForm : public AbstractProtocolConfigForm, private Ui::UserScript { Q_OBJECT public: UserScriptConfigForm(QWidget *parent = 0); virtual ~UserScriptConfigForm(); static UserScriptConfigForm* createInstance(); virtual void loadWidget(AbstractProtocol *proto); virtual void storeWidget(AbstractProtocol *proto); private: void compileScript(); UserScriptProtocol *protocol_; private slots: void on_programEdit_textChanged(); void on_compileButton_clicked(bool checked = false); }; #endif ostinato-0.9/common/vlan.cpp000066400000000000000000000155721321315675200161750ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "vlan.h" VlanProtocol::VlanProtocol(StreamBase *stream, AbstractProtocol *parent) : AbstractProtocol(stream, parent) { } VlanProtocol::~VlanProtocol() { } AbstractProtocol* VlanProtocol::createInstance(StreamBase *stream, AbstractProtocol *parent) { return new VlanProtocol(stream, parent); } quint32 VlanProtocol::protocolNumber() const { return OstProto::Protocol::kVlanFieldNumber; } void VlanProtocol::protoDataCopyInto(OstProto::Protocol &protocol) const { protocol.MutableExtension(OstProto::vlan)->CopyFrom(data); protocol.mutable_protocol_id()->set_id(protocolNumber()); } void VlanProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol) { if (protocol.protocol_id().id() == protocolNumber() && protocol.HasExtension(OstProto::vlan)) data.MergeFrom(protocol.GetExtension(OstProto::vlan)); } QString VlanProtocol::name() const { return QString("Vlan"); } QString VlanProtocol::shortName() const { return QString("Vlan"); } int VlanProtocol::fieldCount() const { return vlan_fieldCount; } AbstractProtocol::FieldFlags VlanProtocol::fieldFlags(int index) const { AbstractProtocol::FieldFlags flags; flags = AbstractProtocol::fieldFlags(index); switch (index) { case vlan_tpid: case vlan_prio: case vlan_cfiDei: case vlan_vlanId: break; // meta-fields case vlan_isOverrideTpid: flags &= ~FrameField; flags |= MetaField; break; } return flags; } QVariant VlanProtocol::fieldData(int index, FieldAttrib attrib, int streamIndex) const { switch (index) { case vlan_tpid: { quint16 tpid; tpid = data.is_override_tpid() ? data.tpid() : 0x8100; switch(attrib) { case FieldName: return QString("Tag Protocol Id"); case FieldValue: return tpid; case FieldTextValue: return QString("0x%1").arg(tpid, 2, BASE_HEX, QChar('0')); case FieldFrameValue: { QByteArray fv; fv.resize(2); qToBigEndian(tpid, (uchar*) fv.data()); return fv; } default: break; } break; } case vlan_prio: { uint prio = ((data.vlan_tag() >> 13) & 0x07); switch(attrib) { case FieldName: return QString("Priority"); case FieldValue: return prio; case FieldTextValue: return QString("%1").arg(prio); case FieldFrameValue: return QByteArray(1, (char) prio); case FieldBitSize: return 3; default: break; } break; } case vlan_cfiDei: { uint cfiDei = ((data.vlan_tag() >> 12) & 0x01); switch(attrib) { case FieldName: return QString("CFI/DEI"); case FieldValue: return cfiDei; case FieldTextValue: return QString("%1").arg(cfiDei); case FieldFrameValue: return QByteArray(1, (char) cfiDei); case FieldBitSize: return 1; default: break; } break; } case vlan_vlanId: { quint16 vlanId = (data.vlan_tag() & 0x0FFF); switch(attrib) { case FieldName: return QString("VLAN Id"); case FieldValue: return vlanId; case FieldTextValue: return QString("%1").arg(vlanId); case FieldFrameValue: { QByteArray fv; fv.resize(2); qToBigEndian((quint16) vlanId, (uchar*) fv.data()); return fv; } case FieldBitSize: return 12; default: break; } break; } // Meta fields case vlan_isOverrideTpid: switch(attrib) { case FieldValue: return data.is_override_tpid(); default: break; } break; default: break; } return AbstractProtocol::fieldData(index, attrib, streamIndex); } bool VlanProtocol::setFieldData(int index, const QVariant &value, FieldAttrib attrib) { bool isOk = false; if (attrib != FieldValue) goto _exit; switch (index) { case vlan_tpid: { uint tpid = value.toUInt(&isOk); if (isOk) data.set_tpid(tpid); break; } case vlan_prio: { uint prio = value.toUInt(&isOk); if (isOk) data.set_vlan_tag( ((prio & 0x07) << 13) | (data.vlan_tag() & 0x1FFF)); break; } case vlan_cfiDei: { uint cfiDei = value.toUInt(&isOk); if (isOk) data.set_vlan_tag( ((cfiDei & 0x01) << 12) | (data.vlan_tag() & 0xEFFF)); break; } case vlan_vlanId: { uint vlanId = value.toUInt(&isOk); if (isOk) data.set_vlan_tag( (vlanId & 0x0FFF) | (data.vlan_tag() & 0xF000)); break; } // Meta-Fields case vlan_isOverrideTpid: { bool override = value.toUInt(&isOk); if (isOk) data.set_is_override_tpid(override); break; } default: qFatal("%s: unimplemented case %d in switch", __PRETTY_FUNCTION__, index); break; } _exit: return isOk; } ostinato-0.9/common/vlan.h000066400000000000000000000034671321315675200156420ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _VLAN_H #define _VLAN_H #include "abstractprotocol.h" #include "vlan.pb.h" class VlanProtocol : public AbstractProtocol { public: enum Vlanfield { vlan_tpid, vlan_prio, vlan_cfiDei, vlan_vlanId, // meta-fields vlan_isOverrideTpid, vlan_fieldCount }; VlanProtocol(StreamBase *stream, AbstractProtocol *parent = 0); virtual ~VlanProtocol(); static AbstractProtocol* createInstance(StreamBase *stream, AbstractProtocol *parent = 0); virtual quint32 protocolNumber() const; virtual void protoDataCopyInto(OstProto::Protocol &protocol) const; virtual void protoDataCopyFrom(const OstProto::Protocol &protocol); virtual QString name() const; virtual QString shortName() const; virtual int fieldCount() const; virtual AbstractProtocol::FieldFlags fieldFlags(int index) const; virtual QVariant fieldData(int index, FieldAttrib attrib, int streamIndex = 0) const; virtual bool setFieldData(int index, const QVariant &value, FieldAttrib attrib = FieldValue); protected: OstProto::Vlan data; }; #endif ostinato-0.9/common/vlan.proto000066400000000000000000000017221321315675200165460ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ import "protocol.proto"; package OstProto; message Vlan { // VLAN presence/absence optional bool is_override_tpid = 1; // VLAN values optional uint32 tpid = 2; optional uint32 vlan_tag = 3; // includes prio, cfi and vlanid } extend Protocol { optional Vlan vlan = 205; } ostinato-0.9/common/vlan.ui000066400000000000000000000076541321315675200160320ustar00rootroot00000000000000 Vlan 0 0 274 106 Form true Override TPID Priority CFI/DEI VLAN false >HH HH; true 0 1 2 3 4 5 6 7 true 0 1 true 0 Qt::Horizontal 111 20 Qt::Vertical 20 51 cbTpidOverride toggled(bool) leTpid setEnabled(bool) 59 41 59 57 ostinato-0.9/common/vlanconfig.cpp000066400000000000000000000047331321315675200173600ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "vlanconfig.h" #include "vlan.h" VlanConfigForm::VlanConfigForm(QWidget *parent) : AbstractProtocolConfigForm(parent) { setupUi(this); } VlanConfigForm::~VlanConfigForm() { } VlanConfigForm* VlanConfigForm::createInstance() { return new VlanConfigForm; } void VlanConfigForm::loadWidget(AbstractProtocol *proto) { cbTpidOverride->setChecked( proto->fieldData( VlanProtocol::vlan_isOverrideTpid, AbstractProtocol::FieldValue ).toBool()); leTpid->setText(uintToHexStr( proto->fieldData( VlanProtocol::vlan_tpid, AbstractProtocol::FieldValue) .toUInt(), 2)); cmbPrio->setCurrentIndex( proto->fieldData( VlanProtocol::vlan_prio, AbstractProtocol::FieldValue) .toUInt()); cmbCfiDei->setCurrentIndex( proto->fieldData( VlanProtocol::vlan_cfiDei, AbstractProtocol::FieldValue) .toUInt()); leVlanId->setText( proto->fieldData( VlanProtocol::vlan_vlanId, AbstractProtocol::FieldValue) .toString()); } void VlanConfigForm::storeWidget(AbstractProtocol *proto) { bool isOk; proto->setFieldData( VlanProtocol::vlan_isOverrideTpid, cbTpidOverride->isChecked()); proto->setFieldData( VlanProtocol::vlan_tpid, leTpid->text().remove(QChar(' ')).toUInt(&isOk, BASE_HEX)); proto->setFieldData( VlanProtocol::vlan_prio, cmbPrio->currentIndex()); proto->setFieldData( VlanProtocol::vlan_cfiDei, cmbCfiDei->currentIndex()); proto->setFieldData( VlanProtocol::vlan_vlanId, leVlanId->text()); } ostinato-0.9/common/vlanconfig.h000066400000000000000000000021461321315675200170210ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _VLAN_CONFIG_H #define _VLAN_CONFIG_H #include "abstractprotocolconfig.h" #include "ui_vlan.h" class VlanConfigForm : public AbstractProtocolConfigForm, private Ui::Vlan { Q_OBJECT public: VlanConfigForm(QWidget *parent = 0); virtual ~VlanConfigForm(); static VlanConfigForm* createInstance(); virtual void loadWidget(AbstractProtocol *proto); virtual void storeWidget(AbstractProtocol *proto); }; #endif ostinato-0.9/common/vlanpdml.cpp000066400000000000000000000060531321315675200170440ustar00rootroot00000000000000/* Copyright (C) 2011 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "vlanpdml.h" #include "eth2.pb.h" #include "vlan.pb.h" PdmlVlanProtocol::PdmlVlanProtocol() { ostProtoId_ = OstProto::Protocol::kVlanFieldNumber; } PdmlProtocol* PdmlVlanProtocol::createInstance() { return new PdmlVlanProtocol(); } void PdmlVlanProtocol::preProtocolHandler(QString /*name*/, const QXmlStreamAttributes& /*attributes*/, int /*expectedPos*/, OstProto::Protocol *pbProto, OstProto::Stream *stream) { OstProto::Vlan *vlan = pbProto->MutableExtension(OstProto::vlan); vlan->set_tpid(0x8100); vlan->set_is_override_tpid(true); // If a eth2 protocol precedes vlan, we remove the eth2 protocol // 'coz the eth2.etherType is actually the vlan.tpid // // We assume that the current protocol is the last in the stream int index = stream->protocol_size() - 1; if ((index > 1) && (stream->protocol(index).protocol_id().id() == OstProto::Protocol::kVlanFieldNumber) && (stream->protocol(index - 1).protocol_id().id() == OstProto::Protocol::kEth2FieldNumber)) { stream->mutable_protocol()->SwapElements(index, index - 1); Q_ASSERT(stream->protocol(index).protocol_id().id() == OstProto::Protocol::kEth2FieldNumber); stream->mutable_protocol()->RemoveLast(); } } void PdmlVlanProtocol::unknownFieldHandler(QString name, int /*pos*/, int /*size*/, const QXmlStreamAttributes &attributes, OstProto::Protocol *pbProto, OstProto::Stream *stream) { if (name == "vlan.id") { bool isOk; OstProto::Vlan *vlan = pbProto->MutableExtension(OstProto::vlan); uint tag = attributes.value("unmaskedvalue").isEmpty() ? attributes.value("value").toString().toUInt(&isOk, kBaseHex) : attributes.value("unmaskedvalue").toString().toUInt(&isOk,kBaseHex); vlan->set_vlan_tag(tag); } else if (name == "vlan.etype") { OstProto::Protocol *proto = stream->add_protocol(); proto->mutable_protocol_id()->set_id( OstProto::Protocol::kEth2FieldNumber); bool isOk; OstProto::Eth2 *eth2 = proto->MutableExtension(OstProto::eth2); eth2->set_type(attributes.value("value") .toString().toUInt(&isOk, kBaseHex)); eth2->set_is_override_type(true); } } ostinato-0.9/common/vlanpdml.h000066400000000000000000000023661321315675200165140ustar00rootroot00000000000000/* Copyright (C) 2011 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _VLAN_PDML_H #define _VLAN_PDML_H #include "pdmlprotocol.h" class PdmlVlanProtocol : public PdmlProtocol { public: static PdmlProtocol* createInstance(); virtual void preProtocolHandler(QString name, const QXmlStreamAttributes &attributes, int expectedPos, OstProto::Protocol *pbProto, OstProto::Stream *stream); virtual void unknownFieldHandler(QString name, int pos, int size, const QXmlStreamAttributes &attributes, OstProto::Protocol *pbProto, OstProto::Stream *stream); protected: PdmlVlanProtocol(); }; #endif ostinato-0.9/common/vlanstack.h000066400000000000000000000016161321315675200166620ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _VLAN_STACK_H #define _VLAN_STACK_H #include "comboprotocol.h" #include "svlan.h" #include "vlan.h" typedef ComboProtocol VlanStackProtocol; #endif ostinato-0.9/common/vlanstack.proto000066400000000000000000000015511321315675200175740ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ import "protocol.proto"; package OstProto; // Stacked VLAN (2 tags) message VlanStack { // Empty since this is a 'combo' protocol } extend Protocol { optional VlanStack vlanStack = 208; } ostinato-0.9/common/vlanstackconfig.h000066400000000000000000000020701321315675200200430ustar00rootroot00000000000000/* Copyright (C) 2014 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _VLAN_STACK_CONFIG_H #define _VLAN_STACK_CONFIG_H #include "comboprotocolconfig.h" #include "svlanconfig.h" #include "vlanconfig.h" #include "svlan.h" #include "vlan.h" #include "protocol.pb.h" typedef ComboProtocolConfigForm < OstProto::Protocol::kVlanStackFieldNumber, SVlanConfigForm, VlanConfigForm, SVlanProtocol, VlanProtocol > VlanStackConfigForm; #endif ostinato-0.9/extra/000077500000000000000000000000001321315675200143525ustar00rootroot00000000000000ostinato-0.9/extra/extra.pro000066400000000000000000000000551321315675200162170ustar00rootroot00000000000000TEMPLATE = subdirs SUBDIRS = \ qhexedit2 ostinato-0.9/extra/qhexedit2/000077500000000000000000000000001321315675200162475ustar00rootroot00000000000000ostinato-0.9/extra/qhexedit2/qhexedit2.pro000066400000000000000000000004771321315675200206760ustar00rootroot00000000000000TEMPLATE = lib CONFIG += qt staticlib warn_on QT += core gui greaterThan(QT_MAJOR_VERSION, 4): QT += widgets VERSION = 4.0.0 DEFINES += QHEXEDIT_EXPORTS HEADERS = src/chunks.h\ src/commands.h \ src/qhexedit.h \ SOURCES = src/chunks.cpp \ src/commands.cpp \ src/qhexedit.cpp ostinato-0.9/extra/qhexedit2/src/000077500000000000000000000000001321315675200170365ustar00rootroot00000000000000ostinato-0.9/extra/qhexedit2/src/VERSION000066400000000000000000000000321321315675200201010ustar00rootroot00000000000000Release 0.8.4, 2017-01-16 ostinato-0.9/extra/qhexedit2/src/chunks.cpp000066400000000000000000000204441321315675200210410ustar00rootroot00000000000000#include "chunks.h" #include #define NORMAL 0 #define HIGHLIGHTED 1 #define BUFFER_SIZE 0x10000 #define CHUNK_SIZE 0x1000 #define READ_CHUNK_MASK Q_INT64_C(0xfffffffffffff000) // ***************************************** Constructors and file settings Chunks::Chunks(QObject *parent): QObject(parent) { QBuffer *buf = new QBuffer(this); setIODevice(*buf); } Chunks::Chunks(QIODevice &ioDevice, QObject *parent): QObject(parent) { setIODevice(ioDevice); } bool Chunks::setIODevice(QIODevice &ioDevice) { _ioDevice = &ioDevice; bool ok = _ioDevice->open(QIODevice::ReadOnly); if (ok) // Try to open IODevice { _size = _ioDevice->size(); _ioDevice->close(); } else // Fallback is an empty buffer { QBuffer *buf = new QBuffer(this); _ioDevice = buf; _size = 0; } _chunks.clear(); _pos = 0; return ok; } // ***************************************** Getting data out of Chunks QByteArray Chunks::data(qint64 pos, qint64 maxSize, QByteArray *highlighted) { qint64 ioDelta = 0; int chunkIdx = 0; Chunk chunk; QByteArray buffer; // Do some checks and some arrangements if (highlighted) highlighted->clear(); if (pos >= _size) return buffer; if (maxSize < 0) maxSize = _size; else if ((pos + maxSize) > _size) maxSize = _size - pos; _ioDevice->open(QIODevice::ReadOnly); while (maxSize > 0) { chunk.absPos = LLONG_MAX; bool chunksLoopOngoing = true; while ((chunkIdx < _chunks.count()) && chunksLoopOngoing) { // In this section, we track changes before our required data and // we take the editdet data, if availible. ioDelta is a difference // counter to justify the read pointer to the original data, if // data in between was deleted or inserted. chunk = _chunks[chunkIdx]; if (chunk.absPos > pos) chunksLoopOngoing = false; else { chunkIdx += 1; qint64 count; qint64 chunkOfs = pos - chunk.absPos; if (maxSize > ((qint64)chunk.data.size() - chunkOfs)) { count = (qint64)chunk.data.size() - chunkOfs; ioDelta += CHUNK_SIZE - chunk.data.size(); } else count = maxSize; if (count > 0) { buffer += chunk.data.mid(chunkOfs, (int)count); maxSize -= count; pos += count; if (highlighted) *highlighted += chunk.dataChanged.mid(chunkOfs, (int)count); } } } if ((maxSize > 0) && (pos < chunk.absPos)) { // In this section, we read data from the original source. This only will // happen, whe no copied data is available qint64 byteCount; QByteArray readBuffer; if ((chunk.absPos - pos) > maxSize) byteCount = maxSize; else byteCount = chunk.absPos - pos; maxSize -= byteCount; _ioDevice->seek(pos + ioDelta); readBuffer = _ioDevice->read(byteCount); buffer += readBuffer; if (highlighted) *highlighted += QByteArray(readBuffer.size(), NORMAL); pos += readBuffer.size(); } } _ioDevice->close(); return buffer; } bool Chunks::write(QIODevice &iODevice, qint64 pos, qint64 count) { if (count == -1) count = _size; bool ok = iODevice.open(QIODevice::WriteOnly); if (ok) { for (qint64 idx=pos; idx < count; idx += BUFFER_SIZE) { QByteArray ba = data(idx, BUFFER_SIZE); iODevice.write(ba); } iODevice.close(); } return ok; } // ***************************************** Set and get highlighting infos void Chunks::setDataChanged(qint64 pos, bool dataChanged) { if ((pos < 0) || (pos >= _size)) return; int chunkIdx = getChunkIndex(pos); qint64 posInBa = pos - _chunks[chunkIdx].absPos; _chunks[chunkIdx].dataChanged[(int)posInBa] = char(dataChanged); } bool Chunks::dataChanged(qint64 pos) { QByteArray highlighted; data(pos, 1, &highlighted); return bool(highlighted.at(0)); } // ***************************************** Search API qint64 Chunks::indexOf(const QByteArray &ba, qint64 from) { qint64 result = -1; QByteArray buffer; for (qint64 pos=from; (pos < _size) && (result < 0); pos += BUFFER_SIZE) { buffer = data(pos, BUFFER_SIZE + ba.size() - 1); int findPos = buffer.indexOf(ba); if (findPos >= 0) result = pos + (qint64)findPos; } return result; } qint64 Chunks::lastIndexOf(const QByteArray &ba, qint64 from) { qint64 result = -1; QByteArray buffer; for (qint64 pos=from; (pos > 0) && (result < 0); pos -= BUFFER_SIZE) { qint64 sPos = pos - BUFFER_SIZE - (qint64)ba.size() + 1; if (sPos < 0) sPos = 0; buffer = data(sPos, pos - sPos); int findPos = buffer.lastIndexOf(ba); if (findPos >= 0) result = sPos + (qint64)findPos; } return result; } // ***************************************** Char manipulations bool Chunks::insert(qint64 pos, char b) { if ((pos < 0) || (pos > _size)) return false; int chunkIdx; if (pos == _size) chunkIdx = getChunkIndex(pos-1); else chunkIdx = getChunkIndex(pos); qint64 posInBa = pos - _chunks[chunkIdx].absPos; _chunks[chunkIdx].data.insert(posInBa, b); _chunks[chunkIdx].dataChanged.insert(posInBa, char(1)); for (int idx=chunkIdx+1; idx < _chunks.size(); idx++) _chunks[idx].absPos += 1; _size += 1; _pos = pos; return true; } bool Chunks::overwrite(qint64 pos, char b) { if ((pos < 0) || (pos >= _size)) return false; int chunkIdx = getChunkIndex(pos); qint64 posInBa = pos - _chunks[chunkIdx].absPos; _chunks[chunkIdx].data[(int)posInBa] = b; _chunks[chunkIdx].dataChanged[(int)posInBa] = char(1); _pos = pos; return true; } bool Chunks::removeAt(qint64 pos) { if ((pos < 0) || (pos >= _size)) return false; int chunkIdx = getChunkIndex(pos); qint64 posInBa = pos - _chunks[chunkIdx].absPos; _chunks[chunkIdx].data.remove(posInBa, 1); _chunks[chunkIdx].dataChanged.remove(posInBa, 1); for (int idx=chunkIdx+1; idx < _chunks.size(); idx++) _chunks[idx].absPos -= 1; _size -= 1; _pos = pos; return true; } // ***************************************** Utility functions char Chunks::operator[](qint64 pos) { return data(pos, 1)[0]; } qint64 Chunks::pos() { return _pos; } qint64 Chunks::size() { return _size; } int Chunks::getChunkIndex(qint64 absPos) { // This routine checks, if there is already a copied chunk available. If os, it // returns a reference to it. If there is no copied chunk available, original // data will be copied into a new chunk. int foundIdx = -1; int insertIdx = 0; qint64 ioDelta = 0; for (int idx=0; idx < _chunks.size(); idx++) { Chunk chunk = _chunks[idx]; if ((absPos >= chunk.absPos) && (absPos < (chunk.absPos + chunk.data.size()))) { foundIdx = idx; break; } if (absPos < chunk.absPos) { insertIdx = idx; break; } ioDelta += chunk.data.size() - CHUNK_SIZE; insertIdx = idx + 1; } if (foundIdx == -1) { Chunk newChunk; qint64 readAbsPos = absPos - ioDelta; qint64 readPos = (readAbsPos & READ_CHUNK_MASK); _ioDevice->open(QIODevice::ReadOnly); _ioDevice->seek(readPos); newChunk.data = _ioDevice->read(CHUNK_SIZE); _ioDevice->close(); newChunk.absPos = absPos - (readAbsPos - readPos); newChunk.dataChanged = QByteArray(newChunk.data.size(), char(0)); _chunks.insert(insertIdx, newChunk); foundIdx = insertIdx; } return foundIdx; } #ifdef MODUL_TEST int Chunks::chunkSize() { return _chunks.size(); } #endif ostinato-0.9/extra/qhexedit2/src/chunks.h000066400000000000000000000037451321315675200205130ustar00rootroot00000000000000#ifndef CHUNKS_H #define CHUNKS_H /** \cond docNever */ /*! The Chunks class is the storage backend for QHexEdit. * * When QHexEdit loads data, Chunks access them using a QIODevice interface. When the app uses * a QByteArray interface, QBuffer is used to provide again a QIODevice like interface. No data * will be changed, therefore Chunks opens the QIODevice in QIODevice::ReadOnly mode. After every * access Chunks closes the QIODevice, that's why external applications can overwrite files while * QHexEdit shows them. * * When the the user starts to edit the data, Chunks creates a local copy of a chunk of data (4 * kilobytes) and notes all changes there. Parallel to that chunk, there is a second chunk, * which keep track of which bytes are changed and which not. * */ #include struct Chunk { QByteArray data; QByteArray dataChanged; qint64 absPos; }; class Chunks: public QObject { Q_OBJECT public: // Constructors and file settings Chunks(QObject *parent); Chunks(QIODevice &ioDevice, QObject *parent); bool setIODevice(QIODevice &ioDevice); // Getting data out of Chunks QByteArray data(qint64 pos=0, qint64 count=-1, QByteArray *highlighted=0); bool write(QIODevice &iODevice, qint64 pos=0, qint64 count=-1); // Set and get highlighting infos void setDataChanged(qint64 pos, bool dataChanged); bool dataChanged(qint64 pos); // Search API qint64 indexOf(const QByteArray &ba, qint64 from); qint64 lastIndexOf(const QByteArray &ba, qint64 from); // Char manipulations bool insert(qint64 pos, char b); bool overwrite(qint64 pos, char b); bool removeAt(qint64 pos); // Utility functions char operator[](qint64 pos); qint64 pos(); qint64 size(); private: int getChunkIndex(qint64 absPos); QIODevice * _ioDevice; qint64 _pos; qint64 _size; QList _chunks; #ifdef MODUL_TEST public: int chunkSize(); #endif }; /** \endcond docNever */ #endif // CHUNKS_H ostinato-0.9/extra/qhexedit2/src/commands.cpp000066400000000000000000000100411321315675200213370ustar00rootroot00000000000000#include "commands.h" #include // Helper class to store single byte commands class CharCommand : public QUndoCommand { public: enum CCmd {insert, removeAt, overwrite}; CharCommand(Chunks * chunks, CCmd cmd, qint64 charPos, char newChar, QUndoCommand *parent=0); void undo(); void redo(); bool mergeWith(const QUndoCommand *command); int id() const { return 1234; } private: Chunks * _chunks; qint64 _charPos; bool _wasChanged; char _newChar; char _oldChar; CCmd _cmd; }; CharCommand::CharCommand(Chunks * chunks, CCmd cmd, qint64 charPos, char newChar, QUndoCommand *parent) : QUndoCommand(parent) { _chunks = chunks; _charPos = charPos; _newChar = newChar; _cmd = cmd; } bool CharCommand::mergeWith(const QUndoCommand *command) { const CharCommand *nextCommand = static_cast(command); bool result = false; if (_cmd != CharCommand::removeAt) { if (nextCommand->_cmd == overwrite) if (nextCommand->_charPos == _charPos) { _newChar = nextCommand->_newChar; result = true; } } return result; } void CharCommand::undo() { switch (_cmd) { case insert: _chunks->removeAt(_charPos); break; case overwrite: _chunks->overwrite(_charPos, _oldChar); _chunks->setDataChanged(_charPos, _wasChanged); break; case removeAt: _chunks->insert(_charPos, _oldChar); _chunks->setDataChanged(_charPos, _wasChanged); break; } } void CharCommand::redo() { switch (_cmd) { case insert: _chunks->insert(_charPos, _newChar); break; case overwrite: _oldChar = (*_chunks)[_charPos]; _wasChanged = _chunks->dataChanged(_charPos); _chunks->overwrite(_charPos, _newChar); break; case removeAt: _oldChar = (*_chunks)[_charPos]; _wasChanged = _chunks->dataChanged(_charPos); _chunks->removeAt(_charPos); break; } } UndoStack::UndoStack(Chunks * chunks, QObject * parent) : QUndoStack(parent) { _chunks = chunks; _parent = parent; } void UndoStack::insert(qint64 pos, char c) { if ((pos >= 0) && (pos <= _chunks->size())) { QUndoCommand *cc = new CharCommand(_chunks, CharCommand::insert, pos, c); this->push(cc); } } void UndoStack::insert(qint64 pos, const QByteArray &ba) { if ((pos >= 0) && (pos <= _chunks->size())) { QString txt = QString(tr("Inserting %1 bytes")).arg(ba.size()); beginMacro(txt); for (int idx=0; idx < ba.size(); idx++) { QUndoCommand *cc = new CharCommand(_chunks, CharCommand::insert, pos + idx, ba.at(idx)); this->push(cc); } endMacro(); } } void UndoStack::removeAt(qint64 pos, qint64 len) { if ((pos >= 0) && (pos < _chunks->size())) { if (len==1) { QUndoCommand *cc = new CharCommand(_chunks, CharCommand::removeAt, pos, char(0)); this->push(cc); } else { QString txt = QString(tr("Delete %1 chars")).arg(len); beginMacro(txt); for (qint64 cnt=0; cnt= 0) && (pos < _chunks->size())) { QUndoCommand *cc = new CharCommand(_chunks, CharCommand::overwrite, pos, c); this->push(cc); } } void UndoStack::overwrite(qint64 pos, int len, const QByteArray &ba) { if ((pos >= 0) && (pos < _chunks->size())) { QString txt = QString(tr("Overwrite %1 chars")).arg(len); beginMacro(txt); removeAt(pos, len); insert(pos, ba); endMacro(); } } ostinato-0.9/extra/qhexedit2/src/commands.h000066400000000000000000000026431321315675200210150ustar00rootroot00000000000000#ifndef COMMANDS_H #define COMMANDS_H /** \cond docNever */ #include #include "chunks.h" /*! CharCommand is a class to provid undo/redo functionality in QHexEdit. A QUndoCommand represents a single editing action on a document. CharCommand is responsable for manipulations on single chars. It can insert. overwrite and remove characters. A manipulation stores allways two actions 1. redo (or do) action 2. undo action. CharCommand also supports command compression via mergeWidht(). This allows the user to execute a undo command contation e.g. 3 steps in a single command. If you for example insert a new byt "34" this means for the editor doing 3 steps: insert a "00", overwrite it with "03" and the overwrite it with "34". These 3 steps are combined into a single step, insert a "34". The byte array oriented commands are just put into a set of single byte commands, which are pooled together with the macroBegin() and macroEnd() functionality of Qt's QUndoStack. */ class UndoStack : public QUndoStack { Q_OBJECT public: UndoStack(Chunks *chunks, QObject * parent=0); void insert(qint64 pos, char c); void insert(qint64 pos, const QByteArray &ba); void removeAt(qint64 pos, qint64 len=1); void overwrite(qint64 pos, char c); void overwrite(qint64 pos, int len, const QByteArray &ba); private: Chunks * _chunks; QObject * _parent; }; /** \endcond docNever */ #endif // COMMANDS_H ostinato-0.9/extra/qhexedit2/src/license.txt000066400000000000000000000636411321315675200212330ustar00rootroot00000000000000 GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 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. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, 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 and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, 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 library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete 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 distribute a copy of this License along with the Library. 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 Library or any portion of it, thus forming a work based on the Library, 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) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, 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 Library, 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 Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you 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. If distribution of 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 satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be 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. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library 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. 9. 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 Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library 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 with this License. 11. 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 Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library 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 Library. 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. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library 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. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser 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 Library 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 Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, 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 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "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 LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. 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 LIBRARY 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 LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), 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 Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. 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 library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; 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. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it!ostinato-0.9/extra/qhexedit2/src/qhexedit.cpp000066400000000000000000001000701321315675200213530ustar00rootroot00000000000000#include #include #include #include #include #include "qhexedit.h" #include // ********************************************************************** Constructor, destructor QHexEdit::QHexEdit(QWidget *parent) : QAbstractScrollArea(parent) { _addressArea = true; _addressWidth = 4; _asciiArea = true; _overwriteMode = true; _highlighting = true; _readOnly = false; _cursorPosition = 0; _lastEventSize = 0; _hexCharsInLine = 47; _bytesPerLine = 16; _editAreaIsAscii = false; _hexCaps = false; _dynamicBytesPerLine = false; _chunks = new Chunks(this); _undoStack = new UndoStack(_chunks, this); #ifdef Q_OS_WIN32 setFont(QFont("Courier", 10)); #else setFont(QFont("Monospace", 10)); #endif setAddressAreaColor(this->palette().alternateBase().color()); setHighlightingColor(QColor(0xff, 0xff, 0x99, 0xff)); setSelectionColor(this->palette().highlight().color()); connect(&_cursorTimer, SIGNAL(timeout()), this, SLOT(updateCursor())); connect(verticalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(adjust())); connect(horizontalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(adjust())); connect(_undoStack, SIGNAL(indexChanged(int)), this, SLOT(dataChangedPrivate(int))); _cursorTimer.setInterval(500); _cursorTimer.start(); setAddressWidth(4); setAddressArea(true); setAsciiArea(true); setOverwriteMode(true); setHighlighting(true); setReadOnly(false); init(); } QHexEdit::~QHexEdit() { } // ********************************************************************** Properties void QHexEdit::setAddressArea(bool addressArea) { _addressArea = addressArea; adjust(); setCursorPosition(_cursorPosition); viewport()->update(); } bool QHexEdit::addressArea() { return _addressArea; } void QHexEdit::setAddressAreaColor(const QColor &color) { _addressAreaColor = color; viewport()->update(); } QColor QHexEdit::addressAreaColor() { return _addressAreaColor; } void QHexEdit::setAddressOffset(qint64 addressOffset) { _addressOffset = addressOffset; adjust(); setCursorPosition(_cursorPosition); viewport()->update(); } qint64 QHexEdit::addressOffset() { return _addressOffset; } void QHexEdit::setAddressWidth(int addressWidth) { _addressWidth = addressWidth; adjust(); setCursorPosition(_cursorPosition); viewport()->update(); } int QHexEdit::addressWidth() { qint64 size = _chunks->size(); int n = 1; if (size > Q_INT64_C(0x100000000)){ n += 8; size /= Q_INT64_C(0x100000000);} if (size > 0x10000){ n += 4; size /= 0x10000;} if (size > 0x100){ n += 2; size /= 0x100;} if (size > 0x10){ n += 1; size /= 0x10;} if (n > _addressWidth) return n; else return _addressWidth; } void QHexEdit::setAsciiArea(bool asciiArea) { if (!asciiArea) _editAreaIsAscii = false; _asciiArea = asciiArea; adjust(); setCursorPosition(_cursorPosition); viewport()->update(); } bool QHexEdit::asciiArea() { return _asciiArea; } void QHexEdit::setBytesPerLine(int count) { _bytesPerLine = count; _hexCharsInLine = count * 3 - 1; adjust(); setCursorPosition(_cursorPosition); viewport()->update(); } int QHexEdit::bytesPerLine() { return _bytesPerLine; } void QHexEdit::setCursorPosition(qint64 position) { // 1. delete old cursor _blink = false; viewport()->update(_cursorRect); // 2. Check, if cursor in range? if (position > (_chunks->size() * 2 - 1)) position = _chunks->size() * 2 - (_overwriteMode ? 1 : 0); if (position < 0) position = 0; // 3. Calc new position of cursor _bPosCurrent = position / 2; _pxCursorY = ((position / 2 - _bPosFirst) / _bytesPerLine + 1) * _pxCharHeight; int x = (position % (2 * _bytesPerLine)); if (_editAreaIsAscii) { _pxCursorX = x / 2 * _pxCharWidth + _pxPosAsciiX; _cursorPosition = position & 0xFFFFFFFFFFFFFFFELL; } else { _pxCursorX = (((x / 2) * 3) + (x % 2)) * _pxCharWidth + _pxPosHexX; _cursorPosition = position; } if (_overwriteMode) _cursorRect = QRect(_pxCursorX - horizontalScrollBar()->value(), _pxCursorY + _pxCursorWidth, _pxCharWidth, _pxCursorWidth); else _cursorRect = QRect(_pxCursorX - horizontalScrollBar()->value(), _pxCursorY - _pxCharHeight + 4, _pxCursorWidth, _pxCharHeight); // 4. Immediately draw new cursor _blink = true; viewport()->update(_cursorRect); emit currentAddressChanged(_bPosCurrent); } qint64 QHexEdit::cursorPosition(QPoint pos) { // Calc cursor position depending on a graphical position qint64 result = -1; int posX = pos.x() + horizontalScrollBar()->value(); int posY = pos.y() - 3; if ((posX >= _pxPosHexX) && (posX < (_pxPosHexX + (1 + _hexCharsInLine) * _pxCharWidth))) { _editAreaIsAscii = false; int x = (posX - _pxPosHexX) / _pxCharWidth; x = (x / 3) * 2 + x % 3; int y = (posY / _pxCharHeight) * 2 * _bytesPerLine; result = _bPosFirst * 2 + x + y; } else if (_asciiArea && (posX >= _pxPosAsciiX) && (posX < (_pxPosAsciiX + (1 + _bytesPerLine) * _pxCharWidth))) { _editAreaIsAscii = true; int x = 2 * (posX - _pxPosAsciiX) / _pxCharWidth; int y = (posY / _pxCharHeight) * 2 * _bytesPerLine; result = _bPosFirst * 2 + x + y; } return result; } qint64 QHexEdit::cursorPosition() { return _cursorPosition; } void QHexEdit::setData(const QByteArray &ba) { _data = ba; _bData.setData(_data); setData(_bData); } QByteArray QHexEdit::data() { return _chunks->data(0, -1); } void QHexEdit::setHighlighting(bool highlighting) { _highlighting = highlighting; viewport()->update(); } bool QHexEdit::highlighting() { return _highlighting; } void QHexEdit::setHighlightingColor(const QColor &color) { _brushHighlighted = QBrush(color); _penHighlighted = QPen(viewport()->palette().color(QPalette::WindowText)); viewport()->update(); } QColor QHexEdit::highlightingColor() { return _brushHighlighted.color(); } void QHexEdit::setOverwriteMode(bool overwriteMode) { _overwriteMode = overwriteMode; emit overwriteModeChanged(overwriteMode); } bool QHexEdit::overwriteMode() { return _overwriteMode; } void QHexEdit::setSelectionColor(const QColor &color) { _brushSelection = QBrush(color); _penSelection = QPen(Qt::white); viewport()->update(); } QColor QHexEdit::selectionColor() { return _brushSelection.color(); } bool QHexEdit::isReadOnly() { return _readOnly; } void QHexEdit::setReadOnly(bool readOnly) { _readOnly = readOnly; } void QHexEdit::setHexCaps(const bool isCaps) { if (_hexCaps != isCaps) { _hexCaps = isCaps; viewport()->update(); } } bool QHexEdit::hexCaps() { return _hexCaps; } void QHexEdit::setDynamicBytesPerLine(const bool isDynamic) { _dynamicBytesPerLine = isDynamic; resizeEvent(NULL); } bool QHexEdit::dynamicBytesPerLine() { return _dynamicBytesPerLine; } // ********************************************************************** Access to data of qhexedit bool QHexEdit::setData(QIODevice &iODevice) { bool ok = _chunks->setIODevice(iODevice); init(); dataChangedPrivate(); return ok; } QByteArray QHexEdit::dataAt(qint64 pos, qint64 count) { return _chunks->data(pos, count); } bool QHexEdit::write(QIODevice &iODevice, qint64 pos, qint64 count) { return _chunks->write(iODevice, pos, count); } // ********************************************************************** Char handling void QHexEdit::insert(qint64 index, char ch) { _undoStack->insert(index, ch); refresh(); } void QHexEdit::remove(qint64 index, qint64 len) { _undoStack->removeAt(index, len); refresh(); } void QHexEdit::replace(qint64 index, char ch) { _undoStack->overwrite(index, ch); refresh(); } // ********************************************************************** ByteArray handling void QHexEdit::insert(qint64 pos, const QByteArray &ba) { _undoStack->insert(pos, ba); refresh(); } void QHexEdit::replace(qint64 pos, qint64 len, const QByteArray &ba) { _undoStack->overwrite(pos, len, ba); refresh(); } // ********************************************************************** Utility functions void QHexEdit::ensureVisible() { if (_cursorPosition < (_bPosFirst * 2)) verticalScrollBar()->setValue((int)(_cursorPosition / 2 / _bytesPerLine)); if (_cursorPosition > ((_bPosFirst + (_rowsShown - 1)*_bytesPerLine) * 2)) verticalScrollBar()->setValue((int)(_cursorPosition / 2 / _bytesPerLine) - _rowsShown + 1); if (_pxCursorX < horizontalScrollBar()->value()) horizontalScrollBar()->setValue(_pxCursorX); if ((_pxCursorX + _pxCharWidth) > (horizontalScrollBar()->value() + viewport()->width())) horizontalScrollBar()->setValue(_pxCursorX + _pxCharWidth - viewport()->width()); viewport()->update(); } qint64 QHexEdit::indexOf(const QByteArray &ba, qint64 from) { qint64 pos = _chunks->indexOf(ba, from); if (pos > -1) { qint64 curPos = pos*2; setCursorPosition(curPos + ba.length()*2); resetSelection(curPos); setSelection(curPos + ba.length()*2); ensureVisible(); } return pos; } bool QHexEdit::isModified() { return _modified; } qint64 QHexEdit::lastIndexOf(const QByteArray &ba, qint64 from) { qint64 pos = _chunks->lastIndexOf(ba, from); if (pos > -1) { qint64 curPos = pos*2; setCursorPosition(curPos - 1); resetSelection(curPos); setSelection(curPos + ba.length()*2); ensureVisible(); } return pos; } void QHexEdit::redo() { _undoStack->redo(); setCursorPosition(_chunks->pos()*(_editAreaIsAscii ? 1 : 2)); refresh(); } QString QHexEdit::selectionToReadableString() { QByteArray ba = _chunks->data(getSelectionBegin(), getSelectionEnd() - getSelectionBegin()); return toReadable(ba); } void QHexEdit::setFont(const QFont &font) { QWidget::setFont(font); _pxCharWidth = fontMetrics().width(QLatin1Char('2')); _pxCharHeight = fontMetrics().height(); _pxGapAdr = _pxCharWidth / 2; _pxGapAdrHex = _pxCharWidth; _pxGapHexAscii = 2 * _pxCharWidth; _pxCursorWidth = _pxCharHeight / 7; _pxSelectionSub = _pxCharHeight / 5; viewport()->update(); } QString QHexEdit::toReadableString() { QByteArray ba = _chunks->data(); return toReadable(ba); } void QHexEdit::undo() { _undoStack->undo(); setCursorPosition(_chunks->pos()*(_editAreaIsAscii ? 1 : 2)); refresh(); } // ********************************************************************** Handle events void QHexEdit::keyPressEvent(QKeyEvent *event) { // Cursor movements if (event->matches(QKeySequence::MoveToNextChar)) { qint64 pos = _cursorPosition + 1; if (_editAreaIsAscii) pos += 1; setCursorPosition(pos); resetSelection(pos); } if (event->matches(QKeySequence::MoveToPreviousChar)) { qint64 pos = _cursorPosition - 1; if (_editAreaIsAscii) pos -= 1; setCursorPosition(pos); resetSelection(pos); } if (event->matches(QKeySequence::MoveToEndOfLine)) { qint64 pos = _cursorPosition - (_cursorPosition % (2 * _bytesPerLine)) + (2 * _bytesPerLine) - 1; setCursorPosition(pos); resetSelection(_cursorPosition); } if (event->matches(QKeySequence::MoveToStartOfLine)) { qint64 pos = _cursorPosition - (_cursorPosition % (2 * _bytesPerLine)); setCursorPosition(pos); resetSelection(_cursorPosition); } if (event->matches(QKeySequence::MoveToPreviousLine)) { setCursorPosition(_cursorPosition - (2 * _bytesPerLine)); resetSelection(_cursorPosition); } if (event->matches(QKeySequence::MoveToNextLine)) { setCursorPosition(_cursorPosition + (2 * _bytesPerLine)); resetSelection(_cursorPosition); } if (event->matches(QKeySequence::MoveToNextPage)) { setCursorPosition(_cursorPosition + (((_rowsShown - 1) * 2 * _bytesPerLine))); resetSelection(_cursorPosition); } if (event->matches(QKeySequence::MoveToPreviousPage)) { setCursorPosition(_cursorPosition - (((_rowsShown - 1) * 2 * _bytesPerLine))); resetSelection(_cursorPosition); } if (event->matches(QKeySequence::MoveToEndOfDocument)) { setCursorPosition(_chunks->size() * 2 ); resetSelection(_cursorPosition); } if (event->matches(QKeySequence::MoveToStartOfDocument)) { setCursorPosition(0); resetSelection(_cursorPosition); } // Select commands if (event->matches(QKeySequence::SelectAll)) { resetSelection(0); setSelection(2 * _chunks->size() + 1); } if (event->matches(QKeySequence::SelectNextChar)) { qint64 pos = _cursorPosition + 1; if (_editAreaIsAscii) pos += 1; setCursorPosition(pos); setSelection(pos); } if (event->matches(QKeySequence::SelectPreviousChar)) { qint64 pos = _cursorPosition - 1; if (_editAreaIsAscii) pos -= 1; setSelection(pos); setCursorPosition(pos); } if (event->matches(QKeySequence::SelectEndOfLine)) { qint64 pos = _cursorPosition - (_cursorPosition % (2 * _bytesPerLine)) + (2 * _bytesPerLine) - 1; setCursorPosition(pos); setSelection(pos); } if (event->matches(QKeySequence::SelectStartOfLine)) { qint64 pos = _cursorPosition - (_cursorPosition % (2 * _bytesPerLine)); setCursorPosition(pos); setSelection(pos); } if (event->matches(QKeySequence::SelectPreviousLine)) { qint64 pos = _cursorPosition - (2 * _bytesPerLine); setCursorPosition(pos); setSelection(pos); } if (event->matches(QKeySequence::SelectNextLine)) { qint64 pos = _cursorPosition + (2 * _bytesPerLine); setCursorPosition(pos); setSelection(pos); } if (event->matches(QKeySequence::SelectNextPage)) { qint64 pos = _cursorPosition + (((viewport()->height() / _pxCharHeight) - 1) * 2 * _bytesPerLine); setCursorPosition(pos); setSelection(pos); } if (event->matches(QKeySequence::SelectPreviousPage)) { qint64 pos = _cursorPosition - (((viewport()->height() / _pxCharHeight) - 1) * 2 * _bytesPerLine); setCursorPosition(pos); setSelection(pos); } if (event->matches(QKeySequence::SelectEndOfDocument)) { qint64 pos = _chunks->size() * 2; setCursorPosition(pos); setSelection(pos); } if (event->matches(QKeySequence::SelectStartOfDocument)) { qint64 pos = 0; setCursorPosition(pos); setSelection(pos); } // Edit Commands if (!_readOnly) { /* Cut */ if (event->matches(QKeySequence::Cut)) { QByteArray ba = _chunks->data(getSelectionBegin(), getSelectionEnd() - getSelectionBegin()).toHex(); for (qint64 idx = 32; idx < ba.size(); idx +=33) ba.insert(idx, "\n"); QClipboard *clipboard = QApplication::clipboard(); clipboard->setText(ba); if (_overwriteMode) { qint64 len = getSelectionEnd() - getSelectionBegin(); replace(getSelectionBegin(), (int)len, QByteArray((int)len, char(0))); } else { remove(getSelectionBegin(), getSelectionEnd() - getSelectionBegin()); } setCursorPosition(2 * getSelectionBegin()); resetSelection(2 * getSelectionBegin()); } else /* Paste */ if (event->matches(QKeySequence::Paste)) { QClipboard *clipboard = QApplication::clipboard(); QByteArray ba = QByteArray().fromHex(clipboard->text().toLatin1()); if (_overwriteMode) { ba = ba.left(std::min(ba.size(), (_chunks->size() - _bPosCurrent))); replace(_bPosCurrent, ba.size(), ba); } else insert(_bPosCurrent, ba); setCursorPosition(_cursorPosition + 2 * ba.size()); resetSelection(getSelectionBegin()); } else /* Delete char */ if (event->matches(QKeySequence::Delete)) { if (getSelectionBegin() != getSelectionEnd()) { _bPosCurrent = getSelectionBegin(); if (_overwriteMode) { QByteArray ba = QByteArray(getSelectionEnd() - getSelectionBegin(), char(0)); replace(_bPosCurrent, ba.size(), ba); } else { remove(_bPosCurrent, getSelectionEnd() - getSelectionBegin()); } } else { if (_overwriteMode) replace(_bPosCurrent, char(0)); else remove(_bPosCurrent, 1); } setCursorPosition(2 * _bPosCurrent); resetSelection(2 * _bPosCurrent); } else /* Backspace */ if ((event->key() == Qt::Key_Backspace) && (event->modifiers() == Qt::NoModifier)) { if (getSelectionBegin() != getSelectionEnd()) { _bPosCurrent = getSelectionBegin(); setCursorPosition(2 * _bPosCurrent); if (_overwriteMode) { QByteArray ba = QByteArray(getSelectionEnd() - getSelectionBegin(), char(0)); replace(_bPosCurrent, ba.size(), ba); } else { remove(_bPosCurrent, getSelectionEnd() - getSelectionBegin()); } resetSelection(2 * _bPosCurrent); } else { bool behindLastByte = false; if ((_cursorPosition / 2) == _chunks->size()) behindLastByte = true; _bPosCurrent -= 1; if (_overwriteMode) replace(_bPosCurrent, char(0)); else remove(_bPosCurrent, 1); if (!behindLastByte) _bPosCurrent -= 1; setCursorPosition(2 * _bPosCurrent); resetSelection(2 * _bPosCurrent); } } else /* undo */ if (event->matches(QKeySequence::Undo)) { undo(); } else /* redo */ if (event->matches(QKeySequence::Redo)) { redo(); } else if ((QApplication::keyboardModifiers() == Qt::NoModifier) || (QApplication::keyboardModifiers() == Qt::KeypadModifier) || (QApplication::keyboardModifiers() == Qt::ShiftModifier) || (QApplication::keyboardModifiers() == (Qt::AltModifier | Qt::ControlModifier)) || (QApplication::keyboardModifiers() == Qt::GroupSwitchModifier)) { /* Hex and ascii input */ int key; if (_editAreaIsAscii) key = (uchar)event->text()[0].toLatin1(); else key = int(event->text()[0].toLower().toLatin1()); if ((((key >= '0' && key <= '9') || (key >= 'a' && key <= 'f')) && _editAreaIsAscii == false) || (key >= ' ' && _editAreaIsAscii)) { if (getSelectionBegin() != getSelectionEnd()) { if (_overwriteMode) { qint64 len = getSelectionEnd() - getSelectionBegin(); replace(getSelectionBegin(), (int)len, QByteArray((int)len, char(0))); } else { remove(getSelectionBegin(), getSelectionEnd() - getSelectionBegin()); _bPosCurrent = getSelectionBegin(); } setCursorPosition(2 * _bPosCurrent); resetSelection(2 * _bPosCurrent); } // If insert mode, then insert a byte if (_overwriteMode == false) if ((_cursorPosition % 2) == 0) insert(_bPosCurrent, char(0)); // Change content if (_chunks->size() > 0) { char ch = key; if (!_editAreaIsAscii){ QByteArray hexValue = _chunks->data(_bPosCurrent, 1).toHex(); if ((_cursorPosition % 2) == 0) hexValue[0] = key; else hexValue[1] = key; ch = QByteArray().fromHex(hexValue)[0]; } replace(_bPosCurrent, ch); if (_editAreaIsAscii) setCursorPosition(_cursorPosition + 2); else setCursorPosition(_cursorPosition + 1); resetSelection(_cursorPosition); } } } } /* Copy */ if (event->matches(QKeySequence::Copy)) { QByteArray ba = _chunks->data(getSelectionBegin(), getSelectionEnd() - getSelectionBegin()).toHex(); for (qint64 idx = 32; idx < ba.size(); idx +=33) ba.insert(idx, "\n"); QClipboard *clipboard = QApplication::clipboard(); clipboard->setText(ba); } // Switch between insert/overwrite mode if ((event->key() == Qt::Key_Insert) && (event->modifiers() == Qt::NoModifier)) { setOverwriteMode(!overwriteMode()); setCursorPosition(_cursorPosition); } // switch from hex to ascii edit if (event->key() == Qt::Key_Tab && !_editAreaIsAscii){ _editAreaIsAscii = true; setCursorPosition(_cursorPosition); } // switch from ascii to hex edit if (event->key() == Qt::Key_Backtab && _editAreaIsAscii){ _editAreaIsAscii = false; setCursorPosition(_cursorPosition); } refresh(); } void QHexEdit::mouseMoveEvent(QMouseEvent * event) { _blink = false; viewport()->update(); qint64 actPos = cursorPosition(event->pos()); if (actPos >= 0) { setCursorPosition(actPos); setSelection(actPos); } } void QHexEdit::mousePressEvent(QMouseEvent * event) { _blink = false; viewport()->update(); qint64 cPos = cursorPosition(event->pos()); if (cPos >= 0) { resetSelection(cPos); setCursorPosition(cPos); } } void QHexEdit::paintEvent(QPaintEvent *event) { QPainter painter(viewport()); int pxOfsX = horizontalScrollBar()->value(); if (event->rect() != _cursorRect) { int pxPosStartY = _pxCharHeight; // draw some patterns if needed painter.fillRect(event->rect(), viewport()->palette().color(QPalette::Base)); if (_addressArea) painter.fillRect(QRect(-pxOfsX, event->rect().top(), _pxPosHexX - _pxGapAdrHex/2, height()), _addressAreaColor); if (_asciiArea) { int linePos = _pxPosAsciiX - (_pxGapHexAscii / 2); painter.setPen(Qt::gray); painter.drawLine(linePos - pxOfsX, event->rect().top(), linePos - pxOfsX, height()); } painter.setPen(viewport()->palette().color(QPalette::WindowText)); // paint address area if (_addressArea) { QString address; for (int row=0, pxPosY = _pxCharHeight; row <= (_dataShown.size()/_bytesPerLine); row++, pxPosY +=_pxCharHeight) { address = QString("%1").arg(_bPosFirst + row*_bytesPerLine + _addressOffset, _addrDigits, 16, QChar('0')); painter.drawText(_pxPosAdrX - pxOfsX, pxPosY, address); } } // paint hex and ascii area QPen colStandard = QPen(viewport()->palette().color(QPalette::WindowText)); painter.setBackgroundMode(Qt::TransparentMode); for (int row = 0, pxPosY = pxPosStartY; row <= _rowsShown; row++, pxPosY +=_pxCharHeight) { QByteArray hex; int pxPosX = _pxPosHexX - pxOfsX; int pxPosAsciiX2 = _pxPosAsciiX - pxOfsX; qint64 bPosLine = row * _bytesPerLine; for (int colIdx = 0; ((bPosLine + colIdx) < _dataShown.size() && (colIdx < _bytesPerLine)); colIdx++) { QColor c = viewport()->palette().color(QPalette::Base); painter.setPen(colStandard); qint64 posBa = _bPosFirst + bPosLine + colIdx; if ((getSelectionBegin() <= posBa) && (getSelectionEnd() > posBa)) { c = _brushSelection.color(); painter.setPen(_penSelection); } else { if (_highlighting) if (_markedShown.at((int)(posBa - _bPosFirst))) { c = _brushHighlighted.color(); painter.setPen(_penHighlighted); } } // render hex value QRect r; if (colIdx == 0) r.setRect(pxPosX, pxPosY - _pxCharHeight + _pxSelectionSub, 2*_pxCharWidth, _pxCharHeight); else r.setRect(pxPosX - _pxCharWidth, pxPosY - _pxCharHeight + _pxSelectionSub, 3*_pxCharWidth, _pxCharHeight); painter.fillRect(r, c); hex = _hexDataShown.mid((bPosLine + colIdx) * 2, 2); painter.drawText(pxPosX, pxPosY, hexCaps()?hex.toUpper():hex); pxPosX += 3*_pxCharWidth; // render ascii value if (_asciiArea) { int ch = (uchar)_dataShown.at(bPosLine + colIdx); if ( ch < 0x20 ) ch = '.'; r.setRect(pxPosAsciiX2, pxPosY - _pxCharHeight + _pxSelectionSub, _pxCharWidth, _pxCharHeight); painter.fillRect(r, c); painter.drawText(pxPosAsciiX2, pxPosY, QChar(ch)); pxPosAsciiX2 += _pxCharWidth; } } } painter.setBackgroundMode(Qt::TransparentMode); painter.setPen(viewport()->palette().color(QPalette::WindowText)); } // paint cursor if (_blink && !_readOnly && hasFocus()) painter.fillRect(_cursorRect, this->palette().color(QPalette::WindowText)); else { painter.fillRect(QRect(_pxCursorX - pxOfsX, _pxCursorY - _pxCharHeight, _pxCharWidth, _pxCharHeight), viewport()->palette().color(QPalette::Base)); if (_editAreaIsAscii) { QByteArray ba = _dataShown.mid((_cursorPosition - _bPosFirst) / 2, 1); if (ba != "") { if (ba.at(0) <= ' ') ba[0] = '.'; painter.drawText(_pxCursorX - pxOfsX, _pxCursorY, ba); } } else { painter.drawText(_pxCursorX - pxOfsX, _pxCursorY, _hexDataShown.mid(_cursorPosition - _bPosFirst, 1)); } } // emit event, if size has changed if (_lastEventSize != _chunks->size()) { _lastEventSize = _chunks->size(); emit currentSizeChanged(_lastEventSize); } } void QHexEdit::resizeEvent(QResizeEvent *) { if (_dynamicBytesPerLine) { int pxFixGaps = 0; if (_addressArea) pxFixGaps = addressWidth() * _pxCharWidth + _pxGapAdr; pxFixGaps += _pxGapAdrHex; if (_asciiArea) pxFixGaps += _pxGapHexAscii; // +1 because the last hex value do not have space. so it is effective one char more int charWidth = (viewport()->width() - pxFixGaps ) / _pxCharWidth + 1; // 2 hex alfa-digits 1 space 1 ascii per byte = 4; if ascii is disabled then 3 // to prevent devision by zero use the min value 1 setBytesPerLine(std::max(charWidth / (_asciiArea ? 4 : 3),1)); } adjust(); } bool QHexEdit::focusNextPrevChild(bool next) { if (_addressArea) { if ( (next && _editAreaIsAscii) || (!next && !_editAreaIsAscii )) return QWidget::focusNextPrevChild(next); else return false; } else { return QWidget::focusNextPrevChild(next); } } // ********************************************************************** Handle selections void QHexEdit::resetSelection() { _bSelectionBegin = _bSelectionInit; _bSelectionEnd = _bSelectionInit; } void QHexEdit::resetSelection(qint64 pos) { pos = pos / 2 ; if (pos < 0) pos = 0; if (pos > _chunks->size()) pos = _chunks->size(); _bSelectionInit = pos; _bSelectionBegin = pos; _bSelectionEnd = pos; } void QHexEdit::setSelection(qint64 pos) { pos = pos / 2; if (pos < 0) pos = 0; if (pos > _chunks->size()) pos = _chunks->size(); if (pos >= _bSelectionInit) { _bSelectionEnd = pos; _bSelectionBegin = _bSelectionInit; } else { _bSelectionBegin = pos; _bSelectionEnd = _bSelectionInit; } } int QHexEdit::getSelectionBegin() { return _bSelectionBegin; } int QHexEdit::getSelectionEnd() { return _bSelectionEnd; } // ********************************************************************** Private utility functions void QHexEdit::init() { _undoStack->clear(); setAddressOffset(0); resetSelection(0); setCursorPosition(0); verticalScrollBar()->setValue(0); _modified = false; } void QHexEdit::adjust() { // recalc Graphics if (_addressArea) { _addrDigits = addressWidth(); _pxPosHexX = _pxGapAdr + _addrDigits*_pxCharWidth + _pxGapAdrHex; } else _pxPosHexX = _pxGapAdrHex; _pxPosAdrX = _pxGapAdr; _pxPosAsciiX = _pxPosHexX + _hexCharsInLine * _pxCharWidth + _pxGapHexAscii; // set horizontalScrollBar() int pxWidth = _pxPosAsciiX; if (_asciiArea) pxWidth += _bytesPerLine*_pxCharWidth; horizontalScrollBar()->setRange(0, pxWidth - viewport()->width()); horizontalScrollBar()->setPageStep(viewport()->width()); // set verticalScrollbar() _rowsShown = ((viewport()->height()-4)/_pxCharHeight); int lineCount = (int)(_chunks->size() / (qint64)_bytesPerLine) + 1; verticalScrollBar()->setRange(0, lineCount - _rowsShown); verticalScrollBar()->setPageStep(_rowsShown); int value = verticalScrollBar()->value(); _bPosFirst = (qint64)value * _bytesPerLine; _bPosLast = _bPosFirst + (qint64)(_rowsShown * _bytesPerLine) - 1; if (_bPosLast >= _chunks->size()) _bPosLast = _chunks->size() - 1; readBuffers(); setCursorPosition(_cursorPosition); } void QHexEdit::dataChangedPrivate(int) { _modified = _undoStack->index() != 0; adjust(); emit dataChanged(); } void QHexEdit::refresh() { ensureVisible(); readBuffers(); } void QHexEdit::readBuffers() { _dataShown = _chunks->data(_bPosFirst, _bPosLast - _bPosFirst + _bytesPerLine + 1, &_markedShown); _hexDataShown = QByteArray(_dataShown.toHex()); } QString QHexEdit::toReadable(const QByteArray &ba) { QString result; for (int i=0; i < ba.size(); i += 16) { QString addrStr = QString("%1").arg(_addressOffset + i, addressWidth(), 16, QChar('0')); QString hexStr; QString ascStr; for (int j=0; j<16; j++) { if ((i + j) < ba.size()) { hexStr.append(" ").append(ba.mid(i+j, 1).toHex()); char ch = ba[i + j]; if ((ch < 0x20) || (ch > 0x7e)) ch = '.'; ascStr.append(QChar(ch)); } } result += addrStr + " " + QString("%1").arg(hexStr, -48) + " " + QString("%1").arg(ascStr, -17) + "\n"; } return result; } void QHexEdit::updateCursor() { if (_blink) _blink = false; else _blink = true; viewport()->update(_cursorRect); } ostinato-0.9/extra/qhexedit2/src/qhexedit.h000066400000000000000000000370641321315675200210340ustar00rootroot00000000000000#ifndef QHEXEDIT_H #define QHEXEDIT_H #include #include #include #include "chunks.h" #include "commands.h" #ifdef QHEXEDIT_EXPORTS #define QHEXEDIT_API Q_DECL_EXPORT #elif QHEXEDIT_IMPORTS #define QHEXEDIT_API Q_DECL_IMPORT #else #define QHEXEDIT_API #endif /** \mainpage QHexEdit is a binary editor widget for Qt. \version Version 0.8.3 \image html qhexedit.png */ /** QHexEdit is a hex editor widget written in C++ for the Qt (Qt4, Qt5) framework. It is a simple editor for binary data, just like QPlainTextEdit is for text data. There are sip configuration files included, so it is easy to create bindings for PyQt and you can use this widget also in python 2 and 3. QHexEdit takes the data of a QByteArray (setData()) and shows it. You can use the mouse or the keyboard to navigate inside the widget. If you hit the keys (0..9, a..f) you will change the data. Changed data is highlighted and can be accessed via data(). Normaly QHexEdit works in the overwrite Mode. You can set overwriteMode(false) and insert data. In this case the size of data() increases. It is also possible to delete bytes (del or backspace), here the size of data decreases. You can select data with keyboard hits or mouse movements. The copy-key will copy the selected data into the clipboard. The cut-key copies also but delets it afterwards. In overwrite mode, the paste function overwrites the content of the (does not change the length) data. In insert mode, clipboard data will be inserted. The clipboard content is expected in ASCII Hex notation. Unknown characters will be ignored. QHexEdit comes with undo/redo functionality. All changes can be undone, by pressing the undo-key (usually ctr-z). They can also be redone afterwards. The undo/redo framework is cleared, when setData() sets up a new content for the editor. You can search data inside the content with indexOf() and lastIndexOf(). The replace() function is to change located subdata. This 'replaced' data can also be undone by the undo/redo framework. QHexEdit is based on QIODevice, that's why QHexEdit can handle big amounts of data. The size of edited data can be more then two gigabytes without any restrictions. */ class QHEXEDIT_API QHexEdit : public QAbstractScrollArea { Q_OBJECT /*! Property address area switch the address area on or off. Set addressArea true (show it), false (hide it). */ Q_PROPERTY(bool addressArea READ addressArea WRITE setAddressArea) /*! Property address area color sets (setAddressAreaColor()) the backgorund color of address areas. You can also read the color (addressaAreaColor()). */ Q_PROPERTY(QColor addressAreaColor READ addressAreaColor WRITE setAddressAreaColor) /*! Property addressOffset is added to the Numbers of the Address Area. A offset in the address area (left side) is sometimes usefull, whe you show only a segment of a complete memory picture. With setAddressOffset() you set this property - with addressOffset() you get the current value. */ Q_PROPERTY(qint64 addressOffset READ addressOffset WRITE setAddressOffset) /*! Set and get the minimum width of the address area, width in characters. */ Q_PROPERTY(int addressWidth READ addressWidth WRITE setAddressWidth) /*! Switch the ascii area on (true, show it) or off (false, hide it). */ Q_PROPERTY(bool asciiArea READ asciiArea WRITE setAsciiArea) /*! Set and get bytes number per line.*/ Q_PROPERTY(int bytesPerLine READ bytesPerLine WRITE setBytesPerLine) /*! Porperty cursorPosition sets or gets the position of the editor cursor in QHexEdit. Every byte in data has to cursor positions: the lower and upper Nibble. Maximum cursor position is factor two of data.size(). */ Q_PROPERTY(qint64 cursorPosition READ cursorPosition WRITE setCursorPosition) /*! Property data holds the content of QHexEdit. Call setData() to set the content of QHexEdit, data() returns the actual content. When calling setData() with a QByteArray as argument, QHexEdit creates a internal copy of the data If you want to edit big files please use setData(), based on QIODevice. */ Q_PROPERTY(QByteArray data READ data WRITE setData NOTIFY dataChanged) /*! That property defines if the hex values looks as a-f if the value is false(default) or A-F if value is true. */ Q_PROPERTY(bool hexCaps READ hexCaps WRITE setHexCaps) /*! Property defines the dynamic calculation of bytesPerLine parameter depends of width of widget. set this property true to avoid horizontal scrollbars and show the maximal possible data. defalut value is false*/ Q_PROPERTY(bool dynamicBytesPerLine READ dynamicBytesPerLine WRITE setDynamicBytesPerLine) /*! Switch the highlighting feature on or of: true (show it), false (hide it). */ Q_PROPERTY(bool highlighting READ highlighting WRITE setHighlighting) /*! Property highlighting color sets (setHighlightingColor()) the backgorund color of highlighted text areas. You can also read the color (highlightingColor()). */ Q_PROPERTY(QColor highlightingColor READ highlightingColor WRITE setHighlightingColor) /*! Porperty overwrite mode sets (setOverwriteMode()) or gets (overwriteMode()) the mode in which the editor works. In overwrite mode the user will overwrite existing data. The size of data will be constant. In insert mode the size will grow, when inserting new data. */ Q_PROPERTY(bool overwriteMode READ overwriteMode WRITE setOverwriteMode) /*! Property selection color sets (setSelectionColor()) the backgorund color of selected text areas. You can also read the color (selectionColor()). */ Q_PROPERTY(QColor selectionColor READ selectionColor WRITE setSelectionColor) /*! Porperty readOnly sets (setReadOnly()) or gets (isReadOnly) the mode in which the editor works. In readonly mode the the user can only navigate through the data and select data; modifying is not possible. This property's default is false. */ Q_PROPERTY(bool readOnly READ isReadOnly WRITE setReadOnly) /*! Set the font of the widget. Please use fixed width fonts like Mono or Courier.*/ Q_PROPERTY(QFont font READ font WRITE setFont) public: /*! Creates an instance of QHexEdit. \param parent Parent widget of QHexEdit. */ QHexEdit(QWidget *parent=0); // Access to data of qhexedit /*! Sets the data of QHexEdit. The QIODevice will be opend just before reading and closed immediately afterwards. This is to allow other programs to rewrite the file while editing it. */ bool setData(QIODevice &iODevice); /*! Givs back the data as a QByteArray starting at position \param pos and delivering \param count bytes. */ QByteArray dataAt(qint64 pos, qint64 count=-1); /*! Givs back the data into a \param iODevice starting at position \param pos and delivering \param count bytes. */ bool write(QIODevice &iODevice, qint64 pos=0, qint64 count=-1); // Char handling /*! Inserts a char. \param pos Index position, where to insert \param ch Char, which is to insert The char will be inserted and size of data grows. */ void insert(qint64 pos, char ch); /*! Removes len bytes from the content. \param pos Index position, where to remove \param len Amount of bytes to remove */ void remove(qint64 pos, qint64 len=1); /*! Replaces a char. \param pos Index position, where to overwrite \param ch Char, which is to insert The char will be overwritten and size remains constant. */ void replace(qint64 pos, char ch); // ByteArray handling /*! Inserts a byte array. \param pos Index position, where to insert \param ba QByteArray, which is to insert The QByteArray will be inserted and size of data grows. */ void insert(qint64 pos, const QByteArray &ba); /*! Replaces \param len bytes with a byte array \param ba \param pos Index position, where to overwrite \param ba QByteArray, which is inserted \param len count of bytes to overwrite The data is overwritten and size of data may change. */ void replace(qint64 pos, qint64 len, const QByteArray &ba); // Utility functioins /*! Calc cursor position from graphics position * \param point from where the cursor position should be calculated * \return Cursor postioin */ qint64 cursorPosition(QPoint point); /*! Ensure the cursor to be visble */ void ensureVisible(); /*! Find first occurence of ba in QHexEdit data * \param ba Data to find * \param from Point where the search starts * \return pos if fond, else -1 */ qint64 indexOf(const QByteArray &ba, qint64 from); /*! Returns if any changes where done on document * \return true when document is modified else false */ bool isModified(); /*! Find last occurence of ba in QHexEdit data * \param ba Data to find * \param from Point where the search starts * \return pos if fond, else -1 */ qint64 lastIndexOf(const QByteArray &ba, qint64 from); /*! Gives back a formatted image of the selected content of QHexEdit */ QString selectionToReadableString(); /*! Set Font of QHexEdit * \param font */ void setFont(const QFont &font); /*! Gives back a formatted image of the content of QHexEdit */ QString toReadableString(); public slots: /*! Redoes the last operation. If there is no operation to redo, i.e. there is no redo step in the undo/redo history, nothing happens. */ void redo(); /*! Undoes the last operation. If there is no operation to undo, i.e. there is no undo step in the undo/redo history, nothing happens. */ void undo(); signals: /*! Contains the address, where the cursor is located. */ void currentAddressChanged(qint64 address); /*! Contains the size of the data to edit. */ void currentSizeChanged(qint64 size); /*! The signal is emitted every time, the data is changed. */ void dataChanged(); /*! The signal is emitted every time, the overwrite mode is changed. */ void overwriteModeChanged(bool state); /*! \cond docNever */ public: ~QHexEdit(); // Properties bool addressArea(); void setAddressArea(bool addressArea); QColor addressAreaColor(); void setAddressAreaColor(const QColor &color); qint64 addressOffset(); void setAddressOffset(qint64 addressArea); int addressWidth(); void setAddressWidth(int addressWidth); bool asciiArea(); void setAsciiArea(bool asciiArea); int bytesPerLine(); void setBytesPerLine(int count); qint64 cursorPosition(); void setCursorPosition(qint64 position); QByteArray data(); void setData(const QByteArray &ba); void setHexCaps(const bool isCaps); bool hexCaps(); void setDynamicBytesPerLine(const bool isDynamic); bool dynamicBytesPerLine(); bool highlighting(); void setHighlighting(bool mode); QColor highlightingColor(); void setHighlightingColor(const QColor &color); bool overwriteMode(); void setOverwriteMode(bool overwriteMode); bool isReadOnly(); void setReadOnly(bool readOnly); QColor selectionColor(); void setSelectionColor(const QColor &color); protected: // Handle events void keyPressEvent(QKeyEvent *event); void mouseMoveEvent(QMouseEvent * event); void mousePressEvent(QMouseEvent * event); void paintEvent(QPaintEvent *event); void resizeEvent(QResizeEvent *); virtual bool focusNextPrevChild(bool next); private: // Handle selections void resetSelection(qint64 pos); // set selectionStart and selectionEnd to pos void resetSelection(); // set selectionEnd to selectionStart void setSelection(qint64 pos); // set min (if below init) or max (if greater init) int getSelectionBegin(); int getSelectionEnd(); // Private utility functions void init(); void readBuffers(); QString toReadable(const QByteArray &ba); private slots: void adjust(); // recalc pixel positions void dataChangedPrivate(int idx=0); // emit dataChanged() signal void refresh(); // ensureVisible() and readBuffers() void updateCursor(); // update blinking cursor private: // Name convention: pixel positions start with _px int _pxCharWidth, _pxCharHeight; // char dimensions (dpendend on font) int _pxPosHexX; // X-Pos of HeaxArea int _pxPosAdrX; // X-Pos of Address Area int _pxPosAsciiX; // X-Pos of Ascii Area int _pxGapAdr; // gap left from AddressArea int _pxGapAdrHex; // gap between AddressArea and HexAerea int _pxGapHexAscii; // gap between HexArea and AsciiArea int _pxCursorWidth; // cursor width int _pxSelectionSub; // offset selection rect int _pxCursorX; // current cursor pos int _pxCursorY; // current cursor pos // Name convention: absolute byte positions in chunks start with _b qint64 _bSelectionBegin; // first position of Selection qint64 _bSelectionEnd; // end of Selection qint64 _bSelectionInit; // memory position of Selection qint64 _bPosFirst; // position of first byte shown qint64 _bPosLast; // position of last byte shown qint64 _bPosCurrent; // current position // variables to store the property values bool _addressArea; // left area of QHexEdit QColor _addressAreaColor; int _addressWidth; bool _asciiArea; qint64 _addressOffset; int _bytesPerLine; int _hexCharsInLine; bool _highlighting; bool _overwriteMode; QBrush _brushSelection; QPen _penSelection; QBrush _brushHighlighted; QPen _penHighlighted; bool _readOnly; bool _hexCaps; bool _dynamicBytesPerLine; // other variables bool _editAreaIsAscii; // flag about the ascii mode edited int _addrDigits; // real no of addressdigits, may be > addressWidth bool _blink; // help get cursor blinking QBuffer _bData; // buffer, when setup with QByteArray Chunks *_chunks; // IODevice based access to data QTimer _cursorTimer; // for blinking cursor qint64 _cursorPosition; // absolute positioin of cursor, 1 Byte == 2 tics QRect _cursorRect; // physical dimensions of cursor QByteArray _data; // QHexEdit's data, when setup with QByteArray QByteArray _dataShown; // data in the current View QByteArray _hexDataShown; // data in view, transformed to hex qint64 _lastEventSize; // size, which was emitted last time QByteArray _markedShown; // marked data in view bool _modified; // Is any data in editor modified? int _rowsShown; // lines of text shown UndoStack * _undoStack; // Stack to store edit actions for undo/redo /*! \endcond docNever */ }; #endif // QHEXEDIT_H ostinato-0.9/install.pri000066400000000000000000000005511321315675200154120ustar00rootroot00000000000000# A custom install path prefix can be provided by passing PREFIX=/absolute/path # to qmake; if one is not provided, we use the below defaults - isEmpty(PREFIX) { unix:PREFIX = "/usr/local/" macx:PREFIX = "/Applications/" win32:PREFIX = "../" } macx { target.path = $$PREFIX/Ostinato } else { target.path = $$PREFIX/bin } INSTALLS += target ostinato-0.9/ost.pro000066400000000000000000000006171321315675200145620ustar00rootroot00000000000000TEMPLATE = subdirs SUBDIRS = client server ostproto ostprotogui rpc extra client.target = client client.file = client/ostinato.pro client.depends = ostproto ostprotogui rpc extra server.target = server server.file = server/drone.pro server.depends = ostproto rpc ostproto.file = common/ostproto.pro ostprotogui.file = common/ostprotogui.pro ostprotogui.depends = extra rpc.file = rpc/pbrpc.pro ostinato-0.9/protobuf.pri000066400000000000000000000021121321315675200155770ustar00rootroot00000000000000# # Qt qmake integration with Google Protocol Buffers compiler protoc # # To compile protocol buffers with qt qmake, specify PROTOS variable and # include this file # # Example: # PROTOS = a.proto b.proto # include(protobuf.pri) # # By default protoc looks for .proto files (including the imported ones) in # the current directory where protoc is run. If you need to include additional # paths specify the PROTOPATH variable # PROTOPATH += . PROTOPATHS = for(p, PROTOPATH):PROTOPATHS += --proto_path=$${p} protobuf_decl.name = protobuf header protobuf_decl.input = PROTOS protobuf_decl.output = ${QMAKE_FILE_BASE}.pb.h protobuf_decl.commands = protoc --cpp_out="." $${PROTOPATHS} ${QMAKE_FILE_NAME} protobuf_decl.variable_out = GENERATED_FILES QMAKE_EXTRA_COMPILERS += protobuf_decl protobuf_impl.name = protobuf implementation protobuf_impl.input = PROTOS protobuf_impl.output = ${QMAKE_FILE_BASE}.pb.cc protobuf_impl.depends = ${QMAKE_FILE_BASE}.pb.h protobuf_impl.commands = $$escape_expand(\\n) protobuf_impl.variable_out = GENERATED_SOURCES QMAKE_EXTRA_COMPILERS += protobuf_impl ostinato-0.9/rpc/000077500000000000000000000000001321315675200140135ustar00rootroot00000000000000ostinato-0.9/rpc/pbhelper.h000066400000000000000000000131171321315675200157700ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _PB_HELPER_H #define _PB_HELPER_H #include #include #include #if 0 // not reqd. any longer? class PbHelper { public: // FIXME: Change msg from * to & void ForceSetSingularDefault(::google::protobuf::Message *msg) { const ::google::protobuf::Descriptor *desc; ::google::protobuf::Message::Reflection *refl; qDebug("In %s", __FUNCTION__); desc = msg->GetDescriptor(); refl = msg->GetReflection(); for (int i=0; i < desc->field_count(); i++) { const ::google::protobuf::FieldDescriptor *f; f = desc->field(i); // Ensure field is singular and not already set if (f->label() == ::google::protobuf::FieldDescriptor::LABEL_REPEATED) continue; if (refl->HasField(f)) continue; switch(f->type()) { case ::google::protobuf::FieldDescriptor::TYPE_DOUBLE: refl->SetDouble(f, refl->GetDouble(f)); break; case ::google::protobuf::FieldDescriptor::TYPE_FLOAT: refl->SetFloat(f, refl->GetFloat(f)); break; case ::google::protobuf::FieldDescriptor::TYPE_INT32: case ::google::protobuf::FieldDescriptor::TYPE_SINT32: case ::google::protobuf::FieldDescriptor::TYPE_SFIXED32: refl->SetInt32(f, refl->GetInt32(f)); break; case ::google::protobuf::FieldDescriptor::TYPE_INT64: case ::google::protobuf::FieldDescriptor::TYPE_SINT64: case ::google::protobuf::FieldDescriptor::TYPE_SFIXED64: refl->SetInt64(f, refl->GetInt64(f)); break; case ::google::protobuf::FieldDescriptor::TYPE_UINT32: case ::google::protobuf::FieldDescriptor::TYPE_FIXED32: refl->SetUInt32(f, refl->GetUInt32(f)); break; case ::google::protobuf::FieldDescriptor::TYPE_UINT64: case ::google::protobuf::FieldDescriptor::TYPE_FIXED64: refl->SetUInt64(f, refl->GetUInt64(f)); break; case ::google::protobuf::FieldDescriptor::TYPE_BOOL: refl->SetBool(f, refl->GetBool(f)); break; case ::google::protobuf::FieldDescriptor::TYPE_ENUM: refl->SetEnum(f, refl->GetEnum(f)); break; case ::google::protobuf::FieldDescriptor::TYPE_STRING: case ::google::protobuf::FieldDescriptor::TYPE_BYTES: refl->SetString(f, refl->GetString(f)); break; case ::google::protobuf::FieldDescriptor::TYPE_MESSAGE: case ::google::protobuf::FieldDescriptor::TYPE_GROUP: ForceSetSingularDefault(refl->MutableMessage(f)); // recursion! break; default: qDebug("unhandled Field Type"); break; } } } bool update( ::google::protobuf::Message *target, ::google::protobuf::Message *source) { // FIXME(HI): Depracate: use MergeFrom() directly qDebug("In %s", __FUNCTION__); target->MergeFrom(*source); return true; #if 0 ::google::protobuf::Message::Reflection *sourceRef; ::google::protobuf::Message::Reflection *targetRef; std::vector srcFieldList; if (source->GetDescriptor()->full_name() != target->GetDescriptor()->full_name()) goto _error_exit; sourceRef = source->GetReflection(); targetRef = target->GetReflection(); sourceRef->ListFields(&srcFieldList); for (uint i=0; i < srcFieldList.size(); i++) { const ::google::protobuf::FieldDescriptor *srcField, *targetField; srcField = srcFieldList[i]; targetField = target->GetDescriptor()->FindFieldByName( srcField->name()); switch(targetField->type()) { case ::google::protobuf::FieldDescriptor::TYPE_UINT32: targetRef->SetUInt32(targetField, sourceRef->GetUInt32(srcField)); break; case ::google::protobuf::FieldDescriptor::TYPE_BOOL: targetRef->SetBool(targetField, sourceRef->GetBool(srcField)); break; case ::google::protobuf::FieldDescriptor::TYPE_STRING: targetRef->SetString(targetField, sourceRef->GetString(srcField)); break; default: qDebug("unhandled Field Type"); break; } } _error_exit: qDebug("%s: error!", __FUNCTION__); return false; #endif } }; #endif #endif ostinato-0.9/rpc/pbqtio.h000066400000000000000000000014751321315675200154710ustar00rootroot00000000000000#ifndef _PBQTIO_H #define _PBQTIO_H #include class PbQtInputStream : public google::protobuf::io::CopyingInputStream { public: PbQtInputStream(QIODevice *dev) : dev_(dev) {}; int Read(void *buffer, int size) { if (dev_->bytesAvailable()) return dev_->read(static_cast(buffer), size); else return 0; } private: QIODevice *dev_; }; class PbQtOutputStream : public google::protobuf::io::CopyingOutputStream { public: PbQtOutputStream(QIODevice *dev) : dev_(dev) {}; bool Write(const void *buffer, int size) { if (dev_->write(static_cast(buffer), size) == size) return true; else return false; } private: QIODevice *dev_; }; #endif ostinato-0.9/rpc/pbrpc.pro000066400000000000000000000003371321315675200156460ustar00rootroot00000000000000TEMPLATE = lib CONFIG += qt staticlib QT += network DEFINES += HAVE_REMOTE LIBS += -lprotobuf HEADERS += rpcserver.h rpcconn.h pbrpccontroller.h pbrpcchannel.h pbqtio.h SOURCES += rpcserver.cpp rpcconn.cpp pbrpcchannel.cpp ostinato-0.9/rpc/pbrpcchannel.cpp000066400000000000000000000350411321315675200171610ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "pbrpcchannel.h" #include "pbqtio.h" #include #include static uchar msgBuf[4096]; PbRpcChannel::PbRpcChannel(QString serverName, quint16 port, const ::google::protobuf::Message ¬ifProto) : notifPrototype(notifProto) { isPending = false; pendingMethodId = -1; // don't care as long as isPending is false method = NULL; controller = NULL; done = NULL; response = NULL; mServerHost = serverName; mServerPort = port; mpSocket = new QTcpSocket(this); inStream = new google::protobuf::io::CopyingInputStreamAdaptor( new PbQtInputStream(mpSocket)); inStream->SetOwnsCopyingStream(true); outStream = new google::protobuf::io::CopyingOutputStreamAdaptor( new PbQtOutputStream(mpSocket)); outStream->SetOwnsCopyingStream(true); // FIXME: Not quite sure why this ain't working! // QMetaObject::connectSlotsByName(this); connect(mpSocket, SIGNAL(connected()), this, SLOT(on_mpSocket_connected())); connect(mpSocket, SIGNAL(disconnected()), this, SLOT(on_mpSocket_disconnected())); connect(mpSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(on_mpSocket_stateChanged(QAbstractSocket::SocketState))); connect(mpSocket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(on_mpSocket_error(QAbstractSocket::SocketError))); connect(mpSocket, SIGNAL(readyRead()), this, SLOT(on_mpSocket_readyRead())); } PbRpcChannel::~PbRpcChannel() { delete inStream; delete outStream; delete mpSocket; } void PbRpcChannel::establish() { qDebug("In %s", __FUNCTION__); mpSocket->connectToHost(mServerHost, mServerPort); } void PbRpcChannel::establish(QString serverName, quint16 port) { mServerHost = serverName; mServerPort = port; establish(); } void PbRpcChannel::tearDown() { qDebug("In %s", __FUNCTION__); mpSocket->disconnectFromHost(); } void PbRpcChannel::CallMethod( const ::google::protobuf::MethodDescriptor *method, ::google::protobuf::RpcController *controller, const ::google::protobuf::Message *req, ::google::protobuf::Message *response, ::google::protobuf::Closure* done) { char* msg = (char*) &msgBuf[0]; int len; bool ret; if (isPending) { RpcCall call; qDebug("RpcChannel: queueing rpc since method %d is pending;<----\n " "queued method = %d:%s\n" "queued message = \n%s\n---->", pendingMethodId, method->index(), method->name().c_str(), req->DebugString().c_str()); call.method = method; call.controller = controller; call.request = req; call.response = response; call.done = done; pendingCallList.append(call); qDebug("pendingCallList size = %d", pendingCallList.size()); Q_ASSERT(pendingCallList.size() < 100); return; } if (!req->IsInitialized()) { qWarning("RpcChannel: missing required fields in request <----"); qDebug("req = %s\n%s", method->input_type()->name().c_str(), req->DebugString().c_str()); qDebug("error = \n%s\n--->", req->InitializationErrorString().c_str()); controller->SetFailed("Required fields missing"); done->Run(); return; } pendingMethodId = method->index(); this->method=method; this->controller=controller; this->done=done; this->response=response; isPending = true; len = req->ByteSize(); *((quint16*)(msg+0)) = qToBigEndian(quint16(PB_MSG_TYPE_REQUEST)); // type *((quint16*)(msg+2)) = qToBigEndian(quint16(method->index())); // method id *((quint32*)(msg+4)) = qToBigEndian(quint32(len)); // len // Avoid printing stats since it happens every couple of seconds if (pendingMethodId != 13) { qDebug("client(%s) sending %d bytes <----", __FUNCTION__, PB_HDR_SIZE + len); BUFDUMP(msg, PB_HDR_SIZE); qDebug("method = %d:%s\n req = %s\n%s\n---->", method->index(), method->name().c_str(), method->input_type()->name().c_str(), req->DebugString().c_str()); } mpSocket->write(msg, PB_HDR_SIZE); ret = req->SerializeToZeroCopyStream(outStream); Q_ASSERT(ret == true); Q_UNUSED(ret); outStream->Flush(); } void PbRpcChannel::on_mpSocket_readyRead() { const uchar *msg; int msgLen; static bool parsing = false; static quint16 type, method; static quint32 len; _top: //qDebug("%s(entry): bytesAvail = %d", __FUNCTION__, mpSocket->bytesAvailable()); if (!parsing) { // Do we have an entire header? If not, we'll wait ... if (inStream->Next((const void**)&msg, &msgLen) == false) { qDebug("No more data or stream error"); goto _exit; } if (msgLen < PB_HDR_SIZE) { qDebug("read less than PB_HDR_SIZE bytes; putting back"); inStream->BackUp(msgLen); goto _exit; } type = qFromBigEndian(msg+0); method = qFromBigEndian(msg+2); len = qFromBigEndian(msg+4); if (msgLen > PB_HDR_SIZE) inStream->BackUp(msgLen - PB_HDR_SIZE); //BUFDUMP(msg, PB_HDR_SIZE); //qDebug("type = %hu, method = %hu, len = %u", type, method, len); parsing = true; } switch (type) { case PB_MSG_TYPE_BINBLOB: { static quint32 cumLen = 0; QIODevice *blob; int l = 0; blob = static_cast(controller)->binaryBlob(); Q_ASSERT(blob != NULL); msgLen = 0; while (cumLen < len) { if (inStream->Next((const void**)&msg, &msgLen) == false) { //qDebug("No more data or stream error"); goto _exit; } l = qMin(msgLen, int(len - cumLen)); blob->write((char*)msg, l); cumLen += l; //qDebug("%s: bin blob rcvd %d/%d/%d", __PRETTY_FUNCTION__, l, cumLen, len); } if (l < msgLen) { qDebug("read extra bytes after blob; putting back"); inStream->BackUp(msgLen - l); } qDebug("%s: bin blob rcvd %d/%d", __PRETTY_FUNCTION__, cumLen, len); if (cumLen < len) goto _exit; cumLen = 0; if (!isPending) { qWarning("not waiting for response"); goto _error_exit2; } if (pendingMethodId != method) { qWarning("invalid method id %d (expected = %d)", method, pendingMethodId); goto _error_exit2; } break; } case PB_MSG_TYPE_RESPONSE: { static quint32 cumLen = 0; static QByteArray buffer; int l = 0; if (!isPending) { qWarning("not waiting for response"); goto _error_exit; } if (pendingMethodId != method) { qWarning("invalid method id %d (expected = %d)", method, pendingMethodId); goto _error_exit; } msgLen = 0; while (cumLen < len) { if (inStream->Next((const void**)&msg, &msgLen) == false) { //qDebug("No more data or stream error"); goto _exit; } l = qMin(msgLen, int(len - cumLen)); buffer.append(QByteArray((char*)msg, l)); cumLen += l; //qDebug("%s: buffer rcvd %d/%d/%d", __PRETTY_FUNCTION__, l, cumLen, len); } if (l < msgLen) { qDebug("read extra bytes after response; putting back"); inStream->BackUp(msgLen - l); } #if 0 // not needed? if (cumLen < len) goto _exit; #endif if (len) response->ParseFromArray((const void*)buffer, len); cumLen = 0; buffer.resize(0); // Avoid printing stats if (method != 13) { qDebug("client(%s): Received Msg <---- ", __FUNCTION__); qDebug("method = %d:%s\nresp = %s\n%s\n---->", method, this->method->name().c_str(), this->method->output_type()->name().c_str(), response->DebugString().c_str()); } if (!response->IsInitialized()) { qWarning("RpcChannel: missing required fields in response <----"); qDebug("resp = %s\n%s", this->method->output_type()->name().c_str(), response->DebugString().c_str()); qDebug("error = \n%s\n--->", response->InitializationErrorString().c_str()); controller->SetFailed("Required fields missing"); } break; } case PB_MSG_TYPE_ERROR: { static quint32 cumLen = 0; static QByteArray error; int l = 0; msgLen = 0; while (cumLen < len) { if (inStream->Next((const void**)&msg, &msgLen) == false) { //qDebug("No more data or stream error"); goto _exit; } l = qMin(msgLen, int(len - cumLen)); error.append(QByteArray((char*)msg, l)); cumLen += l; //qDebug("%s: error rcvd %d/%d/%d", __PRETTY_FUNCTION__, l, cumLen, len); } if (l < msgLen) { qDebug("read extra bytes after error; putting back"); inStream->BackUp(msgLen - l); } qDebug("%s: error rcvd %d/%d", __PRETTY_FUNCTION__, cumLen, len); if (cumLen < len) goto _exit; static_cast(controller)->SetFailed( QString::fromUtf8(error, len)); cumLen = 0; error.resize(0); if (!isPending) { qWarning("not waiting for response"); goto _error_exit2; } if (pendingMethodId != method) { qWarning("invalid method id %d (expected = %d)", method, pendingMethodId); goto _error_exit2; } break; } case PB_MSG_TYPE_NOTIFY: { notif = notifPrototype.New(); if (!notif) { qWarning("failed to alloc notify"); goto _error_exit; } if (len) notif->ParseFromBoundedZeroCopyStream(inStream, len); qDebug("client(%s): Received Notif Msg <---- ", __FUNCTION__); qDebug("type = %d\nnotif = \n%s\n---->", method, notif->DebugString().c_str()); if (!notif->IsInitialized()) { qWarning("RpcChannel: missing required fields in notify <----"); qDebug("notify = \n%s", notif->DebugString().c_str()); qDebug("error = \n%s\n--->", notif->InitializationErrorString().c_str()); } else emit notification(method, notif); delete notif; notif = NULL; parsing = false; goto _exit; break; } default: qFatal("%s: unexpected type %d", __PRETTY_FUNCTION__, type); goto _error_exit; } done->Run(); pendingMethodId = -1; this->method = NULL; controller = NULL; response = NULL; isPending = false; parsing = false; if (pendingCallList.size()) { RpcCall call = pendingCallList.takeFirst(); qDebug("RpcChannel: executing queued method <----\n" "method = %d:%s\n" "req = %s\n%s\n---->", call.method->index(), call.method->name().c_str(), call.method->input_type()->name().c_str(), call.request->DebugString().c_str()); CallMethod(call.method, call.controller, call.request, call.response, call.done); } goto _exit; _error_exit: inStream->Skip(len); _error_exit2: parsing = false; qDebug("client(%s) discarding received msg <----", __FUNCTION__); qDebug("method = %d\n---->", method); _exit: // If we have some data still available continue reading/parsing if (inStream->Next((const void**)&msg, &msgLen)) { if (msgLen >= PB_HDR_SIZE) { inStream->BackUp(msgLen); qDebug("===>> MORE DATA PENDING (%d bytes)... CONTINUE", msgLen); goto _top; } } if (mpSocket->bytesAvailable()) qDebug("%s (exit): bytesAvail = %lld", __FUNCTION__, mpSocket->bytesAvailable()); return; } void PbRpcChannel::on_mpSocket_stateChanged( QAbstractSocket::SocketState socketState) { qDebug("In %s", __FUNCTION__); emit stateChanged(socketState); } void PbRpcChannel::on_mpSocket_connected() { qDebug("In %s", __FUNCTION__); emit connected(); } void PbRpcChannel::on_mpSocket_disconnected() { qDebug("In %s", __FUNCTION__); pendingMethodId = -1; method = NULL; controller = NULL; response = NULL; isPending = false; // \todo convert parsing from static to data member //parsing = false pendingCallList.clear(); emit disconnected(); } void PbRpcChannel::on_mpSocket_error(QAbstractSocket::SocketError socketError) { qDebug("In %s", __FUNCTION__); emit error(socketError); } ostinato-0.9/rpc/pbrpcchannel.h000066400000000000000000000075261321315675200166350ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _PB_RPC_CHANNEL_H #define _PB_RPC_CHANNEL_H #include #include #include #include #include #include #include #include "pbrpccommon.h" #include "pbrpccontroller.h" class PbRpcChannel : public QObject, public ::google::protobuf::RpcChannel { Q_OBJECT // If isPending is TRUE, then controller, done, response // and pendingMethodId correspond to the last method called by // the service stub bool isPending; int pendingMethodId; // controller, done, response are set to the corresponding values // passed by the stub to CallMethod(). They are reset to NULL when // we get a response back from the server in on_mpSocket_readyRead() // after calling done->Run(). /*! \todo (MED) : change controller, done and response to references instead of pointers? */ const ::google::protobuf::MethodDescriptor *method; ::google::protobuf::RpcController *controller; ::google::protobuf::Closure *done; ::google::protobuf::Message *response; typedef struct _RpcCall { const ::google::protobuf::MethodDescriptor *method; ::google::protobuf::RpcController *controller; const ::google::protobuf::Message *request; ::google::protobuf::Message *response; ::google::protobuf::Closure *done; } RpcCall; QList pendingCallList; const ::google::protobuf::Message ¬ifPrototype; ::google::protobuf::Message *notif; QString mServerHost; quint16 mServerPort; QTcpSocket *mpSocket; ::google::protobuf::io::CopyingInputStreamAdaptor *inStream; ::google::protobuf::io::CopyingOutputStreamAdaptor *outStream; public: PbRpcChannel(QString serverName, quint16 port, const ::google::protobuf::Message ¬ifProto); ~PbRpcChannel(); void establish(); void establish(QString serverName, quint16 port); void tearDown(); const QString serverName() const { return mpSocket->peerName(); } quint16 serverPort() const { return mServerPort; } QAbstractSocket::SocketState state() const { return mpSocket->state(); } void CallMethod(const ::google::protobuf::MethodDescriptor *method, ::google::protobuf::RpcController *controller, const ::google::protobuf::Message *req, ::google::protobuf::Message *response, ::google::protobuf::Closure* done); signals: void connected(); void disconnected(); void error(QAbstractSocket::SocketError socketError); void stateChanged(QAbstractSocket::SocketState socketState); void notification(int notifType, ::google::protobuf::Message *notifData); private slots: void on_mpSocket_connected(); void on_mpSocket_disconnected(); void on_mpSocket_stateChanged(QAbstractSocket::SocketState socketState); void on_mpSocket_error(QAbstractSocket::SocketError socketError); void on_mpSocket_readyRead(); }; #endif ostinato-0.9/rpc/pbrpccommon.h000066400000000000000000000022741321315675200165100ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _PB_RPC_COMMON_H #define _PB_RPC_COMMON_H // Print a HexDump #define BUFDUMP(ptr, len) qDebug("%s", QString(QByteArray((char*)(ptr), \ (len)).toHex()).toAscii().data()); /* ** RPC Header (8) ** - MSG_TYPE (2) ** - METHOD_ID/NOTIF_TYPE (2) ** - LEN (4) [not including this header] */ #define PB_HDR_SIZE 8 #define PB_MSG_TYPE_REQUEST 1 #define PB_MSG_TYPE_RESPONSE 2 #define PB_MSG_TYPE_BINBLOB 3 #define PB_MSG_TYPE_ERROR 4 #define PB_MSG_TYPE_NOTIFY 5 #endif ostinato-0.9/rpc/pbrpccontroller.h000066400000000000000000000053611321315675200174030ustar00rootroot00000000000000/* Copyright (C) 2010, 2014 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _PB_RPC_CONTROLLER_H #define _PB_RPC_CONTROLLER_H #include #include class QIODevice; /*! PbRpcController takes ownership of the 'request' and 'response' messages and will delete them when it itself is destroyed */ class PbRpcController : public ::google::protobuf::RpcController { public: PbRpcController(::google::protobuf::Message *request, ::google::protobuf::Message *response) { request_ = request; response_ = response; Reset(); } ~PbRpcController() { delete request_; delete response_; } ::google::protobuf::Message* request() { return request_; } ::google::protobuf::Message* response() { return response_; } // Client Side Methods void Reset() { failed = false; disconnect = false; notif = true; blob = NULL; errStr = ""; } bool Failed() const { return failed; } void StartCancel() { /*! \todo (MED) */} std::string ErrorText() const { return errStr.toStdString(); } // Server Side Methods void SetFailed(const QString &reason) { failed = true; errStr = reason; qWarning("%s", qPrintable(errStr)); } void SetFailed(const std::string &reason) { SetFailed(QString::fromStdString(reason)); } QString ErrorString() const { return errStr; } bool IsCanceled() const { return false; }; void NotifyOnCancel(::google::protobuf::Closure* /* callback */) { /*! \todo (MED) */ } void TriggerDisconnect() { disconnect = true; } bool Disconnect() const { return disconnect; } void EnableNotif(bool enabled) { notif = enabled; } bool NotifEnabled() { return notif; } // srivatsp added QIODevice* binaryBlob() { return blob; }; void setBinaryBlob(QIODevice *binaryBlob) { blob = binaryBlob; }; private: bool failed; bool disconnect; bool notif; QIODevice *blob; QString errStr; ::google::protobuf::Message *request_; ::google::protobuf::Message *response_; }; #endif ostinato-0.9/rpc/rpcconn.cpp000066400000000000000000000306551321315675200161720ustar00rootroot00000000000000/* Copyright (C) 2010, 2014 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "rpcconn.h" #include "pbqtio.h" #include "pbrpccommon.h" #include "pbrpccontroller.h" #include #include #include #include #include #include #include #include #include #include #include #include #include static QThreadStorage connId; RpcConnection::RpcConnection(int socketDescriptor, ::google::protobuf::Service *service) : socketDescriptor(socketDescriptor), service(service) { inStream = NULL; outStream = NULL; isPending = false; pendingMethodId = -1; // don't care as long as isPending is false isCompatCheckDone = false; isNotifEnabled = true; } RpcConnection::~RpcConnection() { qDebug("destroying connection to %s: %d", clientSock->peerAddress().toString().toAscii().constData(), clientSock->peerPort()); // If still connected, disconnect if (clientSock->state() != QAbstractSocket::UnconnectedState) { clientSock->disconnectFromHost(); clientSock->waitForDisconnected(); } delete inStream; delete outStream; delete clientSock; } void RpcConnection::start() { QString id = QString("[%1:%2] "); clientSock = new QTcpSocket; if (!clientSock->setSocketDescriptor(socketDescriptor)) { qWarning("Unable to initialize TCP socket for incoming connection"); return; } qDebug("clientSock Thread = %p", clientSock->thread()); qsrand(QDateTime::currentDateTime().toTime_t()); connId.setLocalData(new QString(id.arg(clientSock->peerAddress().toString()) .arg(clientSock->peerPort()))); qDebug("accepting new connection from %s: %d", clientSock->peerAddress().toString().toAscii().constData(), clientSock->peerPort()); inStream = new google::protobuf::io::CopyingInputStreamAdaptor( new PbQtInputStream(clientSock)); inStream->SetOwnsCopyingStream(true); outStream = new google::protobuf::io::CopyingOutputStreamAdaptor( new PbQtOutputStream(clientSock)); outStream->SetOwnsCopyingStream(true); connect(clientSock, SIGNAL(readyRead()), this, SLOT(on_clientSock_dataAvail())); connect(clientSock, SIGNAL(disconnected()), this, SLOT(on_clientSock_disconnected())); connect(clientSock, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(on_clientSock_error(QAbstractSocket::SocketError))); } void RpcConnection::writeHeader(char* header, quint16 type, quint16 method, quint32 length) { *((quint16*)(header+0)) = qToBigEndian(type); *((quint16*)(header+2)) = qToBigEndian(method); *((quint32*)(header+4)) = qToBigEndian(length); } void RpcConnection::sendRpcReply(PbRpcController *controller) { google::protobuf::Message *response = controller->response(); QIODevice *blob; char msgBuf[PB_HDR_SIZE]; char* const msg = &msgBuf[0]; int len; if (controller->Failed()) { QByteArray err = controller->ErrorString().toUtf8(); qWarning("rpc failed (%s)", qPrintable(controller->ErrorString())); len = err.size(); writeHeader(msg, PB_MSG_TYPE_ERROR, pendingMethodId, len); clientSock->write(msg, PB_HDR_SIZE); clientSock->write(err.constData(), len); goto _exit; } blob = controller->binaryBlob(); if (blob) { len = blob->size(); qDebug("is binary blob of len %d", len); writeHeader(msg, PB_MSG_TYPE_BINBLOB, pendingMethodId, len); clientSock->write(msg, PB_HDR_SIZE); blob->seek(0); while (!blob->atEnd()) { int l; len = blob->read(msg, sizeof(msgBuf)); l = clientSock->write(msg, len); Q_ASSERT(l == len); Q_UNUSED(l); } goto _exit; } if (!response->IsInitialized()) { qWarning("response missing required fields!! <----"); qDebug("response = \n%s" "missing = \n%s---->", response->DebugString().c_str(), response->InitializationErrorString().c_str()); qFatal("exiting"); goto _exit; } len = response->ByteSize(); writeHeader(msg, PB_MSG_TYPE_RESPONSE, pendingMethodId, len); // Avoid printing stats since it happens once every couple of seconds if (pendingMethodId != 13) { qDebug("Server(%s): sending %d bytes to client <----", __FUNCTION__, len + PB_HDR_SIZE); BUFDUMP(msg, 8); qDebug("method = %d\nreq = \n%s---->", pendingMethodId, response->DebugString().c_str()); } clientSock->write(msg, PB_HDR_SIZE); response->SerializeToZeroCopyStream(outStream); outStream->Flush(); if (pendingMethodId == 15) { isCompatCheckDone = true; isNotifEnabled = controller->NotifEnabled(); } _exit: if (controller->Disconnect()) clientSock->disconnectFromHost(); delete controller; isPending = false; } void RpcConnection::sendNotification(int notifType, SharedProtobufMessage notifData) { char msgBuf[PB_HDR_SIZE]; char* const msg = &msgBuf[0]; int len; if (!isCompatCheckDone) return; if (!isNotifEnabled) return; if (!notifData->IsInitialized()) { qWarning("notification missing required fields!! <----"); qDebug("notif = \n%s" "missing = \n%s---->", notifData->DebugString().c_str(), notifData->InitializationErrorString().c_str()); qFatal("exiting"); return; } len = notifData->ByteSize(); writeHeader(msg, PB_MSG_TYPE_NOTIFY, notifType, len); qDebug("Server(%s): sending %d bytes to client <----", __FUNCTION__, len + PB_HDR_SIZE); BUFDUMP(msg, 8); qDebug("notif = %d\ndata = \n%s---->", notifType, notifData->DebugString().c_str()); clientSock->write(msg, PB_HDR_SIZE); notifData->SerializeToZeroCopyStream(outStream); outStream->Flush(); } void RpcConnection::on_clientSock_disconnected() { qDebug("connection closed from %s: %d", clientSock->peerAddress().toString().toAscii().constData(), clientSock->peerPort()); deleteLater(); emit closed(); } void RpcConnection::on_clientSock_error(QAbstractSocket::SocketError socketError) { qDebug("%s (%d)", clientSock->errorString().toAscii().constData(), socketError); } void RpcConnection::on_clientSock_dataAvail() { uchar msg[PB_HDR_SIZE]; int msgLen; quint16 type, method; quint32 len; const ::google::protobuf::MethodDescriptor *methodDesc; ::google::protobuf::Message *req, *resp; PbRpcController *controller; QString error; bool disconnect = false; // Do we have enough bytes for a msg header? // If yes, peek into the header and get msg length if (clientSock->bytesAvailable() < PB_HDR_SIZE) return; msgLen = clientSock->peek((char*)msg, PB_HDR_SIZE); if (msgLen != PB_HDR_SIZE) { qWarning("asked to peek %d bytes, was given only %d bytes", PB_HDR_SIZE, msgLen); return; } len = qFromBigEndian(&msg[4]); // Is the full msg available to read? If not, wait till such time if (clientSock->bytesAvailable() < (PB_HDR_SIZE+len)) return; msgLen = clientSock->read((char*)msg, PB_HDR_SIZE); Q_ASSERT(msgLen == PB_HDR_SIZE); type = qFromBigEndian(&msg[0]); method = qFromBigEndian(&msg[2]); len = qFromBigEndian(&msg[4]); //qDebug("type = %d, method = %d, len = %d", type, method, len); if (type != PB_MSG_TYPE_REQUEST) { qDebug("server(%s): unexpected msg type %d (expected %d)", __FUNCTION__, type, PB_MSG_TYPE_REQUEST); error = QString("unexpected msg type %1; expected %2") .arg(type).arg(PB_MSG_TYPE_REQUEST); goto _error_exit; } // If RPC is not checkVersion, ensure compat check is already done if (!isCompatCheckDone && method != 15) { qDebug("server(%s): version compatibility check pending", __FUNCTION__); error = "version compatibility check pending"; disconnect = true; goto _error_exit; } if (method >= service->GetDescriptor()->method_count()) { qDebug("server(%s): invalid method id %d", __FUNCTION__, method); error = QString("invalid RPC method %1").arg(method); goto _error_exit; } methodDesc = service->GetDescriptor()->method(method); if (!methodDesc) { qDebug("server(%s): invalid method id %d", __FUNCTION__, method); error = QString("invalid RPC method %1").arg(method); goto _error_exit; } if (isPending) { qDebug("server(%s): rpc pending, try again", __FUNCTION__); error = QString("RPC %1() is pending; only one RPC allowed at a time; " "try again!").arg(QString::fromStdString( service->GetDescriptor()->method( pendingMethodId)->name())); goto _error_exit; } pendingMethodId = method; isPending = true; req = service->GetRequestPrototype(methodDesc).New(); resp = service->GetResponsePrototype(methodDesc).New(); if (len) { bool ok = req->ParseFromBoundedZeroCopyStream(inStream, len); if (!ok) qWarning("ParseFromBoundedZeroCopyStream fail " "for method %d and len %d", method, len); } if (!req->IsInitialized()) { qWarning("Missing required fields in request <----"); qDebug("method = %d\n" "req = \n%s" "missing = \n%s----->", method, req->DebugString().c_str(), req->InitializationErrorString().c_str()); error = QString("RPC %1() missing required fields in request - %2") .arg(QString::fromStdString( service->GetDescriptor()->method( pendingMethodId)->name()), QString(req->InitializationErrorString().c_str())); delete req; delete resp; goto _error_exit2; } if (method != 13) { qDebug("Server(%s): successfully received/parsed msg <----", __FUNCTION__); qDebug("method = %d\n" "req = \n%s---->", method, req->DebugString().c_str()); } controller = new PbRpcController(req, resp); //qDebug("before service->callmethod()"); service->CallMethod(methodDesc, controller, req, resp, google::protobuf::NewCallback(this, &RpcConnection::sendRpcReply, controller)); return; _error_exit: inStream->Skip(len); _error_exit2: qDebug("server(%s): return error %s for msg from client", __FUNCTION__, qPrintable(error)); pendingMethodId = method; isPending = true; controller = new PbRpcController(NULL, NULL); controller->SetFailed(error); if (disconnect) controller->TriggerDisconnect(); sendRpcReply(controller); return; } void RpcConnection::connIdMsgHandler(QtMsgType /*type*/, const char* msg) { if (connId.hasLocalData()) { QString newMsg(*connId.localData()); newMsg.append(msg); newMsg.replace(QChar('\n'), QString("\n").append(*connId.localData())); fprintf(stderr, "%s\n", qPrintable(newMsg)); fflush(stderr); return; } fprintf(stderr, "%s\n", msg); fflush(stderr); } ostinato-0.9/rpc/rpcconn.h000066400000000000000000000041341321315675200156300ustar00rootroot00000000000000/* Copyright (C) 2010, 2014 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _RPC_CONNECTION_H #define _RPC_CONNECTION_H #include "sharedprotobufmessage.h" #include // forward declarations class PbRpcController; class QTcpSocket; namespace google { namespace protobuf { class Service; namespace io { class CopyingInputStreamAdaptor; class CopyingOutputStreamAdaptor; } class Message; } } class RpcConnection : public QObject { Q_OBJECT public: RpcConnection(int socketDescriptor, ::google::protobuf::Service *service); virtual ~RpcConnection(); static void connIdMsgHandler(QtMsgType type, const char* msg); private: void writeHeader(char* header, quint16 type, quint16 method, quint32 length); void sendRpcReply(PbRpcController *controller); signals: void closed(); public slots: void sendNotification(int notifType, SharedProtobufMessage notifData); private slots: void start(); void on_clientSock_dataAvail(); void on_clientSock_error(QAbstractSocket::SocketError socketError); void on_clientSock_disconnected(); private: int socketDescriptor; QTcpSocket *clientSock; ::google::protobuf::Service *service; ::google::protobuf::io::CopyingInputStreamAdaptor *inStream; ::google::protobuf::io::CopyingOutputStreamAdaptor *outStream; bool isPending; int pendingMethodId; bool isCompatCheckDone; bool isNotifEnabled; }; #endif ostinato-0.9/rpc/rpcserver.cpp000066400000000000000000000045231321315675200165360ustar00rootroot00000000000000/* Copyright (C) 2010, 2014 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "rpcserver.h" #include "rpcconn.h" #include // FIXME: QThreadX till we change minimum version of Qt from Qt4.3+ to Qt4.4+ class QThreadX: public QThread { protected: virtual ~QThreadX() { qDebug("QThreadX going down!"); } void run() { exec(); } }; RpcServer::RpcServer() { service = NULL; qInstallMsgHandler(RpcConnection::connIdMsgHandler); } RpcServer::~RpcServer() { } bool RpcServer::registerService(::google::protobuf::Service *service, QHostAddress address, quint16 tcpPortNum) { this->service = service; if (!listen(address, tcpPortNum)) { qDebug("Unable to start the server on <%s>: %s", qPrintable(address.toString()), errorString().toAscii().constData()); return false; } qDebug("The server is running on %s: %d", serverAddress().toString().toAscii().constData(), serverPort()); return true; } void RpcServer::incomingConnection(int socketDescriptor) { QThread *thread = new QThreadX; // FIXME:QThreadX pending Qt4.4+ RpcConnection *conn = new RpcConnection(socketDescriptor, service); conn->moveToThread(thread); connect(thread, SIGNAL(started()), conn, SLOT(start())); // NOTE: conn "self-destructs" after emitting closed // use 'closed' to stop execution of the thread connect(conn, SIGNAL(closed()), thread, SLOT(quit())); // setup thread to "self-destruct" when it is done connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater())); connect(this, SIGNAL(notifyClients(int, SharedProtobufMessage)), conn, SLOT(sendNotification(int, SharedProtobufMessage))); thread->start(); } ostinato-0.9/rpc/rpcserver.h000066400000000000000000000025321321315675200162010ustar00rootroot00000000000000/* Copyright (C) 2010, 2014 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _RPC_SERVER_H #define _RPC_SERVER_H #include "sharedprotobufmessage.h" #include // forward declaration namespace google { namespace protobuf { class Service; class Message; } } class RpcServer : public QTcpServer { Q_OBJECT public: RpcServer(); //! \todo (LOW) use 'parent' param virtual ~RpcServer(); bool registerService(::google::protobuf::Service *service, QHostAddress address, quint16 tcpPortNum); signals: void notifyClients(int notifType, SharedProtobufMessage notifData); protected: void incomingConnection(int socketDescriptor); private: ::google::protobuf::Service *service; }; #endif ostinato-0.9/rpc/sharedprotobufmessage.h000066400000000000000000000041401321315675200205570ustar00rootroot00000000000000/* Copyright (C) 2015 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _SHARED_PROTOBUF_MESSAGE_H #define _SHARED_PROTOBUF_MESSAGE_H #include #include // TODO: Use QSharedPointer instead once the minimum Qt version becomes >= 4.5 template class SharedPointer { public: SharedPointer(T *ptr = 0) : ptr_(ptr) { mutex_ = new QMutex(); refCnt_ = new unsigned int; *refCnt_ = 1; qDebug("sharedptr %p(constr) refcnt %p(%u)", this, refCnt_, *refCnt_); } ~SharedPointer() { mutex_->lock(); (*refCnt_)--; if (*refCnt_ == 0) { delete ptr_; delete refCnt_; mutex_->unlock(); delete mutex_; qDebug("sharedptr %p destroyed", this); return; } qDebug("sharedptr %p(destr) refcnt %p(%u)", this, refCnt_, *refCnt_); mutex_->unlock(); } SharedPointer(const SharedPointer &other) { ptr_ = other.ptr_; refCnt_ = other.refCnt_; mutex_ = other.mutex_; mutex_->lock(); (*refCnt_)++; qDebug("sharedptr %p(copy) refcnt %p(%u)", this, refCnt_,*refCnt_); mutex_->unlock(); } T* operator->() const { return ptr_; } protected: T *ptr_; // use uint+mutex to simulate a QAtomicInt unsigned int *refCnt_; QMutex *mutex_; }; typedef class SharedPointer< ::google::protobuf::Message> SharedProtobufMessage; #endif ostinato-0.9/server/000077500000000000000000000000001321315675200145355ustar00rootroot00000000000000ostinato-0.9/server/abstractport.cpp000066400000000000000000000562251321315675200177630ustar00rootroot00000000000000/* Copyright (C) 2010-2012 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #define __STDC_FORMAT_MACROS #include "abstractport.h" #include "../common/abstractprotocol.h" #include "../common/streambase.h" #include "devicemanager.h" #include "packetbuffer.h" #include #include #include #include #include AbstractPort::AbstractPort(int id, const char *device) { isUsable_ = true; data_.mutable_port_id()->set_id(id); data_.set_name(device); //! \todo (LOW) admin enable/disable of port data_.set_is_enabled(true); data_.set_is_exclusive_control(false); isSendQueueDirty_ = false; rateAccuracy_ = kHighAccuracy; linkState_ = OstProto::LinkStateUnknown; minPacketSetSize_ = 1; deviceManager_ = new DeviceManager(this); maxStatsValue_ = ULLONG_MAX; // assume 64-bit stats memset((void*) &stats_, 0, sizeof(stats_)); resetStats(); } AbstractPort::~AbstractPort() { delete deviceManager_; } void AbstractPort::init() { } /*! Can we modify Port with these params? Should modify cause port dirty? */ bool AbstractPort::canModify(const OstProto::Port &port, bool *dirty) { bool allow = true; *dirty = false; if (port.has_transmit_mode() && (port.transmit_mode() != data_.transmit_mode())) { *dirty = true; allow = !isTransmitOn(); } if (port.has_is_tracking_stream_stats() && (port.is_tracking_stream_stats() != data_.is_tracking_stream_stats())) { *dirty = true; allow = !isTransmitOn(); } return allow; } bool AbstractPort::modify(const OstProto::Port &port) { bool ret = true; //! \todo Use reflection to find out which fields are set if (port.has_is_exclusive_control()) { bool val = port.is_exclusive_control(); ret = setExclusiveControl(val); if (ret) data_.set_is_exclusive_control(val); } if (port.has_transmit_mode()) data_.set_transmit_mode(port.transmit_mode()); if (port.has_is_tracking_stream_stats()) ret |= setTrackStreamStats(port.is_tracking_stream_stats()); if (port.has_user_name()) { data_.set_user_name(port.user_name()); } return ret; } DeviceManager* AbstractPort::deviceManager() { return deviceManager_; } StreamBase* AbstractPort::streamAtIndex(int index) { Q_ASSERT(index < streamList_.size()); return streamList_.at(index); } StreamBase* AbstractPort::stream(int streamId) { for (int i = 0; i < streamList_.size(); i++) { if ((uint)streamId == streamList_.at(i)->id()) return streamList_.at(i); } return NULL; } bool AbstractPort::addStream(StreamBase *stream) { streamList_.append(stream); isSendQueueDirty_ = true; return true; } bool AbstractPort::deleteStream(int streamId) { for (int i = 0; i < streamList_.size(); i++) { StreamBase *stream; if ((uint)streamId == streamList_.at(i)->id()) { stream = streamList_.takeAt(i); delete stream; isSendQueueDirty_ = true; return true; } } return false; } void AbstractPort::addNote(QString note) { QString notes = QString::fromStdString(data_.notes()); note.prepend("
  • "); note.append("
  • "); if (notes.isEmpty()) notes="Limitation(s)
      "; else notes.remove("
    "); notes.append(note); notes.append(""); data_.set_notes(notes.toStdString()); } bool AbstractPort::setTrackStreamStats(bool enable) { data_.set_is_tracking_stream_stats(enable); return true; } AbstractPort::Accuracy AbstractPort::rateAccuracy() { return rateAccuracy_; } bool AbstractPort::setRateAccuracy(Accuracy accuracy) { rateAccuracy_ = accuracy; return true; } void AbstractPort::updatePacketList() { switch(data_.transmit_mode()) { case OstProto::kSequentialTransmit: updatePacketListSequential(); break; case OstProto::kInterleavedTransmit: updatePacketListInterleaved(); break; default: Q_ASSERT(false); // Unreachable!!! break; } } void AbstractPort::updatePacketListSequential() { long sec = 0; long nsec = 0; qDebug("In %s", __FUNCTION__); // First sort the streams by ordinalValue qSort(streamList_.begin(), streamList_.end(), StreamBase::StreamLessThan); clearPacketList(); for (int i = 0; i < streamList_.size(); i++) { if (streamList_[i]->isEnabled()) { int len = 0; ulong n, x, y; ulong burstSize; double ibg = 0; quint64 ibg1 = 0, ibg2 = 0; quint64 nb1 = 0, nb2 = 0; double ipg = 0; quint64 ipg1 = 0, ipg2 = 0; quint64 npx1 = 0, npx2 = 0; quint64 npy1 = 0, npy2 = 0; quint64 loopDelay; ulong frameVariableCount = streamList_[i]->frameVariableCount(); // We derive n, x, y such that // n * x + y = total number of packets to be sent switch (streamList_[i]->sendUnit()) { case OstProto::StreamControl::e_su_bursts: burstSize = streamList_[i]->burstSize(); x = AbstractProtocol::lcm(frameVariableCount, burstSize); n = ulong(burstSize * streamList_[i]->numBursts()) / x; y = ulong(burstSize * streamList_[i]->numBursts()) % x; if (streamList_[i]->burstRate() > 0) { ibg = 1e9/double(streamList_[i]->burstRate()); ibg1 = quint64(ceil(ibg)); ibg2 = quint64(floor(ibg)); nb1 = quint64((ibg - double(ibg2)) * double(x)); nb2 = x - nb1; } loopDelay = ibg2; break; case OstProto::StreamControl::e_su_packets: x = frameVariableCount; n = 2; while (x < minPacketSetSize_) x = frameVariableCount*n++; n = streamList_[i]->numPackets() / x; y = streamList_[i]->numPackets() % x; burstSize = x + y; if (streamList_[i]->packetRate() > 0) { ipg = 1e9/double(streamList_[i]->packetRate()); ipg1 = quint64(ceil(ipg)); ipg2 = quint64(floor(ipg)); npx1 = quint64((ipg - double(ipg2)) * double(x)); npx2 = x - npx1; npy1 = quint64((ipg - double(ipg2)) * double(y)); npy2 = y - npy1; } loopDelay = ipg2; break; default: qWarning("Unhandled stream control unit %d", streamList_[i]->sendUnit()); continue; } qDebug("\nframeVariableCount = %lu", frameVariableCount); qDebug("n = %lu, x = %lu, y = %lu, burstSize = %lu", n, x, y, burstSize); qDebug("ibg = %g", ibg); qDebug("ibg1 = %" PRIu64, ibg1); qDebug("nb1 = %" PRIu64, nb1); qDebug("ibg2 = %" PRIu64, ibg2); qDebug("nb2 = %" PRIu64 "\n", nb2); qDebug("ipg = %g", ipg); qDebug("ipg1 = %" PRIu64, ipg1); qDebug("npx1 = %" PRIu64, npx1); qDebug("npy1 = %" PRIu64, npy1); qDebug("ipg2 = %" PRIu64, ipg2); qDebug("npx2 = %" PRIu64, npx2); qDebug("npy2 = %" PRIu64 "\n", npy2); if (n > 1) loopNextPacketSet(x, n, 0, loopDelay); else if (n == 0) x = 0; for (uint j = 0; j < (x+y); j++) { if (j == 0 || frameVariableCount > 1) { len = streamList_[i]->frameValue( pktBuf_, sizeof(pktBuf_), j); } if (len <= 0) continue; qDebug("q(%d, %d) sec = %lu nsec = %lu", i, j, sec, nsec); appendToPacketList(sec, nsec, pktBuf_, len); if ((j > 0) && (((j+1) % burstSize) == 0)) { nsec += (j < nb1) ? ibg1 : ibg2; while (nsec >= long(1e9)) { sec++; nsec -= long(1e9); } } else { if (j < x) nsec += (j < npx1) ? ipg1 : ipg2; else nsec += ((j-x) < npy1) ? ipg1 : ipg2; while (nsec >= long(1e9)) { sec++; nsec -= long(1e9); } } } switch(streamList_[i]->nextWhat()) { case ::OstProto::StreamControl::e_nw_stop: goto _stop_no_more_pkts; case ::OstProto::StreamControl::e_nw_goto_id: /*! \todo (MED): define and use streamList_[i].d.control().goto_stream_id(); */ /*! \todo (MED): assumes goto Id is less than current!!!! To support goto to any id, do if goto_id > curr_id then i = goto_id; goto restart; else returnToQIdx = 0; */ setPacketListLoopMode(true, 0, streamList_[i]->sendUnit() == StreamBase::e_su_bursts ? ibg1 : ipg1); goto _stop_no_more_pkts; case ::OstProto::StreamControl::e_nw_goto_next: break; default: qFatal("---------- %s: Unhandled case (%d) -----------", __FUNCTION__, streamList_[i]->nextWhat() ); break; } } // if (stream is enabled) } // for (numStreams) _stop_no_more_pkts: isSendQueueDirty_ = false; } void AbstractPort::updatePacketListInterleaved() { int numStreams = 0; quint64 minGap = ULLONG_MAX; quint64 duration = quint64(1e9); QList ibg1, ibg2; QList nb1, nb2; QList ipg1, ipg2; QList np1, np2; QList schedSec, schedNsec; QList pktCount, burstCount; QList burstSize; QList isVariable; QList pktBuf; QList pktLen; qDebug("In %s", __FUNCTION__); clearPacketList(); if (streamList_.size() == 0) { isSendQueueDirty_ = false; return; } // First sort the streams by ordinalValue qSort(streamList_.begin(), streamList_.end(), StreamBase::StreamLessThan); for (int i = 0; i < streamList_.size(); i++) { if (!streamList_[i]->isEnabled()) continue; double numBursts = 0; double numPackets = 0; quint64 _burstSize = 0; double ibg = 0; quint64 _ibg1 = 0, _ibg2 = 0; quint64 _nb1 = 0, _nb2 = 0; double ipg = 0; quint64 _ipg1 = 0, _ipg2 = 0; quint64 _np1 = 0, _np2 = 0; switch (streamList_[i]->sendUnit()) { case OstProto::StreamControl::e_su_bursts: numBursts = streamList_[i]->burstRate(); if (streamList_[i]->burstRate() > 0) { ibg = 1e9/double(streamList_[i]->burstRate()); _ibg1 = quint64(ceil(ibg)); _ibg2 = quint64(floor(ibg)); _nb1 = quint64((ibg - double(_ibg2)) * double(numBursts)); _nb2 = quint64(numBursts) - _nb1; _burstSize = streamList_[i]->burstSize(); } break; case OstProto::StreamControl::e_su_packets: numPackets = streamList_[i]->packetRate(); if (streamList_[i]->packetRate() > 0) { ipg = 1e9/double(streamList_[i]->packetRate()); _ipg1 = llrint(ceil(ipg)); _ipg2 = quint64(floor(ipg)); _np1 = quint64((ipg - double(_ipg2)) * double(numPackets)); _np2 = quint64(numPackets) - _np1; _burstSize = 1; } break; default: qWarning("Unhandled stream control unit %d", streamList_[i]->sendUnit()); continue; } qDebug("numBursts = %g, numPackets = %g\n", numBursts, numPackets); qDebug("ibg = %g", ibg); qDebug("ibg1 = %" PRIu64, _ibg1); qDebug("nb1 = %" PRIu64, _nb1); qDebug("ibg2 = %" PRIu64, _ibg2); qDebug("nb2 = %" PRIu64 "\n", _nb2); qDebug("ipg = %g", ipg); qDebug("ipg1 = %" PRIu64, _ipg1); qDebug("np1 = %" PRIu64, _np1); qDebug("ipg2 = %" PRIu64, _ipg2); qDebug("np2 = %" PRIu64 "\n", _np2); if (_ibg2 && (_ibg2 < minGap)) minGap = _ibg2; if (_ibg1 && (_ibg1 > duration)) duration = _ibg1; ibg1.append(_ibg1); ibg2.append(_ibg2); nb1.append(_nb1); nb2.append(_nb1); burstSize.append(_burstSize); if (_ipg2 && (_ipg2 < minGap)) minGap = _ipg2; if (_np1) { if (_ipg1 && (_ipg1 > duration)) duration = _ipg1; } else { if (_ipg2 && (_ipg2 > duration)) duration = _ipg2; } ipg1.append(_ipg1); ipg2.append(_ipg2); np1.append(_np1); np2.append(_np1); schedSec.append(0); schedNsec.append(0); pktCount.append(0); burstCount.append(0); if (streamList_[i]->isFrameVariable()) { isVariable.append(true); pktBuf.append(QByteArray()); pktLen.append(0); } else { isVariable.append(false); pktBuf.append(QByteArray()); pktBuf.last().resize(kMaxPktSize); pktLen.append(streamList_[i]->frameValue( (uchar*)pktBuf.last().data(), pktBuf.last().size(), 0)); } numStreams++; } // for i qDebug("minGap = %" PRIu64, minGap); qDebug("duration = %" PRIu64, duration); uchar* buf; int len; quint64 durSec = duration/ulong(1e9); quint64 durNsec = duration % ulong(1e9); quint64 sec = 0; quint64 nsec = 0; quint64 lastPktTxSec = 0; quint64 lastPktTxNsec = 0; do { for (int i = 0; i < numStreams; i++) { // If a packet is not scheduled yet, look at the next stream if ((schedSec.at(i) > sec) || (schedNsec.at(i) > nsec)) continue; for (uint j = 0; j < burstSize[i]; j++) { if (isVariable.at(i)) { buf = pktBuf_; len = streamList_[i]->frameValue(pktBuf_, sizeof(pktBuf_), pktCount[i]); } else { buf = (uchar*) pktBuf.at(i).data(); len = pktLen.at(i); } if (len <= 0) continue; qDebug("q(%d) sec = %" PRIu64 " nsec = %" PRIu64, i, sec, nsec); appendToPacketList(sec, nsec, buf, len); lastPktTxSec = sec; lastPktTxNsec = nsec; pktCount[i]++; schedNsec[i] += (pktCount.at(i) < np1.at(i)) ? ipg1.at(i) : ipg2.at(i); while (schedNsec.at(i) >= 1e9) { schedSec[i]++; schedNsec[i] -= long(1e9); } } burstCount[i]++; schedNsec[i] += (burstCount.at(i) < nb1.at(i)) ? ibg1.at(i) : ibg2.at(i); while (schedNsec.at(i) >= 1e9) { schedSec[i]++; schedNsec[i] -= long(1e9); } } nsec += minGap; while (nsec >= 1e9) { sec++; nsec -= long(1e9); } } while ((sec < durSec) || (nsec < durNsec)); qint64 delaySec = durSec - lastPktTxSec; qint64 delayNsec = durNsec - lastPktTxNsec; while (delayNsec < 0) { delayNsec += long(1e9); delaySec--; } qDebug("loop Delay = %" PRId64 "/%" PRId64, delaySec, delayNsec); setPacketListLoopMode(true, delaySec, delayNsec); isSendQueueDirty_ = false; } void AbstractPort::stats(PortStats *stats) { stats->rxPkts = (stats_.rxPkts >= epochStats_.rxPkts) ? stats_.rxPkts - epochStats_.rxPkts : stats_.rxPkts + (maxStatsValue_ - epochStats_.rxPkts); stats->rxBytes = (stats_.rxBytes >= epochStats_.rxBytes) ? stats_.rxBytes - epochStats_.rxBytes : stats_.rxBytes + (maxStatsValue_ - epochStats_.rxBytes); stats->rxPps = stats_.rxPps; stats->rxBps = stats_.rxBps; stats->txPkts = (stats_.txPkts >= epochStats_.txPkts) ? stats_.txPkts - epochStats_.txPkts : stats_.txPkts + (maxStatsValue_ - epochStats_.txPkts); stats->txBytes = (stats_.txBytes >= epochStats_.txBytes) ? stats_.txBytes - epochStats_.txBytes : stats_.txBytes + (maxStatsValue_ - epochStats_.txBytes); stats->txPps = stats_.txPps; stats->txBps = stats_.txBps; stats->rxDrops = (stats_.rxDrops >= epochStats_.rxDrops) ? stats_.rxDrops - epochStats_.rxDrops : stats_.rxDrops + (maxStatsValue_ - epochStats_.rxDrops); stats->rxErrors = (stats_.rxErrors >= epochStats_.rxErrors) ? stats_.rxErrors - epochStats_.rxErrors : stats_.rxErrors + (maxStatsValue_ - epochStats_.rxErrors); stats->rxFifoErrors = (stats_.rxFifoErrors >= epochStats_.rxFifoErrors) ? stats_.rxFifoErrors - epochStats_.rxFifoErrors : stats_.rxFifoErrors + (maxStatsValue_ - epochStats_.rxFifoErrors); stats->rxFrameErrors = (stats_.rxFrameErrors >= epochStats_.rxFrameErrors) ? stats_.rxFrameErrors - epochStats_.rxFrameErrors : stats_.rxFrameErrors + (maxStatsValue_ - epochStats_.rxFrameErrors); } void AbstractPort::streamStats(uint guid, OstProto::StreamStatsList *stats) { if (streamStats_.contains(guid)) { StreamStatsTuple sst = streamStats_.value(guid); OstProto::StreamStats *s = stats->add_stream_stats(); s->mutable_stream_guid()->set_id(guid); s->mutable_port_id()->set_id(id()); s->set_tx_pkts(sst.tx_pkts); s->set_tx_bytes(sst.tx_bytes); s->set_rx_pkts(sst.rx_pkts); s->set_rx_bytes(sst.rx_bytes); } } void AbstractPort::streamStatsAll(OstProto::StreamStatsList *stats) { // FIXME: change input param to a non-OstProto type and/or have // a getFirst/Next like API? StreamStatsIterator i(streamStats_); while (i.hasNext()) { i.next(); StreamStatsTuple sst = i.value(); OstProto::StreamStats *s = stats->add_stream_stats(); s->mutable_stream_guid()->set_id(i.key()); s->mutable_port_id()->set_id(id()); s->set_tx_pkts(sst.tx_pkts); s->set_tx_bytes(sst.tx_bytes); s->set_rx_pkts(sst.rx_pkts); s->set_rx_bytes(sst.rx_bytes); } } void AbstractPort::resetStreamStats(uint guid) { streamStats_.remove(guid); } void AbstractPort::resetStreamStatsAll() { streamStats_.clear(); } void AbstractPort::clearDeviceNeighbors() { deviceManager_->clearDeviceNeighbors(); isSendQueueDirty_ = true; } void AbstractPort::resolveDeviceNeighbors() { // For a user triggered 'Resolve Neighbors', the behaviour we want is // IP not in cache - send ARP/NDP request // IP present in cache, but unresolved - re-send ARP/NDP request // IP present in cache and resolved - don't sent ARP/NDP // // Device does not resend ARP/NDP requests if the IP address is // already present in the cache, irrespective of whether it is // resolved or not (this is done to avoid sending duplicate requests). // // So, to get the behaviour we want, let's clear all unresolved neighbors // before calling resolve deviceManager_->clearDeviceNeighbors(Device::kUnresolvedNeighbors); // Resolve gateway for each device first ... deviceManager_->resolveDeviceGateways(); // ... then resolve neighbor for each unique frame of each stream // NOTE: // 1. All the frames may have the same destination ip,but may have // different source ip so may belong to a different emulated device; // so we cannot optimize and send only one ARP // 2. For a unidirectional stream, at egress, this will create ARP // entries on the DUT for each of the source addresses // // TODO(optimization): Identify if stream does not vary in srcIp or dstIp // - in which case resolve for only one frame of the stream for (int i = 0; i < streamList_.size(); i++) { const StreamBase *stream = streamList_.at(i); int frameCount = stream->frameVariableCount(); for (int j = 0; j < frameCount; j++) { // we need the packet contents only uptil the L3 header int pktLen = stream->frameValue(pktBuf_, kMaxL3PktSize, j); if (pktLen) { PacketBuffer pktBuf(pktBuf_, pktLen); deviceManager_->resolveDeviceNeighbor(&pktBuf); } } } isSendQueueDirty_ = true; } quint64 AbstractPort::deviceMacAddress(int streamId, int frameIndex) { // we need the packet contents only uptil the L3 header StreamBase *s = stream(streamId); int pktLen = s->frameValue(pktBuf_, kMaxL3PktSize, frameIndex); if (pktLen) { PacketBuffer pktBuf(pktBuf_, pktLen); return deviceManager_->deviceMacAddress(&pktBuf); } return 0; } quint64 AbstractPort::neighborMacAddress(int streamId, int frameIndex) { // we need the packet contents only uptil the L3 header StreamBase *s = stream(streamId); int pktLen = s->frameValue(pktBuf_, kMaxL3PktSize, frameIndex); if (pktLen) { PacketBuffer pktBuf(pktBuf_, pktLen); return deviceManager_->neighborMacAddress(&pktBuf); } return 0; } ostinato-0.9/server/abstractport.h000066400000000000000000000115221321315675200174170ustar00rootroot00000000000000/* Copyright (C) 2010-2012 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _SERVER_ABSTRACT_PORT_H #define _SERVER_ABSTRACT_PORT_H #include "streamstats.h" #include #include #include "../common/protocol.pb.h" class DeviceManager; class StreamBase; class PacketBuffer; class QIODevice; // TODO: send notification back to client(s) #define notify qWarning class AbstractPort { public: struct PortStats { quint64 rxPkts; quint64 rxBytes; quint64 rxPps; quint64 rxBps; quint64 rxDrops; quint64 rxErrors; quint64 rxFifoErrors; quint64 rxFrameErrors; quint64 txPkts; quint64 txBytes; quint64 txPps; quint64 txBps; }; enum Accuracy { kHighAccuracy, kMediumAccuracy, kLowAccuracy, }; AbstractPort(int id, const char *device); virtual ~AbstractPort(); bool isUsable() { return isUsable_; } virtual void init(); int id() { return data_.port_id().id(); } const char* name() { return data_.name().c_str(); } void protoDataCopyInto(OstProto::Port *port) { port->CopyFrom(data_); } bool canModify(const OstProto::Port &port, bool *dirty); bool modify(const OstProto::Port &port); virtual OstProto::LinkState linkState() { return linkState_; } virtual bool hasExclusiveControl() = 0; virtual bool setExclusiveControl(bool exclusive) = 0; int streamCount() { return streamList_.size(); } StreamBase* streamAtIndex(int index); StreamBase* stream(int streamId); bool addStream(StreamBase *stream); bool deleteStream(int streamId); bool isDirty() { return isSendQueueDirty_; } void setDirty() { isSendQueueDirty_ = true; } virtual bool setTrackStreamStats(bool enable); Accuracy rateAccuracy(); virtual bool setRateAccuracy(Accuracy accuracy); virtual void clearPacketList() = 0; virtual void loopNextPacketSet(qint64 size, qint64 repeats, long repeatDelaySec, long repeatDelayNsec) = 0; virtual bool appendToPacketList(long sec, long nsec, const uchar *packet, int length) = 0; virtual void setPacketListLoopMode(bool loop, quint64 secDelay, quint64 nsecDelay) = 0; void updatePacketList(); virtual void startTransmit() = 0; virtual void stopTransmit() = 0; virtual bool isTransmitOn() = 0; virtual void startCapture() = 0; virtual void stopCapture() = 0; virtual bool isCaptureOn() = 0; virtual QIODevice* captureData() = 0; void stats(PortStats *stats); void resetStats() { epochStats_ = stats_; } // FIXME: combine single and All calls? void streamStats(uint guid, OstProto::StreamStatsList *stats); void streamStatsAll(OstProto::StreamStatsList *stats); void resetStreamStats(uint guid); void resetStreamStatsAll(); DeviceManager* deviceManager(); virtual void startDeviceEmulation() = 0; virtual void stopDeviceEmulation() = 0; virtual int sendEmulationPacket(PacketBuffer *pktBuf) = 0; void clearDeviceNeighbors(); void resolveDeviceNeighbors(); quint64 deviceMacAddress(int streamId, int frameIndex); quint64 neighborMacAddress(int streamId, int frameIndex); protected: void addNote(QString note); void updatePacketListSequential(); void updatePacketListInterleaved(); bool isUsable_; OstProto::Port data_; OstProto::LinkState linkState_; ulong minPacketSetSize_; Accuracy rateAccuracy_; quint64 maxStatsValue_; struct PortStats stats_; StreamStats streamStats_; //! \todo Need lock for stats access/update DeviceManager *deviceManager_; private: bool isSendQueueDirty_; static const int kMaxPktSize = 16384; uchar pktBuf_[kMaxPktSize]; // When finding a corresponding device for a packet, we need to inspect // only uptil the L3 header; in the worst case this would be - // mac (12) + 4 x vlan (16) + ethType (2) + ipv6 (40) = 74 bytes // let's round it up to 80 bytes static const int kMaxL3PktSize = 80; /*! \note StreamBase::id() and index into streamList[] are NOT same! */ QList streamList_; struct PortStats epochStats_; }; #endif ostinato-0.9/server/bsdport.cpp000066400000000000000000000235231321315675200167230ustar00rootroot00000000000000/* Copyright (C) 2012 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "bsdport.h" #ifdef Q_OS_BSD4 #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef Q_OS_MAC #define ifr_flagshigh ifr_flags #define IFF_PPROMISC (IFF_PROMISC << 16) #endif QList BsdPort::allPorts_; BsdPort::StatsMonitor *BsdPort::monitor_; const quint32 kMaxValue32 = 0xffffffff; BsdPort::BsdPort(int id, const char *device) : PcapPort(id, device) { isPromisc_ = true; clearPromisc_ = false; // We don't need per port Rx/Tx monitors for Bsd delete monitorRx_; delete monitorTx_; monitorRx_ = monitorTx_ = NULL; // We have one monitor for both Rx/Tx of all ports if (!monitor_) monitor_ = new StatsMonitor(); data_.set_is_exclusive_control(hasExclusiveControl()); minPacketSetSize_ = 16; qDebug("adding dev to all ports list <%s>", device); allPorts_.append(this); maxStatsValue_ = ULONG_MAX; } BsdPort::~BsdPort() { qDebug("In %s", __FUNCTION__); if (monitor_->isRunning()) { monitor_->stop(); monitor_->wait(); } if (clearPromisc_) { int sd = socket(AF_INET, SOCK_DGRAM, 0); struct ifreq ifr; memset(&ifr, 0, sizeof(ifr)); strncpy(ifr.ifr_name, name(), sizeof(ifr.ifr_name)); if (ioctl(sd, SIOCGIFFLAGS, &ifr) != -1) { short promisc = IFF_PPROMISC >> 16; if (ifr.ifr_flagshigh & promisc) { ifr.ifr_flagshigh &= ~promisc; if (ioctl(sd, SIOCSIFFLAGS, &ifr) == -1) qDebug("Failed clearing promisc flag. SIOCSIFFLAGS failed: %s", strerror(errno)); else qDebug("Cleared promisc successfully"); } else qDebug("clear_promisc is set but IFF_PPROMISC is not?"); } else qDebug("Failed clearing promisc flag. SIOCGIFFLAGS failed: %s", strerror(errno)); close(sd); } } void BsdPort::init() { if (!monitor_->isRunning()) monitor_->start(); monitor_->waitForSetupFinished(); if (!isPromisc_) addNote("Non Promiscuous Mode"); } bool BsdPort::hasExclusiveControl() { // TODO return false; } bool BsdPort::setExclusiveControl(bool /*exclusive*/) { // TODO return false; } BsdPort::StatsMonitor::StatsMonitor() : QThread() { stop_ = false; setupDone_ = false; } void BsdPort::StatsMonitor::run() { int mib[] = {CTL_NET, PF_ROUTE, 0, 0, NET_RT_IFLIST, 0}; const int mibLen = sizeof(mib)/sizeof(mib[0]); QHash portStats; QHash linkState; int sd; QByteArray buf; size_t len; char *p, *end; int count; struct ifreq ifr; // // We first setup stuff before we start polling for stats // if (sysctl(mib, mibLen, NULL, &len, NULL, 0) < 0) { qWarning("sysctl NET_RT_IFLIST(1) failed (%s)\n", strerror(errno)); return; } qDebug("sysctl mib returns reqd len = %d\n", (int) len); len *= 2; // for extra room, just in case! buf.fill('\0', len); if (sysctl(mib, mibLen, buf.data(), &len, NULL, 0) < 0) { qWarning("sysctl NET_RT_IFLIST(2) failed(%s)\n", strerror(errno)); return; } sd = socket(AF_INET, SOCK_DGRAM, 0); Q_ASSERT(sd >= 0); memset(&ifr, 0, sizeof(ifr)); // // Populate the port stats hash table // p = buf.data(); end = p + len; count = 0; while (p < end) { struct if_msghdr *ifm = (struct if_msghdr*) p; struct sockaddr_dl *sdl = (struct sockaddr_dl*) (ifm + 1); if (ifm->ifm_type == RTM_IFINFO) { char ifname[1024]; strncpy(ifname, sdl->sdl_data, sdl->sdl_nlen); ifname[sdl->sdl_nlen] = 0; qDebug("if: %s(%d, %d)", ifname, ifm->ifm_index, sdl->sdl_index); foreach(BsdPort* port, allPorts_) { if (strncmp(port->name(), sdl->sdl_data, sdl->sdl_nlen) == 0) { Q_ASSERT(ifm->ifm_index == sdl->sdl_index); portStats[uint(ifm->ifm_index)] = &(port->stats_); linkState[uint(ifm->ifm_index)] = &(port->linkState_); // Set promisc mode, if not already set strncpy(ifr.ifr_name, port->name(), sizeof(ifr.ifr_name)); if (ioctl(sd, SIOCGIFFLAGS, &ifr) != -1) { short promisc = IFF_PPROMISC >> 16; if ((ifr.ifr_flagshigh & promisc) == 0) { ifr.ifr_flagshigh |= promisc; if (ioctl(sd, SIOCSIFFLAGS, &ifr) != -1) { qDebug("%s: set promisc successful", port->name()); port->clearPromisc_ = true; } else { port->isPromisc_ = false; qDebug("%s: failed to set promisc; " "SIOCSIFFLAGS failed (%s)", port->name(), strerror(errno)); } } else qDebug("%s: promisc already set", port->name()); } else { port->isPromisc_ = false; qDebug("%s: failed to set promisc; SIOCGIFFLAGS failed (%s)", port->name(), strerror(errno)); } break; } } count++; } p += ifm->ifm_msglen; } qDebug("port count = %d\n", count); if (count <= 0) { qWarning("no ports in NET_RT_IFLIST - no stats will be available"); return; } close(sd); qDebug("stats for %d ports setup", count); setupDone_ = true; // // We are all set - Let's start polling for stats! // while (!stop_) { if (sysctl(mib, mibLen, buf.data(), &len, NULL, 0) < 0) { qWarning("sysctl NET_RT_IFLIST(3) failed(%s)\n", strerror(errno)); goto _try_later; } p = buf.data(); end = p + len; while (p < end) { struct if_msghdr *ifm = (struct if_msghdr*) p; AbstractPort::PortStats *stats; if (ifm->ifm_type != RTM_IFINFO) goto _next; stats = portStats[ifm->ifm_index]; if (stats) { struct if_data *ifd = &(ifm->ifm_data); OstProto::LinkState *state = linkState[ifm->ifm_index]; u_long in_packets; Q_ASSERT(state); #ifdef Q_OS_MAC *state = ifm->ifm_flags & IFF_RUNNING ? OstProto::LinkStateUp : OstProto::LinkStateDown; #else *state = (OstProto::LinkState) ifd->ifi_link_state; #endif in_packets = ifd->ifi_ipackets + ifd->ifi_noproto; stats->rxPps = ((in_packets >= stats->rxPkts) ? in_packets - stats->rxPkts : in_packets + (kMaxValue32 - stats->rxPkts)) / kRefreshFreq_; stats->rxBps = ((ifd->ifi_ibytes >= stats->rxBytes) ? ifd->ifi_ibytes - stats->rxBytes : ifd->ifi_ibytes + (kMaxValue32 - stats->rxBytes)) / kRefreshFreq_; stats->rxPkts = in_packets; stats->rxBytes = ifd->ifi_ibytes; stats->txPps = ((ifd->ifi_opackets >= stats->txPkts) ? ifd->ifi_opackets - stats->txPkts : ifd->ifi_opackets + (kMaxValue32 - stats->txPkts)) / kRefreshFreq_; stats->txBps = ((ifd->ifi_obytes >= stats->txBytes) ? ifd->ifi_obytes - stats->txBytes : ifd->ifi_obytes + (kMaxValue32 - stats->txBytes)) / kRefreshFreq_; stats->txPkts = ifd->ifi_opackets; stats->txBytes = ifd->ifi_obytes; stats->rxDrops = ifd->ifi_iqdrops; stats->rxErrors = ifd->ifi_ierrors; } _next: p += ifm->ifm_msglen; } _try_later: QThread::sleep(kRefreshFreq_); } portStats.clear(); linkState.clear(); } void BsdPort::StatsMonitor::stop() { stop_ = true; } bool BsdPort::StatsMonitor::waitForSetupFinished(int msecs) { QTime t; t.start(); while (!setupDone_) { if (t.elapsed() > msecs) return false; QThread::msleep(10); } return true; } #endif ostinato-0.9/server/bsdport.h000066400000000000000000000027101321315675200163630ustar00rootroot00000000000000/* Copyright (C) 2012 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _SERVER_BSD_PORT_H #define _SERVER_BSD_PORT_H #include #ifdef Q_OS_BSD4 #include "pcapport.h" class BsdPort : public PcapPort { public: BsdPort(int id, const char *device); ~BsdPort(); void init(); virtual bool hasExclusiveControl(); virtual bool setExclusiveControl(bool exclusive); protected: class StatsMonitor: public QThread { public: StatsMonitor(); void run(); void stop(); bool waitForSetupFinished(int msecs = 10000); private: static const int kRefreshFreq_ = 1; // in seconds bool stop_; bool setupDone_; }; bool isPromisc_; bool clearPromisc_; static QList allPorts_; static StatsMonitor *monitor_; // rx/tx stats for ALL ports }; #endif #endif ostinato-0.9/server/device.cpp000066400000000000000000001011571321315675200165050ustar00rootroot00000000000000/* Copyright (C) 2015 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "device.h" #include "../common/emulproto.pb.h" #include "devicemanager.h" #include "packetbuffer.h" #include #include const int kBaseHex = 16; const quint64 kBcastMac = 0xffffffffffffULL; const quint16 kEthTypeIp4 = 0x0800; const quint16 kEthTypeIp6 = 0x86dd; const int kIp6HdrLen = 40; const quint8 kIpProtoIcmp6 = 58; /* * NOTE: * 1. Device Key is (VLANS + MAC) - is assumed to be unique for a device * 2. Device clients/users (viz. DeviceManager) should take care when * setting params that change the key, if the key is used elsewhere * (e.g. in a hash) */ inline quint32 sumUInt128(UInt128 value) { quint8 *arr = value.toArray(); quint32 sum = 0; for (int i = 0; i < 16; i += 2) sum += qToBigEndian(*((quint16*)(arr + i))); return sum; } inline bool isIp6Mcast(UInt128 ip) { return (ip.hi64() >> 56) == 0xff; } Device::Device(DeviceManager *deviceManager) { deviceManager_ = deviceManager; for (int i = 0; i < kMaxVlan; i++) vlan_[i] = 0; numVlanTags_ = 0; mac_ = 0; hasIp4_ = false; hasIp6_ = false; clearKey(); } void Device::setVlan(int index, quint16 vlan, quint16 tpid) { int ofs; if ((index < 0) || (index >= kMaxVlan)) { qWarning("%s: vlan index %d out of range (0 - %d)", __FUNCTION__, index, kMaxVlan - 1); return; } vlan_[index] = (tpid << 16) | vlan; ofs = index * sizeof(quint16); key_[ofs] = vlan >> 8; key_[ofs+1] = vlan & 0xff; if (index >= numVlanTags_) numVlanTags_ = index + 1; } quint64 Device::mac() { return mac_; } void Device::setMac(quint64 mac) { int ofs = kMaxVlan * sizeof(quint16); mac_ = mac & ~(0xffffULL << 48); memcpy(key_.data() + ofs, (char*)&mac_, sizeof(mac_)); } void Device::setIp4(quint32 address, int prefixLength, quint32 gateway) { ip4_ = address; ip4PrefixLength_ = prefixLength; ip4Gateway_ = gateway; hasIp4_ = true; // Precalculate our mask 'n subnet to avoid doing so at pkt rx/tx time ip4Mask_ = ~0 << (32 - ip4PrefixLength_); ip4Subnet_ = ip4_ & ip4Mask_; } void Device::setIp6(UInt128 address, int prefixLength, UInt128 gateway) { ip6_ = address; ip6PrefixLength_ = prefixLength; ip6Gateway_ = gateway; hasIp6_ = true; // Precalculate our mask 'n subnet to avoid doing so at pkt rx/tx time ip6Mask_ = ~UInt128(0, 0) << (128 - ip6PrefixLength_); ip6Subnet_ = ip6_ & ip6Mask_; } void Device::getConfig(OstEmul::Device *deviceConfig) { for (int i = 0; i < numVlanTags_; i++) deviceConfig->add_vlan(vlan_[i]); deviceConfig->set_mac(mac_); if (hasIp4_) { deviceConfig->set_ip4(ip4_); deviceConfig->set_ip4_prefix_length(ip4PrefixLength_); deviceConfig->set_ip4_default_gateway(ip4Gateway_); } if (hasIp6_) { deviceConfig->mutable_ip6()->set_hi(ip6_.hi64()); deviceConfig->mutable_ip6()->set_lo(ip6_.lo64()); deviceConfig->set_ip6_prefix_length(ip6PrefixLength_); deviceConfig->mutable_ip6_default_gateway()->set_hi(ip6Gateway_.hi64()); deviceConfig->mutable_ip6_default_gateway()->set_lo(ip6Gateway_.lo64()); } } QString Device::config() { QString config; for (int i = 0; i < numVlanTags_; i++) { config.append(i == 0 ? "vlans=" : "|"); config.append( (vlan_[i] >> 16) != kVlanTpid ? QString("0x%1-%2") .arg(vlan_[i] >> 16, 4, kBaseHex, QChar('0')) .arg(vlan_[i] & 0xFFFF) : QString("%1") .arg(vlan_[i] & 0xFFFF)); } config.append(QString(" mac=%1") .arg(mac_, 12, kBaseHex, QChar('0'))); if (hasIp4_) config.append(QString(" ip4=%1/%2") .arg(QHostAddress(ip4_).toString()) .arg(ip4PrefixLength_)); if (hasIp6_) config.append(QString(" ip6=%1/%2") .arg(QHostAddress(ip6_.toArray()).toString()) .arg(ip6PrefixLength_)); return config; } DeviceKey Device::key() { return key_; } void Device::clearKey() { key_.fill(0, kMaxVlan * sizeof(quint16) + sizeof(quint64)); } int Device::encapSize() { // ethernet header + vlans int size = 14 + 4*numVlanTags_; return size; } void Device::encap(PacketBuffer *pktBuf, quint64 dstMac, quint16 type) { int ofs; quint64 srcMac = mac_; uchar *p = pktBuf->push(encapSize()); if (!p) { qWarning("%s: failed to push %d bytes [0x%p, 0x%p]", __FUNCTION__, encapSize(), pktBuf->head(), pktBuf->data()); goto _exit; } *(quint32*)(p ) = qToBigEndian(quint32(dstMac >> 16)); *(quint16*)(p + 4) = qToBigEndian(quint16(dstMac & 0xffff)); *(quint32*)(p + 6) = qToBigEndian(quint32(srcMac >> 16)); *(quint16*)(p + 10) = qToBigEndian(quint16(srcMac & 0xffff)); ofs = 12; for (int i = 0; i < numVlanTags_; i++) { *(quint32*)(p + ofs) = qToBigEndian(vlan_[i]); ofs += 4; } *(quint16*)(p + ofs) = qToBigEndian(type); ofs += 2; Q_ASSERT(ofs == encapSize()); _exit: return; } // We expect pktBuf to point to EthType on entry void Device::receivePacket(PacketBuffer *pktBuf) { quint16 ethType = qFromBigEndian(pktBuf->data()); pktBuf->pull(2); qDebug("%s: ethType 0x%x", __PRETTY_FUNCTION__, ethType); switch(ethType) { case 0x0806: // ARP if (hasIp4_) receiveArp(pktBuf); break; case 0x0800: // IPv4 if (hasIp4_) receiveIp4(pktBuf); break; case 0x86dd: // IPv6 if (hasIp6_) receiveIp6(pktBuf); break; default: break; } // FIXME: temporary hack till DeviceManager clones pbufs pktBuf->push(2); } void Device::transmitPacket(PacketBuffer *pktBuf) { deviceManager_->transmitPacket(pktBuf); } void Device::resolveGateway() { if (hasIp4_) sendArpRequest(ip4Gateway_); if (hasIp6_) sendNeighborSolicit(ip6Gateway_); } void Device::clearNeighbors(Device::NeighborSet set) { QMutableHashIterator arpIter(arpTable_); QMutableHashIterator ndpIter(ndpTable_); switch (set) { case kAllNeighbors: arpTable_.clear(); ndpTable_.clear(); break; case kUnresolvedNeighbors: while (arpIter.hasNext()) { arpIter.next(); if (arpIter.value() == 0) arpIter.remove(); } while (ndpIter.hasNext()) { ndpIter.next(); if (ndpIter.value() == 0) ndpIter.remove(); } break; default: Q_ASSERT(false); // Unreachable! } } // Resolve the Neighbor IP address for this to-be-transmitted pktBuf // We expect pktBuf to point to EthType on entry void Device::resolveNeighbor(PacketBuffer *pktBuf) { quint16 ethType = qFromBigEndian(pktBuf->data()); pktBuf->pull(2); qDebug("%s: ethType 0x%x", __PRETTY_FUNCTION__, ethType); switch(ethType) { case 0x0800: // IPv4 if (hasIp4_) sendArpRequest(pktBuf); break; case 0x86dd: // IPv6 if (hasIp6_) sendNeighborSolicit(pktBuf); break; default: break; } // FIXME: temporary hack till DeviceManager clones pbufs pktBuf->push(2); } // Append this device's neighbors to the list void Device::getNeighbors(OstEmul::DeviceNeighborList *neighbors) { QList ip4List = arpTable_.keys(); QList ip6List = ndpTable_.keys(); QList macList; macList = arpTable_.values(); Q_ASSERT(ip4List.size() == macList.size()); for (int i = 0; i < ip4List.size(); i++) { OstEmul::ArpEntry *arp = neighbors->add_arp(); arp->set_ip4(ip4List.at(i)); arp->set_mac(macList.at(i)); } macList = ndpTable_.values(); Q_ASSERT(ip6List.size() == macList.size()); for (int i = 0; i < ip6List.size(); i++) { OstEmul::NdpEntry *ndp = neighbors->add_ndp(); ndp->mutable_ip6()->set_hi(ip6List.at(i).hi64()); ndp->mutable_ip6()->set_lo(ip6List.at(i).lo64()); ndp->set_mac(macList.at(i)); } } // Are we the source of the given packet? // We expect pktBuf to point to EthType on entry bool Device::isOrigin(const PacketBuffer *pktBuf) { const uchar *pktData = pktBuf->data(); quint16 ethType = qFromBigEndian(pktData); qDebug("%s: ethType 0x%x", __PRETTY_FUNCTION__, ethType); pktData += 2; // We know only about IP packets - adjust for ethType length (2 bytes) // when checking that we have a complete IP header if ((ethType == 0x0800) && hasIp4_) { // IPv4 int ipHdrLen = (pktData[0] & 0x0F) << 2; quint32 srcIp; if (pktBuf->length() < (ipHdrLen+2)) { qDebug("incomplete IPv4 header: expected %d, actual %d", ipHdrLen, pktBuf->length()); return false; } srcIp = qFromBigEndian(pktData + ipHdrLen - 8); qDebug("%s: pktSrcIp/selfIp = 0x%x/0x%x", __FUNCTION__, srcIp, ip4_); return (srcIp == ip4_); } else if ((ethType == kEthTypeIp6) && hasIp6_) { // IPv6 UInt128 srcIp; if (pktBuf->length() < (kIp6HdrLen+2)) { qDebug("incomplete IPv6 header: expected %d, actual %d", kIp6HdrLen, pktBuf->length()-2); return false; } srcIp = qFromBigEndian(pktData + 8); qDebug("%s: pktSrcIp6/selfIp6 = %llx-%llx/%llx-%llx", __FUNCTION__, srcIp.hi64(), srcIp.lo64(), ip6_.hi64(), ip6_.lo64()); return (srcIp == ip6_); } return false; } // Return the mac address corresponding to the dstIp of the given packet // We expect pktBuf to point to EthType on entry quint64 Device::neighborMac(const PacketBuffer *pktBuf) { const uchar *pktData = pktBuf->data(); quint16 ethType = qFromBigEndian(pktData); qDebug("%s: ethType 0x%x", __PRETTY_FUNCTION__, ethType); pktData += 2; // We know only about IP packets if ((ethType == 0x0800) && hasIp4_) { // IPv4 int ipHdrLen = (pktData[0] & 0x0F) << 2; quint32 dstIp, tgtIp; if (pktBuf->length() < ipHdrLen) { qDebug("incomplete IPv4 header: expected %d, actual %d", ipHdrLen, pktBuf->length()); return false; } dstIp = qFromBigEndian(pktData + ipHdrLen - 4); if ((dstIp & 0xF0000000) == 0xE0000000) { // Mcast IP? qDebug("mcast dst %x", dstIp); return (quint64(0x01005e) << 24) | (dstIp & 0x7FFFFF); } tgtIp = ((dstIp & ip4Mask_) == ip4Subnet_) ? dstIp : ip4Gateway_; return arpTable_.value(tgtIp); } else if ((ethType == kEthTypeIp6) && hasIp6_) { // IPv6 UInt128 dstIp, tgtIp; if (pktBuf->length() < (kIp6HdrLen+2)) { qDebug("incomplete IPv6 header: expected %d, actual %d", kIp6HdrLen, pktBuf->length()-2); return false; } dstIp = qFromBigEndian(pktData + 24); if (dstIp.toArray()[0] == 0xFF) { // Mcast IP? qDebug("mcast dst %s", qPrintable(QHostAddress(dstIp.toArray()).toString())); return (quint64(0x3333) << 32) | (dstIp.lo64() & 0xFFFFFFFF); } tgtIp = ((dstIp & ip6Mask_) == ip6Subnet_) ? dstIp : ip6Gateway_; return ndpTable_.value(tgtIp); } return false; } // // Private Methods // /* * --------------------------------------------------------- * IPv4 related private methods * --------------------------------------------------------- */ void Device::receiveArp(PacketBuffer *pktBuf) { PacketBuffer *rspPkt; uchar *pktData = pktBuf->data(); int offset = 0; quint16 hwType, protoType; quint8 hwAddrLen, protoAddrLen; quint16 opCode; quint64 srcMac, tgtMac; quint32 srcIp, tgtIp; // Extract tgtIp first to check quickly if this packet is for us or not tgtIp = qFromBigEndian(pktData + 24); if (tgtIp != ip4_) { qDebug("tgtIp %s is not me %s", qPrintable(QHostAddress(tgtIp).toString()), qPrintable(QHostAddress(ip4_).toString())); return; } // Extract annd verify ARP packet contents hwType = qFromBigEndian(pktData + offset); offset += 2; if (hwType != 1) // Mac goto _invalid_exit; protoType = qFromBigEndian(pktData + offset); offset += 2; if (protoType != 0x0800) // IPv4 goto _invalid_exit; hwAddrLen = pktData[offset]; offset += 1; if (hwAddrLen != 6) goto _invalid_exit; protoAddrLen = pktData[offset]; offset += 1; if (protoAddrLen != 4) goto _invalid_exit; opCode = qFromBigEndian(pktData + offset); offset += 2; srcMac = qFromBigEndian(pktData + offset); offset += 4; srcMac = (srcMac << 16) | qFromBigEndian(pktData + offset); offset += 2; srcIp = qFromBigEndian(pktData + offset); offset += 4; tgtMac = qFromBigEndian(pktData + offset); offset += 4; tgtMac = (tgtMac << 16) | qFromBigEndian(pktData + offset); offset += 2; switch (opCode) { case 1: // ARP Request arpTable_.insert(srcIp, srcMac); rspPkt = new PacketBuffer; rspPkt->reserve(encapSize()); pktData = rspPkt->put(28); if (pktData) { // HTYP, PTYP *(quint32*)(pktData ) = qToBigEndian(quint32(0x00010800)); // HLEN, PLEN, OPER *(quint32*)(pktData+ 4) = qToBigEndian(quint32(0x06040002)); // Source H/W Addr, Proto Addr *(quint32*)(pktData+ 8) = qToBigEndian(quint32(mac_ >> 16)); *(quint16*)(pktData+12) = qToBigEndian(quint16(mac_ & 0xffff)); *(quint32*)(pktData+14) = qToBigEndian(ip4_); // Target H/W Addr, Proto Addr *(quint32*)(pktData+18) = qToBigEndian(quint32(srcMac >> 16)); *(quint16*)(pktData+22) = qToBigEndian(quint16(srcMac & 0xffff)); *(quint32*)(pktData+24) = qToBigEndian(srcIp); } encap(rspPkt, srcMac, 0x0806); transmitPacket(rspPkt); qDebug("Sent ARP Reply for srcIp/tgtIp=%s/%s", qPrintable(QHostAddress(srcIp).toString()), qPrintable(QHostAddress(tgtIp).toString())); break; case 2: // ARP Response arpTable_.insert(srcIp, srcMac); break; default: break; } return; _invalid_exit: qWarning("Invalid ARP content"); return; } // Send ARP request for the IPv4 packet in pktBuf // pktBuf points to start of IP header void Device::sendArpRequest(PacketBuffer *pktBuf) { uchar *pktData = pktBuf->data(); int ipHdrLen = (pktData[0] & 0x0F) << 2; quint32 dstIp, tgtIp; if (pktBuf->length() < ipHdrLen) { qDebug("incomplete IPv4 header: expected %d, actual %d", ipHdrLen, pktBuf->length()); return; } dstIp = qFromBigEndian(pktData + ipHdrLen - 4); tgtIp = ((dstIp & ip4Mask_) == ip4Subnet_) ? dstIp : ip4Gateway_; sendArpRequest(tgtIp); } void Device::sendArpRequest(quint32 tgtIp) { quint32 srcIp = ip4_; PacketBuffer *reqPkt; uchar *pktData; // Validate target IP if (!tgtIp) return; // This function will be called once per unique stream - which // may all have the same dst IP; even if dst IP are different the // gateway for the different dst IP may all be same. However, // we don't want to send duplicate ARP requests, so we check // if the tgtIP is already in the cache (resolved or unresolved) // and if so, we don't resend it if (arpTable_.contains(tgtIp)) return; reqPkt = new PacketBuffer; reqPkt->reserve(encapSize()); pktData = reqPkt->put(28); if (pktData) { // HTYP, PTYP *(quint32*)(pktData ) = qToBigEndian(quint32(0x00010800)); // HLEN, PLEN, OPER *(quint32*)(pktData+ 4) = qToBigEndian(quint32(0x06040001)); // Source H/W Addr, Proto Addr *(quint32*)(pktData+ 8) = qToBigEndian(quint32(mac_ >> 16)); *(quint16*)(pktData+12) = qToBigEndian(quint16(mac_ & 0xffff)); *(quint32*)(pktData+14) = qToBigEndian(srcIp); // Target H/W Addr, Proto Addr *(quint32*)(pktData+18) = qToBigEndian(quint32(0)); *(quint16*)(pktData+22) = qToBigEndian(quint16(0)); *(quint32*)(pktData+24) = qToBigEndian(tgtIp); } encap(reqPkt, kBcastMac, 0x0806); transmitPacket(reqPkt); arpTable_.insert(tgtIp, 0); qDebug("Sent ARP Request for srcIp/tgtIp=%s/%s", qPrintable(QHostAddress(srcIp).toString()), qPrintable(QHostAddress(tgtIp).toString())); } void Device::receiveIp4(PacketBuffer *pktBuf) { uchar *pktData = pktBuf->data(); uchar ipProto; quint32 dstIp; if (pktData[0] != 0x45) { qDebug("%s: Unsupported IP version or options (%02x) ", __FUNCTION__, pktData[0]); goto _invalid_exit; } if (pktBuf->length() < 20) { qDebug("incomplete IPv4 header: expected 20, actual %d", pktBuf->length()); goto _invalid_exit; } // XXX: We don't verify IP Header checksum dstIp = qFromBigEndian(pktData + 16); if (dstIp != ip4_) { qDebug("%s: dstIp %x is not me (%x)", __FUNCTION__, dstIp, ip4_); goto _invalid_exit; } ipProto = pktData[9]; qDebug("%s: ipProto = %d", __FUNCTION__, ipProto); switch (ipProto) { case 1: // ICMP pktBuf->pull(20); receiveIcmp4(pktBuf); break; default: qWarning("%s: Unsupported ipProto %d", __FUNCTION__, ipProto); break; } _invalid_exit: return; } // This function assumes we are replying back to the same IP // that originally sent us the packet and therefore we can reuse the // ingress packet for egress; in other words, it assumes the // original IP header is intact and will just reuse it after // minimal modifications void Device::sendIp4Reply(PacketBuffer *pktBuf) { uchar *pktData = pktBuf->push(20); uchar origTtl = pktData[8]; uchar ipProto = pktData[9]; quint32 srcIp, dstIp, tgtIp, mask; quint32 sum; // Swap src/dst IP addresses dstIp = qFromBigEndian(pktData + 12); // srcIp in original pkt srcIp = qFromBigEndian(pktData + 16); // dstIp in original pkt tgtIp = ((dstIp & ip4Mask_) == ip4Subnet_) ? dstIp : ip4Gateway_; if (!arpTable_.contains(tgtIp)) { qWarning("%s: mac not found for %s; unable to send IPv4 packet", __FUNCTION__, qPrintable(QHostAddress(tgtIp).toString())); return; } *(quint32*)(pktData + 12) = qToBigEndian(srcIp); *(quint32*)(pktData + 16) = qToBigEndian(dstIp); // Reset TTL pktData[8] = 64; // Incremental checksum update (RFC 1624 [Eqn.3]) // HC' = ~(~HC + ~m + m') sum = quint16(~qFromBigEndian(pktData + 10)); // old cksum sum += quint16(~quint16(origTtl << 8 | ipProto)); // old value sum += quint16(pktData[8] << 8 | ipProto); // new value while(sum >> 16) sum = (sum & 0xFFFF) + (sum >> 16); *(quint16*)(pktData + 10) = qToBigEndian(quint16(~sum)); encap(pktBuf, arpTable_.value(tgtIp), 0x0800); transmitPacket(pktBuf); } void Device::receiveIcmp4(PacketBuffer *pktBuf) { uchar *pktData = pktBuf->data(); quint32 sum; // XXX: We don't verify icmp checksum // We handle only ping request if (pktData[0] != 8) { // Echo Request qDebug("%s: Ignoring non echo request (%d)", __FUNCTION__, pktData[0]); return; } pktData[0] = 0; // Echo Reply // Incremental checksum update (RFC 1624 [Eqn.3]) // HC' = ~(~HC + ~m + m') sum = quint16(~qFromBigEndian(pktData + 2)); // old cksum sum += quint16(~quint16(8 << 8 | pktData[1])); // old value sum += quint16(0 << 8 | pktData[1]); // new value while(sum >> 16) sum = (sum & 0xFFFF) + (sum >> 16); *(quint16*)(pktData + 2) = qToBigEndian(quint16(~sum)); sendIp4Reply(pktBuf); qDebug("Sent ICMP Echo Reply"); } /* * --------------------------------------------------------- * IPV6 related private methods * --------------------------------------------------------- */ void Device::receiveIp6(PacketBuffer *pktBuf) { uchar *pktData = pktBuf->data(); uchar ipProto; UInt128 dstIp; if ((pktData[0] & 0xF0) != 0x60) { qDebug("%s: Unsupported IP version (%02x) ", __FUNCTION__, pktData[0]); goto _invalid_exit; } if (pktBuf->length() < kIp6HdrLen) { qDebug("incomplete IPv6 header: expected %d, actual %d", kIp6HdrLen, pktBuf->length()); goto _invalid_exit; } // FIXME: check for specific mcast address(es) instead of any mcast? dstIp = qFromBigEndian(pktData + 24); if (!isIp6Mcast(dstIp) && (dstIp != ip6_)) { qDebug("%s: dstIp %s is not me (%s)", __FUNCTION__, qPrintable(QHostAddress(dstIp.toArray()).toString()), qPrintable(QHostAddress(ip6_.toArray()).toString())); goto _invalid_exit; } ipProto = pktData[6]; switch (ipProto) { case kIpProtoIcmp6: pktBuf->pull(kIp6HdrLen); receiveIcmp6(pktBuf); break; default: break; } _invalid_exit: return; } // pktBuf should point to start of IP payload bool Device::sendIp6(PacketBuffer *pktBuf, UInt128 dstIp, quint8 protocol) { int payloadLen = pktBuf->length(); uchar *p = pktBuf->push(kIp6HdrLen); quint64 dstMac; if (!p) { qWarning("%s: failed to push %d bytes [0x%p, 0x%p]", __FUNCTION__, kIp6HdrLen, pktBuf->head(), pktBuf->data()); goto _error_exit; } // In case of mcast, derive dstMac if ((dstIp.hi64() >> 56) == 0xff) dstMac = (quint64(0x3333) << 32) | (dstIp.lo64() & 0xffffffff); else { UInt128 tgtIp = ((dstIp & ip6Mask_) == ip6Subnet_)? dstIp : ip6Gateway_; dstMac = ndpTable_.value(tgtIp); } if (!dstMac) { qWarning("%s: mac not found for %s; unable to send IPv6 packet", __FUNCTION__, qPrintable(QHostAddress(dstIp.toArray()).toString())); goto _error_exit; } // Ver(4), TrfClass(8), FlowLabel(8) *(quint32*)(p ) = qToBigEndian(quint32(0x60000000)); *(quint16*)(p+ 4) = qToBigEndian(quint16(payloadLen)); p[6] = protocol; p[7] = 255; // HopLimit memcpy(p+ 8, ip6_.toArray(), 16); // Source IP memcpy(p+24, dstIp.toArray(), 16); // Destination IP // FIXME: both these functions should return success/failure encap(pktBuf, dstMac, kEthTypeIp6); transmitPacket(pktBuf); return true; _error_exit: return false; } // This function assumes we are replying back to the same IP // that originally sent us the packet and therefore we can reuse the // ingress packet for egress; in other words, it assumes the // original IP header is intact and will just reuse it after // minimal modifications void Device::sendIp6Reply(PacketBuffer *pktBuf) { uchar *pktData = pktBuf->push(kIp6HdrLen); UInt128 srcIp, dstIp, tgtIp; // Swap src/dst IP addresses dstIp = qFromBigEndian(pktData + 8); // srcIp in original pkt srcIp = qFromBigEndian(pktData + 24); // dstIp in original pkt tgtIp = ((dstIp & ip6Mask_) == ip6Subnet_) ? dstIp : ip6Gateway_; if (!ndpTable_.contains(tgtIp)) { qWarning("%s: mac not found for %s; unable to send IPv6 packet", __FUNCTION__, qPrintable(QHostAddress(tgtIp.toArray()).toString())); return; } memcpy(pktData + 8, srcIp.toArray(), 16); // Source IP memcpy(pktData + 24, dstIp.toArray(), 16); // Destination IP // Reset TTL pktData[7] = 64; encap(pktBuf, ndpTable_.value(tgtIp), 0x86dd); transmitPacket(pktBuf); } void Device::receiveIcmp6(PacketBuffer *pktBuf) { uchar *pktData = pktBuf->data(); quint8 type = pktData[0]; quint32 sum; // XXX: We don't verify icmp checksum switch (type) { case 128: // ICMPv6 Echo Request pktData[0] = 129; // Echo Reply // Incremental checksum update (RFC 1624 [Eqn.3]) // HC' = ~(~HC + ~m + m') sum = quint16(~qFromBigEndian(pktData + 2)); // old cksum sum += quint16(~quint16(128 << 8 | pktData[1])); // old value sum += quint16(129 << 8 | pktData[1]); // new value while(sum >> 16) sum = (sum & 0xFFFF) + (sum >> 16); *(quint16*)(pktData + 2) = qToBigEndian(quint16(~sum)); sendIp6Reply(pktBuf); qDebug("Sent ICMPv6 Echo Reply"); break; case 135: // Neigh Solicit case 136: // Neigh Advt receiveNdp(pktBuf); break; default: break; } } void Device::receiveNdp(PacketBuffer *pktBuf) { uchar *pktData = pktBuf->data(); quint8 type = pktData[0]; int len = pktBuf->length(); int minLen = 24 + (type == 136 ? 8 : 0); // NA should have the Target TLV if (len < minLen) { qDebug("%s: incomplete NS/NA header: expected %d, actual %d", __FUNCTION__, minLen, pktBuf->length()); goto _invalid_exit; } switch (type) { case 135: { // Neigh Solicit // TODO: Validation as per RFC 4861 sendNeighborAdvertisement(pktBuf); break; } case 136: { // Neigh Advt quint8 flags = pktData[4]; const quint8 kSFlag = 0x40; const quint8 kOFlag = 0x20; UInt128 tgtIp = qFromBigEndian(pktData + 8); quint64 mac = ndpTable_.value(tgtIp); // Update NDP table only for solicited responses if (!(flags & kSFlag)) break; if ((flags & kOFlag) || (mac == 0)) { // Check if we have a Target Link-Layer TLV if ((pktData[24] != 2) || (pktData[25] != 1)) goto _invalid_exit; mac = qFromBigEndian(pktData + 26); mac = (mac << 16) | qFromBigEndian(pktData + 30); ndpTable_.insert(tgtIp, mac); } break; } } _invalid_exit: return; } // Send NS for the IPv6 packet in pktBuf // caller is responsible to check that pktBuf originates from this device // pktBuf should point to start of IP header void Device::sendNeighborSolicit(PacketBuffer *pktBuf) { uchar *pktData = pktBuf->data(); UInt128 dstIp, tgtIp; if (pktBuf->length() < kIp6HdrLen) { qDebug("incomplete IPv6 header: expected %d, actual %d", kIp6HdrLen, pktBuf->length()); return; } dstIp = qFromBigEndian(pktData + 24); tgtIp = ((dstIp & ip6Mask_) == ip6Subnet_) ? dstIp : ip6Gateway_; sendNeighborSolicit(tgtIp); } void Device::sendNeighborSolicit(UInt128 tgtIp) { UInt128 dstIp, srcIp = ip6_; PacketBuffer *reqPkt; uchar *pktData; // Validate target IP if (tgtIp == UInt128(0, 0)) return; // Do we already have a NDP entry (resolved or unresolved)? // If so, don't resend (see note in sendArpRequest()) if (ndpTable_.contains(tgtIp)) return; // Form the solicited node address to be used as dstIp // ff02::1:ffXX:XXXX/104 dstIp = UInt128((quint64(0xff02) << 48), (quint64(0x01ff) << 24) | (tgtIp.lo64() & 0xFFFFFF)); reqPkt = new PacketBuffer; reqPkt->reserve(encapSize() + kIp6HdrLen); pktData = reqPkt->put(32); if (pktData) { // Calculate checksum first - // start with fixed fields in ICMP Header and IPv6 Pseudo Header ... quint32 sum = 0x8700 + 0x0101 + 32 + kIpProtoIcmp6; // then variable fields from ICMP header ... sum += sumUInt128(tgtIp); sum += (mac_ >> 32) + ((mac_ >> 16) & 0xffff) + (mac_ & 0xffff); // and variable fields from IPv6 pseudo header sum += sumUInt128(ip6_); sum += sumUInt128(dstIp); while(sum >> 16) sum = (sum & 0xFFFF) + (sum >> 16); // Type, Code *(quint16*)(pktData ) = qToBigEndian(quint16(0x8700)); // Checksum *(quint16*)(pktData+ 2) = qToBigEndian(quint16(~sum)); // Reserved *(quint32*)(pktData+ 4) = qToBigEndian(quint32(0)); // Target IP memcpy(pktData+ 8, tgtIp.toArray(), 16); // Source Addr TLV + MacAddr *(quint16*)(pktData+24) = qToBigEndian(quint16(0x0101)); *(quint32*)(pktData+26) = qToBigEndian(quint32(mac_ >> 16)); *(quint16*)(pktData+30) = qToBigEndian(quint16(mac_ & 0xffff)); } if (!sendIp6(reqPkt, dstIp , kIpProtoIcmp6)) return; ndpTable_.insert(tgtIp, 0); qDebug("Sent NDP Request for srcIp/tgtIp=%s/%s", qPrintable(QHostAddress(srcIp.toArray()).toString()), qPrintable(QHostAddress(tgtIp.toArray()).toString())); } // Send NA for the NS packet in pktBuf // pktBuf should point to start of ICMPv6 header void Device::sendNeighborAdvertisement(PacketBuffer *pktBuf) { PacketBuffer *naPkt; uchar *pktData = pktBuf->data(); quint16 flags = 0x6000; // solicit = 1; overide = 1 uchar *ip6Hdr; UInt128 tgtIp, srcIp; tgtIp = qFromBigEndian(pktData + 8); if (tgtIp != ip6_) { qDebug("%s: NS tgtIp %s is not us %s", __FUNCTION__, qPrintable(QHostAddress(tgtIp.toArray()).toString()), qPrintable(QHostAddress(ip6_.toArray()).toString())); ip6Hdr = pktBuf->push(kIp6HdrLen); return; } ip6Hdr = pktBuf->push(kIp6HdrLen); srcIp = qFromBigEndian(ip6Hdr + 8); if (srcIp == UInt128(0, 0)) { // reset solicit flag flags &= ~0x4000; // NA should be sent to All nodes address srcIp = UInt128(quint64(0xff02) << 48, quint64(1)); } else if (pktBuf->length() >= 32) { // have TLVs? if ((pktData[24] == 0x01) && (pktData[25] == 0x01)) { // Source TLV quint64 mac; mac = qFromBigEndian(pktData + 26); mac = (mac << 16) | qFromBigEndian(pktData + 30); ndpTable_.insert(srcIp, mac); } } naPkt = new PacketBuffer; naPkt->reserve(encapSize() + kIp6HdrLen); pktData = naPkt->put(32); if (pktData) { // Calculate checksum first - // start with fixed fields in ICMP Header and IPv6 Pseudo Header ... quint32 sum = (0x8800 + flags + 0x0201) + (32 + kIpProtoIcmp6); // then variable fields from ICMP header ... sum += sumUInt128(tgtIp); sum += (mac_ >> 32) + ((mac_ >> 16) & 0xffff) + (mac_ & 0xffff); // and variable fields from IPv6 pseudo header sum += sumUInt128(ip6_); sum += sumUInt128(srcIp); while(sum >> 16) sum = (sum & 0xFFFF) + (sum >> 16); // Type, Code *(quint16*)(pktData ) = qToBigEndian(quint16(0x8800)); // Checksum *(quint16*)(pktData+ 2) = qToBigEndian(quint16(~sum)); // Flags-Reserved *(quint32*)(pktData+ 4) = qToBigEndian(quint32(flags << 16)); // Target IP memcpy(pktData+ 8, tgtIp.toArray(), 16); // Target Addr TLV + MacAddr *(quint16*)(pktData+24) = qToBigEndian(quint16(0x0201)); *(quint32*)(pktData+26) = qToBigEndian(quint32(mac_ >> 16)); *(quint16*)(pktData+30) = qToBigEndian(quint16(mac_ & 0xffff)); } if (!sendIp6(naPkt, srcIp , kIpProtoIcmp6)) return; qDebug("Sent Neigh Advt to dstIp for tgtIp=%s/%s", qPrintable(QHostAddress(srcIp.toArray()).toString()), qPrintable(QHostAddress(tgtIp.toArray()).toString())); } bool operator<(const DeviceKey &a1, const DeviceKey &a2) { int i = 0; while (i < a1.size()) { if (uchar(a1.at(i)) < uchar(a2.at(i))) return true; if (uchar(a1.at(i)) > uchar(a2.at(i))) return false; i++; } return false; } ostinato-0.9/server/device.h000066400000000000000000000063141321315675200161510ustar00rootroot00000000000000/* Copyright (C) 2015 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _DEVICE_H #define _DEVICE_H #include "../common/emulproto.pb.h" #include "../common/protocol.pb.h" #include "../common/uint128.h" #include #include class DeviceManager; class PacketBuffer; class DeviceKey: public QByteArray { }; class Device { public: static const quint16 kVlanTpid = 0x8100; enum NeighborSet { kAllNeighbors, kUnresolvedNeighbors }; public: Device(DeviceManager *deviceManager); void setVlan(int index, quint16 vlan, quint16 tpid = kVlanTpid); quint64 mac(); void setMac(quint64 mac); void setIp4(quint32 address, int prefixLength, quint32 gateway); void setIp6(UInt128 address, int prefixLength, UInt128 gateway); void getConfig(OstEmul::Device *deviceConfig); QString config(); DeviceKey key(); void clearKey(); int encapSize(); void encap(PacketBuffer *pktBuf, quint64 dstMac, quint16 type); void receivePacket(PacketBuffer *pktBuf); void transmitPacket(PacketBuffer *pktBuf); void resolveGateway(); void clearNeighbors(Device::NeighborSet set); void resolveNeighbor(PacketBuffer *pktBuf); void getNeighbors(OstEmul::DeviceNeighborList *neighbors); bool isOrigin(const PacketBuffer *pktBuf); quint64 neighborMac(const PacketBuffer *pktBuf); private: // methods void receiveArp(PacketBuffer *pktBuf); void sendArpRequest(PacketBuffer *pktBuf); void sendArpRequest(quint32 tgtIp); void receiveIp4(PacketBuffer *pktBuf); void sendIp4Reply(PacketBuffer *pktBuf); void receiveIcmp4(PacketBuffer *pktBuf); void receiveIp6(PacketBuffer *pktBuf); bool sendIp6(PacketBuffer *pktBuf, UInt128 dstIp, quint8 protocol); void sendIp6Reply(PacketBuffer *pktBuf); void receiveIcmp6(PacketBuffer *pktBuf); void receiveNdp(PacketBuffer *pktBuf); void sendNeighborSolicit(PacketBuffer *pktBuf); void sendNeighborSolicit(UInt128 tgtIp); void sendNeighborAdvertisement(PacketBuffer *pktBuf); private: // data static const int kMaxVlan = 4; DeviceManager *deviceManager_; int numVlanTags_; quint32 vlan_[kMaxVlan]; quint64 mac_; bool hasIp4_; quint32 ip4_; int ip4PrefixLength_; quint32 ip4Gateway_; quint32 ip4Mask_; quint32 ip4Subnet_; bool hasIp6_; UInt128 ip6_; int ip6PrefixLength_; UInt128 ip6Gateway_; UInt128 ip6Mask_; UInt128 ip6Subnet_; DeviceKey key_; QHash arpTable_; QHash ndpTable_; }; bool operator<(const DeviceKey &a1, const DeviceKey &a2); #endif ostinato-0.9/server/devicemanager.cpp000066400000000000000000000342721321315675200200430ustar00rootroot00000000000000/* Copyright (C) 2015 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "devicemanager.h" #include "abstractport.h" #include "device.h" #include "../common/emulation.h" #include "packetbuffer.h" #include "../common/emulproto.pb.h" #include #define __STDC_FORMAT_MACROS #include const quint64 kBcastMac = 0xffffffffffffULL; inline UInt128 UINT128(OstEmul::Ip6Address x) { return UInt128(x.hi(), x.lo()); } inline bool isMacMcast(quint64 mac) { return ((mac >> 40) & 0x01) == 0x01; } // XXX: Port owning DeviceManager already uses locks, so we don't use any // locks within DeviceManager to protect deviceGroupList_ et.al. DeviceManager::DeviceManager(AbstractPort *parent) { port_ = parent; } DeviceManager::~DeviceManager() { foreach(Device *dev, deviceList_) delete dev; foreach(OstProto::DeviceGroup *devGrp, deviceGroupList_) delete devGrp; } int DeviceManager::deviceGroupCount() { return deviceGroupList_.size(); } const OstProto::DeviceGroup* DeviceManager::deviceGroupAtIndex(int index) { if ((index < 0) || (index >= deviceGroupCount())) { qWarning("%s: index %d out of range (0 - %d)", __FUNCTION__, index, deviceGroupCount() - 1); return NULL; } // Sort List by 'id', get the id at 'index' and then corresponding devGrp return deviceGroupList_.value(deviceGroupList_.uniqueKeys().value(index)); } const OstProto::DeviceGroup* DeviceManager::deviceGroup(uint deviceGroupId) { return deviceGroupList_.value(deviceGroupId); } bool DeviceManager::addDeviceGroup(uint deviceGroupId) { OstProto::DeviceGroup *deviceGroup; if (deviceGroupList_.contains(deviceGroupId)) { qWarning("%s: deviceGroup id %u already exists", __FUNCTION__, deviceGroupId); return false; } deviceGroup = newDeviceGroup(port_->id()); deviceGroup->mutable_device_group_id()->set_id(deviceGroupId); deviceGroupList_.insert(deviceGroupId, deviceGroup); enumerateDevices(deviceGroup, kAdd); // Start emulation when first device is added if ((deviceCount() == 1) && port_) port_->startDeviceEmulation(); return true; } bool DeviceManager::deleteDeviceGroup(uint deviceGroupId) { OstProto::DeviceGroup *deviceGroup; if (!deviceGroupList_.contains(deviceGroupId)) { qWarning("%s: deviceGroup id %u does not exist", __FUNCTION__, deviceGroupId); return false; } deviceGroup = deviceGroupList_.take(deviceGroupId); enumerateDevices(deviceGroup, kDelete); delete deviceGroup; // Stop emulation if no devices remain if ((deviceCount() == 0) && port_) port_->stopDeviceEmulation(); return true; } bool DeviceManager::modifyDeviceGroup(const OstProto::DeviceGroup *deviceGroup) { quint32 id = deviceGroup->device_group_id().id(); OstProto::DeviceGroup *myDeviceGroup = deviceGroupList_.value(id); if (!myDeviceGroup) { qWarning("%s: deviceGroup id %u does not exist", __FUNCTION__, id); return false; } enumerateDevices(myDeviceGroup, kDelete); myDeviceGroup->CopyFrom(*deviceGroup); // If mac step is 0, silently override to 1 - otherwise we won't have // unique DeviceKeys if (myDeviceGroup->GetExtension(OstEmul::mac).step() == 0) myDeviceGroup->MutableExtension(OstEmul::mac)->set_step(1); // Default value for ip6 step should be 1 (not 0) if (myDeviceGroup->HasExtension(OstEmul::ip6) && !myDeviceGroup->GetExtension(OstEmul::ip6).has_step()) myDeviceGroup->MutableExtension(OstEmul::ip6) ->mutable_step()->set_lo(1); enumerateDevices(myDeviceGroup, kAdd); return true; } int DeviceManager::deviceCount() { return deviceList_.size(); } void DeviceManager::getDeviceList( OstProto::PortDeviceList *deviceList) { foreach(Device *device, sortedDeviceList_) { OstEmul::Device *dev = deviceList->AddExtension(OstEmul::device); device->getConfig(dev); } } void DeviceManager::receivePacket(PacketBuffer *pktBuf) { uchar *pktData = pktBuf->data(); int offset = 0; Device dk(this); Device *device; quint64 dstMac; quint16 ethType; quint16 vlan; int idx = 0; // We assume pkt is ethernet // TODO: extend for other link layer types // All frames we are interested in should be at least 32 bytes if (pktBuf->length() < 32) { qWarning("short frame of %d bytes, skipping ...", pktBuf->length()); goto _exit; } // Extract dstMac dstMac = qFromBigEndian(pktData + offset); offset += 4; dstMac = (dstMac << 16) | qFromBigEndian(pktData + offset); qDebug("dstMac %012" PRIx64, dstMac); // XXX: Treat multicast as bcast if (isMacMcast(dstMac)) dstMac = kBcastMac; dk.setMac(dstMac); offset += 2; // Skip srcMac - don't care offset += 6; _eth_type: // Extract EthType ethType = qFromBigEndian(pktData + offset); qDebug("%s: ethType 0x%x", __PRETTY_FUNCTION__, ethType); if (tpidList_.contains(ethType)) { offset += 2; vlan = qFromBigEndian(pktData + offset); dk.setVlan(idx++, vlan); offset += 2; qDebug("%s: idx: %d vlan %d", __FUNCTION__, idx, vlan); goto _eth_type; } pktBuf->pull(offset); if (dstMac == kBcastMac) { QList list = bcastList_.values(dk.key()); // FIXME: We need to clone the pktBuf before passing to each // device, otherwise only the first device gets the original // packet - all subsequent ones get the modified packet! // NOTE: modification may not be in the pkt data buffer but // in the HDTE pointers - which is bad as well! foreach(Device *device, list) device->receivePacket(pktBuf); goto _exit; } // Is it destined for us? device = deviceList_.value(dk.key()); if (!device) { qDebug("%s: dstMac %012llx is not us", __FUNCTION__, dstMac); goto _exit; } device->receivePacket(pktBuf); _exit: delete pktBuf; } void DeviceManager::transmitPacket(PacketBuffer *pktBuf) { port_->sendEmulationPacket(pktBuf); } void DeviceManager::resolveDeviceGateways() { foreach(Device *device, deviceList_) { device->resolveGateway(); } } void DeviceManager::clearDeviceNeighbors(Device::NeighborSet set) { foreach(Device *device, deviceList_) device->clearNeighbors(set); } void DeviceManager::getDeviceNeighbors( OstProto::PortNeighborList *neighborList) { int count = 0; foreach(Device *device, sortedDeviceList_) { OstEmul::DeviceNeighborList *neighList = neighborList->AddExtension(OstEmul::device_neighbor); neighList->set_device_index(count++); device->getNeighbors(neighList); } } void DeviceManager::resolveDeviceNeighbor(PacketBuffer *pktBuf) { Device *device = originDevice(pktBuf); if (device) device->resolveNeighbor(pktBuf); } quint64 DeviceManager::deviceMacAddress(PacketBuffer *pktBuf) { Device *device = originDevice(pktBuf); return device ? device->mac() : 0; } quint64 DeviceManager::neighborMacAddress(PacketBuffer *pktBuf) { Device *device = originDevice(pktBuf); return device ? device->neighborMac(pktBuf) : 0; } // ------------------------------------ // // Private Methods // ------------------------------------ // Device* DeviceManager::originDevice(PacketBuffer *pktBuf) { uchar *pktData = pktBuf->data(); int offset = 12; // start parsing after mac addresses Device dk(this); quint16 ethType; quint16 vlan; int idx = 0; // Do we have any devices at all? if (!deviceCount()) return NULL; // pktBuf will not have the correct dstMac populated, so use bcastMac // and search for device by IP dk.setMac(kBcastMac); _eth_type: ethType = qFromBigEndian(pktData + offset); qDebug("%s: ethType 0x%x", __PRETTY_FUNCTION__, ethType); if (tpidList_.contains(ethType)) { offset += 2; vlan = qFromBigEndian(pktData + offset); dk.setVlan(idx++, vlan); offset += 2; qDebug("%s: idx: %d vlan %d", __FUNCTION__, idx, vlan); goto _eth_type; } pktBuf->pull(offset); foreach(Device *device, bcastList_.values(dk.key())) { if (device->isOrigin(pktBuf)) return device; } qDebug("couldn't find origin device for packet"); return NULL; } void DeviceManager::enumerateDevices( const OstProto::DeviceGroup *deviceGroup, Operation oper) { Device dk(this); OstEmul::VlanEmulation pbVlan = deviceGroup->encap() .GetExtension(OstEmul::vlan); int numTags = pbVlan.stack_size(); int n = 1; QList vlanCount; bool hasIp4 = deviceGroup->HasExtension(OstEmul::ip4); bool hasIp6 = deviceGroup->HasExtension(OstEmul::ip6); OstEmul::MacEmulation mac = deviceGroup->GetExtension(OstEmul::mac); OstEmul::Ip4Emulation ip4 = deviceGroup->GetExtension(OstEmul::ip4); OstEmul::Ip6Emulation ip6 = deviceGroup->GetExtension(OstEmul::ip6); /* * vlanCount[] stores the number of unique vlans at each tag level * e.g. for a 3-tag config with 2, 3, 4 vlans at each level respectively * vlanCount = [24, 12, 4] * 0 - 0, 0, 0 * 1 - 0, 0, 1 * 2 - 0, 0, 2 * 3 - 0, 0, 3 * 4 - 0, 1, 0 * 5 - 0, 1, 1 * 6 - 0, 1, 2 * 7 - 0, 1, 3 * 8 - 0, 2, 0 * 9 - 0, 2, 1 * 10 - 0, 2, 2 * 11 - 0, 2, 3 * 12 - 1, 0, 0 * 13 - 1, 0, 1 * 14 - 1, 0, 2 * 15 - 1, 0, 3 * 16 - 1, 1, 0 * 17 - 1, 1, 1 * 18 - 1, 1, 2 * 19 - 1, 1, 3 * 21 - 1, 2, 0 * 21 - 1, 2, 1 * 22 - 1, 2, 2 * 23 - 1, 2, 3 * * Note that vlanCount[0] repesents total-number-of-vlans * * Another way to think about this is that at a particular vlan tag * level, we need to repeat a particular vlan-id as many times as the * next level's count before we can increment the vlan-id at that level * * We use this list to calculate the vlan ids for each tag level for * all the vlans. * * For implementation convenience we append a '1' as the last element */ vlanCount.append(n); for (int i = numTags - 1; i >= 0 ; i--) { OstEmul::VlanEmulation::Vlan vlan = pbVlan.stack(i); n *= vlan.count(); vlanCount.prepend(n); // Update TPID list switch (oper) { case kAdd: tpidList_[vlan.tpid()]++; break; case kDelete: tpidList_[vlan.tpid()]--; if (tpidList_[vlan.tpid()] == 0) tpidList_.remove(vlan.tpid()); break; default: Q_ASSERT(0); // Unreachable } } QHash::const_iterator iter = tpidList_.constBegin(); qDebug("Port %s TPID List:", port_->name()); while (iter != tpidList_.constEnd()) { qDebug("tpid: %x (%d)", iter.key(), iter.value()); iter++; } for (int i = 0; i < vlanCount.at(0); i++) { for (int j = 0; j < numTags; j++) { OstEmul::VlanEmulation::Vlan vlan = pbVlan.stack(j); quint16 vlanAdd = (i/vlanCount.at(j+1) % vlan.count())*vlan.step(); dk.setVlan(j, vlan.vlan_tag() + vlanAdd, vlan.tpid()); } for (uint k = 0; k < deviceGroup->device_count(); k++) { Device *device; quint64 macAdd = k * mac.step(); quint32 ip4Add = k * ip4.step(); UInt128 ip6Add = UINT128(ip6.step()) * k; dk.setMac(mac.address() + macAdd); if (hasIp4) dk.setIp4(ip4.address() + ip4Add, ip4.prefix_length(), ip4.default_gateway()); if (hasIp6) dk.setIp6(UINT128(ip6.address()) + ip6Add, ip6.prefix_length(), UINT128(ip6.default_gateway())); switch (oper) { case kAdd: if (deviceList_.contains(dk.key())) { qWarning("%s: error adding device %s (EEXIST)", __FUNCTION__, qPrintable(dk.config())); break; } device = new Device(this); *device = dk; deviceList_.insert(dk.key(), device); sortedDeviceList_.insert(dk.key(), device); dk.setMac(kBcastMac); bcastList_.insert(dk.key(), device); qDebug("enumerate(add): %s", qPrintable(device->config())); break; case kDelete: device = deviceList_.take(dk.key()); if (!device) { qWarning("%s: error deleting device %s (NOTFOUND)", __FUNCTION__, qPrintable(dk.config())); break; } qDebug("enumerate(del): %s", qPrintable(device->config())); delete device; sortedDeviceList_.take(dk.key()); // already freed above dk.setMac(kBcastMac); bcastList_.take(dk.key()); // device already freed above break; default: Q_ASSERT(0); // Unreachable } } // foreach device } // foreach vlan } ostinato-0.9/server/devicemanager.h000066400000000000000000000045651321315675200175120ustar00rootroot00000000000000/* Copyright (C) 2015 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _DEVICE_MANAGER_H #define _DEVICE_MANAGER_H #include "device.h" #include #include #include #include class AbstractPort; class PacketBuffer; namespace OstProto { class DeviceGroup; }; class DeviceManager { public: DeviceManager(AbstractPort *parent = 0); ~DeviceManager(); int deviceGroupCount(); const OstProto::DeviceGroup* deviceGroupAtIndex(int index); const OstProto::DeviceGroup* deviceGroup(uint deviceGroupId); bool addDeviceGroup(uint deviceGroupId); bool deleteDeviceGroup(uint deviceGroupId); bool modifyDeviceGroup(const OstProto::DeviceGroup *deviceGroup); int deviceCount(); void getDeviceList(OstProto::PortDeviceList *deviceList); void receivePacket(PacketBuffer *pktBuf); void transmitPacket(PacketBuffer *pktBuf); void resolveDeviceGateways(); void clearDeviceNeighbors(Device::NeighborSet set = Device::kAllNeighbors); void resolveDeviceNeighbor(PacketBuffer *pktBuf); void getDeviceNeighbors(OstProto::PortNeighborList *neighborList); quint64 deviceMacAddress(PacketBuffer *pktBuf); quint64 neighborMacAddress(PacketBuffer *pktBuf); private: enum Operation { kAdd, kDelete }; Device* originDevice(PacketBuffer *pktBuf); void enumerateDevices( const OstProto::DeviceGroup *deviceGroup, Operation oper); AbstractPort *port_; QHash deviceGroupList_; QHash deviceList_; // fast access to devices QMap sortedDeviceList_; // sorted access to devices QMultiHash bcastList_; QHash tpidList_; // Key: TPID, Value: RefCount }; #endif ostinato-0.9/server/drone.cpp000066400000000000000000000044231321315675200163530ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "drone.h" #include "myservice.h" #include "rpcserver.h" #include "settings.h" #include "../common/updater.h" #include extern int myport; extern const char* version; extern const char* revision; Drone::Drone(QObject *parent) : QObject(parent) { Updater *updater = new Updater(); rpcServer = new RpcServer(); service = new MyService(); connect(updater, SIGNAL(newVersionAvailable(QString)), this, SLOT(onNewVersion(QString))); updater->checkForNewVersion(); } Drone::~Drone() { delete rpcServer; delete service; } bool Drone::init() { QString addr = appSettings->value(kRpcServerAddress).toString(); QHostAddress address = addr.isEmpty() ? QHostAddress::Any : QHostAddress(addr); Q_ASSERT(rpcServer); qRegisterMetaType("SharedProtobufMessage"); if (address.isNull()) { qWarning("Invalid RpcServer Address <%s> specified. Using 'Any'", qPrintable(addr)); address = QHostAddress::Any; } if (!rpcServer->registerService(service, address, myport ? myport : 7878)) { //qCritical(qPrintable(rpcServer->errorString())); return false; } connect(service, SIGNAL(notification(int, SharedProtobufMessage)), rpcServer, SIGNAL(notifyClients(int, SharedProtobufMessage))); return true; } MyService* Drone::rpcService() { return service; } void Drone::onNewVersion(QString newVersion) { qWarning("%s", qPrintable(QString("New Ostinato version %1 available. " "Visit http://ostinato.org to download").arg(newVersion))); } ostinato-0.9/server/drone.h000066400000000000000000000020501321315675200160120ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _DRONE_H #define _DRONE_H #include class RpcServer; class MyService; class Drone : public QObject { Q_OBJECT public: Drone(QObject *parent = 0); ~Drone(); bool init(); MyService* rpcService(); private slots: void onNewVersion(QString version); private: RpcServer *rpcServer; MyService *service; }; #endif ostinato-0.9/server/drone.pro000066400000000000000000000027461321315675200163770ustar00rootroot00000000000000TEMPLATE = app CONFIG += qt ver_info QT += network script xml QT -= gui DEFINES += HAVE_REMOTE WPCAP linux*:system(grep -q IFLA_STATS64 /usr/include/linux/if_link.h): \ DEFINES += HAVE_IFLA_STATS64 INCLUDEPATH += "../rpc" win32 { CONFIG += console QMAKE_LFLAGS += -static LIBS += -lwpcap -lpacket CONFIG(debug, debug|release) { LIBS += -L"../common/debug" -lostproto LIBS += -L"../rpc/debug" -lpbrpc POST_TARGETDEPS += \ "../common/debug/libostproto.a" \ "../rpc/debug/libpbrpc.a" } else { LIBS += -L"../common/release" -lostproto LIBS += -L"../rpc/release" -lpbrpc POST_TARGETDEPS += \ "../common/release/libostproto.a" \ "../rpc/release/libpbrpc.a" } } else { LIBS += -lpcap LIBS += -L"../common" -lostproto LIBS += -L"../rpc" -lpbrpc POST_TARGETDEPS += "../common/libostproto.a" "../rpc/libpbrpc.a" } LIBS += -lm LIBS += -lprotobuf HEADERS += drone.h \ pcaptransmitter.h \ myservice.h SOURCES += \ devicemanager.cpp \ device.cpp \ drone_main.cpp \ drone.cpp \ portmanager.cpp \ abstractport.cpp \ pcapport.cpp \ pcaptransmitter.cpp \ pcaprxstats.cpp \ pcaptxstats.cpp \ pcaptxthread.cpp \ bsdport.cpp \ linuxport.cpp \ winpcapport.cpp SOURCES += myservice.cpp SOURCES += pcapextra.cpp SOURCES += packetbuffer.cpp QMAKE_DISTCLEAN += object_script.* include (../install.pri) include (../version.pri) ostinato-0.9/server/drone_main.cpp000066400000000000000000000053761321315675200173670ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "drone.h" #include "../common/protocolmanager.h" #include "settings.h" #include #include #include #ifdef Q_OS_UNIX #include #endif extern ProtocolManager *OstProtocolManager; extern char *version; extern char *revision; Drone *drone; QSettings *appSettings; int myport; void cleanup(int /*signum*/) { QCoreApplication::instance()->exit(-1); } int main(int argc, char *argv[]) { int exitCode = 0; QCoreApplication app(argc, argv); // TODO: command line options // -v (--version) // -h (--help) // -p (--portnum) if (argc > 1) myport = atoi(argv[1]); app.setApplicationName("Drone"); app.setOrganizationName("Ostinato"); /* (Portable Mode) If we have a .ini file in the same directory as the executable, we use that instead of the platform specific location and format for the settings */ QString portableIni = QCoreApplication::applicationDirPath() + "/drone.ini"; if (QFile::exists(portableIni)) appSettings = new QSettings(portableIni, QSettings::IniFormat); else appSettings = new QSettings(QSettings::IniFormat, QSettings::UserScope, app.organizationName(), app.applicationName().toLower()); drone = new Drone(); OstProtocolManager = new ProtocolManager(); if (!drone->init()) { exitCode = 1; goto _exit; } qDebug("Version: %s", version); qDebug("Revision: %s", revision); #ifdef Q_OS_UNIX struct sigaction sa; memset(&sa, 0, sizeof(sa)); sa.sa_handler = cleanup; if (sigaction(SIGTERM, &sa, NULL)) qDebug("Failed to install SIGTERM handler. Cleanup may not happen!!!"); if (sigaction(SIGINT, &sa, NULL)) qDebug("Failed to install SIGINT handler. Cleanup may not happen!!!"); #endif exitCode = app.exec(); _exit: delete drone; delete OstProtocolManager; google::protobuf::ShutdownProtobufLibrary(); return exitCode; } ostinato-0.9/server/icons/000077500000000000000000000000001321315675200156505ustar00rootroot00000000000000ostinato-0.9/server/icons/portgroup.png000066400000000000000000000012331321315675200204160ustar00rootroot00000000000000‰PNG  IHDRóÿagAMA¯È7ŠétEXtSoftwareAdobe ImageReadyqÉe<-IDAT8Ë}S;lA}»w¶¥8§ØŽÃ7€RãD ˆ !ACI‡D‹¨h)¶k¢ÐñQ  ”¥¤á° H(4 $ÛI” ó}–™Ù³EP’=ÍÎÝíÎ{ovf•1» ß÷GQt# C—<È‹õû}öÏÔNÍfsŒæ …Âõ\n!I »)¦X,`ié-Üí‚Æ%.—Ëóù */ #include "linuxport.h" #ifdef Q_OS_LINUX #include #include #include #include #include #include #include #include #include #include #include #include QList LinuxPort::allPorts_; LinuxPort::StatsMonitor *LinuxPort::monitor_; const quint32 kMaxValue32 = 0xffffffff; const quint64 kMaxValue64 = 0xffffffffffffffffULL; #ifdef HAVE_IFLA_STATS64 #define X_IFLA_STATS IFLA_STATS64 typedef struct rtnl_link_stats64 x_rtnl_link_stats; #else #define X_IFLA_STATS IFLA_STATS typedef struct rtnl_link_stats x_rtnl_link_stats; #endif LinuxPort::LinuxPort(int id, const char *device) : PcapPort(id, device) { isPromisc_ = true; clearPromisc_ = false; // We don't need per port Rx/Tx monitors for Linux delete monitorRx_; delete monitorTx_; monitorRx_ = monitorTx_ = NULL; // We have one monitor for both Rx/Tx of all ports if (!monitor_) monitor_ = new StatsMonitor(); data_.set_is_exclusive_control(hasExclusiveControl()); minPacketSetSize_ = 16; qDebug("adding dev to all ports list <%s>", device); allPorts_.append(this); // A port can support either 32 or 64 bit stats - we will attempt // to guess this for each port and initialize this variable at // run time when the counter wraps around maxStatsValue_ = 0; } LinuxPort::~LinuxPort() { qDebug("In %s", __FUNCTION__); allPorts_.removeAll(this); if (monitor_->isRunning()) { monitor_->stop(); monitor_->wait(); } if (clearPromisc_) { int sd = socket(AF_INET, SOCK_DGRAM, 0); struct ifreq ifr; memset(&ifr, 0, sizeof(ifr)); strncpy(ifr.ifr_name, name(), sizeof(ifr.ifr_name)); if (ioctl(sd, SIOCGIFFLAGS, &ifr) != -1) { if (ifr.ifr_flags & IFF_PROMISC) { ifr.ifr_flags &= ~IFF_PROMISC; if (ioctl(sd, SIOCSIFFLAGS, &ifr) == -1) qDebug("Failed clearing promisc flag. SIOCSIFFLAGS failed: %s", strerror(errno)); } } else qDebug("Failed clearing promisc flag. SIOCGIFFLAGS failed: %s", strerror(errno)); close(sd); } } void LinuxPort::init() { if (!monitor_->isRunning()) monitor_->start(); monitor_->waitForSetupFinished(); if (!isPromisc_) addNote("Non Promiscuous Mode"); } OstProto::LinkState LinuxPort::linkState() { return linkState_; } bool LinuxPort::hasExclusiveControl() { // TODO return false; } bool LinuxPort::setExclusiveControl(bool /*exclusive*/) { // TODO return false; } LinuxPort::StatsMonitor::StatsMonitor() : QThread() { stop_ = false; setupDone_ = false; ioctlSocket_ = socket(AF_INET, SOCK_DGRAM, 0); Q_ASSERT(ioctlSocket_ >= 0); } LinuxPort::StatsMonitor::~StatsMonitor() { close(ioctlSocket_); } void LinuxPort::StatsMonitor::run() { if (netlinkStats() < 0) { qDebug("netlink stats not available - using /proc stats"); procStats(); } } void LinuxPort::StatsMonitor::procStats() { PortStats **portStats; int fd; QByteArray buf; int len; char *p, *end; int count, index; const char* fmtopt[] = { "%llu%llu%llu%llu%llu%llu%u%u%llu%llu%u%u%u%u%u%u\n", "%llu%llu%llu%llu%llu%llu%n%n%llu%llu%u%u%u%u%u%n\n", }; const char *fmt; // // We first setup stuff before we start polling for stats // fd = open("/proc/net/dev", O_RDONLY); if (fd < 0) { qWarning("Unable to open /proc/net/dev - no stats will be available"); return; } buf.fill('\0', 8192); len = read(fd, (void*) buf.data(), buf.size()); if (len < 0) { qWarning("initial buffer size is too small. no stats will be available"); return; } p = buf.data(); end = p + len; // Select scanf format if (strstr(buf, "compressed")) fmt = fmtopt[0]; else fmt = fmtopt[1]; // Count number of lines - number of ports is 2 less than number of lines count = 0; while (p < end) { if (*p == '\n') count++; p++; } count -= 2; if (count <= 0) { qWarning("no ports in /proc/dev/net - no stats will be available"); return; } portStats = (PortStats**) calloc(count, sizeof(PortStats)); Q_ASSERT(portStats != NULL); // // Populate the port stats array // p = buf.data(); // Skip first two lines while (*p != '\n') p++; p++; while (*p != '\n') p++; p++; index = 0; while (p < end) { char* q; // Skip whitespace while ((p < end) && (*p == ' ')) p++; q = p; // Get interface name while ((q < end) && (*q != ':') && (*q != '\n')) q++; if ((q < end) && (*q == ':')) { foreach(LinuxPort* port, allPorts_) { if (strncmp(port->name(), p, int(q-p)) == 0) { portStats[index] = &(port->stats_); if (setPromisc(port->name())) port->clearPromisc_ = true; else port->isPromisc_ = false; break; } } } index++; // Skip till newline p = q; while (*p != '\n') p++; p++; } Q_ASSERT(index == count); qDebug("stats for %d ports setup", count); setupDone_ = true; // // We are all set - Let's start polling for stats! // while (!stop_) { lseek(fd, 0, SEEK_SET); len = read(fd, (void*) buf.data(), buf.size()); if (len < 0) { if (buf.size() > 1*1024*1024) { qWarning("buffer size hit limit. no more stats"); return; } qDebug("doubling buffer size. curr = %d", buf.size()); buf.resize(buf.size() * 2); continue; } p = buf.data(); end = p + len; // Skip first two lines while (*p != '\n') p++; p++; while (*p != '\n') p++; p++; index = 0; while (p < end) { uint dummy; quint64 rxBytes, rxPkts; quint64 rxErrors, rxDrops, rxFifo, rxFrame; quint64 txBytes, txPkts; // Skip interface name - we assume the number and order of ports // won't change since we parsed the output before we started polling while ((p < end) && (*p != ':') && (*p != '\n')) p++; if (p >= end) break; if (*p == '\n') { index++; continue; } p++; sscanf(p, fmt, &rxBytes, &rxPkts, &rxErrors, &rxDrops, &rxFifo, &rxFrame, &dummy, &dummy, &txBytes, &txPkts, &dummy, &dummy, &dummy, &dummy, &dummy, &dummy); if (index < count) { AbstractPort::PortStats *stats = portStats[index]; if (stats) { // TODO: fix the pps/Bps calc similar to netlink stats stats->rxPps = ((rxPkts >= stats->rxPkts) ? rxPkts - stats->rxPkts : rxPkts + (kMaxValue32 - stats->rxPkts)) / kRefreshFreq_; stats->rxBps = ((rxBytes >= stats->rxBytes) ? rxBytes - stats->rxBytes : rxBytes + (kMaxValue32 - stats->rxBytes)) / kRefreshFreq_; stats->rxPkts = rxPkts; stats->rxBytes = rxBytes; stats->txPps = ((txPkts >= stats->txPkts) ? txPkts - stats->txPkts : txPkts + (kMaxValue32 - stats->txPkts)) / kRefreshFreq_; stats->txBps = ((txBytes >= stats->txBytes) ? txBytes - stats->txBytes : txBytes + (kMaxValue32 - stats->txBytes)) / kRefreshFreq_; stats->txPkts = txPkts; stats->txBytes = txBytes; stats->rxDrops = rxDrops; stats->rxErrors = rxErrors; stats->rxFifoErrors = rxFifo; stats->rxFrameErrors = rxFrame; } } while (*p != '\n') p++; p++; index++; } QThread::sleep(kRefreshFreq_); } free(portStats); } int LinuxPort::StatsMonitor::netlinkStats() { QHash portStats; QHash portMaxStatsValue; QHash linkState; int fd; struct sockaddr_nl local; struct sockaddr_nl kernel; QByteArray buf; int len, count; struct { struct nlmsghdr nlh; struct rtgenmsg rtg; } ifListReq; struct iovec iov; struct msghdr msg; struct nlmsghdr *nlm; bool done = false; // // We first setup stuff before we start polling for stats // fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); if (fd < 0) { qWarning("Unable to open netlink socket (errno %d)", errno); return -1; } memset(&local, 0, sizeof(local)); local.nl_family = AF_NETLINK; if (bind(fd, (struct sockaddr*) &local, sizeof(local)) < 0) { qWarning("Unable to bind netlink socket (errno %d)", errno); return -1; } memset(&ifListReq, 0, sizeof(ifListReq)); ifListReq.nlh.nlmsg_len = sizeof(ifListReq); ifListReq.nlh.nlmsg_type = RTM_GETLINK; ifListReq.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; ifListReq.nlh.nlmsg_pid = 0; ifListReq.rtg.rtgen_family = AF_PACKET; buf.fill('\0', 1024); msg.msg_name = &kernel; msg.msg_namelen = sizeof(kernel); msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_control = NULL; msg.msg_controllen = 0; msg.msg_flags = 0; qDebug("nlmsg_flags = %x", ifListReq.nlh.nlmsg_flags); if (send(fd, (void*)&ifListReq, sizeof(ifListReq), 0) < 0) { qWarning("Unable to send GETLINK request (errno %d)", errno); return -1; } count = 0; _retry: // Find required size of buffer and resize accordingly while (1) { iov.iov_base = buf.data(); iov.iov_len = buf.size(); msg.msg_flags = 0; // Peek at reply to check buffer size required len = recvmsg(fd, &msg, MSG_PEEK|MSG_TRUNC); if (len < 0) { if (errno == EINTR || errno == EAGAIN) continue; qWarning("netlink recv error %d", errno); return -1; } else if (len == 0) { qWarning("netlink closed the socket on my face!"); return -1; } else { if (msg.msg_flags & MSG_TRUNC) { if (len == buf.size()) // Older Kernel returns truncated size { qDebug("netlink buffer size %d not enough", buf.size()); qDebug("retrying with double the size"); // Double the size and retry buf.resize(buf.size()*2); continue; } else // Newer Kernel returns actual size required { qDebug("netlink required buffer size = %d", len); buf.resize(len); continue; } } else qDebug("buffer size %d enough for netlink", buf.size()); break; } } msg.msg_flags = 0; // Actually receive the reply now len = recvmsg(fd, &msg, 0); if (len < 0) { if (errno == EINTR || errno == EAGAIN) goto _retry; qWarning("netlink recv error %d", errno); return -1; } else if (len == 0) { qWarning("netlink socket closed unexpectedly"); return -1; } // // Populate the port stats hash table // nlm = (struct nlmsghdr*) buf.data(); while (NLMSG_OK(nlm, (uint)len)) { struct ifinfomsg *ifi; struct rtattr *rta; int rtaLen; char ifname[64] = ""; if (nlm->nlmsg_type == NLMSG_DONE) { done = true; break; } if (nlm->nlmsg_type == NLMSG_ERROR) { struct nlmsgerr *err = (struct nlmsgerr*) NLMSG_DATA(nlm); qDebug("RTNETLINK error %d", err->error); done = true; break; } Q_ASSERT(nlm->nlmsg_type == RTM_NEWLINK); ifi = (struct ifinfomsg*) NLMSG_DATA(nlm); rta = IFLA_RTA(ifi); rtaLen = len - NLMSG_LENGTH(sizeof(*ifi)); while (RTA_OK(rta, rtaLen)) { if (rta->rta_type == IFLA_IFNAME) { strncpy(ifname, (char*)RTA_DATA(rta), RTA_PAYLOAD(rta)); ifname[RTA_PAYLOAD(rta)] = 0; break; } rta = RTA_NEXT(rta, rtaLen); } qDebug("if: %s(%d)", ifname, ifi->ifi_index); foreach(LinuxPort* port, allPorts_) { if (strcmp(port->name(), ifname) == 0) { portStats[uint(ifi->ifi_index)] = &(port->stats_); portMaxStatsValue[uint(ifi->ifi_index)] = &(port->maxStatsValue_); linkState[uint(ifi->ifi_index)] = &(port->linkState_); if (setPromisc(port->name())) port->clearPromisc_ = true; else port->isPromisc_ = false; count++; break; } } nlm = NLMSG_NEXT(nlm, len); } if (!done) goto _retry; qDebug("port count = %d\n", count); if (count <= 0) { qWarning("no ports in RTNETLINK GET_LINK - no stats will be available"); return - 1; } qDebug("stats for %d ports setup", count); setupDone_ = true; // // We are all set - Let's start polling for stats! // while (!stop_) { if (send(fd, (void*)&ifListReq, sizeof(ifListReq), 0) < 0) { qWarning("Unable to send GETLINK request (errno %d)", errno); goto _try_later; } done = false; _retry_recv: msg.msg_flags = 0; len = recvmsg(fd, &msg, 0); if (len < 0) { if (errno == EINTR || errno == EAGAIN) goto _retry_recv; qWarning("netlink recv error %d", errno); break; } else if (len == 0) { qWarning("netlink socket closed unexpectedly"); break; } nlm = (struct nlmsghdr*) buf.data(); while (NLMSG_OK(nlm, (uint)len)) { struct ifinfomsg *ifi; struct rtattr *rta; int rtaLen; if (nlm->nlmsg_type == NLMSG_DONE) { done = true; break; } if (nlm->nlmsg_type == NLMSG_ERROR) { struct nlmsgerr *err = (struct nlmsgerr*) NLMSG_DATA(nlm); qDebug("RTNETLINK error: %s", strerror(-err->error)); done = true; break; } Q_ASSERT(nlm->nlmsg_type == RTM_NEWLINK); ifi = (struct ifinfomsg*) NLMSG_DATA(nlm); rta = IFLA_RTA(ifi); rtaLen = len - NLMSG_LENGTH(sizeof(*ifi)); while (RTA_OK(rta, rtaLen)) { if (rta->rta_type == X_IFLA_STATS) { x_rtnl_link_stats *rtnlStats = (x_rtnl_link_stats*) RTA_DATA(rta); AbstractPort::PortStats *stats = portStats[ifi->ifi_index]; quint64 *maxStatsValue = portMaxStatsValue[ifi->ifi_index]; OstProto::LinkState *state = linkState[ifi->ifi_index]; if (!stats) break; if (rtnlStats->rx_packets >= stats->rxPkts) { stats->rxPps = (rtnlStats->rx_packets - stats->rxPkts) / kRefreshFreq_; } else { if (*maxStatsValue == 0) { *maxStatsValue = stats->rxPkts > kMaxValue32 ? kMaxValue64 : kMaxValue32; } stats->rxPps = ((*maxStatsValue - stats->rxPkts) + rtnlStats->rx_packets) / kRefreshFreq_; } if (rtnlStats->rx_bytes >= stats->rxBytes) { stats->rxBps = (rtnlStats->rx_bytes - stats->rxBytes) / kRefreshFreq_; } else { if (*maxStatsValue == 0) { *maxStatsValue = stats->rxBytes > kMaxValue32 ? kMaxValue64 : kMaxValue32; } stats->rxBps = ((*maxStatsValue - stats->rxBytes) + rtnlStats->rx_bytes) / kRefreshFreq_; } stats->rxPkts = rtnlStats->rx_packets; stats->rxBytes = rtnlStats->rx_bytes; if (rtnlStats->tx_packets >= stats->txPkts) { stats->txPps = (rtnlStats->tx_packets - stats->txPkts) / kRefreshFreq_; } else { if (*maxStatsValue == 0) { *maxStatsValue = stats->txPkts > kMaxValue32 ? kMaxValue64 : kMaxValue32; } stats->txPps = ((*maxStatsValue - stats->txPkts) + rtnlStats->tx_packets) / kRefreshFreq_; } if (rtnlStats->tx_bytes >= stats->txBytes) { stats->txBps = (rtnlStats->tx_bytes - stats->txBytes) / kRefreshFreq_; } else { if (*maxStatsValue == 0) { *maxStatsValue = stats->txBytes > kMaxValue32 ? kMaxValue64 : kMaxValue32; } stats->txBps = ((*maxStatsValue - stats->txBytes) + rtnlStats->tx_bytes) / kRefreshFreq_; } stats->txPkts = rtnlStats->tx_packets; stats->txBytes = rtnlStats->tx_bytes; // TODO: export detailed error stats stats->rxDrops = rtnlStats->rx_dropped + rtnlStats->rx_missed_errors; stats->rxErrors = rtnlStats->rx_errors; stats->rxFifoErrors = rtnlStats->rx_fifo_errors; stats->rxFrameErrors = rtnlStats->rx_crc_errors + rtnlStats->rx_length_errors + rtnlStats->rx_over_errors + rtnlStats->rx_frame_errors; Q_ASSERT(state); *state = ifi->ifi_flags & IFF_RUNNING ? OstProto::LinkStateUp : OstProto::LinkStateDown; break; } rta = RTA_NEXT(rta, rtaLen); } nlm = NLMSG_NEXT(nlm, len); } if (!done) goto _retry_recv; _try_later: QThread::sleep(kRefreshFreq_); } portStats.clear(); linkState.clear(); return 0; } int LinuxPort::StatsMonitor::setPromisc(const char * portName) { struct ifreq ifr; memset(&ifr, 0, sizeof(ifr)); strncpy(ifr.ifr_name, portName, sizeof(ifr.ifr_name)); if (ioctl(ioctlSocket_, SIOCGIFFLAGS, &ifr) != -1) { if ((ifr.ifr_flags & IFF_PROMISC) == 0) { ifr.ifr_flags |= IFF_PROMISC; if (ioctl(ioctlSocket_, SIOCSIFFLAGS, &ifr) != -1) { return 1; } else { qDebug("%s: failed to set promisc; " "SIOCSIFFLAGS failed (%s)", portName, strerror(errno)); } } } else { qDebug("%s: failed to set promisc; SIOCGIFFLAGS failed (%s)", portName, strerror(errno)); } return 0; } void LinuxPort::StatsMonitor::stop() { stop_ = true; } bool LinuxPort::StatsMonitor::waitForSetupFinished(int msecs) { QTime t; t.start(); while (!setupDone_) { if (t.elapsed() > msecs) return false; QThread::msleep(10); } return true; } #endif ostinato-0.9/server/linuxport.h000066400000000000000000000032321321315675200167520ustar00rootroot00000000000000/* Copyright (C) 2011 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _SERVER_LINUX_PORT_H #define _SERVER_LINUX_PORT_H #include #ifdef Q_OS_LINUX #include "pcapport.h" class LinuxPort : public PcapPort { public: LinuxPort(int id, const char *device); ~LinuxPort(); void init(); virtual OstProto::LinkState linkState(); virtual bool hasExclusiveControl(); virtual bool setExclusiveControl(bool exclusive); protected: class StatsMonitor: public QThread { public: StatsMonitor(); ~StatsMonitor(); void run(); void stop(); bool waitForSetupFinished(int msecs = 10000); private: int netlinkStats(); void procStats(); int setPromisc(const char* portName); static const int kRefreshFreq_ = 1; // in seconds bool stop_; bool setupDone_; int ioctlSocket_; }; bool isPromisc_; bool clearPromisc_; static QList allPorts_; static StatsMonitor *monitor_; // rx/tx stats for ALL ports }; #endif #endif ostinato-0.9/server/myservice.cpp000066400000000000000000000707121321315675200172560ustar00rootroot00000000000000/* Copyright (C) 2010-2016 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "myservice.h" #include "drone.h" #if 0 #include #include #include "qdebug.h" #include "../common/protocollistiterator.h" #include "../common/abstractprotocol.h" #endif #include "../common/streambase.h" #include "../rpc/pbrpccontroller.h" #include "device.h" #include "devicemanager.h" #include "portmanager.h" #include extern Drone *drone; extern char *version; MyService::MyService() { PortManager *portManager = PortManager::instance(); int n = portManager->portCount(); for (int i = 0; i < n; i++) { portInfo.append(portManager->port(i)); #if QT_VERSION >= 0x040400 portLock.append(new QReadWriteLock(QReadWriteLock::Recursive)); #else portLock.append(new QReadWriteLock()); #endif } } MyService::~MyService() { while (!portLock.isEmpty()) delete portLock.takeFirst(); //! \todo Use a singleton destroyer instead // http://www.research.ibm.com/designpatterns/pubs/ph-jun96.txt delete PortManager::instance(); } void MyService::getPortIdList(::google::protobuf::RpcController* /*controller*/, const ::OstProto::Void* /*request*/, ::OstProto::PortIdList* response, ::google::protobuf::Closure* done) { qDebug("In %s", __PRETTY_FUNCTION__); // No locks are needed here because the list does not change // and neither does the port_id for (int i = 0; i < portInfo.size(); i++) { ::OstProto::PortId *p; p = response->add_port_id(); p->set_id(portInfo[i]->id()); } done->Run(); } void MyService::getPortConfig(::google::protobuf::RpcController* /*controller*/, const ::OstProto::PortIdList* request, ::OstProto::PortConfigList* response, ::google::protobuf::Closure* done) { qDebug("In %s", __PRETTY_FUNCTION__); for (int i = 0; i < request->port_id_size(); i++) { int id; id = request->port_id(i).id(); if (id < portInfo.size()) { OstProto::Port *p; p = response->add_port(); portLock[id]->lockForRead(); portInfo[id]->protoDataCopyInto(p); portLock[id]->unlock(); } } done->Run(); } void MyService::modifyPort(::google::protobuf::RpcController* /*controller*/, const ::OstProto::PortConfigList* request, ::OstProto::Ack* /*response*/, ::google::protobuf::Closure* done) { // notification needs to be on heap because signal/slot is across threads! OstProto::Notification *notif = new OstProto::Notification; qDebug("In %s", __PRETTY_FUNCTION__); for (int i = 0; i < request->port_size(); i++) { OstProto::Port port; int id; port = request->port(i); id = port.port_id().id(); if (id < portInfo.size()) { bool dirty; if (!portInfo[id]->canModify(port, &dirty)) { qDebug("Port %d cannot be modified - stop tx and retry", id); continue; //! \todo(LOW): Partial status of RPC } portLock[id]->lockForWrite(); portInfo[id]->modify(port); if (dirty) portInfo[id]->updatePacketList(); portLock[id]->unlock(); notif->mutable_port_id_list()->add_port_id()->set_id(id); } } //! \todo (LOW): fill-in response "Ack"???? done->Run(); if (notif->port_id_list().port_id_size()) { notif->set_notif_type(OstProto::portConfigChanged); emit notification(notif->notif_type(), SharedProtobufMessage(notif)); } else delete notif; } void MyService::getStreamIdList(::google::protobuf::RpcController* controller, const ::OstProto::PortId* request, ::OstProto::StreamIdList* response, ::google::protobuf::Closure* done) { int portId; qDebug("In %s", __PRETTY_FUNCTION__); portId = request->id(); if ((portId < 0) || (portId >= portInfo.size())) goto _invalid_port; response->mutable_port_id()->set_id(portId); portLock[portId]->lockForRead(); for (int i = 0; i < portInfo[portId]->streamCount(); i++) { OstProto::StreamId *s; s = response->add_stream_id(); s->set_id(portInfo[portId]->streamAtIndex(i)->id()); } portLock[portId]->unlock(); done->Run(); return; _invalid_port: controller->SetFailed("Invalid Port Id"); done->Run(); } void MyService::getStreamConfig(::google::protobuf::RpcController* controller, const ::OstProto::StreamIdList* request, ::OstProto::StreamConfigList* response, ::google::protobuf::Closure* done) { int portId; qDebug("In %s", __PRETTY_FUNCTION__); portId = request->port_id().id(); if ((portId < 0) || (portId >= portInfo.size())) goto _invalid_port; response->mutable_port_id()->set_id(portId); portLock[portId]->lockForRead(); for (int i = 0; i < request->stream_id_size(); i++) { StreamBase *stream; OstProto::Stream *s; stream = portInfo[portId]->stream(request->stream_id(i).id()); if (!stream) continue; //! \todo(LOW): Partial status of RPC s = response->add_stream(); stream->protoDataCopyInto(*s); } portLock[portId]->unlock(); done->Run(); return; _invalid_port: controller->SetFailed("invalid portid"); done->Run(); } void MyService::addStream(::google::protobuf::RpcController* controller, const ::OstProto::StreamIdList* request, ::OstProto::Ack* /*response*/, ::google::protobuf::Closure* done) { int portId; qDebug("In %s", __PRETTY_FUNCTION__); portId = request->port_id().id(); if ((portId < 0) || (portId >= portInfo.size())) goto _invalid_port; if (portInfo[portId]->isTransmitOn()) goto _port_busy; portLock[portId]->lockForWrite(); for (int i = 0; i < request->stream_id_size(); i++) { StreamBase *stream; // If stream with same id as in request exists already ==> error!! stream = portInfo[portId]->stream(request->stream_id(i).id()); if (stream) continue; //! \todo (LOW): Partial status of RPC // Append a new "default" stream - actual contents of the new stream is // expected in a subsequent "modifyStream" request - set the stream id // now itself however!!! stream = new StreamBase(portId); stream->setId(request->stream_id(i).id()); portInfo[portId]->addStream(stream); } portLock[portId]->unlock(); //! \todo (LOW): fill-in response "Ack"???? done->Run(); return; _port_busy: controller->SetFailed("Port Busy"); goto _exit; _invalid_port: controller->SetFailed("invalid portid"); _exit: done->Run(); } void MyService::deleteStream(::google::protobuf::RpcController* controller, const ::OstProto::StreamIdList* request, ::OstProto::Ack* /*response*/, ::google::protobuf::Closure* done) { int portId; qDebug("In %s", __PRETTY_FUNCTION__); portId = request->port_id().id(); if ((portId < 0) || (portId >= portInfo.size())) goto _invalid_port; if (portInfo[portId]->isTransmitOn()) goto _port_busy; portLock[portId]->lockForWrite(); for (int i = 0; i < request->stream_id_size(); i++) portInfo[portId]->deleteStream(request->stream_id(i).id()); portLock[portId]->unlock(); //! \todo (LOW): fill-in response "Ack"???? done->Run(); return; _port_busy: controller->SetFailed("Port Busy"); goto _exit; _invalid_port: controller->SetFailed("invalid portid"); _exit: done->Run(); } void MyService::modifyStream(::google::protobuf::RpcController* controller, const ::OstProto::StreamConfigList* request, ::OstProto::Ack* /*response*/, ::google::protobuf::Closure* done) { int portId; qDebug("In %s", __PRETTY_FUNCTION__); portId = request->port_id().id(); if ((portId < 0) || (portId >= portInfo.size())) goto _invalid_port; if (portInfo[portId]->isTransmitOn()) goto _port_busy; portLock[portId]->lockForWrite(); for (int i = 0; i < request->stream_size(); i++) { StreamBase *stream; stream = portInfo[portId]->stream(request->stream(i).stream_id().id()); if (stream) { stream->protoDataCopyFrom(request->stream(i)); portInfo[portId]->setDirty(); } } if (portInfo[portId]->isDirty()) portInfo[portId]->updatePacketList(); portLock[portId]->unlock(); //! \todo(LOW): fill-in response "Ack"???? done->Run(); return; _port_busy: controller->SetFailed("Port Busy"); goto _exit; _invalid_port: controller->SetFailed("invalid portid"); _exit: done->Run(); } void MyService::startTransmit(::google::protobuf::RpcController* /*controller*/, const ::OstProto::PortIdList* request, ::OstProto::Ack* /*response*/, ::google::protobuf::Closure* done) { qDebug("In %s", __PRETTY_FUNCTION__); for (int i = 0; i < request->port_id_size(); i++) { int portId; portId = request->port_id(i).id(); if ((portId < 0) || (portId >= portInfo.size())) continue; //! \todo (LOW): partial RPC? portLock[portId]->lockForWrite(); if (portInfo[portId]->isDirty()) portInfo[portId]->updatePacketList(); portInfo[portId]->startTransmit(); portLock[portId]->unlock(); } //! \todo (LOW): fill-in response "Ack"???? done->Run(); } void MyService::stopTransmit(::google::protobuf::RpcController* /*controller*/, const ::OstProto::PortIdList* request, ::OstProto::Ack* /*response*/, ::google::protobuf::Closure* done) { qDebug("In %s", __PRETTY_FUNCTION__); for (int i = 0; i < request->port_id_size(); i++) { int portId; portId = request->port_id(i).id(); if ((portId < 0) || (portId >= portInfo.size())) continue; //! \todo (LOW): partial RPC? portLock[portId]->lockForWrite(); portInfo[portId]->stopTransmit(); portLock[portId]->unlock(); } //! \todo (LOW): fill-in response "Ack"???? done->Run(); } void MyService::startCapture(::google::protobuf::RpcController* /*controller*/, const ::OstProto::PortIdList* request, ::OstProto::Ack* /*response*/, ::google::protobuf::Closure* done) { qDebug("In %s", __PRETTY_FUNCTION__); for (int i = 0; i < request->port_id_size(); i++) { int portId; portId = request->port_id(i).id(); if ((portId < 0) || (portId >= portInfo.size())) continue; //! \todo (LOW): partial RPC? portLock[portId]->lockForWrite(); portInfo[portId]->startCapture(); portLock[portId]->unlock(); } //! \todo (LOW): fill-in response "Ack"???? done->Run(); } void MyService::stopCapture(::google::protobuf::RpcController* /*controller*/, const ::OstProto::PortIdList* request, ::OstProto::Ack* /*response*/, ::google::protobuf::Closure* done) { qDebug("In %s", __PRETTY_FUNCTION__); for (int i=0; i < request->port_id_size(); i++) { int portId; portId = request->port_id(i).id(); if ((portId < 0) || (portId >= portInfo.size())) continue; //! \todo (LOW): partial RPC? portLock[portId]->lockForWrite(); portInfo[portId]->stopCapture(); portLock[portId]->unlock(); } //! \todo (LOW): fill-in response "Ack"???? done->Run(); } void MyService::getCaptureBuffer(::google::protobuf::RpcController* controller, const ::OstProto::PortId* request, ::OstProto::CaptureBuffer* /*response*/, ::google::protobuf::Closure* done) { int portId; qDebug("In %s", __PRETTY_FUNCTION__); portId = request->id(); if ((portId < 0) || (portId >= portInfo.size())) goto _invalid_port; portLock[portId]->lockForWrite(); portInfo[portId]->stopCapture(); static_cast(controller)->setBinaryBlob( portInfo[portId]->captureData()); portLock[portId]->unlock(); done->Run(); return; _invalid_port: controller->SetFailed("invalid portid"); done->Run(); } void MyService::getStats(::google::protobuf::RpcController* /*controller*/, const ::OstProto::PortIdList* request, ::OstProto::PortStatsList* response, ::google::protobuf::Closure* done) { //qDebug("In %s", __PRETTY_FUNCTION__); for (int i = 0; i < request->port_id_size(); i++) { int portId; AbstractPort::PortStats stats; OstProto::PortStats *s; OstProto::PortState *st; portId = request->port_id(i).id(); if ((portId < 0) || (portId >= portInfo.size())) continue; //! \todo(LOW): partial rpc? s = response->add_port_stats(); s->mutable_port_id()->set_id(request->port_id(i).id()); st = s->mutable_state(); portLock[portId]->lockForRead(); st->set_link_state(portInfo[portId]->linkState()); st->set_is_transmit_on(portInfo[portId]->isTransmitOn()); st->set_is_capture_on(portInfo[portId]->isCaptureOn()); portInfo[portId]->stats(&stats); portLock[portId]->unlock(); #if 0 if (portId == 2) qDebug(">%llu", stats.rxPkts); #endif s->set_rx_pkts(stats.rxPkts); s->set_rx_bytes(stats.rxBytes); s->set_rx_pps(stats.rxPps); s->set_rx_bps(stats.rxBps); s->set_tx_pkts(stats.txPkts); s->set_tx_bytes(stats.txBytes); s->set_tx_pps(stats.txPps); s->set_tx_bps(stats.txBps); s->set_rx_drops(stats.rxDrops); s->set_rx_errors(stats.rxErrors); s->set_rx_fifo_errors(stats.rxFifoErrors); s->set_rx_frame_errors(stats.rxFrameErrors); } done->Run(); } void MyService::clearStats(::google::protobuf::RpcController* /*controller*/, const ::OstProto::PortIdList* request, ::OstProto::Ack* /*response*/, ::google::protobuf::Closure* done) { qDebug("In %s", __PRETTY_FUNCTION__); for (int i = 0; i < request->port_id_size(); i++) { int portId; portId = request->port_id(i).id(); if ((portId < 0) || (portId >= portInfo.size())) continue; //! \todo (LOW): partial RPC? portLock[portId]->lockForWrite(); portInfo[portId]->resetStats(); portLock[portId]->unlock(); } //! \todo (LOW): fill-in response "Ack"???? done->Run(); } void MyService::getStreamStats( ::google::protobuf::RpcController* /*controller*/, const ::OstProto::StreamGuidList* request, ::OstProto::StreamStatsList* response, ::google::protobuf::Closure* done) { qDebug("In %s", __PRETTY_FUNCTION__); for (int i = 0; i < request->port_id_list().port_id_size(); i++) { int portId; portId = request->port_id_list().port_id(i).id(); if ((portId < 0) || (portId >= portInfo.size())) continue; //! \todo(LOW): partial rpc? portLock[portId]->lockForRead(); if (request->stream_guid_size()) for (int j = 0; j < request->stream_guid_size(); j++) portInfo[portId]->streamStats(request->stream_guid(j).id(), response); else portInfo[portId]->streamStatsAll(response); portLock[portId]->unlock(); } done->Run(); } void MyService::clearStreamStats( ::google::protobuf::RpcController* /*controller*/, const ::OstProto::StreamGuidList* request, ::OstProto::Ack* /*response*/, ::google::protobuf::Closure* done) { qDebug("In %s", __PRETTY_FUNCTION__); for (int i = 0; i < request->port_id_list().port_id_size(); i++) { int portId; portId = request->port_id_list().port_id(i).id(); if ((portId < 0) || (portId >= portInfo.size())) continue; //! \todo (LOW): partial RPC? portLock[portId]->lockForWrite(); if (request->stream_guid_size()) for (int j = 0; j < request->stream_guid_size(); j++) portInfo[portId]->resetStreamStats( request->stream_guid(j).id()); else portInfo[portId]->resetStreamStatsAll(); portLock[portId]->unlock(); } //! \todo (LOW): fill-in response "Ack"???? done->Run(); } void MyService::checkVersion(::google::protobuf::RpcController* controller, const ::OstProto::VersionInfo* request, ::OstProto::VersionCompatibility* response, ::google::protobuf::Closure* done) { QString myVersion(version); QString clientVersion; QStringList my, client; qDebug("In %s", __PRETTY_FUNCTION__); my = myVersion.split('.'); Q_ASSERT(my.size() >= 2); clientVersion = QString::fromStdString(request->version()); client = clientVersion.split('.'); qDebug("client = %s, my = %s", qPrintable(clientVersion), qPrintable(myVersion)); if (client.size() < 2) goto _invalid_version; // Compare only major and minor numbers if (client[0] == my[0] && client[1] == my[1]) { response->set_result(OstProto::VersionCompatibility::kCompatible); static_cast(controller)->EnableNotif( request->client_name() == "python-ostinato" ? false : true); } else { response->set_result(OstProto::VersionCompatibility::kIncompatible); response->set_notes(QString("Drone needs controller version %1.%2.x") .arg(my[0], my[1]).toStdString()); static_cast(controller)->TriggerDisconnect(); } done->Run(); return; _invalid_version: controller->SetFailed("invalid version information"); done->Run(); } /* * =================================================================== * Device Emulation * =================================================================== * XXX: Streams and Devices are largely non-overlapping from a RPC * point of view but they *do* intersect e.g. when a stream is trying to * find its associated device and info from that device such as src/dst * mac addresses. For this reason, both set of RPCs share the common * port level locking * =================================================================== */ void MyService::getDeviceGroupIdList( ::google::protobuf::RpcController* controller, const ::OstProto::PortId* request, ::OstProto::DeviceGroupIdList* response, ::google::protobuf::Closure* done) { DeviceManager *devMgr; int portId; qDebug("In %s", __PRETTY_FUNCTION__); portId = request->id(); if ((portId < 0) || (portId >= portInfo.size())) goto _invalid_port; devMgr = portInfo[portId]->deviceManager(); response->mutable_port_id()->set_id(portId); portLock[portId]->lockForRead(); for (int i = 0; i < devMgr->deviceGroupCount(); i++) { OstProto::DeviceGroupId *dgid; dgid = response->add_device_group_id(); dgid->CopyFrom(devMgr->deviceGroupAtIndex(i)->device_group_id()); } portLock[portId]->unlock(); done->Run(); return; _invalid_port: controller->SetFailed("Invalid Port Id"); done->Run(); } void MyService::getDeviceGroupConfig( ::google::protobuf::RpcController* controller, const ::OstProto::DeviceGroupIdList* request, ::OstProto::DeviceGroupConfigList* response, ::google::protobuf::Closure* done) { DeviceManager *devMgr; int portId; qDebug("In %s", __PRETTY_FUNCTION__); portId = request->port_id().id(); if ((portId < 0) || (portId >= portInfo.size())) goto _invalid_port; devMgr = portInfo[portId]->deviceManager(); response->mutable_port_id()->set_id(portId); portLock[portId]->lockForRead(); for (int i = 0; i < request->device_group_id_size(); i++) { const OstProto::DeviceGroup *dg; dg = devMgr->deviceGroup(request->device_group_id(i).id()); if (!dg) continue; //! \todo(LOW): Partial status of RPC response->add_device_group()->CopyFrom(*dg); } portLock[portId]->unlock(); done->Run(); return; _invalid_port: controller->SetFailed("invalid portid"); done->Run(); } void MyService::addDeviceGroup( ::google::protobuf::RpcController* controller, const ::OstProto::DeviceGroupIdList* request, ::OstProto::Ack* /*response*/, ::google::protobuf::Closure* done) { DeviceManager *devMgr; int portId; qDebug("In %s", __PRETTY_FUNCTION__); portId = request->port_id().id(); if ((portId < 0) || (portId >= portInfo.size())) goto _invalid_port; devMgr = portInfo[portId]->deviceManager(); if (portInfo[portId]->isTransmitOn()) goto _port_busy; portLock[portId]->lockForWrite(); for (int i = 0; i < request->device_group_id_size(); i++) { quint32 id = request->device_group_id(i).id(); const OstProto::DeviceGroup *dg = devMgr->deviceGroup(id); // If device group with same id as in request exists already ==> error! if (dg) continue; //! \todo (LOW): Partial status of RPC devMgr->addDeviceGroup(id); } portLock[portId]->unlock(); //! \todo (LOW): fill-in response "Ack"???? done->Run(); return; _port_busy: controller->SetFailed("Port Busy"); goto _exit; _invalid_port: controller->SetFailed("invalid portid"); _exit: done->Run(); } void MyService::deleteDeviceGroup( ::google::protobuf::RpcController* controller, const ::OstProto::DeviceGroupIdList* request, ::OstProto::Ack* /*response*/, ::google::protobuf::Closure* done) { DeviceManager *devMgr; int portId; qDebug("In %s", __PRETTY_FUNCTION__); portId = request->port_id().id(); if ((portId < 0) || (portId >= portInfo.size())) goto _invalid_port; devMgr = portInfo[portId]->deviceManager(); if (portInfo[portId]->isTransmitOn()) goto _port_busy; portLock[portId]->lockForWrite(); for (int i = 0; i < request->device_group_id_size(); i++) devMgr->deleteDeviceGroup(request->device_group_id(i).id()); portLock[portId]->unlock(); //! \todo (LOW): fill-in response "Ack"???? done->Run(); return; _port_busy: controller->SetFailed("Port Busy"); goto _exit; _invalid_port: controller->SetFailed("invalid portid"); _exit: done->Run(); } void MyService::modifyDeviceGroup( ::google::protobuf::RpcController* controller, const ::OstProto::DeviceGroupConfigList* request, ::OstProto::Ack* /*response*/, ::google::protobuf::Closure* done) { DeviceManager *devMgr; int portId; qDebug("In %s", __PRETTY_FUNCTION__); portId = request->port_id().id(); if ((portId < 0) || (portId >= portInfo.size())) goto _invalid_port; devMgr = portInfo[portId]->deviceManager(); if (portInfo[portId]->isTransmitOn()) goto _port_busy; portLock[portId]->lockForWrite(); for (int i = 0; i < request->device_group_size(); i++) devMgr->modifyDeviceGroup(&request->device_group(i)); portLock[portId]->unlock(); //! \todo(LOW): fill-in response "Ack"???? done->Run(); return; _port_busy: controller->SetFailed("Port Busy"); goto _exit; _invalid_port: controller->SetFailed("invalid portid"); _exit: done->Run(); } void MyService::getDeviceList( ::google::protobuf::RpcController* controller, const ::OstProto::PortId* request, ::OstProto::PortDeviceList* response, ::google::protobuf::Closure* done) { DeviceManager *devMgr; int portId; qDebug("In %s", __PRETTY_FUNCTION__); portId = request->id(); if ((portId < 0) || (portId >= portInfo.size())) goto _invalid_port; devMgr = portInfo[portId]->deviceManager(); response->mutable_port_id()->set_id(portId); portLock[portId]->lockForRead(); devMgr->getDeviceList(response); portLock[portId]->unlock(); done->Run(); return; _invalid_port: controller->SetFailed("Invalid Port Id"); done->Run(); } void MyService::resolveDeviceNeighbors( ::google::protobuf::RpcController* /*controller*/, const ::OstProto::PortIdList* request, ::OstProto::Ack* /*response*/, ::google::protobuf::Closure* done) { qDebug("In %s", __PRETTY_FUNCTION__); for (int i = 0; i < request->port_id_size(); i++) { int portId; portId = request->port_id(i).id(); if ((portId < 0) || (portId >= portInfo.size())) continue; //! \todo (LOW): partial RPC? portLock[portId]->lockForWrite(); portInfo[portId]->resolveDeviceNeighbors(); portLock[portId]->unlock(); } //! \todo (LOW): fill-in response "Ack"???? done->Run(); } void MyService::clearDeviceNeighbors( ::google::protobuf::RpcController* /*controller*/, const ::OstProto::PortIdList* request, ::OstProto::Ack* /*response*/, ::google::protobuf::Closure* done) { qDebug("In %s", __PRETTY_FUNCTION__); for (int i = 0; i < request->port_id_size(); i++) { int portId; portId = request->port_id(i).id(); if ((portId < 0) || (portId >= portInfo.size())) continue; //! \todo (LOW): partial RPC? portLock[portId]->lockForWrite(); portInfo[portId]->clearDeviceNeighbors(); portLock[portId]->unlock(); } //! \todo (LOW): fill-in response "Ack"???? done->Run(); } void MyService::getDeviceNeighbors( ::google::protobuf::RpcController* controller, const ::OstProto::PortId* request, ::OstProto::PortNeighborList* response, ::google::protobuf::Closure* done) { DeviceManager *devMgr; int portId; qDebug("In %s", __PRETTY_FUNCTION__); portId = request->id(); if ((portId < 0) || (portId >= portInfo.size())) goto _invalid_port; devMgr = portInfo[portId]->deviceManager(); response->mutable_port_id()->set_id(portId); portLock[portId]->lockForRead(); devMgr->getDeviceNeighbors(response); portLock[portId]->unlock(); done->Run(); return; _invalid_port: controller->SetFailed("Invalid Port Id"); done->Run(); } /* * =================================================================== * Friends * TODO: Encap these global functions into a DeviceBroker singleton? * =================================================================== */ quint64 getDeviceMacAddress(int portId, int streamId, int frameIndex) { MyService *service = drone->rpcService(); DeviceManager *devMgr = NULL; quint64 mac; if (!service) return 0; if ((portId >= 0) && (portId < service->portInfo.size())) devMgr = service->portInfo[portId]->deviceManager(); if (!devMgr || !devMgr->deviceCount()) return 0; /* * FIXME: We don't need lockForWrite, only lockForRead here. * However, this function is called in the following sequence * modifyPort() --> updatePacketList() --> frameValue() * where modifyPort has already taken a write lock. Qt allows * recursive locks, but not of a different type, so we are * forced to use lockForWrite here - till we find a different * solution. */ service->portLock[portId]->lockForWrite(); mac = service->portInfo[portId]->deviceMacAddress(streamId, frameIndex); service->portLock[portId]->unlock(); return mac; } quint64 getNeighborMacAddress(int portId, int streamId, int frameIndex) { MyService *service = drone->rpcService(); DeviceManager *devMgr = NULL; quint64 mac; if (!service) return 0; if ((portId >= 0) && (portId < service->portInfo.size())) devMgr = service->portInfo[portId]->deviceManager(); if (!devMgr || !devMgr->deviceCount()) return 0; /* * FIXME: We don't need lockForWrite, only lockForRead here. * See comment in getDeviceMacAddress() for more */ service->portLock[portId]->lockForWrite(); mac = service->portInfo[portId]->neighborMacAddress(streamId, frameIndex); service->portLock[portId]->unlock(); return mac; } ostinato-0.9/server/myservice.h000066400000000000000000000172431321315675200167230ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _MY_SERVICE_H #define _MY_SERVICE_H #include "../common/protocol.pb.h" #include "../rpc/sharedprotobufmessage.h" #include #include #include #define MAX_PKT_HDR_SIZE 1536 #define MAX_STREAM_NAME_SIZE 64 class AbstractPort; class MyService: public QObject, public OstProto::OstService { Q_OBJECT public: MyService(); virtual ~MyService(); /* Methods provided by the service */ virtual void getPortIdList(::google::protobuf::RpcController* controller, const ::OstProto::Void* request, ::OstProto::PortIdList* response, ::google::protobuf::Closure* done); virtual void getPortConfig(::google::protobuf::RpcController* controller, const ::OstProto::PortIdList* request, ::OstProto::PortConfigList* response, ::google::protobuf::Closure* done); virtual void modifyPort(::google::protobuf::RpcController* /*controller*/, const ::OstProto::PortConfigList* request, ::OstProto::Ack* response, ::google::protobuf::Closure* done); virtual void getStreamIdList(::google::protobuf::RpcController* controller, const ::OstProto::PortId* request, ::OstProto::StreamIdList* response, ::google::protobuf::Closure* done); virtual void getStreamConfig(::google::protobuf::RpcController* controller, const ::OstProto::StreamIdList* request, ::OstProto::StreamConfigList* response, ::google::protobuf::Closure* done); virtual void addStream(::google::protobuf::RpcController* controller, const ::OstProto::StreamIdList* request, ::OstProto::Ack* response, ::google::protobuf::Closure* done); virtual void deleteStream(::google::protobuf::RpcController* controller, const ::OstProto::StreamIdList* request, ::OstProto::Ack* response, ::google::protobuf::Closure* done); virtual void modifyStream(::google::protobuf::RpcController* controller, const ::OstProto::StreamConfigList* request, ::OstProto::Ack* response, ::google::protobuf::Closure* done); virtual void startTransmit(::google::protobuf::RpcController* controller, const ::OstProto::PortIdList* request, ::OstProto::Ack* response, ::google::protobuf::Closure* done); virtual void stopTransmit(::google::protobuf::RpcController* controller, const ::OstProto::PortIdList* request, ::OstProto::Ack* response, ::google::protobuf::Closure* done); virtual void startCapture(::google::protobuf::RpcController* controller, const ::OstProto::PortIdList* request, ::OstProto::Ack* response, ::google::protobuf::Closure* done); virtual void stopCapture(::google::protobuf::RpcController* controller, const ::OstProto::PortIdList* request, ::OstProto::Ack* response, ::google::protobuf::Closure* done); virtual void getCaptureBuffer(::google::protobuf::RpcController* controller, const ::OstProto::PortId* request, ::OstProto::CaptureBuffer* response, ::google::protobuf::Closure* done); virtual void getStats(::google::protobuf::RpcController* controller, const ::OstProto::PortIdList* request, ::OstProto::PortStatsList* response, ::google::protobuf::Closure* done); virtual void clearStats(::google::protobuf::RpcController* controller, const ::OstProto::PortIdList* request, ::OstProto::Ack* response, ::google::protobuf::Closure* done); virtual void getStreamStats(::google::protobuf::RpcController* controller, const ::OstProto::StreamGuidList* request, ::OstProto::StreamStatsList* response, ::google::protobuf::Closure* done); virtual void clearStreamStats(::google::protobuf::RpcController* controller, const ::OstProto::StreamGuidList* request, ::OstProto::Ack* response, ::google::protobuf::Closure* done); virtual void checkVersion(::google::protobuf::RpcController* controller, const ::OstProto::VersionInfo* request, ::OstProto::VersionCompatibility* response, ::google::protobuf::Closure* done); // DeviceGroup and Protocol Emulation virtual void getDeviceGroupIdList( ::google::protobuf::RpcController* controller, const ::OstProto::PortId* request, ::OstProto::DeviceGroupIdList* response, ::google::protobuf::Closure* done); virtual void getDeviceGroupConfig( ::google::protobuf::RpcController* controller, const ::OstProto::DeviceGroupIdList* request, ::OstProto::DeviceGroupConfigList* response, ::google::protobuf::Closure* done); virtual void addDeviceGroup( ::google::protobuf::RpcController* controller, const ::OstProto::DeviceGroupIdList* request, ::OstProto::Ack* response, ::google::protobuf::Closure* done); virtual void deleteDeviceGroup( ::google::protobuf::RpcController* controller, const ::OstProto::DeviceGroupIdList* request, ::OstProto::Ack* response, ::google::protobuf::Closure* done); virtual void modifyDeviceGroup( ::google::protobuf::RpcController* controller, const ::OstProto::DeviceGroupConfigList* request, ::OstProto::Ack* response, ::google::protobuf::Closure* done); virtual void getDeviceList( ::google::protobuf::RpcController* controller, const ::OstProto::PortId* request, ::OstProto::PortDeviceList* response, ::google::protobuf::Closure* done); virtual void resolveDeviceNeighbors( ::google::protobuf::RpcController* controller, const ::OstProto::PortIdList* request, ::OstProto::Ack* response, ::google::protobuf::Closure* done); virtual void clearDeviceNeighbors( ::google::protobuf::RpcController* controller, const ::OstProto::PortIdList* request, ::OstProto::Ack* response, ::google::protobuf::Closure* done); virtual void getDeviceNeighbors( ::google::protobuf::RpcController* controller, const ::OstProto::PortId* request, ::OstProto::PortNeighborList* response, ::google::protobuf::Closure* done); friend quint64 getDeviceMacAddress( int portId, int streamId, int frameIndex); friend quint64 getNeighborMacAddress( int portId, int streamId, int frameIndex); signals: void notification(int notifType, SharedProtobufMessage notifData); private: /* * NOTES: * - AbstractPort::id() and index into portInfo[] are same! * - portLock[] size and order should be same as portInfo[] as the * same index is used for both. * - we assume that once populated by the constructor, the list(s) * never change (objects in the list can change, but not the list itself) * - locking is at port granularity, not at stream granularity - for now * this seems sufficient. Revisit later, if required */ QList portInfo; QList portLock; }; #endif ostinato-0.9/server/packetbuffer.cpp000066400000000000000000000040601321315675200177020ustar00rootroot00000000000000/* Copyright (C) 2015 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "packetbuffer.h" // PacketBuffer with full control PacketBuffer::PacketBuffer(int size) { if (size == 0) size = 1600; buffer_ = new uchar[size]; is_own_buffer_ = true; head_ = data_ = tail_ = buffer_; end_ = head_ + size; } // PacketBuffer wrapping already existing const buffer PacketBuffer::PacketBuffer(const uchar *buffer, int size) { // FIXME: ugly const_cast hack!! buffer_ = const_cast(buffer); is_own_buffer_ = false; head_ = data_ = buffer_; tail_ = end_ = buffer_ + size; } PacketBuffer::~PacketBuffer() { if (is_own_buffer_) delete[] buffer_; } int PacketBuffer::length() const { return tail_ - data_; } uchar* PacketBuffer::head() const { return head_; } uchar* PacketBuffer::data() const { return data_; } uchar* PacketBuffer::tail() const { return tail_; } uchar* PacketBuffer::end() const { return end_; } void PacketBuffer::reserve(int len) { data_ += len; tail_ += len; } uchar* PacketBuffer::pull(int len) { if ((tail_ - data_) < len) return NULL; data_ += len; return data_; } uchar* PacketBuffer::push(int len) { if ((data_ - head_) < len) return NULL; data_ -= len; return data_; } uchar* PacketBuffer::put(int len) { uchar *oldTail = tail_; if ((end_ - tail_) < len) return NULL; tail_ += len; return oldTail; } ostinato-0.9/server/packetbuffer.h000066400000000000000000000022771321315675200173570ustar00rootroot00000000000000/* Copyright (C) 2015 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _PACKET_BUFFER_H #define _PACKET_BUFFER_H #include class PacketBuffer { public: PacketBuffer(int size = 0); PacketBuffer(const uchar *buffer, int size); ~PacketBuffer(); int length() const; uchar* head() const; uchar* data() const; uchar* tail() const; uchar* end() const; void reserve(int len); uchar* pull(int len); uchar* push(int len); uchar* put(int len); private: uchar *buffer_; bool is_own_buffer_; uchar *head_, *data_, *tail_, *end_; }; #endif ostinato-0.9/server/packetsequence.h000066400000000000000000000050731321315675200177130ustar00rootroot00000000000000/* Copyright (C) 2010-2016 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _PACKET_SEQUENCE_H #define _PACKET_SEQUENCE_H #include "pcapextra.h" #include "../common/sign.h" #include "streamstats.h" class PacketSequence { public: PacketSequence(bool trackGuidStats) { trackGuidStats_ = trackGuidStats; sendQueue_ = pcap_sendqueue_alloc(1*1024*1024); lastPacket_ = NULL; packets_ = 0; bytes_ = 0; usecDuration_ = 0; repeatCount_ = 1; repeatSize_ = 1; usecDelay_ = 0; } ~PacketSequence() { pcap_sendqueue_destroy(sendQueue_); } bool hasFreeSpace(int size) { if ((sendQueue_->len + size) <= sendQueue_->maxlen) return true; else return false; } int appendPacket(const struct pcap_pkthdr *pktHeader, const uchar *pktData) { int ret; if (lastPacket_) { usecDuration_ += (pktHeader->ts.tv_sec - lastPacket_->ts.tv_sec) * long(1e6); usecDuration_ += (pktHeader->ts.tv_usec - lastPacket_->ts.tv_usec); } packets_++; bytes_ += pktHeader->caplen; lastPacket_ = (struct pcap_pkthdr *) (sendQueue_->buffer + sendQueue_->len); ret = pcap_sendqueue_queue(sendQueue_, pktHeader, pktData); if (trackGuidStats_ && (ret >= 0)) { uint guid; if (SignProtocol::packetGuid(pktData, pktHeader->caplen, &guid)) { streamStatsMeta_[guid].tx_pkts++; streamStatsMeta_[guid].tx_bytes += pktHeader->caplen; } } return ret; } pcap_send_queue *sendQueue_; struct pcap_pkthdr *lastPacket_; long packets_; long bytes_; ulong usecDuration_; int repeatCount_; int repeatSize_; long usecDelay_; StreamStats streamStatsMeta_; private: bool trackGuidStats_; }; #endif ostinato-0.9/server/pcapextra.cpp000066400000000000000000000036771321315675200172450ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "pcapextra.h" #include // memcpy() #include // malloc(), free() /* NOTE: All code borrowed from WinPcap */ #ifndef Q_OS_WIN32 pcap_send_queue* pcap_sendqueue_alloc (u_int memsize) { pcap_send_queue *tqueue; /* Allocate the queue */ tqueue = (pcap_send_queue*)malloc(sizeof(pcap_send_queue)); if(tqueue == NULL){ return NULL; } /* Allocate the buffer */ tqueue->buffer = (char*)malloc(memsize); if(tqueue->buffer == NULL){ free(tqueue); return NULL; } tqueue->maxlen = memsize; tqueue->len = 0; return tqueue; } void pcap_sendqueue_destroy (pcap_send_queue *queue) { free(queue->buffer); free(queue); } int pcap_sendqueue_queue (pcap_send_queue *queue, const struct pcap_pkthdr *pkt_header, const u_char *pkt_data) { if(queue->len + sizeof(struct pcap_pkthdr) + pkt_header->caplen > queue->maxlen) { return -1; } /* Copy the pcap_pkthdr header*/ memcpy(queue->buffer + queue->len, pkt_header, sizeof(struct pcap_pkthdr)); queue->len += sizeof(struct pcap_pkthdr); /* copy the packet */ memcpy(queue->buffer + queue->len, pkt_data, pkt_header->caplen); queue->len += pkt_header->caplen; return 0; } #endif ostinato-0.9/server/pcapextra.h000066400000000000000000000022151321315675200166750ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _PCAP_EXTRA_H #define _PCAP_EXTRA_H #include #include #ifndef Q_OS_WIN32 #define PCAP_OPENFLAG_PROMISCUOUS 1 struct pcap_send_queue { u_int maxlen; u_int len; char *buffer; }; pcap_send_queue* pcap_sendqueue_alloc (u_int memsize); void pcap_sendqueue_destroy (pcap_send_queue *queue); int pcap_sendqueue_queue (pcap_send_queue *queue, const struct pcap_pkthdr *pkt_header, const u_char *pkt_data); #endif #endif ostinato-0.9/server/pcapport.cpp000066400000000000000000000446341321315675200171040ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "pcapport.h" #include "devicemanager.h" #include "packetbuffer.h" #include pcap_if_t *PcapPort::deviceList_ = NULL; PcapPort::PcapPort(int id, const char *device) : AbstractPort(id, device) { monitorRx_ = new PortMonitor(device, kDirectionRx, &stats_); monitorTx_ = new PortMonitor(device, kDirectionTx, &stats_); transmitter_ = new PcapTransmitter(device, streamStats_); capturer_ = new PortCapturer(device); emulXcvr_ = new EmulationTransceiver(device, deviceManager_); rxStatsPoller_ = new PcapRxStats(device, streamStats_); if (!monitorRx_->handle() || !monitorTx_->handle()) isUsable_ = false; if (!deviceList_) { char errbuf[PCAP_ERRBUF_SIZE]; if (pcap_findalldevs(&deviceList_, errbuf) == -1) qDebug("Error in pcap_findalldevs_ex: %s\n", errbuf); } for (pcap_if_t *dev = deviceList_; dev != NULL; dev = dev->next) { if (strcmp(device, dev->name) == 0) { if (dev->name) data_.set_name(dev->name); if (dev->description) data_.set_description(dev->description); //! \todo set port IP addr also } } } void PcapPort::init() { if (!monitorTx_->isDirectional()) transmitter_->useExternalStats(&stats_); transmitter_->setHandle(monitorRx_->handle()); updateNotes(); monitorRx_->start(); monitorTx_->start(); } PcapPort::~PcapPort() { qDebug("In %s", __FUNCTION__); if (monitorRx_) monitorRx_->stop(); if (monitorTx_) monitorTx_->stop(); rxStatsPoller_->stop(); delete rxStatsPoller_; delete emulXcvr_; delete capturer_; delete transmitter_; if (monitorRx_) monitorRx_->wait(); delete monitorRx_; if (monitorTx_) monitorTx_->wait(); delete monitorTx_; } void PcapPort::updateNotes() { QString notes; if ((!monitorRx_->isPromiscuous()) || (!monitorTx_->isPromiscuous())) notes.append("
  • Non Promiscuous Mode
  • "); if (!monitorRx_->isDirectional() && !hasExclusiveControl()) notes.append("
  • Rx Frames/Bytes: Includes non Ostinato Tx pkts also (Tx by Ostinato are not included)
  • "); if (!monitorTx_->isDirectional() && !hasExclusiveControl()) notes.append("
  • Tx Frames/Bytes: Only Ostinato Tx pkts (Tx by others NOT included)
  • "); if (notes.isEmpty()) data_.set_notes(""); else data_.set_notes(QString("Limitation(s)" "
      %1
    " "Rx/Tx Rates are also subject to above limitation(s)"). arg(notes).toStdString()); } bool PcapPort::setTrackStreamStats(bool enable) { bool val = enable ? startStreamStatsTracking() : stopStreamStatsTracking(); if (val) AbstractPort::setTrackStreamStats(enable); return val; } bool PcapPort::setRateAccuracy(AbstractPort::Accuracy accuracy) { if (transmitter_->setRateAccuracy(accuracy)) { AbstractPort::setRateAccuracy(accuracy); return true; } return false; } void PcapPort::startDeviceEmulation() { emulXcvr_->start(); } void PcapPort::stopDeviceEmulation() { emulXcvr_->stop(); } int PcapPort::sendEmulationPacket(PacketBuffer *pktBuf) { return emulXcvr_->transmitPacket(pktBuf); } bool PcapPort::startStreamStatsTracking() { if (!transmitter_->setStreamStatsTracking(true)) goto _tx_fail; if (!rxStatsPoller_->start()) goto _rx_fail; /* * If RxPoller receives both IN and OUT packets, packets Tx on this * port will also be received by it and we consider it to be a Rx (IN) * packet incorrectly - so adjust Rx stats for this case * XXX - ideally, RxPoller should do this adjustment, but given our * design, it is easier to implement in transmitter */ transmitter_->adjustRxStreamStats(!rxStatsPoller_->isDirectional()); return true; _rx_fail: transmitter_->setStreamStatsTracking(false); _tx_fail: qWarning("failed to start stream stats tracking"); return false; } bool PcapPort::stopStreamStatsTracking() { if (!transmitter_->setStreamStatsTracking(false)) goto _tx_fail; if (!rxStatsPoller_->stop()) goto _rx_fail; return true; _rx_fail: transmitter_->setStreamStatsTracking(true); _tx_fail: qWarning("failed to stop stream stats tracking"); return false; } /* * ------------------------------------------------------------------- * * Port Monitor * ------------------------------------------------------------------- * */ PcapPort::PortMonitor::PortMonitor(const char *device, Direction direction, AbstractPort::PortStats *stats) { int ret; char errbuf[PCAP_ERRBUF_SIZE] = ""; bool noLocalCapture; direction_ = direction; isDirectional_ = true; isPromisc_ = true; noLocalCapture = true; stats_ = stats; stop_ = false; _retry: #ifdef Q_OS_WIN32 int flags = 0; if (isPromisc_) flags |= PCAP_OPENFLAG_PROMISCUOUS; if (noLocalCapture) flags |= PCAP_OPENFLAG_NOCAPTURE_LOCAL; handle_ = pcap_open(device, 64 /* FIXME */, flags, 1000 /* ms */, NULL, errbuf); #else handle_ = pcap_open_live(device, 64 /* FIXME */, int(isPromisc_), 1000 /* ms */, errbuf); #endif if (handle_ == NULL) { if (isPromisc_ && QString(errbuf).contains("promiscuous")) { qDebug("Can't set promiscuous mode, trying non-promisc %s", device); isPromisc_ = false; goto _retry; } else if (noLocalCapture && QString(errbuf).contains("loopback")) { qDebug("Can't set no local capture mode %s", device); noLocalCapture = false; goto _retry; } else goto _open_error; } #ifdef Q_OS_WIN32 // pcap_setdirection() API is not supported in Windows. // NOTE: WinPcap 4.1.1 and above exports a dummy API that returns -1 // but since we would like to work with previous versions of WinPcap // also, we assume the API does not exist ret = -1; #else switch (direction_) { case kDirectionRx: ret = pcap_setdirection(handle_, PCAP_D_IN); break; case kDirectionTx: ret = pcap_setdirection(handle_, PCAP_D_OUT); break; default: ret = -1; // avoid 'may be used uninitialized' warning Q_ASSERT(false); } #endif if (ret < 0) goto _set_direction_error; return; _set_direction_error: qDebug("Error setting direction(%d) %s: %s\n", direction, device, pcap_geterr(handle_)); isDirectional_ = false; return; _open_error: qDebug("%s: Error opening port %s: %s\n", __FUNCTION__, device, errbuf); } PcapPort::PortMonitor::~PortMonitor() { if (handle_) pcap_close(handle_); } void PcapPort::PortMonitor::run() { while (!stop_) { int ret; struct pcap_pkthdr *hdr; const uchar *data; ret = pcap_next_ex(handle_, &hdr, &data); switch (ret) { case 1: switch (direction_) { case kDirectionRx: stats_->rxPkts++; stats_->rxBytes += hdr->len; break; case kDirectionTx: if (isDirectional_) { stats_->txPkts++; stats_->txBytes += hdr->len; } break; default: Q_ASSERT(false); } //! \todo TODO pkt/bit rates break; case 0: //qDebug("%s: timeout. continuing ...", __PRETTY_FUNCTION__); continue; case -1: qWarning("%s: error reading packet (%d): %s", __PRETTY_FUNCTION__, ret, pcap_geterr(handle_)); break; case -2: qWarning("%s: error reading packet (%d): %s", __PRETTY_FUNCTION__, ret, pcap_geterr(handle_)); break; default: qFatal("%s: Unexpected return value %d", __PRETTY_FUNCTION__, ret); } } } void PcapPort::PortMonitor::stop() { stop_ = true; pcap_breakloop(handle()); } /* * ------------------------------------------------------------------- * * Port Capturer * ------------------------------------------------------------------- * */ PcapPort::PortCapturer::PortCapturer(const char *device) { device_ = QString::fromAscii(device); stop_ = false; state_ = kNotStarted; if (!capFile_.open()) qWarning("Unable to open temp cap file"); qDebug("cap file = %s", capFile_.fileName().toAscii().constData()); dumpHandle_ = NULL; handle_ = NULL; } PcapPort::PortCapturer::~PortCapturer() { capFile_.close(); } void PcapPort::PortCapturer::run() { int flag = PCAP_OPENFLAG_PROMISCUOUS; char errbuf[PCAP_ERRBUF_SIZE] = ""; qDebug("In %s", __PRETTY_FUNCTION__); if (!capFile_.isOpen()) { qWarning("temp cap file is not open"); goto _exit; } _retry: handle_ = pcap_open_live(device_.toAscii().constData(), 65535, flag, 1000 /* ms */, errbuf); if (handle_ == NULL) { if (flag && QString(errbuf).contains("promiscuous")) { qDebug("%s:can't set promiscuous mode, trying non-promisc", device_.toAscii().constData()); flag = 0; goto _retry; } else { qDebug("%s: Error opening port %s: %s\n", __FUNCTION__, device_.toAscii().constData(), errbuf); goto _exit; } } dumpHandle_ = pcap_dump_open(handle_, capFile_.fileName().toAscii().constData()); state_ = kRunning; while (1) { int ret; struct pcap_pkthdr *hdr; const uchar *data; ret = pcap_next_ex(handle_, &hdr, &data); switch (ret) { case 1: pcap_dump((uchar*) dumpHandle_, hdr, data); break; case 0: // timeout: just go back to the loop break; case -1: qWarning("%s: error reading packet (%d): %s", __PRETTY_FUNCTION__, ret, pcap_geterr(handle_)); break; case -2: default: qFatal("%s: Unexpected return value %d", __PRETTY_FUNCTION__, ret); } if (stop_) { qDebug("user requested capture stop\n"); break; } } pcap_dump_close(dumpHandle_); pcap_close(handle_); dumpHandle_ = NULL; handle_ = NULL; stop_ = false; _exit: state_ = kFinished; } void PcapPort::PortCapturer::start() { // FIXME: return error if (state_ == kRunning) { qWarning("Capture start requested but is already running!"); return; } state_ = kNotStarted; QThread::start(); while (state_ == kNotStarted) QThread::msleep(10); } void PcapPort::PortCapturer::stop() { if (state_ == kRunning) { stop_ = true; while (state_ == kRunning) QThread::msleep(10); } else { // FIXME: return error qWarning("Capture stop requested but is not running!"); return; } } bool PcapPort::PortCapturer::isRunning() { return (state_ == kRunning); } QFile* PcapPort::PortCapturer::captureFile() { return &capFile_; } /* * ------------------------------------------------------------------- * * Transmit+Receiver for Device/ProtocolEmulation * ------------------------------------------------------------------- * */ PcapPort::EmulationTransceiver::EmulationTransceiver(const char *device, DeviceManager *deviceManager) { device_ = QString::fromAscii(device); deviceManager_ = deviceManager; stop_ = false; state_ = kNotStarted; handle_ = NULL; } PcapPort::EmulationTransceiver::~EmulationTransceiver() { stop(); } void PcapPort::EmulationTransceiver::run() { int flags = PCAP_OPENFLAG_PROMISCUOUS; char errbuf[PCAP_ERRBUF_SIZE] = ""; struct bpf_program bpf; #if 0 const char *capture_filter = "arp or icmp or icmp6 or " "(vlan and (arp or icmp or icmp6)) or " "(vlan and vlan and (arp or icmp or icmp6)) or " "(vlan and vlan and vlan and (arp or icmp or icmp6)) or " "(vlan and vlan and vlan and vlan and (arp or icmp or icmp6))"; /* Ideally we should use the above filter, but the 'vlan' capture filter in libpcap is implemented as a kludge. From the pcap-filter man page - vlan [vlan_id] Note that the first vlan keyword encountered in expression changes the decoding offsets for the remainder of expression on the assumption that the packet is a VLAN packet. The vlan [vlan_id] expression may be used more than once, to filter on VLAN hierarchies. Each use of that expression increments the filter offsets by 4. See https://ask.wireshark.org/questions/31953/unusual-behavior-with-stacked-vlan-tags-and-capture-filter So we use the modified filter expression that works as we intend. If ever libpcap changes their implementation, this will need to change as well. */ #else const char *capture_filter = "arp or icmp or icmp6 or " "(vlan and (arp or icmp or icmp6)) or " "(vlan and (arp or icmp or icmp6)) or " "(vlan and (arp or icmp or icmp6)) or " "(vlan and (arp or icmp or icmp6))"; #endif const int optimize = 1; qDebug("In %s", __PRETTY_FUNCTION__); #ifdef Q_OS_WIN32 flags |= PCAP_OPENFLAG_NOCAPTURE_LOCAL; #endif #ifdef Q_OS_WIN32 _retry: // NOCAPTURE_LOCAL needs windows only pcap_open() handle_ = pcap_open(qPrintable(device_), 65535, flags, 100 /* ms */, NULL, errbuf); #else handle_ = pcap_open_live(qPrintable(device_), 65535, flags, 100 /* ms */, errbuf); #endif if (handle_ == NULL) { if (flags && QString(errbuf).contains("promiscuous")) { notify("Unable to set promiscuous mode on <%s> - " "device emulation will not work", qPrintable(device_)); goto _exit; } #ifdef Q_OS_WIN32 else if ((flags & PCAP_OPENFLAG_NOCAPTURE_LOCAL) && QString(errbuf).contains("loopback")) { qDebug("Can't set no local capture mode %s", qPrintable(device_)); flags &= ~PCAP_OPENFLAG_NOCAPTURE_LOCAL; goto _retry; } #endif else { notify("Unable to open <%s> [%s] - device emulation will not work", qPrintable(device_), errbuf); goto _exit; } } // TODO: for now the filter is hardcoded to accept tagged/untagged // ARP/NDP or ICMPv4/v6; when more protocols are added, we may need // to derive this filter based on which protocols are configured // on the devices if (pcap_compile(handle_, &bpf, capture_filter, optimize, 0) < 0) { qWarning("%s: error compiling filter: %s", qPrintable(device_), pcap_geterr(handle_)); goto _skip_filter; } if (pcap_setfilter(handle_, &bpf) < 0) { qWarning("%s: error setting filter: %s", qPrintable(device_), pcap_geterr(handle_)); goto _skip_filter; } _skip_filter: state_ = kRunning; while (1) { int ret; struct pcap_pkthdr *hdr; const uchar *data; ret = pcap_next_ex(handle_, &hdr, &data); switch (ret) { case 1: { PacketBuffer *pktBuf = new PacketBuffer(data, hdr->caplen); #if 0 for (int i = 0; i < 64; i++) { printf("%02x ", data[i]); if (i % 16 == 0) printf("\n"); } printf("\n"); #endif // XXX: deviceManager should free pktBuf before returning // from this call; if it needs to process the pkt async // it should make a copy as the pktBuf's data buffer is // owned by libpcap which does not guarantee data will // persist across calls to pcap_next_ex() deviceManager_->receivePacket(pktBuf); break; } case 0: // timeout: just go back to the loop break; case -1: qWarning("%s: error reading packet (%d): %s", __PRETTY_FUNCTION__, ret, pcap_geterr(handle_)); break; case -2: default: qFatal("%s: Unexpected return value %d", __PRETTY_FUNCTION__, ret); } if (stop_) { qDebug("user requested receiver stop\n"); break; } } pcap_close(handle_); handle_ = NULL; stop_ = false; _exit: state_ = kFinished; } void PcapPort::EmulationTransceiver::start() { if (state_ == kRunning) { qWarning("Receive start requested but is already running!"); return; } state_ = kNotStarted; QThread::start(); while (state_ == kNotStarted) QThread::msleep(10); } void PcapPort::EmulationTransceiver::stop() { if (state_ == kRunning) { stop_ = true; while (state_ == kRunning) QThread::msleep(10); } else { qWarning("Receive stop requested but is not running!"); return; } } bool PcapPort::EmulationTransceiver::isRunning() { return (state_ == kRunning); } int PcapPort::EmulationTransceiver::transmitPacket(PacketBuffer *pktBuf) { return pcap_sendpacket(handle_, pktBuf->data(), pktBuf->length()); } ostinato-0.9/server/pcapport.h000066400000000000000000000114571321315675200165460ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _SERVER_PCAP_PORT_H #define _SERVER_PCAP_PORT_H #include #include #include #include "abstractport.h" #include "pcapextra.h" #include "pcaprxstats.h" #include "pcaptransmitter.h" class PcapPort : public AbstractPort { public: PcapPort(int id, const char *device); ~PcapPort(); void init(); virtual bool hasExclusiveControl() { return false; } virtual bool setExclusiveControl(bool /*exclusive*/) { return false; } virtual bool setTrackStreamStats(bool enable); virtual bool setRateAccuracy(AbstractPort::Accuracy accuracy); virtual void clearPacketList() { transmitter_->clearPacketList(); setPacketListLoopMode(false, 0, 0); } virtual void loopNextPacketSet(qint64 size, qint64 repeats, long repeatDelaySec, long repeatDelayNsec) { transmitter_->loopNextPacketSet(size, repeats, repeatDelaySec, repeatDelayNsec); } virtual bool appendToPacketList(long sec, long nsec, const uchar *packet, int length) { return transmitter_->appendToPacketList(sec, nsec, packet, length); } virtual void setPacketListLoopMode(bool loop, quint64 secDelay, quint64 nsecDelay) { transmitter_->setPacketListLoopMode(loop, secDelay, nsecDelay); } virtual void startTransmit() { Q_ASSERT(!isDirty()); transmitter_->start(); } virtual void stopTransmit() { transmitter_->stop(); } virtual bool isTransmitOn() { return transmitter_->isRunning(); } virtual void startCapture() { capturer_->start(); } virtual void stopCapture() { capturer_->stop(); } virtual bool isCaptureOn() { return capturer_->isRunning(); } virtual QIODevice* captureData() { return capturer_->captureFile(); } virtual void startDeviceEmulation(); virtual void stopDeviceEmulation(); virtual int sendEmulationPacket(PacketBuffer *pktBuf); protected: enum Direction { kDirectionRx, kDirectionTx }; class PortMonitor: public QThread { public: PortMonitor(const char *device, Direction direction, AbstractPort::PortStats *stats); ~PortMonitor(); void run(); void stop(); pcap_t* handle() { return handle_; } Direction direction() { return direction_; } bool isDirectional() { return isDirectional_; } bool isPromiscuous() { return isPromisc_; } protected: AbstractPort::PortStats *stats_; bool stop_; private: pcap_t *handle_; Direction direction_; bool isDirectional_; bool isPromisc_; }; class PortCapturer: public QThread { public: PortCapturer(const char *device); ~PortCapturer(); void run(); void start(); void stop(); bool isRunning(); QFile* captureFile(); private: enum State { kNotStarted, kRunning, kFinished }; QString device_; volatile bool stop_; QTemporaryFile capFile_; pcap_t *handle_; pcap_dumper_t *dumpHandle_; volatile State state_; }; class EmulationTransceiver: public QThread { public: EmulationTransceiver(const char *device, DeviceManager *deviceManager); ~EmulationTransceiver(); void run(); void start(); void stop(); bool isRunning(); int transmitPacket(PacketBuffer *pktBuf); private: enum State { kNotStarted, kRunning, kFinished }; QString device_; DeviceManager *deviceManager_; volatile bool stop_; pcap_t *handle_; volatile State state_; }; PortMonitor *monitorRx_; PortMonitor *monitorTx_; void updateNotes(); private: bool startStreamStatsTracking(); bool stopStreamStatsTracking(); PcapTransmitter *transmitter_; PortCapturer *capturer_; EmulationTransceiver *emulXcvr_; PcapRxStats *rxStatsPoller_; static pcap_if_t *deviceList_; }; #endif ostinato-0.9/server/pcaprxstats.cpp000066400000000000000000000117431321315675200176230ustar00rootroot00000000000000/* Copyright (C) 2016 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "pcaprxstats.h" #include "pcapextra.h" #include "../common/sign.h" #define notify qWarning // FIXME PcapRxStats::PcapRxStats(const char *device, StreamStats &portStreamStats) : streamStats_(portStreamStats) { device_ = QString::fromAscii(device); stop_ = false; state_ = kNotStarted; isDirectional_ = true; handle_ = NULL; } pcap_t* PcapRxStats::handle() { return handle_; } void PcapRxStats::run() { int flags = PCAP_OPENFLAG_PROMISCUOUS; char errbuf[PCAP_ERRBUF_SIZE] = ""; struct bpf_program bpf; const int optimize = 1; QString capture_filter = QString("(ether[len - 4:4] == 0x%1)").arg( SignProtocol::magic(), 0, BASE_HEX); // XXX: Exclude ICMP packets which contain an embedded signed packet // For now we check upto 4 vlan tags capture_filter.append( "and not (" "icmp or " "(vlan and icmp) or " "(vlan and icmp) or " "(vlan and icmp) or " "(vlan and icmp) " ")"); qDebug("In %s", __PRETTY_FUNCTION__); handle_ = pcap_open_live(qPrintable(device_), 65535, flags, 100 /* ms */, errbuf); if (handle_ == NULL) { if (flags && QString(errbuf).contains("promiscuous")) { notify("Unable to set promiscuous mode on <%s> - " "stream stats rx will not work", qPrintable(device_)); goto _exit; } else { notify("Unable to open <%s> [%s] - stream stats rx will not work", qPrintable(device_), errbuf); goto _exit; } } #ifdef Q_OS_WIN32 // pcap_setdirection() API is not supported in Windows. // NOTE: WinPcap 4.1.1 and above exports a dummy API that returns -1 // but since we would like to work with previous versions of WinPcap // also, we assume the API does not exist isDirectional_ = false; #else if (pcap_setdirection(handle_, PCAP_D_IN) < 0) { qDebug("RxStats: Error setting IN direction %s: %s\n", qPrintable(device_), pcap_geterr(handle_)); isDirectional_ = false; } #endif if (pcap_compile(handle_, &bpf, qPrintable(capture_filter), optimize, 0) < 0) { qWarning("%s: error compiling filter: %s", qPrintable(device_), pcap_geterr(handle_)); goto _skip_filter; } if (pcap_setfilter(handle_, &bpf) < 0) { qWarning("%s: error setting filter: %s", qPrintable(device_), pcap_geterr(handle_)); goto _skip_filter; } _skip_filter: state_ = kRunning; while (1) { int ret; struct pcap_pkthdr *hdr; const uchar *data; ret = pcap_next_ex(handle_, &hdr, &data); switch (ret) { case 1: { uint guid; if (SignProtocol::packetGuid(data, hdr->caplen, &guid)) { streamStats_[guid].rx_pkts++; streamStats_[guid].rx_bytes += hdr->caplen; } break; } case 0: // timeout: just go back to the loop break; case -1: qWarning("%s: error reading packet (%d): %s", __PRETTY_FUNCTION__, ret, pcap_geterr(handle_)); break; case -2: default: qFatal("%s: Unexpected return value %d", __PRETTY_FUNCTION__, ret); } if (stop_) { qDebug("user requested receiver stop\n"); break; } } pcap_close(handle_); handle_ = NULL; stop_ = false; _exit: state_ = kFinished; } bool PcapRxStats::start() { if (state_ == kRunning) { qWarning("RxStats start requested but is already running!"); goto _exit; } state_ = kNotStarted; QThread::start(); while (state_ == kNotStarted) QThread::msleep(10); _exit: return true; } bool PcapRxStats::stop() { if (state_ == kRunning) { stop_ = true; while (state_ == kRunning) QThread::msleep(10); } else qWarning("RxStats stop requested but is not running!"); return true; } bool PcapRxStats::isRunning() { return (state_ == kRunning); } bool PcapRxStats::isDirectional() { return isDirectional_; } ostinato-0.9/server/pcaprxstats.h000066400000000000000000000023721321315675200172660ustar00rootroot00000000000000/* Copyright (C) 2016 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _PCAP_RX_STATS_H #define _PCAP_RX_STATS_H #include "streamstats.h" #include #include class PcapRxStats: public QThread { public: PcapRxStats(const char *device, StreamStats &portStreamStats); pcap_t* handle(); void run(); bool start(); bool stop(); bool isRunning(); bool isDirectional(); private: enum State { kNotStarted, kRunning, kFinished }; QString device_; StreamStats &streamStats_; volatile bool stop_; pcap_t *handle_; volatile State state_; bool isDirectional_; }; #endif ostinato-0.9/server/pcaptransmitter.cpp000066400000000000000000000063671321315675200204750ustar00rootroot00000000000000/* Copyright (C) 2010-2016 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "pcaptransmitter.h" PcapTransmitter::PcapTransmitter( const char *device, StreamStats &portStreamStats) : streamStats_(portStreamStats), txThread_(device) { adjustRxStreamStats_ = false; memset(&stats_, 0, sizeof(stats_)); txStats_.setTxThreadStats(&stats_); txStats_.start(); // TODO: alongwith user transmit start txThread_.setStats(&stats_); connect(&txThread_, SIGNAL(finished()), SLOT(updateTxThreadStreamStats())); } PcapTransmitter::~PcapTransmitter() { txStats_.stop(); // TODO: alongwith user transmit stop } bool PcapTransmitter::setRateAccuracy( AbstractPort::Accuracy accuracy) { return txThread_.setRateAccuracy(accuracy); } void PcapTransmitter::adjustRxStreamStats(bool enable) { adjustRxStreamStats_ = enable; } bool PcapTransmitter::setStreamStatsTracking(bool enable) { return txThread_.setStreamStatsTracking(enable); } void PcapTransmitter::clearPacketList() { txThread_.clearPacketList(); } void PcapTransmitter::loopNextPacketSet( qint64 size, qint64 repeats, long repeatDelaySec, long repeatDelayNsec) { txThread_.loopNextPacketSet(size, repeats, repeatDelaySec, repeatDelayNsec); } bool PcapTransmitter::appendToPacketList(long sec, long nsec, const uchar *packet, int length) { return txThread_.appendToPacketList(sec, nsec, packet, length); } void PcapTransmitter::setHandle(pcap_t *handle) { txThread_.setHandle(handle); } void PcapTransmitter::setPacketListLoopMode( bool loop, quint64 secDelay, quint64 nsecDelay) { txThread_.setPacketListLoopMode(loop, secDelay, nsecDelay); } void PcapTransmitter::useExternalStats(AbstractPort::PortStats *stats) { txStats_.useExternalStats(stats); } void PcapTransmitter::start() { txThread_.start(); } void PcapTransmitter::stop() { txThread_.stop(); } bool PcapTransmitter::isRunning() { return txThread_.isRunning(); } void PcapTransmitter::updateTxThreadStreamStats() { PcapTxThread *txThread = dynamic_cast(sender()); const StreamStats& threadStreamStats = txThread->streamStats(); StreamStatsIterator i(threadStreamStats); while (i.hasNext()) { i.next(); uint guid = i.key(); StreamStatsTuple sst = i.value(); streamStats_[guid].tx_pkts += sst.tx_pkts; streamStats_[guid].tx_bytes += sst.tx_bytes; if (adjustRxStreamStats_) { streamStats_[guid].rx_pkts -= sst.tx_pkts; streamStats_[guid].rx_bytes -= sst.tx_bytes; } } txThread->clearStreamStats(); } ostinato-0.9/server/pcaptransmitter.h000066400000000000000000000034731321315675200201350ustar00rootroot00000000000000/* Copyright (C) 2010-2016 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _PCAP_TRANSMITTER_H #define _PCAP_TRANSMITTER_H #include "abstractport.h" #include "pcaptxstats.h" #include "pcaptxthread.h" #include "statstuple.h" class PcapTransmitter : QObject { Q_OBJECT public: PcapTransmitter(const char *device, StreamStats &portStreamStats); ~PcapTransmitter(); bool setRateAccuracy(AbstractPort::Accuracy accuracy); bool setStreamStatsTracking(bool enable); void adjustRxStreamStats(bool enable); void clearPacketList(); void loopNextPacketSet(qint64 size, qint64 repeats, long repeatDelaySec, long repeatDelayNsec); bool appendToPacketList(long sec, long usec, const uchar *packet, int length); void setPacketListLoopMode(bool loop, quint64 secDelay, quint64 nsecDelay); void setHandle(pcap_t *handle); void useExternalStats(AbstractPort::PortStats *stats); void start(); void stop(); bool isRunning(); private slots: void updateTxThreadStreamStats(); private: StreamStats &streamStats_; PcapTxThread txThread_; PcapTxStats txStats_; StatsTuple stats_; bool adjustRxStreamStats_; }; #endif ostinato-0.9/server/pcaptxstats.cpp000066400000000000000000000034221321315675200176200ustar00rootroot00000000000000/* Copyright (C) 2016 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "pcaptxstats.h" #include "pcaptxstats.h" #include "statstuple.h" PcapTxStats::PcapTxStats() { txThreadStats_ = NULL; stats_ = new AbstractPort::PortStats; usingInternalStats_ = true; stop_ = false; } PcapTxStats::~PcapTxStats() { if (usingInternalStats_) delete stats_; } void PcapTxStats::setTxThreadStats(StatsTuple *stats) { txThreadStats_ = stats; } void PcapTxStats::useExternalStats(AbstractPort::PortStats *stats) { if (usingInternalStats_) delete stats_; stats_ = stats; usingInternalStats_ = false; } void PcapTxStats::start() { QThread::start(); while (!isRunning()) QThread::msleep(10); } void PcapTxStats::stop() { stop_ = true; while (isRunning()) QThread::msleep(10); } void PcapTxStats::run() { Q_ASSERT(txThreadStats_); qDebug("txStats: collection start"); while (1) { stats_->txPkts = txThreadStats_->pkts; stats_->txBytes = txThreadStats_->bytes; if (stop_) break; QThread::msleep(1000); } stop_ = false; qDebug("txStats: collection end"); } ostinato-0.9/server/pcaptxstats.h000066400000000000000000000022361321315675200172670ustar00rootroot00000000000000/* Copyright (C) 2016 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _PCAP_TX_STATS_H #define _PCAP_TX_STATS_H #include "abstractport.h" #include class StatsTuple; class PcapTxStats : public QThread { public: PcapTxStats(); ~PcapTxStats(); void setTxThreadStats(StatsTuple *stats); void useExternalStats(AbstractPort::PortStats *stats); void start(); void stop(); private: void run(); StatsTuple *txThreadStats_; bool usingInternalStats_; AbstractPort::PortStats *stats_; volatile bool stop_; }; #endif ostinato-0.9/server/pcaptxthread.cpp000066400000000000000000000412571321315675200177410ustar00rootroot00000000000000/* Copyright (C) 2010-2016 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "pcaptransmitter.h" #include "statstuple.h" #include "timestamp.h" #define __STDC_FORMAT_MACROS #include PcapTxThread::PcapTxThread(const char *device) { char errbuf[PCAP_ERRBUF_SIZE] = ""; #ifdef Q_OS_WIN32 LARGE_INTEGER freq; if (QueryPerformanceFrequency(&freq)) gTicksFreq = freq.QuadPart; else Q_ASSERT_X(false, "PcapTxThread::PcapTxThread", "This Win32 platform does not support performance counter"); #endif state_ = kNotStarted; stop_ = false; trackStreamStats_ = false; clearPacketList(); handle_ = pcap_open_live(device, 64 /* FIXME */, 0, 1000 /* ms */, errbuf); if (handle_ == NULL) goto _open_error; usingInternalHandle_ = true; stats_ = NULL; return; _open_error: qDebug("%s: Error opening port %s: %s\n", __FUNCTION__, device, errbuf); usingInternalHandle_ = false; } PcapTxThread::~PcapTxThread() { if (usingInternalHandle_) pcap_close(handle_); } bool PcapTxThread::setRateAccuracy( AbstractPort::Accuracy accuracy) { switch (accuracy) { case AbstractPort::kHighAccuracy: udelayFn_ = udelay; qWarning("%s: rate accuracy set to High - busy wait", __FUNCTION__); break; case AbstractPort::kLowAccuracy: udelayFn_ = QThread::usleep; qWarning("%s: rate accuracy set to Low - usleep", __FUNCTION__); break; default: qWarning("%s: unsupported rate accuracy value %d", __FUNCTION__, accuracy); return false; } return true; } bool PcapTxThread::setStreamStatsTracking(bool enable) { trackStreamStats_ = enable; return true; } void PcapTxThread::clearPacketList() { Q_ASSERT(!isRunning()); // \todo lock for packetSequenceList while(packetSequenceList_.size()) delete packetSequenceList_.takeFirst(); currentPacketSequence_ = NULL; repeatSequenceStart_ = -1; repeatSize_ = 0; packetCount_ = 0; packetListSize_ = 0; returnToQIdx_ = -1; setPacketListLoopMode(false, 0, 0); } void PcapTxThread::loopNextPacketSet(qint64 size, qint64 repeats, long repeatDelaySec, long repeatDelayNsec) { currentPacketSequence_ = new PacketSequence(trackStreamStats_); currentPacketSequence_->repeatCount_ = repeats; currentPacketSequence_->usecDelay_ = repeatDelaySec * long(1e6) + repeatDelayNsec/1000; repeatSequenceStart_ = packetSequenceList_.size(); repeatSize_ = size; packetCount_ = 0; packetSequenceList_.append(currentPacketSequence_); } bool PcapTxThread::appendToPacketList(long sec, long nsec, const uchar *packet, int length) { bool op = true; pcap_pkthdr pktHdr; pktHdr.caplen = pktHdr.len = length; pktHdr.ts.tv_sec = sec; pktHdr.ts.tv_usec = nsec/1000; if (currentPacketSequence_ == NULL || !currentPacketSequence_->hasFreeSpace(2*sizeof(pcap_pkthdr)+length)) { if (currentPacketSequence_ != NULL) { long usecs; usecs = (pktHdr.ts.tv_sec - currentPacketSequence_->lastPacket_->ts.tv_sec) * long(1e6); usecs += (pktHdr.ts.tv_usec - currentPacketSequence_->lastPacket_->ts.tv_usec); currentPacketSequence_->usecDelay_ = usecs; } //! \todo (LOW): calculate sendqueue size currentPacketSequence_ = new PacketSequence(trackStreamStats_); packetSequenceList_.append(currentPacketSequence_); // Validate that the pkt will fit inside the new currentSendQueue_ Q_ASSERT(currentPacketSequence_->hasFreeSpace( sizeof(pcap_pkthdr) + length)); } if (currentPacketSequence_->appendPacket(&pktHdr, (u_char*) packet) < 0) { op = false; } packetCount_++; packetListSize_ += repeatSize_ ? currentPacketSequence_->repeatCount_ : 1; if (repeatSize_ > 0 && packetCount_ == repeatSize_) { qDebug("repeatSequenceStart_=%d, repeatSize_ = %llu", repeatSequenceStart_, repeatSize_); // Set the packetSequence repeatSize Q_ASSERT(repeatSequenceStart_ >= 0); Q_ASSERT(repeatSequenceStart_ < packetSequenceList_.size()); if (currentPacketSequence_ != packetSequenceList_[repeatSequenceStart_]) { PacketSequence *start = packetSequenceList_[repeatSequenceStart_]; currentPacketSequence_->usecDelay_ = start->usecDelay_; start->usecDelay_ = 0; start->repeatSize_ = packetSequenceList_.size() - repeatSequenceStart_; } repeatSize_ = 0; // End current pktSeq and trigger a new pktSeq allocation for next pkt currentPacketSequence_ = NULL; } return op; } void PcapTxThread::setPacketListLoopMode( bool loop, quint64 secDelay, quint64 nsecDelay) { returnToQIdx_ = loop ? 0 : -1; loopDelay_ = secDelay*long(1e6) + nsecDelay/1000; } void PcapTxThread::setHandle(pcap_t *handle) { if (usingInternalHandle_) pcap_close(handle_); handle_ = handle; usingInternalHandle_ = false; } void PcapTxThread::setStats(StatsTuple *stats) { stats_ = stats; } const StreamStats& PcapTxThread::streamStats() { return streamStats_; } void PcapTxThread::clearStreamStats() { streamStats_.clear(); } void PcapTxThread::run() { //! \todo (MED) Stream Mode - continuous: define before implement // NOTE1: We can't use pcap_sendqueue_transmit() directly even on Win32 // 'coz of 2 reasons - there's no way of stopping it before all packets // in the sendQueue are sent out and secondly, stats are available only // when all packets have been sent - no periodic updates // // NOTE2: Transmit on the Rx Handle so that we can receive it back // on the Tx Handle to do stats // // NOTE3: Update pcapExtra counters - port TxStats will be updated in the // 'stats callback' function so that both Rx and Tx stats are updated // together const int kSyncTransmit = 1; int i; long overHead = 0; // overHead should be negative or zero qDebug("packetSequenceList_.size = %d", packetSequenceList_.size()); if (packetSequenceList_.size() <= 0) goto _exit; for(i = 0; i < packetSequenceList_.size(); i++) { qDebug("sendQ[%d]: rptCnt = %d, rptSz = %d, usecDelay = %ld", i, packetSequenceList_.at(i)->repeatCount_, packetSequenceList_.at(i)->repeatSize_, packetSequenceList_.at(i)->usecDelay_); qDebug("sendQ[%d]: pkts = %ld, usecDuration = %ld", i, packetSequenceList_.at(i)->packets_, packetSequenceList_.at(i)->usecDuration_); } lastStats_ = *stats_; // used for stream stats state_ = kRunning; i = 0; while (i < packetSequenceList_.size()) { _restart: int rptSz = packetSequenceList_.at(i)->repeatSize_; int rptCnt = packetSequenceList_.at(i)->repeatCount_; for (int j = 0; j < rptCnt; j++) { for (int k = 0; k < rptSz; k++) { int ret; PacketSequence *seq = packetSequenceList_.at(i+k); #ifdef Q_OS_WIN32 TimeStamp ovrStart, ovrEnd; if (seq->usecDuration_ <= long(1e6)) // 1s { getTimeStamp(&ovrStart); ret = pcap_sendqueue_transmit(handle_, seq->sendQueue_, kSyncTransmit); if (ret >= 0) { stats_->pkts += seq->packets_; stats_->bytes += seq->bytes_; getTimeStamp(&ovrEnd); overHead += seq->usecDuration_ - udiffTimeStamp(&ovrStart, &ovrEnd); Q_ASSERT(overHead <= 0); } if (stop_) ret = -2; } else { ret = sendQueueTransmit(handle_, seq->sendQueue_, overHead, kSyncTransmit); } #else ret = sendQueueTransmit(handle_, seq->sendQueue_, overHead, kSyncTransmit); #endif if (ret >= 0) { long usecs = seq->usecDelay_ + overHead; if (usecs > 0) { (*udelayFn_)(usecs); overHead = 0; } else overHead = usecs; } else { qDebug("error %d in sendQueueTransmit()", ret); qDebug("overHead = %ld", overHead); stop_ = false; goto _exit; } } } // Move to the next Packet Set i += rptSz; } if (returnToQIdx_ >= 0) { long usecs = loopDelay_ + overHead; if (usecs > 0) { (*udelayFn_)(usecs); overHead = 0; } else overHead = usecs; i = returnToQIdx_; goto _restart; } _exit: if (trackStreamStats_) updateStreamStats(); state_ = kFinished; } void PcapTxThread::start() { // FIXME: return error if (state_ == kRunning) { qWarning("Transmit start requested but is already running!"); return; } state_ = kNotStarted; QThread::start(); while (state_ == kNotStarted) QThread::msleep(10); } void PcapTxThread::stop() { if (state_ == kRunning) { stop_ = true; while (state_ == kRunning) QThread::msleep(10); } else { // FIXME: return error qWarning("Transmit stop requested but is not running!"); return; } } bool PcapTxThread::isRunning() { return (state_ == kRunning); } int PcapTxThread::sendQueueTransmit(pcap_t *p, pcap_send_queue *queue, long &overHead, int sync) { TimeStamp ovrStart, ovrEnd; struct timeval ts; struct pcap_pkthdr *hdr = (struct pcap_pkthdr*) queue->buffer; char *end = queue->buffer + queue->len; ts = hdr->ts; getTimeStamp(&ovrStart); while((char*) hdr < end) { uchar *pkt = (uchar*)hdr + sizeof(*hdr); int pktLen = hdr->caplen; if (sync) { long usec = (hdr->ts.tv_sec - ts.tv_sec) * 1000000 + (hdr->ts.tv_usec - ts.tv_usec); getTimeStamp(&ovrEnd); overHead -= udiffTimeStamp(&ovrStart, &ovrEnd); Q_ASSERT(overHead <= 0); usec += overHead; if (usec > 0) { (*udelayFn_)(usec); overHead = 0; } else overHead = usec; ts = hdr->ts; getTimeStamp(&ovrStart); } Q_ASSERT(pktLen > 0); pcap_sendpacket(p, pkt, pktLen); stats_->pkts++; stats_->bytes += pktLen; // Step to the next packet in the buffer hdr = (struct pcap_pkthdr*) (pkt + pktLen); pkt = (uchar*) ((uchar*)hdr + sizeof(*hdr)); // FIXME: superfluous? if (stop_) { return -2; } } return 0; } void PcapTxThread::updateStreamStats() { // If no packets in list, nothing to be done if (!packetListSize_) return; // Get number of tx packets sent during last transmit quint64 pkts = stats_->pkts > lastStats_.pkts ? stats_->pkts - lastStats_.pkts : stats_->pkts + (ULLONG_MAX - lastStats_.pkts); // Calculate - // number of complete repeats of packetList_ // => each PacketSet in the packetList is repeated these many times // number of pkts sent in last partial repeat of packetList_ // - This encompasses 0 or more potentially partial PacketSets // XXX: Note for the above, we consider a PacketSet to include its // own repeats within itself int c = pkts/packetListSize_; int d = pkts%packetListSize_; qDebug("%s:", __FUNCTION__); qDebug("txPkts = %" PRIu64, pkts); qDebug("packetListSize_ = %" PRIu64, packetListSize_); qDebug("c = %d, d = %d\n", c, d); int i; if (!c) goto _last_repeat; i = 0; while (i < packetSequenceList_.size()) { PacketSequence *seq = packetSequenceList_.at(i); int rptSz = seq->repeatSize_; int rptCnt = seq->repeatCount_; for (int k = 0; k < rptSz; k++) { seq = packetSequenceList_.at(i+k); StreamStatsIterator iter(seq->streamStatsMeta_); while (iter.hasNext()) { iter.next(); uint guid = iter.key(); StreamStatsTuple ssm = iter.value(); streamStats_[guid].tx_pkts += c * rptCnt * ssm.tx_pkts; streamStats_[guid].tx_bytes += c * rptCnt * ssm.tx_bytes; } } // Move to the next Packet Set i += rptSz; } _last_repeat: if (!d) goto _done; i = 0; while (i < packetSequenceList_.size()) { PacketSequence *seq = packetSequenceList_.at(i); int rptSz = seq->repeatSize_; int rptCnt = seq->repeatCount_; for (int j = 0; j < rptCnt; j++) { for (int k = 0; k < rptSz; k++) { seq = packetSequenceList_.at(i+k); Q_ASSERT(seq->packets_); if (d >= seq->packets_) { // All packets of this seq were sent StreamStatsIterator iter(seq->streamStatsMeta_); while (iter.hasNext()) { iter.next(); uint guid = iter.key(); StreamStatsTuple ssm = iter.value(); streamStats_[guid].tx_pkts += ssm.tx_pkts; streamStats_[guid].tx_bytes += ssm.tx_bytes; } d -= seq->packets_; } else { // (d < seq->packets_) // not all packets of this seq were sent, so we need to // traverse this seq upto 'd' pkts, parse guid from the // packet and update streamStats struct pcap_pkthdr *hdr = (struct pcap_pkthdr*) seq->sendQueue_->buffer; char *end = seq->sendQueue_->buffer + seq->sendQueue_->len; while(d && ((char*) hdr < end)) { uchar *pkt = (uchar*)hdr + sizeof(*hdr); uint guid; if (SignProtocol::packetGuid(pkt, hdr->caplen, &guid)) { streamStats_[guid].tx_pkts++; streamStats_[guid].tx_bytes += hdr->caplen; } // Step to the next packet in the buffer hdr = (struct pcap_pkthdr*) (pkt + hdr->caplen); d--; } Q_ASSERT(d == 0); goto _done; } } } // Move to the next Packet Set i += rptSz; } _done: return; } void PcapTxThread::udelay(unsigned long usec) { #if defined(Q_OS_WIN32) LARGE_INTEGER tgtTicks; LARGE_INTEGER curTicks; QueryPerformanceCounter(&curTicks); tgtTicks.QuadPart = curTicks.QuadPart + (usec*gTicksFreq)/1000000; while (curTicks.QuadPart < tgtTicks.QuadPart) QueryPerformanceCounter(&curTicks); #elif defined(Q_OS_LINUX) struct timeval delay, target, now; //qDebug("usec delay = %ld", usec); delay.tv_sec = 0; delay.tv_usec = usec; while (delay.tv_usec >= 1000000) { delay.tv_sec++; delay.tv_usec -= 1000000; } gettimeofday(&now, NULL); timeradd(&now, &delay, &target); do { gettimeofday(&now, NULL); } while (timercmp(&now, &target, <)); #else QThread::usleep(usec); #endif } ostinato-0.9/server/pcaptxthread.h000066400000000000000000000047531321315675200174060ustar00rootroot00000000000000/* Copyright (C) 2010-2016 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _PCAP_TX_THREAD_H #define _PCAP_TX_THREAD_H #include "abstractport.h" #include "packetsequence.h" #include "statstuple.h" #include #include class PcapTxThread: public QThread { public: PcapTxThread(const char *device); ~PcapTxThread(); bool setRateAccuracy(AbstractPort::Accuracy accuracy); bool setStreamStatsTracking(bool enable); void clearPacketList(); void loopNextPacketSet(qint64 size, qint64 repeats, long repeatDelaySec, long repeatDelayNsec); bool appendToPacketList(long sec, long usec, const uchar *packet, int length); void setPacketListLoopMode(bool loop, quint64 secDelay, quint64 nsecDelay); void setHandle(pcap_t *handle); void setStats(StatsTuple *stats); const StreamStats& streamStats(); void clearStreamStats(); void run(); void start(); void stop(); bool isRunning(); private: enum State { kNotStarted, kRunning, kFinished }; static void udelay(unsigned long usec); int sendQueueTransmit(pcap_t *p, pcap_send_queue *queue, long &overHead, int sync); void updateStreamStats(); // Intermediate state variables used while building the packet list PacketSequence *currentPacketSequence_; int repeatSequenceStart_; quint64 repeatSize_; quint64 packetCount_; QList packetSequenceList_; quint64 packetListSize_; // count of pkts in packet List including repeats int returnToQIdx_; quint64 loopDelay_; void (*udelayFn_)(unsigned long); bool usingInternalHandle_; pcap_t *handle_; volatile bool stop_; volatile State state_; bool trackStreamStats_; StatsTuple *stats_; StatsTuple lastStats_; StreamStats streamStats_; }; #endif ostinato-0.9/server/portmanager.cpp000066400000000000000000000107001321315675200175560ustar00rootroot00000000000000/* Copyright (C) 2010-2012 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "portmanager.h" #include "bsdport.h" #include "linuxport.h" #include "pcapport.h" #include "settings.h" #include "winpcapport.h" #include #include PortManager *PortManager::instance_ = NULL; PortManager::PortManager() { int i; pcap_if_t *deviceList; pcap_if_t *device; char errbuf[PCAP_ERRBUF_SIZE]; AbstractPort::Accuracy txRateAccuracy; qDebug("Retrieving the device list from the local machine\n"); if (pcap_findalldevs(&deviceList, errbuf) == -1) qDebug("Error in pcap_findalldevs_ex: %s\n", errbuf); txRateAccuracy = rateAccuracy(); for(device = deviceList, i = 0; device != NULL; device = device->next, i++) { AbstractPort *port; qDebug("%d. %s", i, device->name); if (device->description) qDebug(" (%s)\n", device->description); #if defined(Q_OS_WIN32) if (!filterAcceptsPort(device->description)) #else if (!filterAcceptsPort(device->name)) #endif { qDebug("%s (%s) rejected by filter. Skipping!", device->name, device->description); i--; continue; } #if defined(Q_OS_WIN32) port = new WinPcapPort(i, device->name); #elif defined(Q_OS_LINUX) port = new LinuxPort(i, device->name); #elif defined(Q_OS_BSD4) port = new BsdPort(i, device->name); #else port = new PcapPort(i, device->name); #endif if (!port->isUsable()) { qDebug("%s: unable to open %s. Skipping!", __FUNCTION__, device->name); delete port; i--; continue; } if (!port->setRateAccuracy(txRateAccuracy)) qWarning("failed to set rateAccuracy (%d)", txRateAccuracy); portList_.append(port); } pcap_freealldevs(deviceList); foreach(AbstractPort *port, portList_) port->init(); return; } PortManager::~PortManager() { while (!portList_.isEmpty()) delete portList_.takeFirst(); } PortManager* PortManager::instance() { if (!instance_) instance_ = new PortManager; return instance_; } AbstractPort::Accuracy PortManager::rateAccuracy() { QString rateAccuracy = appSettings->value(kRateAccuracyKey, kRateAccuracyDefaultValue).toString(); if (rateAccuracy == "High") return AbstractPort::kHighAccuracy; else if (rateAccuracy == "Low") return AbstractPort::kLowAccuracy; else qWarning("Unsupported RateAccuracy setting - %s", qPrintable(rateAccuracy)); return AbstractPort::kHighAccuracy; } bool PortManager::filterAcceptsPort(const char *name) { QRegExp pattern; QStringList includeList = appSettings->value(kPortListIncludeKey) .toStringList(); QStringList excludeList = appSettings->value(kPortListExcludeKey) .toStringList(); pattern.setPatternSyntax(QRegExp::Wildcard); // An empty (or missing) includeList accepts all ports // NOTE: A blank "IncludeList=" is read as a stringlist with one // string which is empty => treat it same as an empty stringlist if (includeList.isEmpty() || (includeList.size() == 1 && includeList.at(0).isEmpty())) goto _include_pass; foreach (QString str, includeList) { pattern.setPattern(str); if (pattern.exactMatch(name)) goto _include_pass; } // IncludeList is not empty and port did not match a pattern return false; _include_pass: foreach (QString str, excludeList) { pattern.setPattern(str); if (pattern.exactMatch(name)) return false; } // Port did not match a pattern in ExcludeList return true; } ostinato-0.9/server/portmanager.h000066400000000000000000000022501321315675200172240ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _SERVER_PORT_MANAGER_H #define _SERVER_PORT_MANAGER_H #include #include "abstractport.h" class PortManager { public: PortManager(); ~PortManager(); int portCount() { return portList_.size(); } AbstractPort* port(int id) { return portList_[id]; } static PortManager* instance(); private: AbstractPort::Accuracy rateAccuracy(); bool filterAcceptsPort(const char *name); private: QList portList_; static PortManager *instance_; }; #endif ostinato-0.9/server/settings.h000066400000000000000000000022001321315675200165400ustar00rootroot00000000000000/* Copyright (C) 2014 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _SETTINGS_H #define _SETTINGS_H #include #include extern QSettings *appSettings; // // General Section Keys // const QString kRateAccuracyKey("RateAccuracy"); const QString kRateAccuracyDefaultValue("High"); // // RpcServer Section Keys // const QString kRpcServerAddress("RpcServer/Address"); // // PortList Section Keys // const QString kPortListIncludeKey("PortList/Include"); const QString kPortListExcludeKey("PortList/Exclude"); #endif ostinato-0.9/server/statstuple.h000066400000000000000000000014521321315675200171200ustar00rootroot00000000000000/* Copyright (C) 2016 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _STATS_TUPLE_H #define _STATS_TUPLE_H #include struct StatsTuple { quint64 pkts; quint64 bytes; }; #endif ostinato-0.9/server/streamstats.h000066400000000000000000000017271321315675200172670ustar00rootroot00000000000000/* Copyright (C) 2016 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _STREAM_STATS_H #define _STREAM_STATS_H #include struct StreamStatsTuple { quint64 rx_pkts; quint64 rx_bytes; quint64 tx_pkts; quint64 tx_bytes; }; typedef QHash StreamStats; typedef QHashIterator StreamStatsIterator; #endif ostinato-0.9/server/timestamp.h000066400000000000000000000036051321315675200167150ustar00rootroot00000000000000/* Copyright (C) 2010-2016 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _TIMESTAMP_H #define _TIMESTAMP_H #include #if defined(Q_OS_LINUX) typedef struct timeval TimeStamp; static void inline getTimeStamp(TimeStamp *stamp) { gettimeofday(stamp, NULL); } // Returns time diff in usecs between end and start static long inline udiffTimeStamp(const TimeStamp *start, const TimeStamp *end) { struct timeval diff; long usecs; timersub(end, start, &diff); usecs = diff.tv_usec; if (diff.tv_sec) usecs += diff.tv_sec*1e6; return usecs; } #elif defined(Q_OS_WIN32) static quint64 gTicksFreq; typedef LARGE_INTEGER TimeStamp; static void inline getTimeStamp(TimeStamp* stamp) { QueryPerformanceCounter(stamp); } static long inline udiffTimeStamp(const TimeStamp *start, const TimeStamp *end) { if (end->QuadPart >= start->QuadPart) return (end->QuadPart - start->QuadPart)*long(1e6)/gTicksFreq; else { // FIXME: incorrect! what's the max value for this counter before // it rolls over? return (start->QuadPart)*long(1e6)/gTicksFreq; } } #else typedef int TimeStamp; static void inline getTimeStamp(TimeStamp*) {} static long inline udiffTimeStamp(const TimeStamp*, const TimeStamp*) { return 0; } #endif #endif ostinato-0.9/server/winpcapport.cpp000066400000000000000000000145701321315675200176160ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #include "winpcapport.h" #include #include #ifdef Q_OS_WIN32 const uint OID_GEN_MEDIA_CONNECT_STATUS = 0x00010114; WinPcapPort::WinPcapPort(int id, const char *device) : PcapPort(id, device) { monitorRx_->stop(); monitorTx_->stop(); monitorRx_->wait(); monitorTx_->wait(); delete monitorRx_; delete monitorTx_; monitorRx_ = new PortMonitor(device, kDirectionRx, &stats_); monitorTx_ = new PortMonitor(device, kDirectionTx, &stats_); adapter_ = PacketOpenAdapter((CHAR*)device); if (!adapter_) qFatal("Unable to open adapter %s", device); linkStateOid_ = (PPACKET_OID_DATA) malloc(sizeof(PACKET_OID_DATA) + sizeof(uint)); if (!linkStateOid_) qFatal("failed to alloc oidData"); data_.set_is_exclusive_control(hasExclusiveControl()); minPacketSetSize_ = 256; } WinPcapPort::~WinPcapPort() { } OstProto::LinkState WinPcapPort::linkState() { memset(linkStateOid_, 0, sizeof(PACKET_OID_DATA) + sizeof(uint)); linkStateOid_->Oid = OID_GEN_MEDIA_CONNECT_STATUS; linkStateOid_->Length = sizeof(uint); if (PacketRequest(adapter_, 0, linkStateOid_)) { uint state; if (linkStateOid_->Length == sizeof(state)) { memcpy((void*)&state, (void*)linkStateOid_->Data, linkStateOid_->Length); if (state == 0) linkState_ = OstProto::LinkStateUp; else if (state == 1) linkState_ = OstProto::LinkStateDown; } } return linkState_; } bool WinPcapPort::hasExclusiveControl() { QString portName(adapter_->Name + strlen("\\Device\\NPF_")); QString bindConfigFilePath(QCoreApplication::applicationDirPath() + "/bindconfig.exe"); int exitCode; qDebug("%s: %s", __FUNCTION__, portName.toAscii().constData()); if (!QFile::exists(bindConfigFilePath)) return false; exitCode = QProcess::execute(bindConfigFilePath, QStringList() << "comp" << portName); qDebug("%s: exit code %d", __FUNCTION__, exitCode); if (exitCode == 0) return true; else return false; } bool WinPcapPort::setExclusiveControl(bool exclusive) { QString portName(adapter_->Name + strlen("\\Device\\NPF_")); QString bindConfigFilePath(QCoreApplication::applicationDirPath() + "/bindconfig.exe"); QString status; qDebug("%s: %s", __FUNCTION__, portName.toAscii().constData()); if (!QFile::exists(bindConfigFilePath)) return false; status = exclusive ? "disable" : "enable"; QProcess::execute(bindConfigFilePath, QStringList() << "comp" << portName << status); updateNotes(); return (exclusive == hasExclusiveControl()); } WinPcapPort::PortMonitor::PortMonitor(const char *device, Direction direction, AbstractPort::PortStats *stats) : PcapPort::PortMonitor(device, direction, stats) { if (handle()) pcap_setmode(handle(), MODE_STAT); } void WinPcapPort::PortMonitor::run() { struct timeval lastTs; quint64 lastTxPkts = 0; quint64 lastTxBytes = 0; qDebug("in %s", __PRETTY_FUNCTION__); lastTs.tv_sec = 0; lastTs.tv_usec = 0; while (!stop_) { int ret; struct pcap_pkthdr *hdr; const uchar *data; ret = pcap_next_ex(handle(), &hdr, &data); switch (ret) { case 1: { quint64 pkts = *((quint64*)(data + 0)); quint64 bytes = *((quint64*)(data + 8)); // TODO: is it 12 or 16? bytes -= pkts * 12; uint usec = (hdr->ts.tv_sec - lastTs.tv_sec) * 1000000 + (hdr->ts.tv_usec - lastTs.tv_usec); switch (direction()) { case kDirectionRx: stats_->rxPkts += pkts; stats_->rxBytes += bytes; stats_->rxPps = (pkts * 1000000) / usec; stats_->rxBps = (bytes * 1000000) / usec; break; case kDirectionTx: if (isDirectional()) { stats_->txPkts += pkts; stats_->txBytes += bytes; } else { // Assuming stats_->txXXX are updated externally quint64 txPkts = stats_->txPkts; quint64 txBytes = stats_->txBytes; pkts = txPkts - lastTxPkts; bytes = txBytes - lastTxBytes; lastTxPkts = txPkts; lastTxBytes = txBytes; } stats_->txPps = (pkts * 1000000) / usec; stats_->txBps = (bytes * 1000000) / usec; break; default: Q_ASSERT(false); } break; } case 0: //qDebug("%s: timeout. continuing ...", __PRETTY_FUNCTION__); continue; case -1: qWarning("%s: error reading packet (%d): %s", __PRETTY_FUNCTION__, ret, pcap_geterr(handle())); break; case -2: qWarning("%s: error reading packet (%d): %s", __PRETTY_FUNCTION__, ret, pcap_geterr(handle())); break; default: qFatal("%s: Unexpected return value %d", __PRETTY_FUNCTION__, ret); } lastTs.tv_sec = hdr->ts.tv_sec; lastTs.tv_usec = hdr->ts.tv_usec; if (!stop_) QThread::msleep(1000); } } #endif ostinato-0.9/server/winpcapport.h000066400000000000000000000025511321315675200172570ustar00rootroot00000000000000/* Copyright (C) 2010 Srivats P. This file is part of "Ostinato" This 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 3 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, see */ #ifndef _SERVER_WIN_PCAP_PORT_H #define _SERVER_WIN_PCAP_PORT_H #include #ifdef Q_OS_WIN32 #include "pcapport.h" #include class WinPcapPort : public PcapPort { public: WinPcapPort(int id, const char *device); ~WinPcapPort(); virtual OstProto::LinkState linkState(); virtual bool hasExclusiveControl(); virtual bool setExclusiveControl(bool exclusive); protected: class PortMonitor: public PcapPort::PortMonitor { public: PortMonitor(const char *device, Direction direction, AbstractPort::PortStats *stats); void run(); }; private: LPADAPTER adapter_; PPACKET_OID_DATA linkStateOid_ ; }; #endif #endif ostinato-0.9/test/000077500000000000000000000000001321315675200142065ustar00rootroot00000000000000ostinato-0.9/test/main.cpp000066400000000000000000000060411321315675200156370ustar00rootroot00000000000000 #include "ostprotolib.h" #include "pcapfileformat.h" #include "protocol.pb.h" #include "protocolmanager.h" #include "settings.h" #include #include #include #include extern ProtocolManager *OstProtocolManager; QSettings *appSettings; #if defined(Q_OS_WIN32) QString kGzipPathDefaultValue; QString kDiffPathDefaultValue; QString kAwkPathDefaultValue; #endif /* * Dummy Stuff for successful linking */ char *version = ""; char *revision = ""; quint64 getDeviceMacAddress( int /*portId*/, int /*streamId*/, int /*frameIndex*/) { return 0; } quint64 getNeighborMacAddress( int /*portId*/, int /*streamId*/, int /*frameIndex*/) { return 0; } int usage(int /*argc*/, char* argv[]) { printf("usage:\n"); printf("%s \n", argv[0]); printf("command -\n"); printf(" importpcap\n"); return 255; } /* End of dummy stuff */ int testImportPcap(int argc, char* argv[]) { bool isOk; QString error; if (argc != 3) { printf("usage:\n"); printf("%s importpcap \n", argv[0]); return 255; } OstProto::StreamConfigList streams; QString inFile(argv[2]); isOk = pcapFileFormat.open(inFile, streams, error); if (!error.isEmpty()) { printf("%s: %s\n", inFile.toAscii().constData(), error.toAscii().constData()); } if (!isOk) return 1; return 0; } int main(int argc, char* argv[]) { QCoreApplication app(argc, argv); int exitCode = 0; // app init starts ... #if defined(Q_OS_WIN32) kGzipPathDefaultValue = app.applicationDirPath() + "/gzip.exe"; kDiffPathDefaultValue = app.applicationDirPath() + "/diff.exe"; kAwkPathDefaultValue = app.applicationDirPath() + "/gawk.exe"; #endif app.setApplicationName("Ostinato"); app.setOrganizationName("Ostinato"); OstProtocolManager = new ProtocolManager(); /* (Portable Mode) If we have a .ini file in the same directory as the executable, we use that instead of the platform specific location and format for the settings */ QString portableIni = QCoreApplication::applicationDirPath() + "/ostinato.ini"; if (QFile::exists(portableIni)) appSettings = new QSettings(portableIni, QSettings::IniFormat); else appSettings = new QSettings(); OstProtoLib::setExternalApplicationPaths( appSettings->value(kTsharkPathKey, kTsharkPathDefaultValue).toString(), appSettings->value(kGzipPathKey, kGzipPathDefaultValue).toString(), appSettings->value(kDiffPathKey, kDiffPathDefaultValue).toString(), appSettings->value(kAwkPathKey, kAwkPathDefaultValue).toString()); // ... app init finished // // identify and run specified test // if (argc < 2) exitCode = usage(argc, argv); else if (strcmp(argv[1],"importpcap") == 0) exitCode = testImportPcap(argc, argv); else exitCode = usage(argc, argv); delete appSettings; return exitCode; } ostinato-0.9/test/test.pro000066400000000000000000000022011321315675200157020ustar00rootroot00000000000000TEMPLATE = app CONFIG += qt console QT += xml network script INCLUDEPATH += "../rpc/" "../common/" "../client" win32 { LIBS += -lwpcap -lpacket CONFIG(debug, debug|release) { LIBS += -L"../common/debug" -lostprotogui -lostproto LIBS += -L"../rpc/debug" -lpbrpc POST_TARGETDEPS += \ "../common/debug/libostprotogui.a" \ "../common/debug/libostproto.a" \ "../rpc/debug/libpbrpc.a" } else { LIBS += -L"../common/release" -lostprotogui -lostproto LIBS += -L"../rpc/release" -lpbrpc POST_TARGETDEPS += \ "../common/release/libostprotogui.a" \ "../common/release/libostproto.a" \ "../rpc/release/libpbrpc.a" } } else { LIBS += -lpcap LIBS += -L"../common" -lostprotogui -lostproto LIBS += -L"../rpc" -lpbrpc POST_TARGETDEPS += \ "../common/libostprotogui.a" \ "../common/libostproto.a" \ "../rpc/libpbrpc.a" } LIBS += -lprotobuf LIBS += -L"../extra/qhexedit2/$(OBJECTS_DIR)/" -lqhexedit2 HEADERS += SOURCES += main.cpp QMAKE_DISTCLEAN += object_script.* include(../install.pri) ostinato-0.9/version.pri000066400000000000000000000015551321315675200154360ustar00rootroot00000000000000APP_VERSION = 0.9 APP_REVISION = $(shell git rev-parse --short=12 --verify HEAD) #uncomment the below line in a source package and fill-in the correct revision #APP_REVISION = @ ver_info { APP_VERSION_FILE = version.cpp revtarget.target = $$APP_VERSION_FILE win32:revtarget.commands = echo "const char *version = \"$$APP_VERSION\";" \ "const char *revision = \"$$APP_REVISION\";" \ > $$APP_VERSION_FILE unix:revtarget.commands = echo \ "\"const char *version = \\\"$$APP_VERSION\\\";" \ "const char *revision = \\\"$$APP_REVISION\\\";\"" \ > $$APP_VERSION_FILE revtarget.depends = $$SOURCES $$HEADERS $$FORMS $$POST_TARGETDEPS SOURCES += $$APP_VERSION_FILE QMAKE_EXTRA_TARGETS += revtarget POST_TARGETDEPS += $$APP_VERSION_FILE QMAKE_DISTCLEAN += $$APP_VERSION_FILE }