ostinato-0.7.1/0000700000175300010010000000000012537544130012675 5ustar srivatspNoneostinato-0.7.1/.vimrc0000700000175300010010000000011612537544000014013 0ustar srivatspNoneset shiftwidth=4 set tabstop=8 set softtabstop=4 set expandtab set cindent ostinato-0.7.1/binding/0000700000175300010010000000000012537544000014303 5ustar srivatspNoneostinato-0.7.1/binding/binding.pro0000700000175300010010000000007312537544000016442 0ustar srivatspNoneTEMPLATE = lib CONFIG += pkg_info include(../version.pri) ostinato-0.7.1/binding/core.py0000700000175300010010000000446312537544000015617 0ustar srivatspNone# 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 import os from rpc import OstinatoRpcChannel, OstinatoRpcController, RpcError import protocols.protocol_pb2 as ost_pb from __init__ import __version__ class DroneProxy(object): def __init__(self, host_name, port_number=7878): self.host = host_name self.port = port_number self.channel = OstinatoRpcChannel() self.stub = ost_pb.OstService_Stub(self.channel) self.void = ost_pb.Void() for method in self.stub.GetDescriptor().methods: fn = lambda request=self.void, method_name=method.name: \ self.callRpcMethod(method_name, request) self.__dict__[method.name] = fn def hostName(self): return self.host def portNumber(self): return self.port def connect(self): self.channel.connect(self.host, self.port) ver = ost_pb.VersionInfo() ver.client_name = 'python-ostinato' ver.version = __version__ compat = self.checkVersion(ver) if compat.result == ost_pb.VersionCompatibility.kIncompatible: raise RpcError('incompatible version %s (%s)' % (ver.version, compat.notes)) def disconnect(self): self.channel.disconnect() def callRpcMethod(self, method_name, request): controller = OstinatoRpcController() ost_pb.OstService_Stub.__dict__[method_name]( self.stub, controller, request, None) return controller.response def saveCaptureBuffer(self, buffer, file_name): f= open(file_name, 'wb') f.write(buffer) f.flush() os.fsync(f.fileno()) f.close() ostinato-0.7.1/binding/example.py0000700000175300010010000001315212537544000016315 0ustar srivatspNone#! /usr/bin/env python # standard modules import logging import os import sys import time # ostinato modules # (user scripts using the installed package should prepend ostinato. i.e # ostinato.core and ostinato.protocols) from core import ost_pb, DroneProxy from protocols.mac_pb2 import mac from protocols.ip4_pb2 import ip4, Ip4 # initialize defaults use_defaults = False host_name = '127.0.0.1' tx_port_number = 0 rx_port_number = 0 # setup logging log = logging.getLogger(__name__) logging.basicConfig(level=logging.INFO) # command-line option/arg processing if len(sys.argv) > 1: if sys.argv[1] in ('-d', '--use-defaults'): use_defaults = True if sys.argv[1] in ('-h', '--help'): print('%s [OPTION]...' % (sys.argv[0])) print('Options:') print(' -d --use-defaults run using default values') print(' -h --help show this help') sys.exit(0) print('') print('This example expects the following topology -') print('') print(' +-------+ +-------+') print(' | |Tx--->----| |') print(' | Drone | | DUT |') print(' | |Rx---<----| |') print(' +-------+ +-------+') print('') print('Drone has 2 ports connected to DUT. Packets sent on the Tx port') print('are expected to be received back on the Rx port') print('') print('An easy way to simulate the above topology is to select the loopback') print('port as both Tx and Rx ports') print('') if not use_defaults: s = raw_input('Drone\'s Hostname/IP [%s]: ' % (host_name)) host_name = s or host_name drone = DroneProxy(host_name) try: # connect to drone log.info('connecting to drone(%s:%d)' % (drone.hostName(), drone.portNumber())) drone.connect() # retreive port id list log.info('retreiving port list') port_id_list = drone.getPortIdList() # retreive port config list log.info('retreiving port config for all ports') port_config_list = drone.getPortConfig(port_id_list) if len(port_config_list.port) == 0: log.warning('drone has no ports!') sys.exit(1) # print port list and get tx/rx port id print('Port List') print('---------') for port in port_config_list.port: print('%d.%s (%s)' % (port.port_id.id, port.name, port.description)) # use a loopback port as default tx/rx port if ('lo' in port.name or 'loopback' in port.description.lower()): tx_port_number = port.port_id.id rx_port_number = port.port_id.id if not use_defaults: p = raw_input('Tx Port Id [%d]: ' % (tx_port_number)) if p: tx_port_number = int(p) p = raw_input('Rx Port Id [%d]: ' % (rx_port_number)) if p: rx_port_number = int(p) tx_port = ost_pb.PortIdList() tx_port.port_id.add().id = tx_port_number; rx_port = ost_pb.PortIdList() rx_port.port_id.add().id = rx_port_number; # add a stream stream_id = ost_pb.StreamIdList() stream_id.port_id.CopyFrom(tx_port.port_id[0]) stream_id.stream_id.add().id = 1 log.info('adding tx_stream %d' % stream_id.stream_id[0].id) drone.addStream(stream_id) # configure the stream stream_cfg = ost_pb.StreamConfigList() stream_cfg.port_id.CopyFrom(tx_port.port_id[0]) s = stream_cfg.stream.add() s.stream_id.id = stream_id.stream_id[0].id s.core.is_enabled = True s.control.num_packets = 5 # setup stream protocols as mac:eth2:ip4:udp:payload p = s.protocol.add() p.protocol_id.id = ost_pb.Protocol.kMacFieldNumber p.Extensions[mac].dst_mac = 0x001122334455 p.Extensions[mac].src_mac = 0x00aabbccddee p = s.protocol.add() p.protocol_id.id = ost_pb.Protocol.kEth2FieldNumber p = s.protocol.add() p.protocol_id.id = ost_pb.Protocol.kIp4FieldNumber # reduce typing by creating a shorter reference to p.Extensions[ip4] ip = p.Extensions[ip4] ip.src_ip = 0x01020304 ip.dst_ip = 0x05060708 ip.dst_ip_mode = Ip4.e_im_inc_host s.protocol.add().protocol_id.id = ost_pb.Protocol.kUdpFieldNumber s.protocol.add().protocol_id.id = ost_pb.Protocol.kPayloadFieldNumber log.info('configuring tx_stream %d' % stream_id.stream_id[0].id) drone.modifyStream(stream_cfg) # clear tx/rx stats log.info('clearing tx/rx stats') drone.clearStats(tx_port) drone.clearStats(rx_port) # start capture and transmit log.info('starting capture') drone.startCapture(rx_port) log.info('starting transmit') drone.startTransmit(tx_port) # wait for transmit to finish log.info('waiting for transmit to finish ...') time.sleep(7) # stop transmit and capture log.info('stopping transmit') drone.stopTransmit(tx_port) log.info('stopping capture') drone.stopCapture(rx_port) # get tx/rx stats log.info('retreiving stats') tx_stats = drone.getStats(tx_port) rx_stats = drone.getStats(rx_port) #log.info('--> (tx_stats)' + tx_stats.__str__()) #log.info('--> (rx_stats)' + rx_stats.__str__()) log.info('tx pkts = %d, rx pkts = %d' % (tx_stats.port_stats[0].tx_pkts, rx_stats.port_stats[0].rx_pkts)) # retrieve and dump received packets log.info('getting Rx capture buffer') buff = drone.getCaptureBuffer(rx_port.port_id[0]) drone.saveCaptureBuffer(buff, 'capture.pcap') log.info('dumping Rx capture buffer') os.system('tshark -r capture.pcap') os.remove('capture.pcap') # delete streams log.info('deleting tx_stream %d' % stream_id.stream_id[0].id) drone.deleteStream(stream_id) # bye for now drone.disconnect() except Exception as ex: log.exception(ex) sys.exit(1) ostinato-0.7.1/binding/LICENSE.txt0000700000175300010010000010451312537544000016135 0ustar srivatspNone 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.7.1/binding/protocols/0000700000175300010010000000000012537544000016327 5ustar srivatspNoneostinato-0.7.1/binding/protocols/__init__.py0000700000175300010010000000127412537544000020447 0ustar srivatspNone# 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 ostinato-0.7.1/binding/README.txt0000700000175300010010000000046112537544000016005 0ustar srivatspNone=============== python-ostinato =============== python-ostinato provides python bindings for Ostinato. Ostinato is a network packet/traffic generator and analyzer. It aims to be "Wireshark in Reverse" and become complementary to Wireshark. Documentation is available in the wiki at http://ostinato.org ostinato-0.7.1/binding/rpc.py0000700000175300010010000001401412537544000015444 0ustar srivatspNone# 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 from google.protobuf.message import EncodeError, DecodeError from google.protobuf.service import RpcChannel from google.protobuf.service import RpcController import logging import socket import struct import sys class PeerClosedConnError(Exception): def __init__(self, msg): self.msg = msg def __str__(self): return self.msg class RpcError(Exception): def __init__(self, msg): self.msg = msg def __str__(self): return self.msg class RpcMismatchError(Exception): def __init__(self, msg, received, expected): self.msg = msg self.received = received self.expected = expected def __str__(self): return '%s - Expected method %d, Received method %d' % ( self.msg, self.expected, self.received) class OstinatoRpcController(RpcController): def __init__(self): super(OstinatoRpcController, self).__init__() class OstinatoRpcChannel(RpcChannel): def __init__(self): self.log = logging.getLogger(__name__) self.log.debug('opening socket') def connect(self, host, port): self.peer = '%s:%d' % (host, port) self.log.debug('connecting to %s', self.peer) try: self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.sock.connect((host, port)) except socket.error as e: error = 'ERROR: Unable to connect to Drone %s (%s)' % ( self.peer, str(e)) print(error) raise def disconnect(self): self.log.debug('closing socket') self.sock.close() def CallMethod(self, method, controller, request, response_class, done): MSG_HDR_SIZE = 8 MSG_TYPE_REQUEST = 1 MSG_TYPE_RESPONSE = 2 MSG_TYPE_BLOB = 3 MSG_TYPE_ERROR = 4 error = '' try: self.log.info('invoking RPC %s(%s): %s', method.name, type(request).__name__, response_class.__name__) self.log.debug('serializing request arg %s', request) req = request.SerializeToString() hdr = struct.pack('>HHI', MSG_TYPE_REQUEST, method.index, len(req)) self.log.debug('req.hdr = %r', hdr) self.sock.sendall(hdr + req) # receive and parse header self.log.debug('receiving response hdr') hdr = '' while len(hdr) < MSG_HDR_SIZE: chunk = self.sock.recv(MSG_HDR_SIZE - len(hdr)) if chunk == '': raise PeerClosedConnError('connection closed by peer') hdr = hdr + chunk (msg_type, method_index, resp_len) = struct.unpack('>HHI', hdr) self.log.debug('resp hdr: type = %d, method = %d, len = %d', msg_type, method_index, resp_len) # receive and parse the actual response message self.log.debug('receiving response data') resp = '' while len(resp) < resp_len: chunk = self.sock.recv(resp_len - len(resp)) if chunk == '': raise PeerClosedConnError('connection closed by peer') resp = resp + chunk # verify response method is same as the one requested if method_index != method.index: raise RpcMismatchError('RPC mismatch', expected = method.index, received = method_index) if msg_type == MSG_TYPE_RESPONSE: response = response_class() response.ParseFromString(resp) self.log.debug('parsed response %s', response) elif msg_type == MSG_TYPE_BLOB: response = resp elif msg_type == MSG_TYPE_ERROR: raise RpcError(unicode(resp, 'utf-8')) else: raise RpcError('unknown RPC msg type %d' % msg_type) controller.response = response except socket.error as e: error = 'ERROR: RPC %s() to Drone %s failed (%s)' % ( method.name, self.peer, e) self.log.exception(error+e) raise except PeerClosedConnError as e: error = 'ERROR: Drone %s closed connection receiving reply ' \ 'for RPC %s() (%s)' % ( self.peer, method.name, e) self.log.exception(error) raise except EncodeError as e: error = 'ERROR: Failed to serialize %s arg for RPC %s() ' \ 'to Drone %s (%s)' % ( type(request).__name__, method.name, self.peer, e) self.log.exception(error) raise except DecodeError as e: error = 'ERROR: Failed to parse %s response for RPC %s() ' \ 'from Drone %s (%s)' % ( type(response).__name__, method.name, self.peer, e) self.log.exception(error) raise except RpcMismatchError as e: error = 'ERROR: Rpc Mismatch for RPC %s() (%s)' % ( method.name, e) self.log.exception(error) raise except RpcError as e: error = 'ERROR: error received for RPC %s() (%s) ' % ( method.name, e) self.log.exception(error) raise finally: if error: print(error) ostinato-0.7.1/binding/setup.py0000700000175300010010000000557212537544000016031 0ustar srivatspNone#!/usr/bin/env python # 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 import json import os import shutil import sys from setuptools import Command, setup from setuptools.command.sdist import sdist as _sdist def read(fname): return open(os.path.join(os.path.dirname(__file__), fname)).read() def ensure_cwd(): if os.path.split(os.getcwd())[1] != 'binding': print('ERROR: This script needs to be run from the binding directory') print('Current Working Directory is %s' % os.getcwd()) sys.exit(1) class sdist(_sdist): def run(self): ensure_cwd() _sdist.run(self) class sdist_clean(Command): description = 'clean stuff generated by sdist' user_options = [] def initialize_options(self): return def finalize_options(self): return def run(self): ensure_cwd() shutil.rmtree('dist', ignore_errors = True) shutil.rmtree('python-ostinato.egg-info', ignore_errors = True) shutil.rmtree('python_ostinato.egg-info', ignore_errors = True) # ------- script starts from here ------- # with open(os.path.join(os.path.dirname(__file__), 'pkg_info.json')) as f: pkg_info = json.load(f) setup(name = 'python-ostinato', version = pkg_info['version'], author = 'Srivats P', author_email = 'pstavirs@gmail.com', license = "GPLv3+", url = 'http://ostinato.org', description = 'python-ostinato provides python bindings for the Ostinato network packet/traffic generator and analyzer', long_description = read('README.txt'), install_requires = ['protobuf>=2.3.0'], packages = ['ostinato', 'ostinato.protocols'], package_dir = {'ostinato': '.'}, package_data = {'ostinato': ['pkg_info.json', 'LICENSE.txt']}, platforms = ['Any'], classifiers = [ 'Development Status :: 4 - Beta', 'Programming Language :: Python :: 2.7', 'Intended Audience :: Telecommunications Industry', 'License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)', 'Topic :: Software Development :: Testing :: Traffic Generation', 'Topic :: System :: Networking'], cmdclass={ 'sdist': sdist, 'sdist_clean': sdist_clean}, ) ostinato-0.7.1/binding/__init__.py0000700000175300010010000000165312537544000016424 0ustar srivatspNone# 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 import json import logging from os.path import dirname with open(dirname(__file__) + '/pkg_info.json') as f: _info = json.load(f) __version__ = _info['version'] __revision__ = _info['revision'] __log__ = logging.getLogger(__name__) ostinato-0.7.1/client/0000700000175300010010000000000012537544000014147 5ustar srivatspNoneostinato-0.7.1/client/about.ui0000700000175300010010000001527112537544000015631 0ustar srivatspNone 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-2014 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.7.1/client/dumpview.cpp0000700000175300010010000002722612537544000016527 0ustar srivatspNone/* 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.7.1/client/dumpview.h0000700000175300010010000000406712537544000016172 0ustar srivatspNone/* 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 > 48) && (c < 126)) return true; else return false; } private: QRect mOffsetPaneTopRect; QRect mDumpPaneTopRect; QRect mAsciiPaneTopRect; int mSelectedRow, mSelectedCol; int mLineHeight; int mCharWidth; }; ostinato-0.7.1/client/hexlineedit.cpp0000700000175300010010000000446712537544000017173 0ustar srivatspNone/* 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.7.1/client/hexlineedit.h0000700000175300010010000000204412537544000016625 0ustar srivatspNone/* 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.7.1/client/icons/0000700000175300010010000000000012537544000015262 5ustar srivatspNoneostinato-0.7.1/client/icons/about.png0000700000175300010010000000201412537544000017102 0ustar srivatspNone‰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.7.1/client/icons/arrow_left.png0000700000175300010010000000053112537544000020136 0ustar srivatspNone‰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.7.1/client/icons/arrow_right.png0000700000175300010010000000053512537544000020325 0ustar srivatspNone‰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.7.1/client/icons/arrow_up.png0000700000175300010010000000056412537544000017636 0ustar srivatspNone‰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.7.1/client/icons/bullet_error.png0000700000175300010010000000070612537544000020476 0ustar srivatspNone‰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.7.1/client/icons/bullet_green.png0000700000175300010010000000044712537544000020447 0ustar srivatspNone‰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.7.1/client/icons/bullet_orange.png0000700000175300010010000000043312537544000020615 0ustar srivatspNone‰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.7.1/client/icons/bullet_white.png0000700000175300010010000000031112537544000020455 0ustar srivatspNone‰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.7.1/client/icons/bullet_yellow.png0000700000175300010010000000043712537544000020661 0ustar srivatspNone‰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.7.1/client/icons/control_play.png0000700000175300010010000000112012537544000020472 0ustar srivatspNone‰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.7.1/client/icons/control_stop.png0000700000175300010010000000062312537544000020521 0ustar srivatspNone‰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.7.1/client/icons/deco_exclusive.png0000700000175300010010000000143112537544000020773 0ustar srivatspNone‰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.7.1/client/icons/delete.png0000700000175300010010000000131312537544000017233 0ustar srivatspNone‰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.7.1/client/icons/exit.png0000700000175300010010000000126012537544000016743 0ustar srivatspNone‰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.7.1/client/icons/gaps.png0000700000175300010010000000661312537544000016733 0ustar srivatspNone‰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.7.1/client/icons/logo.icns0000700000175300010010000010507712537544000017115 0ustar srivatspNoneicnsŠ?is32ùr:ÿûÿþñ:ÿ`&æ$ÿüÿ_Rÿüÿÿ•…x?æ5ËÿûÿKrÿýþäpwh+=*~ÿÿøÿ0 ÿüö*9æ~¥– þûÝþÖÿÎnÃ¿Ž™feÏ%fÝÿOÉÿkWÀoXQ)%Uÿ‚4qÿúÿ.K*Cåež‰B"ÿûýö!ôìýLâÿQvšö/òüÿ‘gŽJÿü´ÿbæûþ+ÿøÿÔ Úÿÿ)ègäÿ[ÿÿd€NvÿýÿêG(P8•ÿÿ§FiDÿüÿÿØÿcÔýþÿ¨7EUÿü€ÿ “WæûÒS /¢ÿüÿ ”<%,&V©øÿý‚ÿ ýý%ÿþÿÿüþƒÿo7ÿûÿþñ7ÿ]‹"æ ÿüÿ\Pÿûþÿ“ƒu;å1ÊÿûÿHoÿüþänte':&|ÿÿøÿ,Ÿÿüõ&5å{£”žþûÝþÕÿÍl¾Œ—‹cbÎ! cÜÿKÈÿiT¿mUN%!Rÿ€1oÿúÿ*G&@åcœ‡?ÿûýöô ìýLáÿNs˜ö+òüÿdŒFÿü³ÿ_æûþ(ÿ÷ÿÓ Ùÿÿ%èdäÿXÿÿa}KtÿýÿêD%M5”ÿÿ¦CgAÿüÿÿØÿ`Óýþÿ§3BQÿü€ÿ ‘TæúÒP+ ÿüÿ ’8!)"S¨øÿý‚ÿ ýý"œÿþÿÿüþƒÿp8ÿûÿþñ8ÿ^‹#æ!ÿüÿ]Pÿüÿÿ”ƒv<å2ÊÿûÿIpÿüþänuf(;'|ÿÿøÿ-Ÿÿüõ'6å|¤”žþûÝþÕÿÍlþŒ—ŒccÎ"dÜÿLÈÿiU¿mVO&"Sÿ1oÿúÿ+H'Aåc‡@ÿûýöô ìýLâÿOt˜ö,òüÿeGÿü³ÿ`æûþ)ÿøÿÓ Ùÿÿ&èeäÿYÿÿb~KtÿýÿêE&N6”ÿÿ¦CgBÿüÿÿØÿaÔýþÿ§4BRÿü€ÿ ’TæúÒQ,¡ÿüÿ ’9")#T¨øÿý‚ÿ ýý#œÿþÿÿüþƒÿs8mkÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿil32 —1!Ì„ÿþÿ— ÿüÿg1¦EÿŽvÿýÿÿò„èýƒÿýÿo éü=Vöëê#÷ýÿÿûÿ[2øüƒÿüów¾(“T»ÿK­ÿþÿÿûÿ;Aÿü‚ÿýÿw!ò*%¾’ Šÿ‘lÿý€ÿýõ2TÿüƒÿÇÛÂë¼Y'5B!4ÿúÿýð+sÿü‚ÿç û.#/oG#µÿýÿþã!Ÿÿþ€ÿýó\!£éô@ÿð* ˜‚ÿþÿÍÇ‚ÿåñùü%9ÿ÷#…Êÿÿýþÿþÿ¨"íý€ÿaÆÀÿ2´ÿÿ*3ÿù#¿sCÿýÿè°ÄûÿwHÿüýÿxôÿ¥9ÿü&<ÿð ½­ýûm6êÿA…ÿûò]ÿúÿ,‰ÿNÿÞÖz-á<'%&_ÿæ"ÓÿmÏ€ÿçXiÿ¿ ‰“!$€# "WÿÿŸLŸtÿý€ÿ è7!0lT" uÿ'&€# &ÆÿùÿD!Nÿü€ÿýÿnL?"#>áÿý#%%&Ñÿÿ÷¯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.7.1/client/icons/logo.ico0000700000175300010010000000427612537544000016732 0ustar srivatspNone ¨( @ !!!///000???@@@AAAPPPQQQ___```ooopppqqq€€€‘‘‘   ¡¡¡¯¯¯°°°±±±¿¿¿ÀÀÀÁÁÁÏÏÏÐÐÐÑÑÑàààðððñññþþþÿÿÿ&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&%%&&&&&&&&&&&&&&&&&&% &&&&&&&&&&&&&&&&&&&&&&&&&& #&&&&&&&&&&&&&&&&&&  #&&&&&&&&&&&&&&& &&&& #&&&&%%&&&&&&&&& &&&&&&  &%&&%%&&&&&&"%&& &&&%&&&& &&&&&&&&&&&&&&##&&&%%& &&&&&&&&&&&#!&&&&&&&&&&&&&&&&&&& &&&&&&&&"& &&%"&&&&#&&&&&&&#&& &&&&#&&&&&& &&&&&&&&&& &&&&&&&&&&#&&&&&&&&&& &&&&&&&&&&##&&&&&&&&&  &&&&&&%%&&&&&&&#&&&&&&&&%&&&%&&&&&""# &&%&&&&&&&&&&& &&&&#&& &&&&&& &&&$&&&&&&## &#&&&& &&& &&&& &# & &#&&&&&&&&&&&&#&&% &&&&&&&&& &&&&&& &&&&&&&&&&#&&&&&& &&&&&&&&&&  &&&&&&&   #&&&&& &%&&&&&& % #&&&&& &&&&&&&&$ & &&&&&#&&&&&&&&& &&&&&&&&&&&&&&&" && &&&& $&&&&&&&&#"&! #&&&&&ostinato-0.7.1/client/icons/logo.png0000700000175300010010000004404312537544000016740 0ustar srivatspNone‰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.7.1/client/icons/magnifier.png0000700000175300010010000000114712537544000017737 0ustar srivatspNone‰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.7.1/client/icons/name.png0000700000175300010010000000537512537544000016725 0ustar srivatspNone‰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.7.1/client/icons/portgroup_add.png0000700000175300010010000000141512537544000020645 0ustar srivatspNone‰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.7.1/client/icons/portgroup_connect.png0000700000175300010010000000135412537544000021550 0ustar srivatspNone‰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.7.1/client/icons/portgroup_delete.png0000700000175300010010000000140712537544000021360 0ustar srivatspNone‰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.7.1/client/icons/portstats_clear.png0000700000175300010010000000055712537544000021213 0ustar srivatspNone‰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.7.1/client/icons/portstats_clear_all.png0000700000175300010010000000134012537544000022032 0ustar srivatspNone‰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.7.1/client/icons/portstats_filter.png0000700000175300010010000000066012537544000021405 0ustar srivatspNone‰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.7.1/client/icons/preferences.png0000700000175300010010000000111012537544000020265 0ustar srivatspNone‰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.7.1/client/icons/qt.png0000700000175300010010000000376512537544000016432 0ustar srivatspNone‰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.7.1/client/icons/sound_mute.png0000700000175300010010000000073212537544000020157 0ustar srivatspNone‰PNG  IHDRóÿagAMA¯È7ŠétEXtSoftwareAdobe ImageReadyqÉe<lIDAT8ËÅ“¿KaÆצþ€þ†f'¡ÁÉ»„‘(µó==R""ŽãÄÓóǦ‡!8„!N¹DÔR!A-AÔæúôžØèA xyáù|ù>/¯€ã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.7.1/client/icons/sound_none.png0000700000175300010010000000064112537544000020143 0ustar srivatspNone‰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.7.1/client/icons/stream_add.png0000700000175300010010000000145612537544000020104 0ustar srivatspNone‰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.7.1/client/icons/stream_delete.png0000700000175300010010000000151712537544000020614 0ustar srivatspNone‰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.7.1/client/icons/stream_edit.png0000700000175300010010000000154112537544000020274 0ustar srivatspNone‰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+//ÿ?Æ!“ */ #include "mainwindow.h" #include "../common/ostprotolib.h" #include "../common/protocolmanager.h" #include "../common/protocolwidgetfactory.h" #include "preferences.h" #include "settings.h" #include #include #include #include extern const char* version; extern const char* revision; extern ProtocolManager *OstProtocolManager; extern ProtocolWidgetFactory *OstProtocolWidgetFactory; 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); 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(); mainWindow = new MainWindow; mainWindow->show(); exitCode = app.exec(); delete mainWindow; delete appSettings; delete OstProtocolManager; google::protobuf::ShutdownProtobufLibrary(); return exitCode; } ostinato-0.7.1/client/mainwindow.cpp0000700000175300010010000001161312537544000017034 0ustar srivatspNone/* 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 "portgrouplist.h" #include "portstatswindow.h" #include "portswindow.h" #include "preferences.h" #include "settings.h" #include "ui_about.h" #include "updater.h" #include #include extern const char* version; extern const char* revision; PortGroupList *pgl; MainWindow::MainWindow(QWidget *parent) : QMainWindow (parent) { QString serverApp = QCoreApplication::applicationDirPath(); Updater *updater = new Updater(); #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 localServer_ = new QProcess(this); localServer_->setProcessChannelMode(QProcess::ForwardedChannels); localServer_->start(serverApp, QStringList()); 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("Statistics"), this); statsDock->setObjectName("statsDock"); statsDock->setFeatures( statsDock->features() & ~QDockWidget::DockWidgetClosable); setupUi(this); menuFile->insertActions(menuFile->actions().at(0), 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 0 { DbgThread *dbg = new DbgThread(pgl); dbg->start(); } #endif } MainWindow::~MainWindow() { #ifdef Q_OS_WIN32 //! \todo - find a way to terminate cleanly localServer_->kill(); #else localServer_->terminate(); #endif delete pgl; QByteArray layout = saveState(0); appSettings->setValue(kApplicationWindowLayout, layout); appSettings->setValue(kApplicationWindowGeometryKey, geometry()); localServer_->waitForFinished(); delete localServer_; } 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); actionViewShowMyReservedPortsOnly->setChecked(false); } 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::onNewVersion(QString newVersion) { statusBar()->showMessage(QString("New Ostinato version %1 available. " "Visit http://ostinato.org to download").arg(newVersion)); } ostinato-0.7.1/client/mainwindow.h0000700000175300010010000000266112537544000016504 0ustar srivatspNone/* 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 class PortsWindow; class PortStatsWindow; class QDockWidget; class QProcess; class MainWindow : public QMainWindow, private Ui::MainWindow { Q_OBJECT private: QProcess *localServer_; PortsWindow *portsWindow; PortStatsWindow *statsWindow; QDockWidget *portsDock; QDockWidget *statsDock; QRect defaultGeometry_; QByteArray defaultLayout_; public: MainWindow(QWidget *parent = 0); ~MainWindow(); public slots: void on_actionPreferences_triggered(); void on_actionViewRestoreDefaults_triggered(); void on_actionHelpAbout_triggered(); private slots: void onNewVersion(QString version); }; #endif ostinato-0.7.1/client/mainwindow.ui0000700000175300010010000000554712537544000016700 0ustar srivatspNone MainWindow 0 0 700 550 Ostinato :/icons/about.png 0 0 700 21 &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 ostinato-0.7.1/client/modeltest.cpp0000700000175300010010000004717012537544000016667 0ustar srivatspNone/**************************************************************************** ** ** 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.7.1/client/modeltest.h0000700000175300010010000000433412537544000016327 0ustar srivatspNone/**************************************************************************** ** ** 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.7.1/client/modeltest.pri0000700000175300010010000000014512537544000016666 0ustar srivatspNoneINCLUDEPATH += $$PWD DEPENDPATH += $$PWD SOURCES += $$PWD/modeltest.cpp HEADERS += $$PWD/modeltest.h ostinato-0.7.1/client/ostinato.pro0000700000175300010010000000461312537544000016540 0ustar srivatspNoneTEMPLATE = 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 { 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 += \ dumpview.h \ hexlineedit.h \ mainwindow.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 \ updater.h \ variablefieldswidget.h FORMS += \ about.ui \ mainwindow.ui \ portconfigdialog.ui \ portstatsfilter.ui \ portstatswindow.ui \ portswindow.ui \ preferences.ui \ streamconfigdialog.ui \ variablefieldswidget.ui SOURCES += \ dumpview.cpp \ stream.cpp \ hexlineedit.cpp \ main.cpp \ mainwindow.cpp \ packetmodel.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 \ updater.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.7.1/client/ostinato.qrc0000700000175300010010000000303212537544000016517 0ustar srivatspNone 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/exit.png icons/gaps.png icons/logo.png icons/magnifier.png icons/name.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/sound_mute.png icons/sound_none.png icons/stream_add.png icons/stream_delete.png icons/stream_duplicate.png icons/stream_edit.png ostinato-0.7.1/client/ostinato.rc0000700000175300010010000000010212537544000016331 0ustar srivatspNoneIDI_ICON1 ICON DISCARDABLE "icons/logo.ico" ostinato-0.7.1/client/packetmodel.cpp0000700000175300010010000001416112537544000017151 0ustar srivatspNone/* 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.7.1/client/packetmodel.h0000700000175300010010000000342412537544000016616 0ustar srivatspNone/* 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.7.1/client/port.cpp0000700000175300010010000003706512537544000015655 0ustar srivatspNone/* 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 "abstractfileformat.h" #include #include #include #include #include #include extern QMainWindow *mainWindow; uint Port::mAllocStreamId = 0; static const int kEthOverhead = 20; uint Port::newStreamId() { 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; } Port::~Port() { qDebug("%s", __FUNCTION__); while (!mStreams.isEmpty()) delete mStreams.takeFirst(); } 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); 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::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; } 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; } 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(); return true; } bool Port::deleteStreamAt(int index) { if (index >= mStreams.size()) return false; delete mStreams.takeAt(index); updateStreamOrdinalsFromIndex(); recalculateAverageRates(); 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::when_syncComplete() { //reorderStreamsByOrdinals(); mLastSyncStreamList.clear(); for (int i=0; iid()); } 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++; } } 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; AbstractFileFormat *fmt = AbstractFileFormat::fileFormatFromFile(fileName); if (fmt == NULL) 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->openStreamsOffline(fileName, streams, error); qDebug("after open offline"); while (!fmt->isFinished()) qApp->processEvents(); qDebug("wait over for offline 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(); } _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); AbstractFileFormat *fmt = AbstractFileFormat::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->saveStreamsOffline(streams, fileName, error); qDebug("after save offline"); while (!fmt->isFinished()) qApp->processEvents(); qDebug("wait over for offline 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; } ostinato-0.7.1/client/port.h0000700000175300010010000001155412537544000015315 0ustar srivatspNone/* 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 "stream.h" //class StreamModel; class Port : public QObject { Q_OBJECT static uint mAllocStreamId; 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 double avgPacketsPerSec_; double avgBitsPerSec_; int numActiveStreams_; QList mLastSyncStreamList; QList mStreams; // sorted by stream's ordinal value uint newStreamId(); void updateStreamOrdinalsFromIndex(); void reorderStreamsByOrdinals(); 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; } 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() { return (d.is_enabled()?AdminEnable:AdminDisable); } bool hasExclusiveControl() { return d.is_exclusive_control(); } OstProto::TransmitMode transmitMode() { return d.transmit_mode(); } double averagePacketRate() { return avgPacketsPerSec_; } double averageBitRate() { return avgBitsPerSec_; } //void setAdminEnable(AdminStatus status) { mAdminStatus = status; } void setAlias(QString &alias) { mUserAlias = alias; } //void setExclusive(bool flag); int numStreams() { return mStreams.size(); } Stream* streamByIndex(int index) { Q_ASSERT(index < mStreams.size()); return mStreams[index]; } 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(name()) .append(".XXXXXX")); return capFile_; } // 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); //@} void getDeletedStreamsSinceLastSync(OstProto::StreamIdList &streamIdList); void getNewStreamsSinceLastSync(OstProto::StreamIdList &streamIdList); void getModifiedStreamsSinceLastSync( OstProto::StreamConfigList &streamConfigList); 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); signals: void portRateChanged(int portGroupId, int portId); void portDataChanged(int portGroupId, int portId); void streamListChanged(int portGroupId, int portId); }; #endif ostinato-0.7.1/client/portconfigdialog.cpp0000700000175300010010000000700212537544000020207 0ustar srivatspNone/* 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, 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()); } 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()); // 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(); QDialog::accept(); } ostinato-0.7.1/client/portconfigdialog.h0000700000175300010010000000212612537544000017656 0ustar srivatspNone/* 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, QWidget *parent); private: virtual void accept(); OstProto::Port &portConfig_; enum { kNone, kSelf, kOther } reservedBy_; QString myself_; }; #endif ostinato-0.7.1/client/portconfigdialog.ui0000700000175300010010000000616312537544000020051 0ustar srivatspNone PortConfigDialog 0 0 244 233 Port Config Transmit Mode Sequential Streams true Interleaved Streams Reservation Reserved by: Reserve Exclusive Control 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.7.1/client/portgroup.cpp0000700000175300010010000006541312537544000016730 0ustar srivatspNone/* 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 "settings.h" #include #include #include #include #include #include #include #include using ::google::protobuf::NewCallback; extern QMainWindow *mainWindow; extern char *version; quint32 PortGroup::mPortGroupAllocId = 0; PortGroup::PortGroup(QHostAddress ip, 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; 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(ip, 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; } // ------------------------------------------------ // 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); 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(); 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::warning(NULL, tr("No ports in portgroup"), QString("The portgroup %1:%2 does not contain any ports!\n\n" "Packet Transmit/Capture requires elevated privileges. " "Please ensure that you are running 'drone' - the server " "component of Ostinato with admin/root OR setuid privilege.\n\n" "For more information see " "http://code.google.com/p/ostinato/wiki/FAQ#" "Q._Port_group_has_no_interfaces") .arg(serverAddress().toString()) .arg(int(serverPort()))); } } 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))); qDebug("before port append\n"); mPorts.append(p); } 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) getStreamIdList(); _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); 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::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::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, controller)); } void PortGroup::processModifyPortAck(PbRpcController *controller) { qDebug("In %s", __FUNCTION__); if (controller->Failed()) { qDebug("%s: rpc failed(%s)", __FUNCTION__, qPrintable(controller->ErrorString())); } 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()); 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; } 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(); // Are we done for all ports? if (numPorts() && portIndex >= (numPorts()-1)) { // FIXME(HI): some way to reset streammodel getStreamConfigList(); } _exit: delete controller; } void PortGroup::getStreamConfigList() { qDebug("requesting stream config list ..."); for (int portIndex = 0; portIndex < numPorts(); 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)); } // Are we done for all ports? if (portIndex >= numPorts()) { // FIXME(HI): some way to reset streammodel } _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::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::processClearStatsAck, controller)); } _exit: return; } void PortGroup::processClearStatsAck(PbRpcController *controller) { qDebug("In %s", __FUNCTION__); // Refresh stats immediately after a stats clear/reset getPortStats(); delete controller; } ostinato-0.7.1/client/portgroup.h0000700000175300010010000001210112537544000016357 0ustar srivatspNone/* 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 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_; public: // FIXME(HIGH): member access QList mPorts; public: PortGroup(QHostAddress ip = QHostAddress::LocalHost, quint16 port = DEFAULT_SERVER_PORT); ~PortGroup(); void connectToHost() { reconnect = true; compat = kUnknown; rpcChannel->establish(); } void connectToHost(QHostAddress ip, quint16 port) { reconnect = true; compat = kUnknown; rpcChannel->establish(ip, port); } void disconnectFromHost() { reconnect = false; rpcChannel->tearDown(); } int numPorts() const { return mPorts.size(); } quint32 id() const { return mPortGroupId; } const QString& userAlias() const { return mUserAlias; } void setUserAlias(QString alias) { mUserAlias = alias; }; const QHostAddress& serverAddress() const { return rpcChannel->serverAddress(); } quint16 serverPort() const { return rpcChannel->serverPort(); } 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 modifyPort(int portId, OstProto::Port portConfig); void processModifyPortAck(PbRpcController *controller); void processUpdatedPortConfig(PbRpcController *controller); void getStreamIdList(); void processStreamIdList(int portIndex, PbRpcController *controller); void getStreamConfigList(); void processStreamConfigList(int portIndex, PbRpcController *controller); void processModifyStreamAck(OstProto::Ack *ack); 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 getPortStats(); void processPortStatsList(); void clearPortStats(QList *portList = NULL); void processClearStatsAck(PbRpcController *controller); signals: void portGroupDataChanged(int portGroupId, int portId = 0xFFFF); void portListAboutToBeChanged(quint32 portGroupId); void portListChanged(quint32 portGroupId); void statsChanged(quint32 portGroupId); 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); }; #endif ostinato-0.7.1/client/portgrouplist.cpp0000700000175300010010000000717612537544000017626 0ustar srivatspNone/* 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" // TODO(LOW): Remove #include PortGroupList::PortGroupList() : mPortGroupListModel(this), mStreamListModel(this), mPortStatsModel(this, this) { PortGroup *pg; #ifdef QT_NO_DEBUG streamModelTester_ = NULL; portModelTester_ = NULL; portStatsModelTester_ = NULL; #else streamModelTester_ = new ModelTest(getStreamModel()); portModelTester_ = new ModelTest(getPortModel()); portStatsModelTester_ = new ModelTest(getPortStatsModel()); #endif // Add the "Local" Port Group pg = new PortGroup; addPortGroup(*pg); } PortGroupList::~PortGroupList() { delete portStatsModelTester_; delete portModelTester_; delete streamModelTester_; 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(); } //.................... // 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.7.1/client/portgrouplist.h0000700000175300010010000000404412537544000017262 0ustar srivatspNone/* 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 "portgroup.h" #include #include #include "portmodel.h" #include "streammodel.h" #include "portstatsmodel.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; QObject *streamModelTester_; QObject *portModelTester_; QObject *portStatsModelTester_; // Methods public: PortGroupList(); ~PortGroupList(); PortModel* getPortModel() { return &mPortGroupListModel; } PortStatsModel* getPortStatsModel() { return &mPortStatsModel; } StreamModel* getStreamModel() { return &mStreamListModel; } 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); private: int indexOfPortGroup(quint32 portGroupId); }; #endif ostinato-0.7.1/client/portmodel.cpp0000700000175300010010000002210312537544000016661 0ustar srivatspNone/* 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())->serverAddress().toString()). 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->name()) .arg(rsvdBy) .arg(port->description()); } else if ((role == Qt::DecorationRole)) { return portIconFactory[port->linkState()][port->hasExclusiveControl()]; } 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.7.1/client/portmodel.h0000700000175300010010000000431412537544000016332 0ustar srivatspNone/* 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: 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.7.1/client/portstatsfilter.ui0000700000175300010010000001057112537544000017766 0ustar srivatspNone 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.7.1/client/portstatsfilterdialog.cpp0000700000175300010010000000742112537544000021313 0ustar srivatspNone/* 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.7.1/client/portstatsfilterdialog.h0000700000175300010010000000310412537544000020752 0ustar srivatspNone/* 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.7.1/client/portstatsmodel.cpp0000700000175300010010000002146612537544000017753 0ustar srivatspNone/* 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 quint64(stats.rx_pkts()); case e_STAT_FRAMES_SENT: return quint64(stats.tx_pkts()); case e_STAT_FRAME_SEND_RATE: return quint64(stats.tx_pps()); case e_STAT_FRAME_RECV_RATE: return quint64(stats.rx_pps()); case e_STAT_BYTES_RCVD: return quint64(stats.rx_bytes()); case e_STAT_BYTES_SENT: return quint64(stats.tx_bytes()); case e_STAT_BYTE_SEND_RATE: return quint64(stats.tx_bps()); case e_STAT_BYTE_RECV_RATE: return 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 quint64(stats.rx_drops()); case e_STAT_RX_ERRORS: return quint64(stats.rx_errors()); case e_STAT_RX_FIFO_ERRORS: return quint64(stats.rx_fifo_errors()); case e_STAT_RX_FRAME_ERRORS: return 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.7.1/client/portstatsmodel.h0000700000175300010010000000734112537544000017414 0ustar srivatspNone/* 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.7.1/client/portstatsproxymodel.h0000700000175300010010000000301712537544000020512 0ustar srivatspNone/* 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.7.1/client/portstatswindow.cpp0000700000175300010010000001542412537544000020157 0ustar srivatspNone/* 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 "settings.h" #include 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()); } 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::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_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); } } // '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); } } 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); } } /* ------------ Private Methods -------------- */ QModelIndexList PortStatsWindow::selectedColumns() { QModelIndexList indexList = tvPortStats->selectionModel()->selectedColumns(); QModelIndexList sourceIndexList; if (!proxyStatsModel) return indexList; foreach(QModelIndex index, indexList) sourceIndexList.append(proxyStatsModel->mapToSource(index)); return sourceIndexList; } ostinato-0.7.1/client/portstatswindow.h0000700000175300010010000000313012537544000017613 0ustar srivatspNone/* 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 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_tbFilter_clicked(); private: QModelIndexList selectedColumns(); PortGroupList *pgl; PortStatsModel *model; QSortFilterProxyModel *proxyStatsModel; }; #endif ostinato-0.7.1/client/portstatswindow.ui0000700000175300010010000001260512537544000020010 0ustar srivatspNone PortStatsWindow 0 0 502 415 Form QFrame::Panel QFrame::Raised Start Tx Starts transmit on selected port(s) Start Transmit :/icons/control_play.png Stop Tx Stops transmit on selected port(s) Stop Trasmit :/icons/control_stop.png Clear Selected Port Stats Clears statistics of the selected port(s) Clear :/icons/portstats_clear.png Clear All Ports Stats Clears statistics of all ports Clear All :/icons/portstats_clear_all.png Start Capture Captures packets on the selected port(s) Start Capture :/icons/sound_none.png Stop Capture End capture on selecteed port(s) Stop Capture :/icons/sound_mute.png View Capture Buffer View captured packets on selected port(s) View Capture :/icons/magnifier.png Qt::Vertical Qt::Horizontal 40 20 Select which ports to view Filter :/icons/portstats_filter.png ostinato-0.7.1/client/portswindow.cpp0000700000175300010010000006005512537544000017263 0ustar srivatspNone/* 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 "abstractfileformat.h" #include "portconfigdialog.h" #include "settings.h" #include "streamconfigdialog.h" #include "streamlistdelegate.h" #include #include #include #include #include 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); 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 StramList 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 and StreamList actions combined make this window's actions addActions(tvPortList->actions()); sep = new QAction(this); sep->setSeparator(true); addAction(sep); addActions(tvStreamList->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(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 - 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; } 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) { QModelIndex currentPort = tvPortList->currentIndex(); StreamConfigDialog *scd; int ret; if (proxyPortModel) currentPort = proxyPortModel->mapToSource(currentPort); if (!index.isValid()) { qDebug("%s: invalid index", __FUNCTION__); return; } scd = new StreamConfigDialog(plm->port(currentPort), index.row(), this); qDebug("stream list activated\n"); ret = scd->exec(); if (ret == QDialog::Accepted) plm->port(currentPort).recalculateAverageRates(); delete scd; } 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())); } if (!current.isValid()) { qDebug("setting stacked widget to blank page"); swDetail->setCurrentIndex(2); // blank page } else { if (plm->isPortGroup(current)) { swDetail->setCurrentIndex(1); // portGroup detail page } else if (plm->isPort(current)) { swDetail->setCurrentIndex(0); // port detail page updatePortRates(); connect(&(plm->port(current)), SIGNAL(portRateChanged(int, int)), SLOT(updatePortRates())); } } } void PortsWindow::when_portModel_dataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight) { qDebug("In %s", __FUNCTION__); #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); // Enable "Edit" only if the single range has a single row if (tvStreamList->selectionModel()->selection().at(0).height() > 1) actionEdit_Stream->setDisabled(true); else 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::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 (IP[:Port])", QLineEdit::Normal, lastNewPortGroup, &ok); if (ok) { QStringList addr = text.split(":"); if (addr.size() == 1) // Port unspecified addr.append(QString().setNum(DEFAULT_SERVER_PORT)); PortGroup *pg = new PortGroup(QHostAddress(addr[0]),addr[1].toUShort()); 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; OstProto::Port config; config.set_transmit_mode(plm->port(current).transmitMode()); config.set_is_exclusive_control(plm->port(current).hasExclusiveControl()); config.set_user_name(plm->port(current).userName().toStdString()); PortConfigDialog dialog(config, this); if (dialog.exec() == QDialog::Accepted) plm->portGroup(current.parent()).modifyPort(current.row(), config); } void PortsWindow::on_actionNew_Stream_triggered() { qDebug("New Stream Action"); // In case nothing is selected, insert 1 row at the top int row = 0, 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 (tvStreamList->selectionModel()->selection().size() == 1) { row = tvStreamList->selectionModel()->selection().at(0).top(); count = tvStreamList->selectionModel()->selection().at(0).height(); } plm->getStreamModel()->insertRows(row, count); } void PortsWindow::on_actionEdit_Stream_triggered() { qDebug("Edit Stream Action"); // Ensure we have only one range selected which contains only one row if ((tvStreamList->selectionModel()->selection().size() == 1) && (tvStreamList->selectionModel()->selection().at(0).height() == 1)) { on_tvStreamList_activated(tvStreamList->selectionModel()-> selection().at(0).topLeft()); } } 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"); 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)); fileName = QFileDialog::getOpenFileName(this, tr("Open Streams"), dirName); 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 = AbstractFileFormat::supportedFileTypes(); 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.On some Linux distros the native dialog can't // distinguish between Ostinato(*) and PCAP(*) #if defined(Q_OS_MAC) || defined(Q_OS_UNIX) 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; 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.7.1/client/portswindow.h0000700000175300010010000000502012537544000016717 0ustar srivatspNone/* 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" /* TODO HIGH MED LOW */ class QAbstractItemDelegate; class QSortFilterProxyModel; class PortsWindow : public QWidget, private Ui::PortsWindow { Q_OBJECT //QAbstractItemModel *slm; // stream list model PortGroupList *plm; public: PortsWindow(PortGroupList *pgl, QWidget *parent = 0); ~PortsWindow(); private: QString lastNewPortGroup; QAbstractItemDelegate *delegate; QSortFilterProxyModel *proxyPortModel; public slots: void showMyReservedPortsOnly(bool enabled); private slots: 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.7.1/client/portswindow.ui0000700000175300010010000002202712537544000017113 0ustar srivatspNone PortsWindow 0 0 710 352 Form Qt::Horizontal false Qt::ActionsContextMenu QAbstractItemView::SingleSelection 0 0 0 0 0 0 0 QFrame::StyledPanel QFrame::Sunken Avg pps true Avg bps false Qt::Horizontal 40 20 Apply Qt::Vertical 20 0 0 1 Qt::ActionsContextMenu QFrame::StyledPanel 1 QAbstractItemView::ExtendedSelection QAbstractItemView::SelectRows Select a port to configure streams Qt::AlignCenter :/icons/portgroup_add.png New Port Group :/icons/portgroup_delete.png Delete Port Group :/icons/portgroup_connect.png Connect Port Group :/icons/portgroup_disconnect.png Disconnect Port Group :/icons/stream_add.png New Stream :/icons/stream_delete.png Delete Stream :/icons/stream_edit.png Edit Stream true Exclusive Port Control (EXPERIMENTAL) Open Streams ... Save Streams ... Port Configuration ... :/icons/stream_duplicate.png Duplicate Stream radioButton toggled(bool) averagePacketsPerSec setEnabled(bool) 313 28 380 28 radioButton_2 toggled(bool) averageBitsPerSec setEnabled(bool) 333 55 395 56 ostinato-0.7.1/client/preferences.cpp0000700000175300010010000001005212537544000017155 0ustar srivatspNone/* 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.7.1/client/preferences.h0000700000175300010010000000223612537544000016627 0ustar srivatspNone/* 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.7.1/client/preferences.ui0000700000175300010010000001377512537544000017027 0ustar srivatspNone 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.7.1/client/settings.h0000700000175300010010000000475112537544000016172 0ustar srivatspNone/* 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.7.1/client/stream.cpp0000700000175300010010000000221612537544000016152 0ustar srivatspNone/* 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; } ostinato-0.7.1/client/stream.h0000700000175300010010000000200312537544000015611 0ustar srivatspNone/* 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.7.1/client/streamconfigdialog.cpp0000700000175300010010000011072512537544000020525 0ustar srivatspNone/* 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(Port &port, uint streamIndex, QWidget *parent) : QDialog (parent), mPort(port) { OstProto::Stream s; mCurrentStreamIndex = streamIndex; mpStream = new Stream; mPort.streamByIndex(mCurrentStreamIndex)->protoDataCopyInto(s); mpStream->protoDataCopyFrom(s); _iter = mpStream->createProtocolListIterator(); isUpdateInProgress = false; setupUi(this); setupUiExtra(); 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 = 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 = 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)), rbPayloadOther, SLOT(setChecked(bool))); connect(rbL4Other, 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&))); variableFieldsWidget->setStream(mpStream); 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()); // TODO(MED): //! \todo Enable navigation of streams pbPrev->setHidden(true); pbNext->setHidden(true); //! \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 /* ** 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; delete mpStream; } 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_pbPrev_clicked() { #if 0 StoreCurrentStream(currStreamIdx); currStreamIdx--; LoadCurrentStream(currStreamIdx); pbPrev->setDisabled((currStreamIdx == 0)); pbNext->setDisabled((currStreamIdx == 2)); #endif } void StreamConfigDialog::on_pbNext_clicked() { #if 0 StoreCurrentStream(currStreamIdx); currStreamIdx++; LoadCurrentStream(currStreamIdx); pbPrev->setDisabled((currStreamIdx == 0)); pbNext->setDisabled((currStreamIdx == 2)); #endif } 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! } } 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); } 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 == ProtoPayload) { 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; } 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) { if (btn->isEnabled()) btn->click(); else { btn->setChecked(true); __updateProtocol(i, id); } } else { switch (i) { case ProtoVlan: _iter->previous(); break; case ProtoPayload: goto _other; default: btn = bgProto[ProtoPayload]->button(id); if (btn && btn->isEnabled()) { btn->click(); break; } else goto _other; } } } // If more protocol(s) beyond payload ... if (_iter->hasNext()) { i = ProtoPayload; goto _other; } goto _done; _other: for (int j = i; j < ProtoMax; j++) { // VLAN doesn't have a "Other" button if (j == ProtoVlan) continue; bgProto[j]->button(ButtonIdOther)->setChecked(true); __updateProtocol(j, ButtonIdOther); } _done: isUpdateInProgress = false; } void StreamConfigDialog::LoadCurrentStream() { QString str; qDebug("loading mpStream %p", mpStream); // Meta Data { 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->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->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)); } } void StreamConfigDialog::on_pbOk_clicked() { QString log; OstProto::Stream s; // Store dialog contents into stream StoreCurrentStream(); if ((mPort.transmitMode() == OstProto::kInterleavedTransmit) && (mpStream->isFrameVariable())) { log += "In 'Interleaved Streams' transmit mode, the count for " "varying fields at transmit time may not be same as configured\n"; } mpStream->preflightCheck(log); if (log.length()) { if (QMessageBox::warning(this, "Preflight Check", log + "\nContinue?", QMessageBox::Yes | QMessageBox::No, QMessageBox::No) == QMessageBox::No) return; } // Copy the data from the "local working copy of stream" to "actual stream" mpStream->protoDataCopyInto(s); mPort.streamByIndex(mCurrentStreamIndex)->protoDataCopyFrom(s); qDebug("stream stored"); lastGeometry = geometry(); lastTopLevelTabIndex = twTopLevel->currentIndex(); lastProtocolDataIndex = tbProtocolData->currentIndex(); accept(); } ostinato-0.7.1/client/streamconfigdialog.h0000700000175300010010000001005412537544000020164 0ustar srivatspNone/* 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(Port &port, uint streamIndex, QWidget *parent = 0); ~StreamConfigDialog(); private: enum ButtonId { ButtonIdNone = 0, ButtonIdOther = -2 }; enum ProtoButtonGroup { ProtoMin, ProtoL1 = 0, ProtoVlan = 1, ProtoL2 = 2, ProtoL3 = 3, ProtoL4 = 4, ProtoL5 = 5, ProtoPayload = 6, ProtoMax }; QButtonGroup *bgProto[ProtoMax]; QStringListModel *mpAvailableProtocolsModel; QStringListModel *mpSelectedProtocolsModel; Port& mPort; 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(); 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.7.1/client/streamconfigdialog.ui0000700000175300010010000013345312537544000020363 0ustar srivatspNone StreamConfigDialog Qt::ApplicationModal 0 0 634 507 0 0 Edit Stream :/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 Qt::Horizontal 241 20 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 592 269 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 L5 None true false Text 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 Payload None true Pattern false Hex Dump false Other 0 0 250 135 Advanced Available Protocols true QAbstractItemView::ExtendedSelection QAbstractItemView::SelectRows Qt::Vertical 20 40 false > :/icons/arrow_right.png Qt::Vertical 20 40 Selected Protocols false ^ :/icons/arrow_up.png false v :/icons/arrow_down.png false - :/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.7.1/client/streamlistdelegate.cpp0000700000175300010010000001260212537544000020541 0ustar srivatspNone/* 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.7.1/client/streamlistdelegate.h0000700000175300010010000000270312537544000020207 0ustar srivatspNone/* 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.7.1/client/streammodel.cpp0000700000175300010010000001704212537544000017176 0ustar srivatspNone/* 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->streamByIndex(index.row())->setName(value.toString()); emit(dataChanged(index, index)); return true; case StreamStatus: mCurrentPort->streamByIndex(index.row())->setEnabled(value.toBool()); emit(dataChanged(index, index)); return true; case StreamNextWhat: if (role == Qt::EditRole) { mCurrentPort->streamByIndex(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(); } 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.7.1/client/streammodel.h0000700000175300010010000000447112537544000016645 0ustar srivatspNone/* 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 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 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.7.1/client/updater.cpp0000700000175300010010000000654312537544000016332 0ustar srivatspNone/* 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 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); #endif } Updater::~Updater() { delete http_; delete file_; } void Updater::checkForNewVersion() { http_ = new QHttp("update.ostinato.org"); file_ = new QTemporaryFile(); 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_->get("/update/pad.xml", 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; if (newVer.at(i).toUInt(&isOk) > curVer.at(i).toUInt(&isOk)) return true; } if (newVer.size() > curVer.size()) return true; return false; } ostinato-0.7.1/client/updater.h0000700000175300010010000000235112537544000015770 0ustar srivatspNone/* 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: QHttp *http_; QTemporaryFile *file_; }; #endif ostinato-0.7.1/client/variablefieldswidget.cpp0000700000175300010010000003247612537544000021052 0ustar srivatspNone/* 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.7.1/client/variablefieldswidget.h0000700000175300010010000000414712537544000020511 0ustar srivatspNone/* 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.7.1/client/variablefieldswidget.ui0000700000175300010010000002001212537544000020664 0ustar srivatspNone 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.7.1/common/0000700000175300010010000000000012537544001014162 5ustar srivatspNoneostinato-0.7.1/common/abstractfileformat.cpp0000700000175300010010000000557112537544000020554 0ustar srivatspNone/* 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 "abstractfileformat.h" #include "fileformat.h" #include "pcapfileformat.h" #include "pdmlfileformat.h" #include "pythonfileformat.h" #include AbstractFileFormat::AbstractFileFormat() { stop_ = false; } AbstractFileFormat::~AbstractFileFormat() { } QDialog* AbstractFileFormat::openOptionsDialog() { return NULL; } QDialog* AbstractFileFormat::saveOptionsDialog() { return NULL; } QStringList AbstractFileFormat::supportedFileTypes() { return QStringList() << "Ostinato (*)" << "PCAP (*)" << "PDML (*.pdml)" << "PythonScript (*.py)"; } void AbstractFileFormat::openStreamsOffline(const QString fileName, OstProto::StreamConfigList &streams, QString &error) { fileName_ = fileName; openStreams_ = &streams; error_ = &error; op_ = kOpen; stop_ = false; start(); } void AbstractFileFormat::saveStreamsOffline( const OstProto::StreamConfigList streams, const QString fileName, QString &error) { saveStreams_ = streams; fileName_ = fileName; error_ = &error; op_ = kSave; stop_ = false; start(); } bool AbstractFileFormat::result() { return result_; } AbstractFileFormat* AbstractFileFormat::fileFormatFromFile( const QString fileName) { if (fileFormat.isMyFileFormat(fileName)) return &fileFormat; if (pdmlFileFormat.isMyFileFormat(fileName)) return &pdmlFileFormat; if (pcapFileFormat.isMyFileFormat(fileName)) return &pcapFileFormat; return NULL; } AbstractFileFormat* AbstractFileFormat::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 AbstractFileFormat::cancel() { stop_ = true; } void AbstractFileFormat::run() { if (op_ == kOpen) result_ = openStreams(fileName_, *openStreams_, *error_); else if (op_ == kSave) result_ = saveStreams(saveStreams_, fileName_, *error_); } ostinato-0.7.1/common/abstractfileformat.h0000700000175300010010000000435712537544000020222 0ustar srivatspNone/* 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 _ABSTRACT_FILE_FORMAT_H #define _ABSTRACT_FILE_FORMAT_H #include "protocol.pb.h" #include #include class QDialog; class AbstractFileFormat : public QThread { Q_OBJECT public: AbstractFileFormat(); virtual ~AbstractFileFormat(); virtual bool openStreams(const QString fileName, OstProto::StreamConfigList &streams, QString &error) = 0; virtual bool saveStreams(const OstProto::StreamConfigList streams, const QString fileName, QString &error) = 0; virtual QDialog* openOptionsDialog(); virtual QDialog* saveOptionsDialog(); void openStreamsOffline(const QString fileName, OstProto::StreamConfigList &streams, QString &error); void saveStreamsOffline(const OstProto::StreamConfigList streams, const QString fileName, QString &error); bool result(); static QStringList supportedFileTypes(); static AbstractFileFormat* fileFormatFromFile(const QString fileName); static AbstractFileFormat* 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: enum kOp { kOpen, kSave }; QString fileName_; OstProto::StreamConfigList *openStreams_; OstProto::StreamConfigList saveStreams_; QString *error_; kOp op_; bool result_; }; #endif ostinato-0.7.1/common/abstractprotocol.cpp0000700000175300010010000010152212537544000020256 0ustar srivatspNone/* 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 /*! \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 = 0; quint16 cksum; AbstractProtocol *p = next; Q_ASSERT(cksumType == CksumIp); while (p) { cksum = p->protocolFrameCksum(streamIndex, cksumType); sum += (quint16) ~cksum; if (cksumScope == CksumScopeAdjacentProtocol) goto out; p = p->next; } if (parent) { cksum = parent->protocolFramePayloadCksum(streamIndex, cksumType, cksumScope); sum += (quint16) ~cksum; } out: while(sum>>16) sum = (sum & 0xFFFF) + (sum >> 16); return (quint16) ~sum; } // 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.7.1/common/abstractprotocol.h0000700000175300010010000001446112537544000017730 0ustar srivatspNone/* 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.7.1/common/abstractprotocolconfig.h0000700000175300010010000000562312537544000021116 0ustar srivatspNone/* 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.7.1/common/arp.cpp0000700000175300010010000006117612537544000015465 0ustar srivatspNone/* 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.7.1/common/arp.h0000700000175300010010000000536712537544000015132 0ustar srivatspNone/* 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.7.1/common/arp.proto0000700000175300010010000000421612537544000016036 0ustar srivatspNone/* 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.7.1/common/arp.ui0000700000175300010010000003452712537544000015320 0ustar srivatspNone 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.7.1/common/arpconfig.cpp0000700000175300010010000002124012537544000016637 0ustar srivatspNone/* 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.7.1/common/arpconfig.h0000700000175300010010000000254612537544000016314 0ustar srivatspNone/* 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.7.1/common/arppdml.cpp0000700000175300010010000000246712537544000016340 0ustar srivatspNone/* 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.7.1/common/arppdml.h0000700000175300010010000000156512537544000016003 0ustar srivatspNone/* 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.7.1/common/comboprotocol.h0000700000175300010010000001330012537544000017213 0ustar srivatspNone/* 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.7.1/common/comboprotocolconfig.h0000700000175300010010000000610712537544000020410 0ustar srivatspNone/* 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.7.1/common/crc32c.cpp0000700000175300010010000001342312537544000015752 0ustar srivatspNone/* 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.7.1/common/crc32c.h0000700000175300010010000000135512537544000015420 0ustar srivatspNone/* 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.7.1/common/dot2llc.h0000700000175300010010000000160212537544000015677 0ustar srivatspNone/* 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.7.1/common/dot2llc.proto0000700000175300010010000000160012537544000016611 0ustar srivatspNone/* 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; // 802.2 LLC message Dot2Llc { // Empty since this is a 'combo' protocol } extend Protocol { optional Dot2Llc dot2Llc = 206; } ostinato-0.7.1/common/dot2llcconfig.h0000700000175300010010000000201412537544000017063 0ustar srivatspNone/* 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.7.1/common/dot2snap.h0000700000175300010010000000161612537544000016073 0ustar srivatspNone/* 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.7.1/common/dot2snap.proto0000700000175300010010000000153312537544000017005 0ustar srivatspNone/* 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.7.1/common/dot2snapconfig.h0000700000175300010010000000203212537544000017252 0ustar srivatspNone/* 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.7.1/common/dot3.cpp0000700000175300010010000001154412537544000015546 0ustar srivatspNone/* 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.7.1/common/dot3.h0000700000175300010010000000347012537544000015212 0ustar srivatspNone/* 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.7.1/common/dot3.proto0000700000175300010010000000154612537544000016130 0ustar srivatspNone/* 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.7.1/common/dot3.ui0000700000175300010010000000355312537544000015402 0ustar srivatspNone 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.7.1/common/dot3config.cpp0000700000175300010010000000324112537544000016727 0ustar srivatspNone/* 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.7.1/common/dot3config.h0000700000175300010010000000215312537544000016375 0ustar srivatspNone/* 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.7.1/common/eth2.cpp0000700000175300010010000001126312537544000015535 0ustar srivatspNone/* 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.7.1/common/eth2.h0000700000175300010010000000343112537544000015200 0ustar srivatspNone/* 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.7.1/common/eth2.proto0000700000175300010010000000155112537544000016115 0ustar srivatspNone/* 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.7.1/common/eth2.ui0000700000175300010010000000334312537544000015370 0ustar srivatspNone 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.7.1/common/eth2config.cpp0000700000175300010010000000322612537544000016723 0ustar srivatspNone/* 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.7.1/common/eth2config.h0000700000175300010010000000217612537544000016373 0ustar srivatspNone/* 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.7.1/common/eth2pdml.cpp0000700000175300010010000000730712537544001016417 0ustar srivatspNone/* 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.7.1/common/eth2pdml.h0000700000175300010010000000207012537544001016054 0ustar srivatspNone/* 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.7.1/common/fileformat.cpp0000700000175300010010000003624612537544001017034 0ustar srivatspNone/* 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 "fileformat.h" #include "crc32c.h" #include #include #include #include const std::string FileFormat::kFileMagicValue = "\xa7\xb7OSTINATO"; FileFormat fileFormat; const int kBaseHex = 16; FileFormat::FileFormat() { /* * 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); } FileFormat::~FileFormat() { } bool FileFormat::openStreams(const QString fileName, OstProto::StreamConfigList &streams, QString &error) { QFile file(fileName); QByteArray buf; int size, contentOffset, contentSize; quint32 calcCksum; OstProto::FileMagic magic; OstProto::FileMeta meta; OstProto::FileContent content; 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), size - kFileMetaDataOffset)) { goto _metadata_parse_fail; } qDebug("%s: File MetaData (INFORMATION) - \n%s", __FUNCTION__, QString().fromStdString(meta.DebugString()).toAscii().constData()); // MetaData Validation(s) if (meta.data().file_type() != OstProto::kStreamsFileType) 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; // Parse full contents if (!content.ParseFromArray( (void*)(buf.constData() + contentOffset), contentSize)) { goto _content_parse_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; _content_parse_fail: error = QString(tr("Failed parsing %1 contents")).arg(fileName); qDebug("Error: %s", QString().fromStdString( content.matter().InitializationErrorString()) .toAscii().constData()); qDebug("Debug: %s", QString().fromStdString( content.matter().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 streams file")).arg(fileName); 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 FileFormat::saveStreams(const OstProto::StreamConfigList streams, const QString fileName, QString &error) { OstProto::FileMagic magic; OstProto::FileMeta meta; OstProto::FileContent content; 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(OstProto::kStreamsFileType); Q_ASSERT(meta.IsInitialized()); if (!streams.IsInitialized()) goto _stream_not_init; content.mutable_matter()->mutable_streams()->CopyFrom(streams); 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; } 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()); 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; _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 FileFormat::isMyFileFormat(const QString fileName) { bool ret = false; QFile file(fileName); QByteArray buf; OstProto::FileMagic magic; if (!file.open(QIODevice::ReadOnly)) goto _exit; buf = file.peek(kFileMagicOffset + kFileMagicSize); if (!magic.ParseFromArray((void*)(buf.constData() + kFileMagicOffset), kFileMagicSize)) goto _close_exit; if (magic.value() == kFileMagicValue) ret = true; _close_exit: file.close(); _exit: return ret; } bool FileFormat::isMyFileType(const QString fileType) { if (fileType.startsWith("Ostinato")) return true; else return false; } void FileFormat::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()); } #pragma GCC diagnostic ignored "-Wdeprecated-declarations" /*! Fixup content to what is expected in the native version */ void FileFormat::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.7.1/common/fileformat.h0000700000175300010010000000362012537544001016467 0ustar srivatspNone/* 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 _FILE_FORMAT_H #define _FILE_FORMAT_H #include "abstractfileformat.h" #include "fileformat.pb.h" class FileFormat : public AbstractFileFormat { public: FileFormat(); ~FileFormat(); virtual bool openStreams(const QString fileName, OstProto::StreamConfigList &streams, QString &error); virtual bool saveStreams(const OstProto::StreamConfigList streams, const QString fileName, QString &error); bool isMyFileFormat(const QString fileName); bool isMyFileType(const QString fileType); private: void initFileMetaData(OstProto::FileMetaData &metaData); void postParseFixup(OstProto::FileMetaData metaData, OstProto::FileContent &content); 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 std::string kFileMagicValue; // Native file format version static const uint kFileFormatVersionMajor = 0; static const uint kFileFormatVersionMinor = 2; static const uint kFileFormatVersionRevision = 4; }; extern FileFormat fileFormat; #endif ostinato-0.7.1/common/fileformat.proto0000700000175300010010000000577712537544001017422 0ustar srivatspNone/* 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; } 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 FileContentMatter { optional StreamConfigList streams = 1; } /* 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 FileContent 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.7.1/common/gmp.cpp0000700000175300010010000005252712537544001015467 0ustar srivatspNone/* 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.7.1/common/gmp.h0000700000175300010010000000622112537544001015122 0ustar srivatspNone/* 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.7.1/common/gmp.proto0000700000175300010010000000550412537544001016041 0ustar srivatspNone/* 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.7.1/common/gmp.ui0000700000175300010010000006235512537544001015322 0ustar srivatspNone 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.7.1/common/gmpconfig.cpp0000700000175300010010000003027412537544001016650 0ustar srivatspNone/* 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.7.1/common/gmpconfig.h0000700000175300010010000000325512537544001016314 0ustar srivatspNone/* 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.7.1/common/hexdump.cpp0000700000175300010010000001214712537544001016350 0ustar srivatspNone/* 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 + kFcsSize); if (pad < 0) pad = 0; len += pad; } return len; } ostinato-0.7.1/common/hexdump.h0000700000175300010010000000400112537544001016003 0ustar srivatspNone/* 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.7.1/common/hexdump.proto0000700000175300010010000000160612537544001016727 0ustar srivatspNone/* 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.7.1/common/hexdump.ui0000700000175300010010000000337612537544001016207 0ustar srivatspNone 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.7.1/common/hexdumpconfig.cpp0000700000175300010010000000370712537544001017540 0ustar srivatspNone/* 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.7.1/common/hexdumpconfig.h0000700000175300010010000000231712537544001017201 0ustar srivatspNone/* 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.7.1/common/icmp.cpp0000700000175300010010000002454412537544001015632 0ustar srivatspNone/* 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.7.1/common/icmp.h0000700000175300010010000000523712537544001015275 0ustar srivatspNone/* 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.7.1/common/icmp.proto0000700000175300010010000000224612537544001016206 0ustar srivatspNone/* 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.7.1/common/icmp.ui0000700000175300010010000001037412537544001015461 0ustar srivatspNone 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.7.1/common/icmp6pdml.cpp0000700000175300010010000000550412537544001016570 0ustar srivatspNone/* 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.7.1/common/icmp6pdml.h0000700000175300010010000000275212537544001016237 0ustar srivatspNone/* 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.7.1/common/icmpconfig.cpp0000700000175300010010000001420512537544001017011 0ustar srivatspNone/* 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.7.1/common/icmpconfig.h0000700000175300010010000000243412537544001016457 0ustar srivatspNone/* 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.7.1/common/icmphelper.h0000700000175300010010000000471312537544001016473 0ustar srivatspNone/* 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.7.1/common/icmppdml.cpp0000700000175300010010000000647212537544001016507 0ustar srivatspNone/* 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.7.1/common/icmppdml.h0000700000175300010010000000304212537544001016142 0ustar srivatspNone/* 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.7.1/common/igmp.cpp0000700000175300010010000002476612537544001015644 0ustar srivatspNone/* 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.7.1/common/igmp.h0000700000175300010010000000514312537544001015275 0ustar srivatspNone/* 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.7.1/common/igmp.proto0000700000175300010010000000142412537544001016207 0ustar srivatspNone/* 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.7.1/common/igmpconfig.cpp0000700000175300010010000000601412537544001017014 0ustar srivatspNone/* 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.7.1/common/igmpconfig.h0000700000175300010010000000214412537544001016461 0ustar srivatspNone/* 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.7.1/common/igmppdml.cpp0000700000175300010010000001143112537544001016502 0ustar srivatspNone/* 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.7.1/common/igmppdml.h0000700000175300010010000000305212537544001016147 0ustar srivatspNone/* 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.7.1/common/intcombobox.h0000700000175300010010000000327012537544001016663 0ustar srivatspNone/* 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.7.1/common/ip4.cpp0000700000175300010010000006035012537544001015371 0ustar srivatspNone/* 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: 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() & 0x0F : 5; 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: { switch(attrib) { case FieldName: return QString("Total Length"); case FieldValue: { int totlen; totlen = data.is_override_totlen() ? data.totlen() : (protocolFramePayloadSize(streamIndex) + 20); return totlen; } case FieldFrameValue: { QByteArray fv; int totlen; totlen = data.is_override_totlen() ? data.totlen() : (protocolFramePayloadSize(streamIndex) + 20); fv.resize(2); qToBigEndian((quint16) totlen, (uchar*) fv.data()); return fv; } case FieldTextValue: { int totlen; totlen = data.is_override_totlen() ? data.totlen() : (protocolFramePayloadSize(streamIndex) + 20); 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; } // 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; } // 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()); // 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.7.1/common/ip4.h0000700000175300010010000000507412537544001015040 0ustar srivatspNone/* 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, // 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.7.1/common/ip4.proto0000700000175300010010000000377512537544001015762 0ustar srivatspNone/* 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]; //! \todo (LOW) IPv4 Options } extend Protocol { optional Ip4 ip4 = 301; } ostinato-0.7.1/common/ip4.ui0000700000175300010010000003264012537544001015225 0ustar srivatspNone 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 TODO 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.7.1/common/ip4config.cpp0000700000175300010010000002173512537544001016563 0ustar srivatspNone/* 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)); 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()); } 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()); } /* * 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.7.1/common/ip4config.h0000700000175300010010000000235112537544001016221 0ustar srivatspNone/* 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.7.1/common/ip4over4.h0000700000175300010010000000572512537544001016023 0ustar srivatspNone/* 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.7.1/common/ip4over4.proto0000700000175300010010000000167712537544001016741 0ustar srivatspNone/* 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.7.1/common/ip4over4config.h0000700000175300010010000000200112537544001017171 0ustar srivatspNone/* 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.7.1/common/ip4over6.h0000700000175300010010000000161012537544001016012 0ustar srivatspNone/* 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.7.1/common/ip4over6.proto0000700000175300010010000000155212537544001016733 0ustar srivatspNone/* 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.7.1/common/ip4over6config.h0000700000175300010010000000201712537544001017202 0ustar srivatspNone/* 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.7.1/common/ip4pdml.cpp0000700000175300010010000000612712537544001016250 0ustar srivatspNone/* 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.version", OstProto::Ip4::kVerHdrlenFieldNumber); 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.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.7.1/common/ip4pdml.h0000700000175300010010000000230012537544001015702 0ustar srivatspNone/* 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.7.1/common/ip6.cpp0000700000175300010010000005716012537544001015400 0ustar srivatspNone/* 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.7.1/common/ip6.h0000700000175300010010000000644012537544001015040 0ustar srivatspNone/* 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.7.1/common/ip6.proto0000700000175300010010000000343012537544001015750 0ustar srivatspNone/* 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.7.1/common/ip6.ui0000700000175300010010000003053112537544001015224 0ustar srivatspNone 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.7.1/common/ip6config.cpp0000700000175300010010000001604412537544001016562 0ustar srivatspNone/* 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.7.1/common/ip6config.h0000700000175300010010000000245612537544001016231 0ustar srivatspNone/* 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.7.1/common/ip6over4.h0000700000175300010010000000161012537544001016012 0ustar srivatspNone/* 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.7.1/common/ip6over4.proto0000700000175300010010000000155212537544001016733 0ustar srivatspNone/* 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.7.1/common/ip6over4config.h0000700000175300010010000000201712537544001017202 0ustar srivatspNone/* 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.7.1/common/ip6over6.h0000700000175300010010000000572512537544001016027 0ustar srivatspNone/* 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.7.1/common/ip6over6.proto0000700000175300010010000000167412537544001016742 0ustar srivatspNone/* 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.7.1/common/ip6over6config.h0000700000175300010010000000174712537544001017215 0ustar srivatspNone/* 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.7.1/common/ip6pdml.cpp0000700000175300010010000000510612537544001016246 0ustar srivatspNone/* 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); fieldMap_.insert("ipv6.class", 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.7.1/common/ip6pdml.h0000700000175300010010000000223612537544001015714 0ustar srivatspNone/* 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.7.1/common/iputils.h0000700000175300010010000000636412537544001016040 0ustar srivatspNone/* 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.7.1/common/ipv4addressdelegate.h0000700000175300010010000000306712537544001020267 0ustar srivatspNone/* 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.7.1/common/ipv6addressdelegate.h0000700000175300010010000000312012537544001020257 0ustar srivatspNone/* 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.7.1/common/ipv6addressvalidator.h0000700000175300010010000000414412537544001020501 0ustar srivatspNone/* 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.7.1/common/llc.cpp0000700000175300010010000001522012537544001015443 0ustar srivatspNone/* 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.7.1/common/llc.h0000700000175300010010000000360012537544001015107 0ustar srivatspNone/* 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.7.1/common/llc.proto0000700000175300010010000000174112537544001016027 0ustar srivatspNone/* 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.7.1/common/llc.ui0000700000175300010010000000725112537544001015303 0ustar srivatspNone 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.7.1/common/llcconfig.cpp0000700000175300010010000000545612537544001016643 0ustar srivatspNone/* 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.7.1/common/llcconfig.h0000700000175300010010000000213612537544001016300 0ustar srivatspNone/* 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.7.1/common/llcpdml.cpp0000700000175300010010000000465512537544001016332 0ustar srivatspNone/* 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.7.1/common/llcpdml.h0000700000175300010010000000223612537544001015770 0ustar srivatspNone/* 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.7.1/common/mac.cpp0000700000175300010010000002231112537544001015430 0ustar srivatspNone/* 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 #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) { } 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; default: qWarning("Unhandled dstMac_mode %d", data.dst_mac_mode()); } switch(attrib) { case FieldName: return QString("Desination"); 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; 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.toString().toULongLong(&isOk, BASE_HEX); if (isOk) data.set_dst_mac(mac); break; } case mac_srcAddr: { quint64 mac = value.toString().toULongLong(&isOk, BASE_HEX); if (isOk) 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(); if (data.dst_mac_mode() != OstProto::Mac::e_mm_fixed) count = AbstractProtocol::lcm(count, data.dst_mac_count()); if (data.src_mac_mode() != OstProto::Mac::e_mm_fixed) count = AbstractProtocol::lcm(count, data.src_mac_count()); return count; } ostinato-0.7.1/common/mac.h0000700000175300010010000000363712537544001015107 0ustar srivatspNone/* 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; }; #endif ostinato-0.7.1/common/mac.proto0000700000175300010010000000260012537544001016010 0ustar srivatspNone/* 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; } // 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.7.1/common/mac.ui0000700000175300010010000001055012537544001015265 0ustar srivatspNone mac 0 0 391 116 Form Address Mode Count Step Destination 120 0 >HH HH HH HH HH HH; Fixed Increment Decrement false 0 false 0 Source >HH HH HH HH HH HH; Fixed Increment Decrement false false 0 Qt::Vertical 20 40 ostinato-0.7.1/common/macconfig.cpp0000700000175300010010000001044212537544001016620 0ustar srivatspNone/* 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" #define MAX_MAC_ITER_COUNT 256 MacConfigForm::MacConfigForm(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); leDstMac->setValidator(new QRegExpValidator(reMac, this)); leSrcMac->setValidator(new QRegExpValidator(reMac, this)); leDstMacCount->setValidator(new QIntValidator(1, MAX_MAC_ITER_COUNT, this)); leSrcMacCount->setValidator(new QIntValidator(1, MAX_MAC_ITER_COUNT, this)); } MacConfigForm::~MacConfigForm() { } MacConfigForm* MacConfigForm::createInstance() { MacConfigForm *f = new MacConfigForm; return f; } void MacConfigForm::on_cmbDstMacMode_currentIndexChanged(int index) { if (index == OstProto::Mac::e_mm_fixed) { leDstMacCount->setEnabled(false); leDstMacStep->setEnabled(false); } else { leDstMacCount->setEnabled(true); leDstMacStep->setEnabled(true); } } void MacConfigForm::on_cmbSrcMacMode_currentIndexChanged(int index) { if (index == OstProto::Mac::e_mm_fixed) { leSrcMacCount->setEnabled(false); leSrcMacStep->setEnabled(false); } else { leSrcMacCount->setEnabled(true); leSrcMacStep->setEnabled(true); } } void MacConfigForm::loadWidget(AbstractProtocol *proto) { leDstMac->setText( proto->fieldData( MacProtocol::mac_dstAddr, AbstractProtocol::FieldTextValue ).toString()); cmbDstMacMode->setCurrentIndex( proto->fieldData( MacProtocol::mac_dstMacMode, AbstractProtocol::FieldValue ).toUInt()); leDstMacCount->setText( proto->fieldData( MacProtocol::mac_dstMacCount, AbstractProtocol::FieldValue ).toString()); leDstMacStep->setText( proto->fieldData( MacProtocol::mac_dstMacStep, AbstractProtocol::FieldValue ).toString()); leSrcMac->setText( proto->fieldData( MacProtocol::mac_srcAddr, AbstractProtocol::FieldTextValue ).toString()); cmbSrcMacMode->setCurrentIndex( proto->fieldData( MacProtocol::mac_srcMacMode, AbstractProtocol::FieldValue ).toUInt()); leSrcMacCount->setText( proto->fieldData( MacProtocol::mac_srcMacCount, AbstractProtocol::FieldValue ).toString()); leSrcMacStep->setText( proto->fieldData( MacProtocol::mac_srcMacStep, AbstractProtocol::FieldValue ).toString()); } void MacConfigForm::storeWidget(AbstractProtocol *proto) { proto->setFieldData( MacProtocol::mac_dstAddr, leDstMac->text().remove(QChar(' '))); proto->setFieldData( MacProtocol::mac_dstMacMode, cmbDstMacMode->currentIndex()); proto->setFieldData( MacProtocol::mac_dstMacCount, leDstMacCount->text()); proto->setFieldData( MacProtocol::mac_dstMacStep, leDstMacStep->text()); proto->setFieldData( MacProtocol::mac_srcAddr, leSrcMac->text().remove(QChar(' '))); proto->setFieldData( MacProtocol::mac_srcMacMode, cmbSrcMacMode->currentIndex()); proto->setFieldData( MacProtocol::mac_srcMacCount, leSrcMacCount->text()); proto->setFieldData( MacProtocol::mac_srcMacStep, leSrcMacStep->text()); } ostinato-0.7.1/common/macconfig.h0000700000175300010010000000234612537544001016271 0ustar srivatspNone/* 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.7.1/common/mld.cpp0000700000175300010010000004216412537544001015454 0ustar srivatspNone/* 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.7.1/common/mld.h0000700000175300010010000000506312537544001015116 0ustar srivatspNone/* 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.7.1/common/mld.proto0000700000175300010010000000142312537544001016026 0ustar srivatspNone/* 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.7.1/common/mldconfig.cpp0000700000175300010010000000550112537544001016634 0ustar srivatspNone/* 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.7.1/common/mldconfig.h0000700000175300010010000000213712537544001016303 0ustar srivatspNone/* 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.7.1/common/mldpdml.cpp0000700000175300010010000001171112537544001016323 0ustar srivatspNone/* 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.7.1/common/mldpdml.h0000700000175300010010000000266312537544001015776 0ustar srivatspNone/* 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.7.1/common/ostproto.pro0000700000175300010010000000350112537544001016577 0ustar srivatspNoneTEMPLATE = lib CONFIG += qt staticlib QT -= gui QT += network script LIBS += \ -lprotobuf PROTOS = \ protocol.proto \ mac.proto \ payload.proto \ eth2.proto \ dot3.proto \ llc.proto \ snap.proto \ dot2llc.proto \ dot2snap.proto \ vlan.proto \ svlan.proto \ vlanstack.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 HEADERS = \ abstractprotocol.h \ comboprotocol.h \ protocolmanager.h \ protocollist.h \ protocollistiterator.h \ streambase.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 \ userscript.h SOURCES = \ abstractprotocol.cpp \ crc32c.cpp \ protocolmanager.cpp \ protocollist.cpp \ protocollistiterator.cpp \ streambase.cpp \ SOURCES += \ mac.cpp \ vlan.cpp \ svlan.cpp \ eth2.cpp \ dot3.cpp \ llc.cpp \ snap.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 \ userscript.cpp QMAKE_DISTCLEAN += object_script.* #binding.depends = compiler_protobuf_py_make_all #QMAKE_EXTRA_TARGETS += binding include(../protobuf.pri) ostinato-0.7.1/common/ostprotogui.pro0000700000175300010010000000465412537544001017316 0ustar srivatspNoneTEMPLATE = 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 \ arp.ui \ ip4.ui \ ip6.ui \ gmp.ui \ icmp.ui \ tcp.ui \ udp.ui \ textproto.ui \ hexdump.ui \ payload.ui \ sample.ui \ userscript.ui PROTOS = \ fileformat.proto # TODO: Move fileformat related stuff into a different library - why? HEADERS = \ ostprotolib.h \ abstractfileformat.h \ fileformat.h \ ipv4addressdelegate.h \ ipv6addressdelegate.h \ pcapfileformat.h \ pdmlfileformat.h \ pythonfileformat.h \ pdmlprotocol.h \ pdmlprotocols.h \ pdmlreader.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 \ 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 \ userscriptconfig.h SOURCES += \ ostprotolib.cpp \ abstractfileformat.cpp \ fileformat.cpp \ pcapfileformat.cpp \ pdmlfileformat.cpp \ pythonfileformat.cpp \ pdmlprotocol.cpp \ pdmlprotocols.cpp \ pdmlreader.cpp \ SOURCES += \ protocolwidgetfactory.cpp \ macconfig.cpp \ vlanconfig.cpp \ eth2config.cpp \ dot3config.cpp \ llcconfig.cpp \ snapconfig.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 \ userscriptconfig.cpp SOURCES += \ vlanpdml.cpp \ svlanpdml.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.7.1/common/ostprotolib.cpp0000700000175300010010000000251012537544001017247 0ustar srivatspNone/* 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.7.1/common/ostprotolib.h0000700000175300010010000000222212537544001016714 0ustar srivatspNone/* 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.7.1/common/payload.cpp0000700000175300010010000001614712537544001016333 0ustar srivatspNone/* 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) - 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.7.1/common/payload.h0000700000175300010010000000402012537544001015763 0ustar srivatspNone/* 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.7.1/common/payload.proto0000700000175300010010000000213312537544001016702 0ustar srivatspNone/* 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.7.1/common/payload.ui0000700000175300010010000000465412537544001016166 0ustar srivatspNone 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.7.1/common/payloadconfig.cpp0000700000175300010010000000441612537544001017515 0ustar srivatspNone/* 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.7.1/common/payloadconfig.h0000700000175300010010000000233612537544001017161 0ustar srivatspNone/* 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.7.1/common/pcapfileformat.cpp0000700000175300010010000004456612537544001017704 0ustar srivatspNone/* 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 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)); } 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::openStreams(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::saveStreams(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.7.1/common/pcapfileformat.h0000700000175300010010000000504112537544001017332 0ustar srivatspNone/* 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 "abstractfileformat.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 AbstractFileFormat { friend class PdmlReader; public: PcapFileFormat(); ~PcapFileFormat(); bool openStreams(const QString fileName, OstProto::StreamConfigList &streams, QString &error); bool saveStreams(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.7.1/common/pcapfileimport.ui0000700000175300010010000000575312537544001017554 0ustar srivatspNone 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.7.1/common/pdmlfileformat.cpp0000700000175300010010000001003412537544001017674 0ustar srivatspNone/* 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::openStreams(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::saveStreams(const OstProto::StreamConfigList streams, const QString fileName, QString &error) { bool isOk = false; QTemporaryFile pcapFile; AbstractFileFormat *fmt = AbstractFileFormat::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->saveStreams(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.7.1/common/pdmlfileformat.h0000700000175300010010000000235512537544001017350 0ustar srivatspNone/* 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 "abstractfileformat.h" class PdmlFileFormat : public AbstractFileFormat { public: PdmlFileFormat(); ~PdmlFileFormat(); virtual bool openStreams(const QString fileName, OstProto::StreamConfigList &streams, QString &error); virtual bool saveStreams(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.7.1/common/pdmlprotocol.cpp0000700000175300010010000001617712537544001017423 0ustar srivatspNone/* 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.7.1/common/pdmlprotocol.h0000700000175300010010000000422512537544001017057 0ustar srivatspNone/* 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.7.1/common/pdmlprotocols.cpp0000700000175300010010000001360712537544001017601 0ustar srivatspNone/* 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.7.1/common/pdmlprotocols.h0000700000175300010010000000375312537544001017247 0ustar srivatspNone/* 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.7.1/common/pdmlreader.cpp0000700000175300010010000003722412537544001017020 0ustar srivatspNone/* 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 "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("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.7.1/common/pdmlreader.h0000700000175300010010000000365012537544001016461 0ustar srivatspNone/* 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.7.1/common/protocol.proto0000700000175300010010000001653312537544001017123 0ustar srivatspNone/* 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; // Frame Length (includes CRC) optional FrameLengthMode len_mode = 14 [default = e_fl_fixed]; 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 = 1]; 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; kEth2FieldNumber = 200; kDot3FieldNumber = 201; kLlcFieldNumber = 202; kSnapFieldNumber = 203; kSvlanFieldNumber = 204; kVlanFieldNumber = 205; kDot2LlcFieldNumber = 206; kDot2SnapFieldNumber = 207; kVlanStackFieldNumber = 208; 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; } 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; } enum NotifType { portConfigChanged = 1; } message Notification { required NotifType notif_type = 1; optional PortIdList port_id_list = 6; } 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); } ostinato-0.7.1/common/protocollist.cpp0000700000175300010010000000146112537544001017430 0ustar srivatspNone/* 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.7.1/common/protocollist.h0000700000175300010010000000145712537544001017102 0ustar srivatspNone/* 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.7.1/common/protocollistiterator.cpp0000700000175300010010000000604412537544001021204 0ustar srivatspNone/* 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.7.1/common/protocollistiterator.h0000700000175300010010000000271012537544001020645 0ustar srivatspNone/* 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.7.1/common/protocolmanager.cpp0000700000175300010010000001630512537544001020072 0ustar srivatspNone/* 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" // 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 "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); // 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::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.7.1/common/protocolmanager.h0000700000175300010010000000323712537544001017537 0ustar srivatspNone/* 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.7.1/common/protocolwidgetfactory.cpp0000700000175300010010000001622212537544001021331 0ustar srivatspNone/* 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" // 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 "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); // 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::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.7.1/common/protocolwidgetfactory.h0000700000175300010010000000257712537544001021006 0ustar srivatspNone/* 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.7.1/common/pythonfileformat.cpp0000700000175300010010000005016012537544001020265 0ustar srivatspNone/* 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::openStreams(const QString /*fileName*/, OstProto::StreamConfigList &/*streams*/, QString &/*error*/) { // NOT SUPPORTED! return false; } bool PythonFileFormat::saveStreams(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 protocol_id field if (fields.at(k)->number() == OstProto::Protocol::kProtocolIdFieldNumber) 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("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(val.c_str()); 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.7.1/common/pythonfileformat.h0000700000175300010010000000346412537544001017737 0ustar srivatspNone/* 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 "abstractfileformat.h" #include class PythonFileFormat : public AbstractFileFormat { public: PythonFileFormat(); ~PythonFileFormat(); virtual bool openStreams(const QString fileName, OstProto::StreamConfigList &streams, QString &error); virtual bool saveStreams(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.7.1/common/sample.cpp0000700000175300010010000003136312537544001016160 0ustar srivatspNone/* 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.7.1/common/sample.h0000700000175300010010000000476412537544001015632 0ustar srivatspNone/* 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.7.1/common/sample.proto0000700000175300010010000000203212537544001016530 0ustar srivatspNone/* 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.7.1/common/sample.ui0000700000175300010010000001136612537544001016014 0ustar srivatspNone 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.7.1/common/sampleconfig.cpp0000700000175300010010000000622512537544001017345 0ustar srivatspNone/* 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.7.1/common/sampleconfig.h0000700000175300010010000000221412537544001017004 0ustar srivatspNone/* 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.7.1/common/samplepdml.cpp0000700000175300010010000000774312537544001017042 0ustar srivatspNone/* 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.7.1/common/samplepdml.h0000700000175300010010000000322712537544001016500 0ustar srivatspNone/* 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.7.1/common/snap.cpp0000700000175300010010000001473712537544001015646 0ustar srivatspNone/* 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.7.1/common/snap.h0000700000175300010010000000363312537544001015304 0ustar srivatspNone/* 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.7.1/common/snap.proto0000700000175300010010000000163612537544001016221 0ustar srivatspNone/* 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.7.1/common/snap.ui0000700000175300010010000000532212537544001015467 0ustar srivatspNone 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.7.1/common/snapconfig.cpp0000700000175300010010000000431212537544001017020 0ustar srivatspNone/* 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.7.1/common/snapconfig.h0000700000175300010010000000214512537544001016467 0ustar srivatspNone/* 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.7.1/common/streambase.cpp0000700000175300010010000003174412537544001017030 0ustar srivatspNone/* 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; StreamBase::StreamBase() : 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 ProtocolListIterator* StreamBase::createProtocolListIterator() const { return new ProtocolListIterator(*currentFrameProtocols); } quint32 StreamBase::id() { 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::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 frameCount; } // 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; } int StreamBase::frameValue(uchar *buf, int bufMaxSize, int frameIndex) const { int pktLen, len = 0; pktLen = frameLen(frameIndex); // pktLen is adjusted for CRC/FCS which will be added by the NIC pktLen -= kFcsSize; if ((pktLen < 0) || (pktLen > bufMaxSize)) return 0; ProtocolListIterator *iter; iter = createProtocolListIterator(); while (iter->hasNext()) { AbstractProtocol *proto; QByteArray ba; proto = iter->next(); ba = proto->protocolFrameValue(frameIndex); if (len + ba.size() < bufMaxSize) memcpy(buf+len, ba.constData(), ba.size()); len += ba.size(); } delete iter; // Pad with zero, if required if (len < pktLen) memset(buf+len, 0, pktLen-len); return pktLen; } bool StreamBase::preflightCheck(QString &result) const { bool pass = true; int count = isFrameSizeVariable() ? frameCount() : 1; for (int i = 0; i < count; i++) { if (frameLen(i) < (frameProtocolLength(i) + kFcsSize)) { result += QString("One or more frames may be truncated - " "frame length should be at least %1.\n") .arg(frameProtocolLength(i) + kFcsSize); pass = false; } if (frameLen(i) > 1522) { result += QString("Jumbo frames may be truncated or dropped " "if not supported by the hardware\n"); pass = false; } } return pass; } bool StreamBase::StreamLessThan(StreamBase* stream1, StreamBase* stream2) { return stream1->ordinal() < stream2->ordinal() ? true : false; } ostinato-0.7.1/common/streambase.h0000700000175300010010000000711212537544001016465 0ustar srivatspNone/* 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 { private: OstProto::StreamId *mStreamId; OstProto::StreamCore *mCore; OstProto::StreamControl *mControl; ProtocolList *currentFrameProtocols; public: StreamBase(); ~StreamBase(); void protoDataCopyFrom(const OstProto::Stream &stream); void protoDataCopyInto(OstProto::Stream &stream) const; 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(); 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 frameVariableCount() const; int frameProtocolLength(int frameIndex) const; int frameCount() const; int frameValue(uchar *buf, int bufMaxSize, int frameIndex) const; bool preflightCheck(QString &result) const; static bool StreamLessThan(StreamBase* stream1, StreamBase* stream2); }; #endif ostinato-0.7.1/common/svlan.cpp0000700000175300010010000000337112537544001016020 0ustar srivatspNone/* 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.7.1/common/svlan.h0000700000175300010010000000235112537544001015462 0ustar srivatspNone/* 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.7.1/common/svlan.proto0000700000175300010010000000142712537544001016401 0ustar srivatspNone/* 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.7.1/common/svlanconfig.h0000700000175300010010000000143512537544001016652 0ustar srivatspNone/* 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.7.1/common/svlanpdml.cpp0000700000175300010010000000742112537544001016675 0ustar srivatspNone/* 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.7.1/common/svlanpdml.h0000700000175300010010000000237212537544001016342 0ustar srivatspNone/* 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.7.1/common/tcp.cpp0000700000175300010010000004154412537544001015467 0ustar srivatspNone/* 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"); } 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.7.1/common/tcp.h0000700000175300010010000000451312537544001015127 0ustar srivatspNone/* 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.7.1/common/tcp.proto0000700000175300010010000000252512537544001016044 0ustar srivatspNone/* 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.7.1/common/tcp.ui0000700000175300010010000001521412537544001015315 0ustar srivatspNone 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 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.7.1/common/tcpconfig.cpp0000700000175300010010000001256112537544001016652 0ustar srivatspNone/* 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.7.1/common/tcpconfig.h0000700000175300010010000000213512537544001016313 0ustar srivatspNone/* 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.7.1/common/tcppdml.cpp0000700000175300010010000000652212537544001016341 0ustar srivatspNone/* 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.7.1/common/tcppdml.h0000700000175300010010000000233512537544001016004 0ustar srivatspNone/* 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.7.1/common/textproto.cpp0000700000175300010010000001374612537544001016754 0ustar srivatspNone/* 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.7.1/common/textproto.h0000700000175300010010000000417012537544001016410 0ustar srivatspNone/* 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.7.1/common/textproto.proto0000700000175300010010000000220712537544001017323 0ustar srivatspNone/* 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.7.1/common/textproto.ui0000700000175300010010000000517512537544001016604 0ustar srivatspNone 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.7.1/common/textprotoconfig.cpp0000700000175300010010000000475612537544001020143 0ustar srivatspNone/* 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.7.1/common/textprotoconfig.h0000700000175300010010000000224112537544001017573 0ustar srivatspNone/* 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.7.1/common/textprotopdml.cpp0000700000175300010010000001126612537544001017624 0ustar srivatspNone/* 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.7.1/common/textprotopdml.h0000700000175300010010000000305512537544001017266 0ustar srivatspNone/* 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.7.1/common/udp.cpp0000700000175300010010000002646712537544001015500 0ustar srivatspNone/* 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); 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.7.1/common/udp.h0000700000175300010010000000402712537544001015131 0ustar srivatspNone/* 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.7.1/common/udp.proto0000700000175300010010000000215212537544001016042 0ustar srivatspNone/* 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.7.1/common/udp.ui0000700000175300010010000001013412537544001015313 0ustar srivatspNone 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.7.1/common/udpconfig.cpp0000700000175300010010000000622212537544001016651 0ustar srivatspNone/* 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.7.1/common/udpconfig.h0000700000175300010010000000213612537544001016316 0ustar srivatspNone/* 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.7.1/common/udppdml.cpp0000700000175300010010000000317612537544001016345 0ustar srivatspNone/* 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.7.1/common/udppdml.h0000700000175300010010000000173512537544001016011 0ustar srivatspNone/* 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.7.1/common/userscript.cpp0000700000175300010010000003406612537544001017105 0ustar srivatspNone/* 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.7.1/common/userscript.h0000700000175300010010000001104212537544001016537 0ustar srivatspNone/* 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.7.1/common/userscript.proto0000700000175300010010000000153612537544001017462 0ustar srivatspNone/* 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.7.1/common/userscript.ui0000700000175300010010000000337612537544001016740 0ustar srivatspNone UserScript 0 0 517 335 Form 10 0 QFrame::Panel QFrame::Sunken 4 4 4 4 4 Unknown Compile ostinato-0.7.1/common/userscriptconfig.cpp0000700000175300010010000000571712537544001020274 0ustar srivatspNone/* 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.7.1/common/userscriptconfig.h0000700000175300010010000000256612537544001017740 0ustar srivatspNone/* 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.7.1/common/vlan.cpp0000700000175300010010000001557212537544001015643 0ustar srivatspNone/* 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.7.1/common/vlan.h0000700000175300010010000000346712537544001015310 0ustar srivatspNone/* 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.7.1/common/vlan.proto0000700000175300010010000000172212537544001016214 0ustar srivatspNone/* 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.7.1/common/vlan.ui0000700000175300010010000000765412537544001015500 0ustar srivatspNone 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.7.1/common/vlanconfig.cpp0000700000175300010010000000473312537544001017026 0ustar srivatspNone/* 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.7.1/common/vlanconfig.h0000700000175300010010000000214612537544001016467 0ustar srivatspNone/* 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.7.1/common/vlanpdml.cpp0000700000175300010010000000605312537544001016512 0ustar srivatspNone/* 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.7.1/common/vlanpdml.h0000700000175300010010000000236612537544001016162 0ustar srivatspNone/* 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.7.1/common/vlanstack.h0000700000175300010010000000161612537544001016330 0ustar srivatspNone/* 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.7.1/common/vlanstack.proto0000700000175300010010000000155112537544001017242 0ustar srivatspNone/* 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.7.1/common/vlanstackconfig.h0000700000175300010010000000207012537544001017511 0ustar srivatspNone/* 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.7.1/COPYING0000700000175300010010000010451312537544000013733 0ustar srivatspNone 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.7.1/extra/0000700000175300010010000000000012537544001014015 5ustar srivatspNoneostinato-0.7.1/extra/extra.pro0000700000175300010010000000005512537544001015665 0ustar srivatspNoneTEMPLATE = subdirs SUBDIRS = \ qhexedit2 ostinato-0.7.1/extra/qhexedit2/0000700000175300010010000000000012537544001015712 5ustar srivatspNoneostinato-0.7.1/extra/qhexedit2/qhexedit2.pro0000700000175300010010000000042312537544001020333 0ustar srivatspNoneTEMPLATE = lib CONFIG += qt staticlib warn_on HEADERS = src/commands.h\ src/qhexedit.h \ src/qhexedit_p.h \ src/xbytearray.h SOURCES = src/commands.cpp \ src/qhexedit.cpp \ src/qhexedit_p.cpp \ src/xbytearray.cpp ostinato-0.7.1/extra/qhexedit2/src/0000700000175300010010000000000012537544001016501 5ustar srivatspNoneostinato-0.7.1/extra/qhexedit2/src/commands.cpp0000700000175300010010000000545312537544001021020 0ustar srivatspNone#include "commands.h" CharCommand::CharCommand(XByteArray * xData, Cmd cmd, int charPos, char newChar, QUndoCommand *parent) : QUndoCommand(parent) { _xData = xData; _charPos = charPos; _newChar = newChar; _cmd = cmd; } bool CharCommand::mergeWith(const QUndoCommand *command) { const CharCommand *nextCommand = static_cast(command); bool result = false; if (_cmd != remove) { if (nextCommand->_cmd == replace) if (nextCommand->_charPos == _charPos) { _newChar = nextCommand->_newChar; result = true; } } return result; } void CharCommand::undo() { switch (_cmd) { case insert: _xData->remove(_charPos, 1); break; case replace: _xData->replace(_charPos, _oldChar); _xData->setDataChanged(_charPos, _wasChanged); break; case remove: _xData->insert(_charPos, _oldChar); _xData->setDataChanged(_charPos, _wasChanged); break; } } void CharCommand::redo() { switch (_cmd) { case insert: _xData->insert(_charPos, _newChar); break; case replace: _oldChar = _xData->data()[_charPos]; _wasChanged = _xData->dataChanged(_charPos); _xData->replace(_charPos, _newChar); break; case remove: _oldChar = _xData->data()[_charPos]; _wasChanged = _xData->dataChanged(_charPos); _xData->remove(_charPos, 1); break; } } ArrayCommand::ArrayCommand(XByteArray * xData, Cmd cmd, int baPos, QByteArray newBa, int len, QUndoCommand *parent) : QUndoCommand(parent) { _cmd = cmd; _xData = xData; _baPos = baPos; _newBa = newBa; _len = len; } void ArrayCommand::undo() { switch (_cmd) { case insert: _xData->remove(_baPos, _newBa.length()); break; case replace: _xData->replace(_baPos, _oldBa); _xData->setDataChanged(_baPos, _wasChanged); break; case remove: _xData->insert(_baPos, _oldBa); _xData->setDataChanged(_baPos, _wasChanged); break; } } void ArrayCommand::redo() { switch (_cmd) { case insert: _xData->insert(_baPos, _newBa); break; case replace: _oldBa = _xData->data().mid(_baPos, _len); _wasChanged = _xData->dataChanged(_baPos, _len); _xData->replace(_baPos, _newBa); break; case remove: _oldBa = _xData->data().mid(_baPos, _len); _wasChanged = _xData->dataChanged(_baPos, _len); _xData->remove(_baPos, _len); break; } } ostinato-0.7.1/extra/qhexedit2/src/commands.h0000700000175300010010000000357512537544001020470 0ustar srivatspNone#ifndef COMMANDS_H #define COMMANDS_H /** \cond docNever */ #include #include "xbytearray.h" /*! CharCommand is a class to prived 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. replace and remove characters. A manipulation stores allways to 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", replace it with "03" and the replace it with "34". These 3 steps are combined into a single step, insert a "34". */ class CharCommand : public QUndoCommand { public: enum { Id = 1234 }; enum Cmd {insert, remove, replace}; CharCommand(XByteArray * xData, Cmd cmd, int charPos, char newChar, QUndoCommand *parent=0); void undo(); void redo(); bool mergeWith(const QUndoCommand *command); int id() const { return Id; } private: XByteArray * _xData; int _charPos; bool _wasChanged; char _newChar; char _oldChar; Cmd _cmd; }; /*! ArrayCommand provides undo/redo functionality for handling binary strings. It can undo/redo insert, replace and remove binary strins (QByteArrays). */ class ArrayCommand : public QUndoCommand { public: enum Cmd {insert, remove, replace}; ArrayCommand(XByteArray * xData, Cmd cmd, int baPos, QByteArray newBa=QByteArray(), int len=0, QUndoCommand *parent=0); void undo(); void redo(); private: Cmd _cmd; XByteArray * _xData; int _baPos; int _len; QByteArray _wasChanged; QByteArray _newBa; QByteArray _oldBa; }; /** \endcond docNever */ #endif // COMMANDS_H ostinato-0.7.1/extra/qhexedit2/src/license.txt0000700000175300010010000006364112537544001020701 0ustar srivatspNone 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.7.1/extra/qhexedit2/src/qhexedit.cpp0000700000175300010010000000551112537544001021025 0ustar srivatspNone#include #include "qhexedit.h" QHexEdit::QHexEdit(QWidget *parent) : QScrollArea(parent) { qHexEdit_p = new QHexEditPrivate(this); setWidget(qHexEdit_p); setWidgetResizable(true); connect(qHexEdit_p, SIGNAL(currentAddressChanged(int)), this, SIGNAL(currentAddressChanged(int))); connect(qHexEdit_p, SIGNAL(currentSizeChanged(int)), this, SIGNAL(currentSizeChanged(int))); connect(qHexEdit_p, SIGNAL(dataChanged()), this, SIGNAL(dataChanged())); connect(qHexEdit_p, SIGNAL(overwriteModeChanged(bool)), this, SIGNAL(overwriteModeChanged(bool))); setFocusPolicy(Qt::NoFocus); } void QHexEdit::insert(int i, const QByteArray & ba) { qHexEdit_p->insert(i, ba); } void QHexEdit::insert(int i, char ch) { qHexEdit_p->insert(i, ch); } void QHexEdit::remove(int pos, int len) { qHexEdit_p->remove(pos, len); } QString QHexEdit::toReadableString() { return qHexEdit_p->toRedableString(); } QString QHexEdit::selectionToReadableString() { return qHexEdit_p->selectionToReadableString(); } void QHexEdit::setAddressArea(bool addressArea) { qHexEdit_p->setAddressArea(addressArea); } void QHexEdit::redo() { qHexEdit_p->redo(); } void QHexEdit::undo() { qHexEdit_p->undo(); } void QHexEdit::setAddressWidth(int addressWidth) { qHexEdit_p->setAddressWidth(addressWidth); } void QHexEdit::setAsciiArea(bool asciiArea) { qHexEdit_p->setAsciiArea(asciiArea); } void QHexEdit::setHighlighting(bool mode) { qHexEdit_p->setHighlighting(mode); } void QHexEdit::setAddressOffset(int offset) { qHexEdit_p->setAddressOffset(offset); } int QHexEdit::addressOffset() { return qHexEdit_p->addressOffset(); } void QHexEdit::setData(const QByteArray &data) { qHexEdit_p->setData(data); } QByteArray QHexEdit::data() { return qHexEdit_p->data(); } void QHexEdit::setAddressAreaColor(const QColor &color) { qHexEdit_p->setAddressAreaColor(color); } QColor QHexEdit::addressAreaColor() { return qHexEdit_p->addressAreaColor(); } void QHexEdit::setHighlightingColor(const QColor &color) { qHexEdit_p->setHighlightingColor(color); } QColor QHexEdit::highlightingColor() { return qHexEdit_p->highlightingColor(); } void QHexEdit::setSelectionColor(const QColor &color) { qHexEdit_p->setSelectionColor(color); } QColor QHexEdit::selectionColor() { return qHexEdit_p->selectionColor(); } void QHexEdit::setOverwriteMode(bool overwriteMode) { qHexEdit_p->setOverwriteMode(overwriteMode); } bool QHexEdit::overwriteMode() { return qHexEdit_p->overwriteMode(); } void QHexEdit::setReadOnly(bool readOnly) { qHexEdit_p->setReadOnly(readOnly); } bool QHexEdit::isReadOnly() { return qHexEdit_p->isReadOnly(); } void QHexEdit::setFont(const QFont &font) { qHexEdit_p->setFont(font); } const QFont & QHexEdit::font() const { return qHexEdit_p->font(); } ostinato-0.7.1/extra/qhexedit2/src/qhexedit.h0000700000175300010010000001633112537544001020474 0ustar srivatspNone#ifndef QHEXEDIT_H #define QHEXEDIT_H #include #include "qhexedit_p.h" /** \mainpage QHexEdit is a binary editor widget for Qt. \version Version 0.6.1 \image html hexedit.png */ /*! QHexEdit is a hex editor widget written in C++ for the Qt (Qt4) 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. 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. This widget can only handle small amounts of data. The size has to be below 10 megabytes, otherwise the scroll sliders ard not shown and you can't scroll any more. */ class QHexEdit : public QScrollArea { Q_OBJECT /*! Property data holds the content of QHexEdit. Call setData() to set the content of QHexEdit, data() returns the actual content. */ Q_PROPERTY(QByteArray data READ data WRITE setData) /*! 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 actual value. */ Q_PROPERTY(int addressOffset READ addressOffset WRITE setAddressOffset) /*! 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 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) /*! 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 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) /*! 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); /*! Inserts a byte array. \param i Index position, where to insert \param ba byte array, which is to insert In overwrite mode, the existing data will be overwritten, in insertmode ba will be insertet and size of data grows. */ void insert(int i, const QByteArray & ba); /*! Inserts a char. \param i Index position, where to insert \param ch Char, which is to insert In overwrite mode, the existing data will be overwritten, in insertmode ba will be insertet and size of data grows. */ void insert(int i, char ch); /*! Removes len bytes from the content. \param pos Index position, where to remove \param len Amount of bytes to remove In overwrite mode, the existing bytes will be overwriten with 0x00. */ void remove(int pos, int len=1); /*! Gives back a formatted image of the content of QHexEdit */ QString toReadableString(); /*! Gives back a formatted image of the selected content of QHexEdit */ QString selectionToReadableString(); /*! \cond docNever */ void setAddressOffset(int offset); int addressOffset(); void setData(QByteArray const &data); QByteArray data(); void setAddressAreaColor(QColor const &color); QColor addressAreaColor(); void setHighlightingColor(QColor const &color); QColor highlightingColor(); void setSelectionColor(QColor const &color); QColor selectionColor(); void setOverwriteMode(bool); bool overwriteMode(); void setReadOnly(bool); bool isReadOnly(); const QFont &font() const; void setFont(const QFont &); /*! \endcond docNever */ 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(); /*! Set the minimum width of the address area. \param addressWidth Width in characters. */ void setAddressWidth(int addressWidth); /*! Switch the address area on or off. \param addressArea true (show it), false (hide it). */ void setAddressArea(bool addressArea); /*! Switch the ascii area on or off. \param asciiArea true (show it), false (hide it). */ void setAsciiArea(bool asciiArea); /*! Switch the highlighting feature on or of. \param mode true (show it), false (hide it). */ void setHighlighting(bool mode); /*! 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(int address); /*! Contains the size of the data to edit. */ void currentSizeChanged(int size); /*! The signal is emited every time, the data is changed. */ void dataChanged(); /*! The signal is emited every time, the overwrite mode is changed. */ void overwriteModeChanged(bool state); private: /*! \cond docNever */ QHexEditPrivate *qHexEdit_p; QHBoxLayout *layout; QScrollArea *scrollArea; /*! \endcond docNever */ }; #endif ostinato-0.7.1/extra/qhexedit2/src/qhexedit_p.cpp0000700000175300010010000005400312537544001021344 0ustar srivatspNone#include #include "qhexedit_p.h" #include "commands.h" const int HEXCHARS_IN_LINE = 47; const int GAP_ADR_HEX = 10; const int GAP_HEX_ASCII = 16; const int BYTES_PER_LINE = 16; QHexEditPrivate::QHexEditPrivate(QScrollArea *parent) : QWidget(parent) { _undoStack = new QUndoStack(this); _scrollArea = parent; setAddressWidth(4); setAddressOffset(0); setAddressArea(true); setAsciiArea(true); setHighlighting(true); setOverwriteMode(true); setReadOnly(false); setAddressAreaColor(QColor(0xd4, 0xd4, 0xd4, 0xff)); setHighlightingColor(QColor(0xff, 0xff, 0x99, 0xff)); setSelectionColor(QColor(0x6d, 0x9e, 0xff, 0xff)); setFont(QFont("Courier", 10)); _size = 0; resetSelection(0); setFocusPolicy(Qt::StrongFocus); connect(&_cursorTimer, SIGNAL(timeout()), this, SLOT(updateCursor())); _cursorTimer.setInterval(500); _cursorTimer.start(); } void QHexEditPrivate::setAddressOffset(int offset) { _xData.setAddressOffset(offset); adjust(); } int QHexEditPrivate::addressOffset() { return _xData.addressOffset(); } void QHexEditPrivate::setData(const QByteArray &data) { _xData.setData(data); _undoStack->clear(); adjust(); setCursorPos(0); } QByteArray QHexEditPrivate::data() { return _xData.data(); } void QHexEditPrivate::setAddressAreaColor(const QColor &color) { _addressAreaColor = color; update(); } QColor QHexEditPrivate::addressAreaColor() { return _addressAreaColor; } void QHexEditPrivate::setHighlightingColor(const QColor &color) { _highlightingColor = color; update(); } QColor QHexEditPrivate::highlightingColor() { return _highlightingColor; } void QHexEditPrivate::setSelectionColor(const QColor &color) { _selectionColor = color; update(); } QColor QHexEditPrivate::selectionColor() { return _selectionColor; } void QHexEditPrivate::setReadOnly(bool readOnly) { _readOnly = readOnly; } bool QHexEditPrivate::isReadOnly() { return _readOnly; } XByteArray & QHexEditPrivate::xData() { return _xData; } void QHexEditPrivate::insert(int index, const QByteArray & ba) { if (ba.length() > 0) { if (_overwriteMode) { QUndoCommand *arrayCommand= new ArrayCommand(&_xData, ArrayCommand::replace, index, ba, ba.length()); _undoStack->push(arrayCommand); emit dataChanged(); } else { QUndoCommand *arrayCommand= new ArrayCommand(&_xData, ArrayCommand::insert, index, ba, ba.length()); _undoStack->push(arrayCommand); emit dataChanged(); } } } void QHexEditPrivate::insert(int index, char ch) { QUndoCommand *charCommand = new CharCommand(&_xData, CharCommand::insert, index, ch); _undoStack->push(charCommand); emit dataChanged(); } void QHexEditPrivate::remove(int index, int len) { if (len > 0) { if (len == 1) { if (_overwriteMode) { QUndoCommand *charCommand = new CharCommand(&_xData, CharCommand::replace, index, char(0)); _undoStack->push(charCommand); emit dataChanged(); } else { QUndoCommand *charCommand = new CharCommand(&_xData, CharCommand::remove, index, char(0)); _undoStack->push(charCommand); emit dataChanged(); } } else { QByteArray ba = QByteArray(len, char(0)); if (_overwriteMode) { QUndoCommand *arrayCommand = new ArrayCommand(&_xData, ArrayCommand::replace, index, ba, ba.length()); _undoStack->push(arrayCommand); emit dataChanged(); } else { QUndoCommand *arrayCommand= new ArrayCommand(&_xData, ArrayCommand::remove, index, ba, len); _undoStack->push(arrayCommand); emit dataChanged(); } } } } void QHexEditPrivate::replace(int index, char ch) { QUndoCommand *charCommand = new CharCommand(&_xData, CharCommand::replace, index, ch); _undoStack->push(charCommand); emit dataChanged(); } void QHexEditPrivate::replace(int index, const QByteArray & ba) { QUndoCommand *arrayCommand= new ArrayCommand(&_xData, ArrayCommand::replace, index, ba, ba.length()); _undoStack->push(arrayCommand); emit dataChanged(); } void QHexEditPrivate::setAddressArea(bool addressArea) { _addressArea = addressArea; adjust(); setCursorPos(_cursorPosition); } void QHexEditPrivate::setAddressWidth(int addressWidth) { _xData.setAddressWidth(addressWidth); setCursorPos(_cursorPosition); } void QHexEditPrivate::setAsciiArea(bool asciiArea) { _asciiArea = asciiArea; adjust(); } void QHexEditPrivate::setFont(const QFont &font) { QWidget::setFont(font); adjust(); } void QHexEditPrivate::setHighlighting(bool mode) { _highlighting = mode; update(); } void QHexEditPrivate::setOverwriteMode(bool overwriteMode) { _overwriteMode = overwriteMode; } bool QHexEditPrivate::overwriteMode() { return _overwriteMode; } void QHexEditPrivate::redo() { _undoStack->redo(); emit dataChanged(); setCursorPos(_cursorPosition); update(); } void QHexEditPrivate::undo() { _undoStack->undo(); emit dataChanged(); setCursorPos(_cursorPosition); update(); } QString QHexEditPrivate::toRedableString() { return _xData.toRedableString(); } QString QHexEditPrivate::selectionToReadableString() { return _xData.toRedableString(getSelectionBegin(), getSelectionEnd()); } void QHexEditPrivate::keyPressEvent(QKeyEvent *event) { int charX = (_cursorX - _xPosHex) / _charWidth; int posX = (charX / 3) * 2 + (charX % 3); int posBa = (_cursorY / _charHeight) * BYTES_PER_LINE + posX / 2; /*****************************************************************************/ /* Cursor movements */ /*****************************************************************************/ if (event->matches(QKeySequence::MoveToNextChar)) { setCursorPos(_cursorPosition + 1); resetSelection(_cursorPosition); } if (event->matches(QKeySequence::MoveToPreviousChar)) { setCursorPos(_cursorPosition - 1); resetSelection(_cursorPosition); } if (event->matches(QKeySequence::MoveToEndOfLine)) { setCursorPos(_cursorPosition | (2 * BYTES_PER_LINE -1)); resetSelection(_cursorPosition); } if (event->matches(QKeySequence::MoveToStartOfLine)) { setCursorPos(_cursorPosition - (_cursorPosition % (2 * BYTES_PER_LINE))); resetSelection(_cursorPosition); } if (event->matches(QKeySequence::MoveToPreviousLine)) { setCursorPos(_cursorPosition - (2 * BYTES_PER_LINE)); resetSelection(_cursorPosition); } if (event->matches(QKeySequence::MoveToNextLine)) { setCursorPos(_cursorPosition + (2 * BYTES_PER_LINE)); resetSelection(_cursorPosition); } if (event->matches(QKeySequence::MoveToNextPage)) { setCursorPos(_cursorPosition + (((_scrollArea->viewport()->height() / _charHeight) - 1) * 2 * BYTES_PER_LINE)); resetSelection(_cursorPosition); } if (event->matches(QKeySequence::MoveToPreviousPage)) { setCursorPos(_cursorPosition - (((_scrollArea->viewport()->height() / _charHeight) - 1) * 2 * BYTES_PER_LINE)); resetSelection(_cursorPosition); } if (event->matches(QKeySequence::MoveToEndOfDocument)) { setCursorPos(_xData.size() * 2); resetSelection(_cursorPosition); } if (event->matches(QKeySequence::MoveToStartOfDocument)) { setCursorPos(0); resetSelection(_cursorPosition); } /*****************************************************************************/ /* Select commands */ /*****************************************************************************/ if (event->matches(QKeySequence::SelectAll)) { resetSelection(0); setSelection(2*_xData.size() + 1); } if (event->matches(QKeySequence::SelectNextChar)) { int pos = _cursorPosition + 1; setCursorPos(pos); setSelection(pos); } if (event->matches(QKeySequence::SelectPreviousChar)) { int pos = _cursorPosition - 1; setSelection(pos); setCursorPos(pos); } if (event->matches(QKeySequence::SelectEndOfLine)) { int pos = _cursorPosition - (_cursorPosition % (2 * BYTES_PER_LINE)) + (2 * BYTES_PER_LINE); setCursorPos(pos); setSelection(pos); } if (event->matches(QKeySequence::SelectStartOfLine)) { int pos = _cursorPosition - (_cursorPosition % (2 * BYTES_PER_LINE)); setCursorPos(pos); setSelection(pos); } if (event->matches(QKeySequence::SelectPreviousLine)) { int pos = _cursorPosition - (2 * BYTES_PER_LINE); setCursorPos(pos); setSelection(pos); } if (event->matches(QKeySequence::SelectNextLine)) { int pos = _cursorPosition + (2 * BYTES_PER_LINE); setCursorPos(pos); setSelection(pos); } if (event->matches(QKeySequence::SelectNextPage)) { int pos = _cursorPosition + (((_scrollArea->viewport()->height() / _charHeight) - 1) * 2 * BYTES_PER_LINE); setCursorPos(pos); setSelection(pos); } if (event->matches(QKeySequence::SelectPreviousPage)) { int pos = _cursorPosition - (((_scrollArea->viewport()->height() / _charHeight) - 1) * 2 * BYTES_PER_LINE); setCursorPos(pos); setSelection(pos); } if (event->matches(QKeySequence::SelectEndOfDocument)) { int pos = _xData.size() * 2; setCursorPos(pos); setSelection(pos); } if (event->matches(QKeySequence::SelectStartOfDocument)) { int pos = 0; setCursorPos(pos); setSelection(pos); } /*****************************************************************************/ /* Edit Commands */ /*****************************************************************************/ if (!_readOnly) { /* Hex input */ int key = int(event->text()[0].toAscii()); if ((key>='0' && key<='9') || (key>='a' && key <= 'f')) { if (getSelectionBegin() != getSelectionEnd()) { posBa = getSelectionBegin(); remove(posBa, getSelectionEnd() - posBa); setCursorPos(2*posBa); resetSelection(2*posBa); } // If insert mode, then insert a byte if (_overwriteMode == false) if ((charX % 3) == 0) { insert(posBa, char(0)); } // Change content if (_xData.size() > 0) { QByteArray hexValue = _xData.data().mid(posBa, 1).toHex(); if ((charX % 3) == 0) hexValue[0] = key; else hexValue[1] = key; replace(posBa, QByteArray().fromHex(hexValue)[0]); setCursorPos(_cursorPosition + 1); resetSelection(_cursorPosition); } } /* Cut & Paste */ if (event->matches(QKeySequence::Cut)) { QString result = QString(); for (int idx = getSelectionBegin(); idx < getSelectionEnd(); idx++) { result += _xData.data().mid(idx, 1).toHex() + " "; if ((idx % 16) == 15) result.append("\n"); } remove(getSelectionBegin(), getSelectionEnd() - getSelectionBegin()); QClipboard *clipboard = QApplication::clipboard(); clipboard->setText(result); setCursorPos(getSelectionBegin()); resetSelection(getSelectionBegin()); } if (event->matches(QKeySequence::Paste)) { QClipboard *clipboard = QApplication::clipboard(); QByteArray ba = QByteArray().fromHex(clipboard->text().toLatin1()); insert(_cursorPosition / 2, ba); setCursorPos(_cursorPosition + 2 * ba.length()); resetSelection(getSelectionBegin()); } /* Delete char */ if (event->matches(QKeySequence::Delete)) { if (getSelectionBegin() != getSelectionEnd()) { posBa = getSelectionBegin(); remove(posBa, getSelectionEnd() - posBa); setCursorPos(2*posBa); resetSelection(2*posBa); } else { if (_overwriteMode) replace(posBa, char(0)); else remove(posBa, 1); } } /* Backspace */ if ((event->key() == Qt::Key_Backspace) && (event->modifiers() == Qt::NoModifier)) { if (getSelectionBegin() != getSelectionEnd()) { posBa = getSelectionBegin(); remove(posBa, getSelectionEnd() - posBa); setCursorPos(2*posBa); resetSelection(2*posBa); } else { if (posBa > 0) { if (_overwriteMode) replace(posBa - 1, char(0)); else remove(posBa - 1, 1); setCursorPos(_cursorPosition - 2); } } } /* undo */ if (event->matches(QKeySequence::Undo)) { undo(); } /* redo */ if (event->matches(QKeySequence::Redo)) { redo(); } } if (event->matches(QKeySequence::Copy)) { QString result = QString(); for (int idx = getSelectionBegin(); idx < getSelectionEnd(); idx++) { result += _xData.data().mid(idx, 1).toHex() + " "; if ((idx % 16) == 15) result.append('\n'); } QClipboard *clipboard = QApplication::clipboard(); clipboard->setText(result); } // Switch between insert/overwrite mode if ((event->key() == Qt::Key_Insert) && (event->modifiers() == Qt::NoModifier)) { _overwriteMode = !_overwriteMode; setCursorPos(_cursorPosition); overwriteModeChanged(_overwriteMode); } _scrollArea->ensureVisible(_cursorX, _cursorY + _charHeight/2, 3, _charHeight/2 + 2); update(); } void QHexEditPrivate::mouseMoveEvent(QMouseEvent * event) { _blink = false; update(); int actPos = cursorPos(event->pos()); setCursorPos(actPos); setSelection(actPos); } void QHexEditPrivate::mousePressEvent(QMouseEvent * event) { _blink = false; update(); int cPos = cursorPos(event->pos()); resetSelection(cPos); setCursorPos(cPos); } void QHexEditPrivate::paintEvent(QPaintEvent *event) { QPainter painter(this); // draw some patterns if needed painter.fillRect(event->rect(), this->palette().color(QPalette::Base)); if (_addressArea) painter.fillRect(QRect(_xPosAdr, event->rect().top(), _xPosHex - GAP_ADR_HEX + 2, height()), _addressAreaColor); if (_asciiArea) { int linePos = _xPosAscii - (GAP_HEX_ASCII / 2); painter.setPen(Qt::gray); painter.drawLine(linePos, event->rect().top(), linePos, height()); } painter.setPen(this->palette().color(QPalette::WindowText)); // calc position int firstLineIdx = ((event->rect().top()/ _charHeight) - _charHeight) * BYTES_PER_LINE; if (firstLineIdx < 0) firstLineIdx = 0; int lastLineIdx = ((event->rect().bottom() / _charHeight) + _charHeight) * BYTES_PER_LINE; if (lastLineIdx > _xData.size()) lastLineIdx = _xData.size(); int yPosStart = ((firstLineIdx) / BYTES_PER_LINE) * _charHeight + _charHeight; // paint address area if (_addressArea) { for (int lineIdx = firstLineIdx, yPos = yPosStart; lineIdx < lastLineIdx; lineIdx += BYTES_PER_LINE, yPos +=_charHeight) { QString address = QString("%1") .arg(lineIdx + _xData.addressOffset(), _xData.realAddressNumbers(), 16, QChar('0')); painter.drawText(_xPosAdr, yPos, address); } } // paint hex area QByteArray hexBa(_xData.data().mid(firstLineIdx, lastLineIdx - firstLineIdx + 1).toHex()); QBrush highLighted = QBrush(_highlightingColor); QPen colHighlighted = QPen(this->palette().color(QPalette::WindowText)); QBrush selected = QBrush(_selectionColor); QPen colSelected = QPen(Qt::white); QPen colStandard = QPen(this->palette().color(QPalette::WindowText)); painter.setBackgroundMode(Qt::TransparentMode); for (int lineIdx = firstLineIdx, yPos = yPosStart; lineIdx < lastLineIdx; lineIdx += BYTES_PER_LINE, yPos +=_charHeight) { QByteArray hex; int xPos = _xPosHex; for (int colIdx = 0; ((lineIdx + colIdx) < _xData.size() and (colIdx < BYTES_PER_LINE)); colIdx++) { int posBa = lineIdx + colIdx; if ((getSelectionBegin() <= posBa) && (getSelectionEnd() > posBa)) { painter.setBackground(selected); painter.setBackgroundMode(Qt::OpaqueMode); painter.setPen(colSelected); } else { if (_highlighting) { // hilight diff bytes painter.setBackground(highLighted); if (_xData.dataChanged(posBa)) { painter.setPen(colHighlighted); painter.setBackgroundMode(Qt::OpaqueMode); } else { painter.setPen(colStandard); painter.setBackgroundMode(Qt::TransparentMode); } } } // render hex value if (colIdx == 0) { hex = hexBa.mid((lineIdx - firstLineIdx) * 2, 2); painter.drawText(xPos, yPos, hex); xPos += 2 * _charWidth; } else { hex = hexBa.mid((lineIdx + colIdx - firstLineIdx) * 2, 2).prepend(" "); painter.drawText(xPos, yPos, hex); xPos += 3 * _charWidth; } } } painter.setBackgroundMode(Qt::TransparentMode); painter.setPen(this->palette().color(QPalette::WindowText)); // paint ascii area if (_asciiArea) { for (int lineIdx = firstLineIdx, yPos = yPosStart; lineIdx < lastLineIdx; lineIdx += BYTES_PER_LINE, yPos +=_charHeight) { int xPosAscii = _xPosAscii; for (int colIdx = 0; ((lineIdx + colIdx) < _xData.size() and (colIdx < BYTES_PER_LINE)); colIdx++) { painter.drawText(xPosAscii, yPos, _xData.asciiChar(lineIdx + colIdx)); xPosAscii += _charWidth; } } } // paint cursor if (_blink) { if (_overwriteMode) painter.fillRect(_cursorX, _cursorY + _charHeight - 2, _charWidth, 2, this->palette().color(QPalette::WindowText)); else painter.fillRect(_cursorX, _cursorY, 2, _charHeight, this->palette().color(QPalette::WindowText)); } if (_size != _xData.size()) { _size = _xData.size(); emit currentSizeChanged(_size); } } void QHexEditPrivate::setCursorPos(int position) { // delete cursor _blink = false; update(); // cursor in range? if (_overwriteMode) { if (position > (_xData.size() * 2 - 1)) position = _xData.size() * 2 - 1; } else { if (position > (_xData.size() * 2)) position = _xData.size() * 2; } if (position < 0) position = 0; // calc position _cursorPosition = position; _cursorY = (position / (2 * BYTES_PER_LINE)) * _charHeight + 4; int x = (position % (2 * BYTES_PER_LINE)); _cursorX = (((x / 2) * 3) + (x % 2)) * _charWidth + _xPosHex; // immiadately draw cursor _blink = true; update(); emit currentAddressChanged(_cursorPosition/2); } int QHexEditPrivate::cursorPos(QPoint pos) { int result = -1; // find char under cursor if ((pos.x() >= _xPosHex) and (pos.x() < (_xPosHex + HEXCHARS_IN_LINE * _charWidth))) { int x = (pos.x() - _xPosHex) / _charWidth; if ((x % 3) == 0) x = (x / 3) * 2; else x = ((x / 3) * 2) + 1; int y = ((pos.y() - 3) / _charHeight) * 2 * BYTES_PER_LINE; result = x + y; } return result; } int QHexEditPrivate::cursorPos() { return _cursorPosition; } void QHexEditPrivate::resetSelection(int pos) { if (pos < 0) pos = 0; pos = pos / 2; _selectionInit = pos; _selectionBegin = pos; _selectionEnd = pos; } void QHexEditPrivate::setSelection(int pos) { if (pos < 0) pos = 0; pos = pos / 2; if (pos >= _selectionInit) { _selectionEnd = pos; _selectionBegin = _selectionInit; } else { _selectionBegin = pos; _selectionEnd = _selectionInit; } } int QHexEditPrivate::getSelectionBegin() { return _selectionBegin; } int QHexEditPrivate::getSelectionEnd() { return _selectionEnd; } void QHexEditPrivate::updateCursor() { if (_blink) _blink = false; else _blink = true; update(_cursorX, _cursorY, _charWidth, _charHeight); } void QHexEditPrivate::adjust() { _charWidth = fontMetrics().width(QLatin1Char('9')); _charHeight = fontMetrics().height(); _xPosAdr = 0; if (_addressArea) _xPosHex = _xData.realAddressNumbers()*_charWidth + GAP_ADR_HEX; else _xPosHex = 0; _xPosAscii = _xPosHex + HEXCHARS_IN_LINE * _charWidth + GAP_HEX_ASCII; // tell QAbstractScollbar, how big we are setMinimumHeight(((_xData.size()/16 + 1) * _charHeight) + 5); setMinimumWidth(_xPosAscii + (BYTES_PER_LINE * _charWidth)); update(); } ostinato-0.7.1/extra/qhexedit2/src/qhexedit_p.h0000700000175300010010000000653712537544001021022 0ustar srivatspNone#ifndef QHEXEDIT_P_H #define QHEXEDIT_P_H /** \cond docNever */ #include #include "xbytearray.h" class QHexEditPrivate : public QWidget { Q_OBJECT public: QHexEditPrivate(QScrollArea *parent); void setAddressAreaColor(QColor const &color); QColor addressAreaColor(); void setAddressOffset(int offset); int addressOffset(); void setCursorPos(int position); int cursorPos(); void setData(QByteArray const &data); QByteArray data(); void setHighlightingColor(QColor const &color); QColor highlightingColor(); void setOverwriteMode(bool overwriteMode); bool overwriteMode(); void setReadOnly(bool readOnly); bool isReadOnly(); void setSelectionColor(QColor const &color); QColor selectionColor(); XByteArray & xData(); void insert(int index, const QByteArray & ba); void insert(int index, char ch); void remove(int index, int len=1); void replace(int index, char ch); void replace(int index, const QByteArray & ba); void setAddressArea(bool addressArea); void setAddressWidth(int addressWidth); void setAsciiArea(bool asciiArea); void setHighlighting(bool mode); virtual void setFont(const QFont &font); void undo(); void redo(); QString toRedableString(); QString selectionToReadableString(); signals: void currentAddressChanged(int address); void currentSizeChanged(int size); void dataChanged(); void overwriteModeChanged(bool state); protected: void keyPressEvent(QKeyEvent * event); void mouseMoveEvent(QMouseEvent * event); void mousePressEvent(QMouseEvent * event); void paintEvent(QPaintEvent *event); int cursorPos(QPoint pos); // calc cursorpos from graphics position. DOES NOT STORE POSITION void resetSelection(int pos); void setSelection(int pos); // set min (if below init) or max (if greater init) int getSelectionBegin(); int getSelectionEnd(); private slots: void updateCursor(); private: void adjust(); QColor _addressAreaColor; QColor _highlightingColor; QColor _selectionColor; QScrollArea *_scrollArea; QTimer _cursorTimer; QUndoStack *_undoStack; XByteArray _xData; // Hält den Inhalt des Hex Editors bool _blink; // true: then cursor blinks bool _renderingRequired; // Flag to store that rendering is necessary bool _addressArea; // left area of QHexEdit bool _asciiArea; // medium area bool _highlighting; // highlighting of changed bytes bool _overwriteMode; bool _readOnly; // true: the user can only look and navigate int _charWidth, _charHeight; // char dimensions (dpendend on font) int _cursorX, _cursorY; // graphics position of the cursor int _cursorPosition; // charakter positioin in stream (on byte ends in to steps) int _xPosAdr, _xPosHex, _xPosAscii; // graphics x-position of the areas int _selectionBegin; // First selected char int _selectionEnd; // Last selected char int _selectionInit; // That's, where we pressed the mouse button int _size; }; /** \endcond docNever */ #endif ostinato-0.7.1/extra/qhexedit2/src/xbytearray.cpp0000700000175300010010000000674112537544001021412 0ustar srivatspNone#include "xbytearray.h" XByteArray::XByteArray() { _oldSize = -99; _addressNumbers = 4; _addressOffset = 0; } int XByteArray::addressOffset() { return _addressOffset; } void XByteArray::setAddressOffset(int offset) { _addressOffset = offset; } int XByteArray::addressWidth() { return _addressNumbers; } void XByteArray::setAddressWidth(int width) { if ((width >= 0) and (width<=6)) { _addressNumbers = width; } } QByteArray & XByteArray::data() { return _data; } void XByteArray::setData(QByteArray data) { _data = data; _changedData = QByteArray(data.length(), char(0)); } bool XByteArray::dataChanged(int i) { return bool(_changedData[i]); } QByteArray XByteArray::dataChanged(int i, int len) { return _changedData.mid(i, len); } void XByteArray::setDataChanged(int i, bool state) { _changedData[i] = char(state); } void XByteArray::setDataChanged(int i, const QByteArray & state) { int length = state.length(); int len; if ((i + length) > _changedData.length()) len = _changedData.length() - i; else len = length; _changedData.replace(i, len, state); } int XByteArray::realAddressNumbers() { if (_oldSize != _data.size()) { // is addressNumbers wide enought? QString test = QString("%1") .arg(_data.size() + _addressOffset, _addressNumbers, 16, QChar('0')); _realAddressNumbers = test.size(); } return _realAddressNumbers; } int XByteArray::size() { return _data.size(); } QByteArray & XByteArray::insert(int i, char ch) { _data.insert(i, ch); _changedData.insert(i, char(1)); return _data; } QByteArray & XByteArray::insert(int i, const QByteArray & ba) { _data.insert(i, ba); _changedData.insert(i, QByteArray(ba.length(), char(1))); return _data; } QByteArray & XByteArray::remove(int i, int len) { _data.remove(i, len); _changedData.remove(i, len); return _data; } QByteArray & XByteArray::replace(int index, char ch) { _data[index] = ch; _changedData[index] = char(1); return _data; } QByteArray & XByteArray::replace(int index, const QByteArray & ba) { int len = ba.length(); return replace(index, len, ba); } QByteArray & XByteArray::replace(int index, int length, const QByteArray & ba) { int len; if ((index + length) > _data.length()) len = _data.length() - index; else len = length; _data.replace(index, len, ba.mid(0, len)); _changedData.replace(index, len, QByteArray(len, char(1))); return _data; } QChar XByteArray::asciiChar(int index) { char ch = _data[index]; if ((ch < 0x20) or (ch > 0x7e)) ch = '.'; return QChar(ch); } QString XByteArray::toRedableString(int start, int end) { int adrWidth = realAddressNumbers(); if (_addressNumbers > adrWidth) adrWidth = _addressNumbers; if (end < 0) end = _data.size(); QString result; for (int i=start; i < end; i += 16) { QString adrStr = QString("%1").arg(_addressOffset + i, adrWidth, 16, QChar('0')); QString hexStr; QString ascStr; for (int j=0; j<16; j++) { if ((i + j) < _data.size()) { hexStr.append(" ").append(_data.mid(i+j, 1).toHex()); ascStr.append(asciiChar(i+j)); } } result += adrStr + " " + QString("%1").arg(hexStr, -48) + " " + QString("%1").arg(ascStr, -17) + "\n"; } return result; } ostinato-0.7.1/extra/qhexedit2/src/xbytearray.h0000700000175300010010000000346612537544001021060 0ustar srivatspNone#ifndef XBYTEARRAY_H #define XBYTEARRAY_H /** \cond docNever */ #include /*! XByteArray represents the content of QHexEcit. XByteArray comprehend the data itself and informations to store if it was changed. The QHexEdit component uses these informations to perform nice rendering of the data XByteArray also provides some functionality to insert, replace and remove single chars and QByteArras. Additionally some functions support rendering and converting to readable strings. */ class XByteArray { public: explicit XByteArray(); int addressOffset(); void setAddressOffset(int offset); int addressWidth(); void setAddressWidth(int width); QByteArray & data(); void setData(QByteArray data); bool dataChanged(int i); QByteArray dataChanged(int i, int len); void setDataChanged(int i, bool state); void setDataChanged(int i, const QByteArray & state); int realAddressNumbers(); int size(); QByteArray & insert(int i, char ch); QByteArray & insert(int i, const QByteArray & ba); QByteArray & remove(int pos, int len); QByteArray & replace(int index, char ch); QByteArray & replace(int index, const QByteArray & ba); QByteArray & replace(int index, int length, const QByteArray & ba); QChar asciiChar(int index); QString toRedableString(int start=0, int end=-1); signals: public slots: private: QByteArray _data; QByteArray _changedData; int _addressNumbers; // wanted width of address area int _addressOffset; // will be added to the real addres inside bytearray int _realAddressNumbers; // real width of address area (can be greater then wanted width) int _oldSize; // size of data }; /** \endcond docNever */ #endif // XBYTEARRAY_H ostinato-0.7.1/install.pri0000700000175300010010000000055112537544001015060 0ustar srivatspNone# 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.7.1/ost.pro0000700000175300010010000000032212537544001014221 0ustar srivatspNoneTEMPLATE = subdirs CONFIG += ordered SUBDIRS = \ extra \ rpc/pbrpc.pro \ common/ostproto.pro \ common/ostprotogui.pro \ server/drone.pro \ client/ostinato.pro \ binding/binding.pro ostinato-0.7.1/protobuf.pri0000700000175300010010000000305712537544001015256 0ustar srivatspNone# # 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.commands = protoc --cpp_out="." --python_out="../binding/protocols" $${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 protobuf_py.name = protobuf python binding protobuf_py.input = PROTOS protobuf_py.output = ../binding/protocols/${QMAKE_FILE_BASE}_pb2.py protobuf_py.commands = $$escape_expand(\\n) #protobuf_py.commands = protoc --python_out="../binding/protocols" $${PROTOPATHS} ${QMAKE_FILE_NAME} protobuf_py.variable_out = GENERATED_FILES QMAKE_EXTRA_COMPILERS += protobuf_py ostinato-0.7.1/rpc/0000700000175300010010000000000012537544001013456 5ustar srivatspNoneostinato-0.7.1/rpc/pbhelper.h0000700000175300010010000001311712537544001015436 0ustar srivatspNone/* 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.7.1/rpc/pbqtio.h0000700000175300010010000000147512537544001015137 0ustar srivatspNone#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.7.1/rpc/pbrpc.pro0000700000175300010010000000033712537544001015314 0ustar srivatspNoneTEMPLATE = 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.7.1/rpc/pbrpcchannel.cpp0000700000175300010010000003201612537544001016626 0ustar srivatspNone/* 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(QHostAddress ip, quint16 port, const ::google::protobuf::Message ¬ifProto) : notifPrototype(notifProto) { isPending = false; pendingMethodId = -1; // don't care as long as isPending is false controller = NULL; done = NULL; response = NULL; mServerAddress = ip; 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(mServerAddress, mServerPort); } void PbRpcChannel::establish(QHostAddress ip, quint16 port) { mServerAddress = ip; 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\n" "queued message = \n%s\n---->", pendingMethodId, method->index(), 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 = \n%s", 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->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\n req = \n%s\n---->", method->index(), 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: 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; } if (len) response->ParseFromBoundedZeroCopyStream(inStream, len); // Avoid printing stats if (method != 13) { qDebug("client(%s): Received Msg <---- ", __FUNCTION__); qDebug("method = %d\nresp = \n%s\n---->", method, response->DebugString().c_str()); } if (!response->IsInitialized()) { qWarning("RpcChannel: missing required fields in response <----"); qDebug("resp = \n%s", 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; controller = NULL; response = NULL; isPending = false; parsing = false; if (pendingCallList.size()) { RpcCall call = pendingCallList.takeFirst(); qDebug("RpcChannel: executing queued method <----\n" "method = %d\n" "req = \n%s\n---->", call.method->index(), 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; 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.7.1/rpc/pbrpcchannel.h0000700000175300010010000000737112537544001016301 0ustar srivatspNone/* 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 "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? */ ::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; QHostAddress mServerAddress; quint16 mServerPort; QTcpSocket *mpSocket; ::google::protobuf::io::CopyingInputStreamAdaptor *inStream; ::google::protobuf::io::CopyingOutputStreamAdaptor *outStream; public: PbRpcChannel(QHostAddress ip, quint16 port, const ::google::protobuf::Message ¬ifProto); ~PbRpcChannel(); void establish(); void establish(QHostAddress ip, quint16 port); void tearDown(); const QHostAddress& serverAddress() const { return mServerAddress; } 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.7.1/rpc/pbrpccommon.h0000700000175300010010000000227412537544001016156 0ustar srivatspNone/* 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.7.1/rpc/pbrpccontroller.h0000700000175300010010000000536112537544001017051 0ustar srivatspNone/* 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.7.1/rpc/rpcconn.cpp0000700000175300010010000003054312537544001015634 0ustar srivatspNone/* 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 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()); 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.7.1/rpc/rpcconn.h0000700000175300010010000000413412537544001015276 0ustar srivatspNone/* 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.7.1/rpc/rpcserver.cpp0000700000175300010010000000442212537544001016202 0ustar srivatspNone/* 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, quint16 tcpPortNum) { this->service = service; if (!listen(QHostAddress::Any, tcpPortNum)) { qDebug("Unable to start the server: %s", 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.7.1/rpc/rpcserver.h0000700000175300010010000000250412537544001015646 0ustar srivatspNone/* 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, quint16 tcpPortNum); signals: void notifyClients(int notifType, SharedProtobufMessage notifData); protected: void incomingConnection(int socketDescriptor); private: ::google::protobuf::Service *service; }; #endif ostinato-0.7.1/rpc/sharedprotobufmessage.h0000700000175300010010000000414012537544001020225 0ustar srivatspNone/* 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.7.1/server/0000700000175300010010000000000012537544001014200 5ustar srivatspNoneostinato-0.7.1/server/abstractport.cpp0000700000175300010010000004372212537544001017427 0ustar srivatspNone/* 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/streambase.h" #include "../common/abstractprotocol.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; linkState_ = OstProto::LinkStateUnknown; minPacketSetSize_ = 1; maxStatsValue_ = ULLONG_MAX; // assume 64-bit stats memset((void*) &stats_, 0, sizeof(stats_)); resetStats(); } AbstractPort::~AbstractPort() { } void AbstractPort::init() { } 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_user_name()) { data_.set_user_name(port.user_name()); } return ret; } 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()); } 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]->burstRate() * streamList_[i]->numBursts()) / x; y = ulong(burstSize * streamList_[i]->burstRate() * 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__); // 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()) 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); } ostinato-0.7.1/server/abstractport.h0000700000175300010010000000665712537544001017102 0ustar srivatspNone/* 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 #include #include "../common/protocol.pb.h" class StreamBase; class QIODevice; 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; }; 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 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 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_; } protected: void addNote(QString note); void updatePacketListSequential(); void updatePacketListInterleaved(); bool isUsable_; OstProto::Port data_; OstProto::LinkState linkState_; ulong minPacketSetSize_; quint64 maxStatsValue_; struct PortStats stats_; //! \todo Need lock for stats access/update private: bool isSendQueueDirty_; static const int kMaxPktSize = 16384; uchar pktBuf_[kMaxPktSize]; /*! \note StreamBase::id() and index into streamList[] are NOT same! */ QList streamList_; struct PortStats epochStats_; }; #endif ostinato-0.7.1/server/bsdport.cpp0000700000175300010010000002347712537544001016401 0ustar srivatspNone/* 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 #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.7.1/server/bsdport.h0000700000175300010010000000271012537544001016031 0ustar srivatspNone/* 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.7.1/server/drone.cpp0000700000175300010010000000270312537544001016020 0ustar srivatspNone/* 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 "rpcserver.h" #include "myservice.h" #include extern int myport; extern const char* version; extern const char* revision; Drone::Drone(QObject *parent) : QObject(parent) { rpcServer = new RpcServer(); service = new MyService(); } Drone::~Drone() { delete rpcServer; delete service; } bool Drone::init() { Q_ASSERT(rpcServer); qRegisterMetaType("SharedProtobufMessage"); if (!rpcServer->registerService(service, myport ? myport : 7878)) { //qCritical(qPrintable(rpcServer->errorString())); return false; } connect(service, SIGNAL(notification(int, SharedProtobufMessage)), rpcServer, SIGNAL(notifyClients(int, SharedProtobufMessage))); return true; } ostinato-0.7.1/server/drone.h0000700000175300010010000000172312537544001015466 0ustar srivatspNone/* 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(); private: RpcServer *rpcServer; MyService *service; }; #endif ostinato-0.7.1/server/drone.pro0000700000175300010010000000241412537544001016035 0ustar srivatspNoneTEMPLATE = app CONFIG += qt ver_info QT += network script 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 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 \ myservice.h SOURCES += \ drone_main.cpp \ drone.cpp \ portmanager.cpp \ abstractport.cpp \ pcapport.cpp \ bsdport.cpp \ linuxport.cpp \ winpcapport.cpp SOURCES += myservice.cpp SOURCES += pcapextra.cpp QMAKE_DISTCLEAN += object_script.* include (../install.pri) include (../version.pri) ostinato-0.7.1/server/drone_main.cpp0000700000175300010010000000540312537544001017024 0ustar srivatspNone/* 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; 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); Drone *drone; // 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.7.1/server/icons/0000700000175300010010000000000012537544001015313 5ustar srivatspNoneostinato-0.7.1/server/icons/portgroup.png0000700000175300010010000000123312537544001020064 0ustar srivatspNone‰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__); 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.7.1/server/linuxport.h0000700000175300010010000000323212537544001016420 0ustar srivatspNone/* 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.7.1/server/myservice.cpp0000700000175300010010000003736212537544001016730 0ustar srivatspNone/* 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 "myservice.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 "portmanager.h" #include 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)); portLock.append(new QReadWriteLock()); } } 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()) { portLock[id]->lockForWrite(); portInfo[id]->modify(port); 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)); } } 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; 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(); 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::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 client 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(); } ostinato-0.7.1/server/myservice.h0000700000175300010010000001201712537544001016363 0ustar srivatspNone/* 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 checkVersion(::google::protobuf::RpcController* controller, const ::OstProto::VersionInfo* request, ::OstProto::VersionCompatibility* response, ::google::protobuf::Closure* done); 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.7.1/server/pcapextra.cpp0000700000175300010010000000367712537544001016713 0ustar srivatspNone/* 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.7.1/server/pcapextra.h0000700000175300010010000000221512537544001016343 0ustar srivatspNone/* 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.7.1/server/pcapport.cpp0000700000175300010010000005425612537544001016553 0ustar srivatspNone/* 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 #ifdef Q_OS_WIN32 #include #endif pcap_if_t *PcapPort::deviceList_ = NULL; #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 PcapPort::PcapPort(int id, const char *device) : AbstractPort(id, device) { monitorRx_ = new PortMonitor(device, kDirectionRx, &stats_); monitorTx_ = new PortMonitor(device, kDirectionTx, &stats_); transmitter_ = new PortTransmitter(device); capturer_ = new PortCapturer(device); 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) { #ifdef Q_OS_WIN32 data_.set_name(QString("if%1").arg(id).toStdString()); #else if (dev->name) data_.set_name(dev->name); #endif 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(); 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()); } 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()); } PcapPort::PortTransmitter::PortTransmitter(const char *device) { char errbuf[PCAP_ERRBUF_SIZE] = ""; #ifdef Q_OS_WIN32 LARGE_INTEGER freq; if (QueryPerformanceFrequency(&freq)) gTicksFreq = ticksFreq_ = freq.QuadPart; else Q_ASSERT_X(false, "PortTransmitter::PortTransmitter", "This Win32 platform does not support performance counter"); #endif state_ = kNotStarted; returnToQIdx_ = -1; loopDelay_ = 0; stop_ = false; stats_ = new AbstractPort::PortStats; usingInternalStats_ = true; handle_ = pcap_open_live(device, 64 /* FIXME */, 0, 1000 /* ms */, errbuf); if (handle_ == NULL) goto _open_error; usingInternalHandle_ = true; return; _open_error: qDebug("%s: Error opening port %s: %s\n", __FUNCTION__, device, errbuf); usingInternalHandle_ = false; } PcapPort::PortTransmitter::~PortTransmitter() { if (usingInternalStats_) delete stats_; if (usingInternalHandle_) pcap_close(handle_); } void PcapPort::PortTransmitter::clearPacketList() { Q_ASSERT(!isRunning()); // \todo lock for packetSequenceList while(packetSequenceList_.size()) delete packetSequenceList_.takeFirst(); currentPacketSequence_ = NULL; repeatSequenceStart_ = -1; repeatSize_ = 0; packetCount_ = 0; returnToQIdx_ = -1; setPacketListLoopMode(false, 0, 0); } void PcapPort::PortTransmitter::loopNextPacketSet(qint64 size, qint64 repeats, long repeatDelaySec, long repeatDelayNsec) { currentPacketSequence_ = new PacketSequence; currentPacketSequence_->repeatCount_ = repeats; currentPacketSequence_->usecDelay_ = repeatDelaySec * long(1e6) + repeatDelayNsec/1000; repeatSequenceStart_ = packetSequenceList_.size(); repeatSize_ = size; packetCount_ = 0; packetSequenceList_.append(currentPacketSequence_); } bool PcapPort::PortTransmitter::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; 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_++; 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 PcapPort::PortTransmitter::setHandle(pcap_t *handle) { if (usingInternalHandle_) pcap_close(handle_); handle_ = handle; usingInternalHandle_ = false; } void PcapPort::PortTransmitter::useExternalStats(AbstractPort::PortStats *stats) { if (usingInternalStats_) delete stats_; stats_ = stats; usingInternalStats_ = false; } void PcapPort::PortTransmitter::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_); } 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_->txPkts += seq->packets_; stats_->txBytes += 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) { udelay(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) { udelay(usecs); overHead = 0; } else overHead = usecs; i = returnToQIdx_; goto _restart; } _exit: state_ = kFinished; } void PcapPort::PortTransmitter::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 PcapPort::PortTransmitter::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 PcapPort::PortTransmitter::isRunning() { return (state_ == kRunning); } int PcapPort::PortTransmitter::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) { udelay(usec); overHead = 0; } else overHead = usec; ts = hdr->ts; getTimeStamp(&ovrStart); } Q_ASSERT(pktLen > 0); pcap_sendpacket(p, pkt, pktLen); stats_->txPkts++; stats_->txBytes += pktLen; // Step to the next packet in the buffer hdr = (struct pcap_pkthdr*) (pkt + pktLen); pkt = (uchar*) ((uchar*)hdr + sizeof(*hdr)); if (stop_) { return -2; } } return 0; } void PcapPort::PortTransmitter::udelay(long usec) { #if defined(Q_OS_WIN32) LARGE_INTEGER tgtTicks; LARGE_INTEGER curTicks; QueryPerformanceCounter(&curTicks); tgtTicks.QuadPart = curTicks.QuadPart + (usec*ticksFreq_)/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 } 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_; } ostinato-0.7.1/server/pcapport.h0000700000175300010010000001564012537544001016212 0ustar srivatspNone/* 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" 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 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(); } 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 PortTransmitter: public QThread { public: PortTransmitter(const char *device); ~PortTransmitter(); 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) { returnToQIdx_ = loop ? 0 : -1; loopDelay_ = secDelay*long(1e6) + nsecDelay/1000; } void setHandle(pcap_t *handle); void useExternalStats(AbstractPort::PortStats *stats); void run(); void start(); void stop(); bool isRunning(); private: enum State { kNotStarted, kRunning, kFinished }; class PacketSequence { public: PacketSequence() { 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) { 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); return pcap_sendqueue_queue(sendQueue_, pktHeader, pktData); } pcap_send_queue *sendQueue_; struct pcap_pkthdr *lastPacket_; long packets_; long bytes_; ulong usecDuration_; int repeatCount_; int repeatSize_; long usecDelay_; }; void udelay(long usec); int sendQueueTransmit(pcap_t *p, pcap_send_queue *queue, long &overHead, int sync); quint64 ticksFreq_; QList packetSequenceList_; PacketSequence *currentPacketSequence_; int repeatSequenceStart_; quint64 repeatSize_; quint64 packetCount_; int returnToQIdx_; quint64 loopDelay_; bool usingInternalStats_; AbstractPort::PortStats *stats_; bool usingInternalHandle_; pcap_t *handle_; volatile bool stop_; volatile State state_; }; 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_; }; PortMonitor *monitorRx_; PortMonitor *monitorTx_; void updateNotes(); private: PortTransmitter *transmitter_; PortCapturer *capturer_; static pcap_if_t *deviceList_; }; #endif ostinato-0.7.1/server/portmanager.cpp0000700000175300010010000000737312537544001017240 0ustar srivatspNone/* 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]; 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); 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; } 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_; } 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.7.1/server/portmanager.h0000700000175300010010000000217512537544001016700 0ustar srivatspNone/* 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: bool filterAcceptsPort(const char *name); private: QList portList_; static PortManager *instance_; }; #endif ostinato-0.7.1/server/settings.h0000700000175300010010000000165112537544001016217 0ustar srivatspNone/* 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; // // PortList Section Keys // const QString kPortListIncludeKey("PortList/Include"); const QString kPortListExcludeKey("PortList/Exclude"); #endif ostinato-0.7.1/server/winpcapport.cpp0000700000175300010010000001457012537544001017264 0ustar srivatspNone/* 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.7.1/server/winpcapport.h0000700000175300010010000000255112537544001016725 0ustar srivatspNone/* 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.7.1/test/0000700000175300010010000000000012537544001013651 5ustar srivatspNoneostinato-0.7.1/test/harness.py0000700000175300010010000000326612537544001015700 0ustar srivatspNone#! /usr/bin/env python class Test: pass class TestSuite: def __init__(self): self.results = [] self.total = 0 self.passed = 0 self.completed = False def test_begin(self, name): test = Test() test.name = name test.passed = False self.running = test print('-----------------------------------------------------------') print('@@TEST: %s' % name) print('-----------------------------------------------------------') def test_end(self, result): if self.running: self.running.passed = result self.results.append(self.running) self.total = self.total + 1 if result: self.passed = self.passed + 1 self.running = None print('@@RESULT: %s' % ('PASS' if result else 'FAIL')) else: raise Exception('Test end without a test begin') def report(self): print('===========================================================') print('TEST REPORT') print('===========================================================') for test in self.results: print('%d: %s' % (test.passed, test.name)) print('Passed: %d/%d' % (self.passed, self.total)) print('Completed: %d' % (self.completed)) def complete(self): self.completed = True def passed(self): return passed == total and self.completed def extract_column(text, col): """Given a text table, return items in the specified column as a list""" lines = text.splitlines() cols = [] for line in lines: cols.append(line.split(None, col)[col-1]) return cols ostinato-0.7.1/test/main.cpp0000700000175300010010000000527412537544001015314 0ustar srivatspNone #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 int usage(int /*argc*/, char* argv[]) { printf("usage:\n"); printf("%s \n", argv[0]); printf("command -\n"); printf(" importpcap\n"); return 255; } 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.openStreams(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.7.1/test/rpctest.py0000700000175300010010000004456512537544001015730 0ustar srivatspNone#! /usr/bin/env python # standard modules import logging import os import subprocess import sys import time sys.path.insert(1, '../binding') from core import ost_pb, DroneProxy from rpc import RpcError from protocols.mac_pb2 import mac from protocols.ip4_pb2 import ip4, Ip4 class Test: pass class TestSuite: def __init__(self): self.results = [] self.total = 0 self.passed = 0 self.completed = False def test_begin(self, name): test = Test() test.name = name test.passed = False self.running = test print('-----------------------------------------------------------') print('@@TEST: %s' % name) print('-----------------------------------------------------------') def test_end(self, result): if self.running: self.running.passed = result self.results.append(self.running) self.total = self.total + 1 if result: self.passed = self.passed + 1 self.running = None print('@@RESULT: %s' % ('PASS' if result else 'FAIL')) else: raise Exception('Test end without a test begin') def report(self): print('===========================================================') print('TEST REPORT') print('===========================================================') for test in self.results: print('%s: %d' % (test.name, test.passed)) print('Passed: %d/%d' % (self.passed, self.total)) print('Completed: %d' % (self.completed)) def complete(self): self.completed = True def passed(self): return passed == total and self.completed # initialize defaults host_name = '127.0.0.1' tx_port_number = -1 rx_port_number = -1 drone_version = ['0', '0', '0'] if sys.platform == 'win32': tshark = r'C:\Program Files\Wireshark\tshark.exe' else: tshark = 'tshark' # setup logging log = logging.getLogger(__name__) logging.basicConfig(level=logging.INFO) print('') print('This test uses the following topology -') print('') print(' +-------+ ') print(' | |Tx--->----+') print(' | Drone | |') print(' | |Rx---<----+') print(' +-------+ ') print('') print('A loopback port is used as both the Tx and Rx ports') print('') suite = TestSuite() drone = DroneProxy(host_name) try: # ----------------------------------------------------------------- # # TESTCASE: Verify any RPC before checkVersion() fails and the server # closes the connection # ----------------------------------------------------------------- # passed = False suite.test_begin('anyRpcBeforeCheckVersionFails') drone.channel.connect(drone.host, drone.port) try: port_id_list = drone.getPortIdList() except RpcError as e: if ('compatibility check pending' in str(e)): passed = True else: raise finally: drone.channel.disconnect() suite.test_end(passed) # ----------------------------------------------------------------- # # TESTCASE: Verify DroneProxy.connect() fails for incompatible version # ----------------------------------------------------------------- # passed = False suite.test_begin('connectFailsForIncompatibleVersion') try: drone.proxy_version = '0.1.1' drone.connect() except RpcError as e: if ('needs client version' in str(e)): passed = True drone_version = str(e).split()[-1].split('.') else: raise finally: drone.proxy_version = None suite.test_end(passed) # ----------------------------------------------------------------- # # TESTCASE: Verify checkVersion() fails for invalid client version format # ----------------------------------------------------------------- # passed = False suite.test_begin('checkVersionFailsForInvalidClientVersion') try: drone.proxy_version = '0-1-1' drone.connect() except RpcError as e: if ('invalid version' in str(e)): passed = True else: raise finally: drone.proxy_version = None suite.test_end(passed) # ----------------------------------------------------------------- # # TESTCASE: Verify checkVersion() returns incompatible if the 'major' # part of the numbering format is # different than the server's version and the server closes # the connection # ----------------------------------------------------------------- # passed = False suite.test_begin('checkVersionReturnsIncompatForDifferentMajorVersion') try: drone.proxy_version = (str(int(drone_version[0])+1) + '.' + drone_version[1]) drone.connect() except RpcError as e: #FIXME: How to check for a closed connection? if ('needs client version' in str(e)): passed = True else: raise finally: drone.proxy_version = None suite.test_end(passed) # ----------------------------------------------------------------- # # TESTCASE: Verify checkVersion() returns incompatible if the 'minor' # part of the numbering format is # different than the server's version and the server closes # the connection # ----------------------------------------------------------------- # passed = False suite.test_begin('checkVersionReturnsIncompatForDifferentMinorVersion') try: drone.proxy_version = (drone_version[0] + '.' + str(int(drone_version[1])+1)) drone.connect() except RpcError as e: #FIXME: How to check for a closed connection? if ('needs client version' in str(e)): passed = True else: raise finally: drone.proxy_version = None suite.test_end(passed) # ----------------------------------------------------------------- # # TESTCASE: Verify checkVersion() returns compatible if the 'revision' # part of the numbering format is # different than the server's version # ----------------------------------------------------------------- # passed = False suite.test_begin('checkVersionReturnsCompatForDifferentRevisionVersion') try: drone.proxy_version = (drone_version[0] + '.' + drone_version[1] + '.' + '999') drone.connect() passed = True except RpcError as e: raise finally: drone.proxy_version = None suite.test_end(passed) # ----------------------------------------------------------------- # # Baseline Configuration for subsequent testcases # ----------------------------------------------------------------- # # connect to drone log.info('connecting to drone(%s:%d)' % (drone.hostName(), drone.portNumber())) drone.connect() # retreive port id list log.info('retreiving port list') port_id_list = drone.getPortIdList() # retreive port config list log.info('retreiving port config for all ports') port_config_list = drone.getPortConfig(port_id_list) if len(port_config_list.port) == 0: log.warning('drone has no ports!') sys.exit(1) # iterate port list to find a loopback port to use as the tx/rx port id print('Port List') print('---------') for port in port_config_list.port: print('%d.%s (%s)' % (port.port_id.id, port.name, port.description)) # use a loopback port as default tx/rx port if ('lo' in port.name or 'loopback' in port.description.lower()): tx_port_number = port.port_id.id rx_port_number = port.port_id.id if tx_port_number < 0 or rx_port_number < 0: log.warning('loopback port not found') sys.exit(1) print('Using port %d as tx/rx port(s)' % tx_port_number) tx_port = ost_pb.PortIdList() tx_port.port_id.add().id = tx_port_number; rx_port = ost_pb.PortIdList() rx_port.port_id.add().id = rx_port_number; # add a stream stream_id = ost_pb.StreamIdList() stream_id.port_id.CopyFrom(tx_port.port_id[0]) stream_id.stream_id.add().id = 1 log.info('adding tx_stream %d' % stream_id.stream_id[0].id) drone.addStream(stream_id) # configure the stream stream_cfg = ost_pb.StreamConfigList() stream_cfg.port_id.CopyFrom(tx_port.port_id[0]) s = stream_cfg.stream.add() s.stream_id.id = stream_id.stream_id[0].id s.core.is_enabled = True s.control.num_packets = 10 # setup stream protocols as mac:eth2:ip4:udp:payload p = s.protocol.add() p.protocol_id.id = ost_pb.Protocol.kMacFieldNumber p.Extensions[mac].dst_mac = 0x001122334455 p.Extensions[mac].src_mac = 0x00aabbccddee p = s.protocol.add() p.protocol_id.id = ost_pb.Protocol.kEth2FieldNumber p = s.protocol.add() p.protocol_id.id = ost_pb.Protocol.kIp4FieldNumber # reduce typing by creating a shorter reference to p.Extensions[ip4] ip = p.Extensions[ip4] ip.src_ip = 0x01020304 ip.dst_ip = 0x05060708 ip.dst_ip_mode = Ip4.e_im_inc_host s.protocol.add().protocol_id.id = ost_pb.Protocol.kUdpFieldNumber s.protocol.add().protocol_id.id = ost_pb.Protocol.kPayloadFieldNumber log.info('configuring tx_stream %d' % stream_id.stream_id[0].id) drone.modifyStream(stream_cfg) # clear tx/rx stats log.info('clearing tx/rx stats') drone.clearStats(tx_port) drone.clearStats(rx_port) # ----------------------------------------------------------------- # # TODO: # TESTCASE: Verify a RPC with missing required fields in request fails # and subsequently passes when the fields are initialized # ----------------------------------------------------------------- # # passed = False # suite.test_begin('rpcWithMissingRequiredFieldsFails') # pid = ost_pb.PortId() # try: # sid_list = drone.getStreamIdList(pid) # except RpcError as e: # if ('missing required fields in request' in str(e)): # passed = True # else: # raise # # try: # pid.id = tx_port_number # sid_list = drone.getStreamIdList(pid) # except RpcError as e: # passed = False # raise # finally: # suite.test_end(passed) # ----------------------------------------------------------------- # # TESTCASE: Verify invoking addStream() during transmit fails # TESTCASE: Verify invoking modifyStream() during transmit fails # TESTCASE: Verify invoking deleteStream() during transmit fails # ----------------------------------------------------------------- # sid = ost_pb.StreamIdList() sid.port_id.CopyFrom(tx_port.port_id[0]) sid.stream_id.add().id = 2 passed = False suite.test_begin('addStreamDuringTransmitFails') drone.startTransmit(tx_port) try: log.info('adding tx_stream %d' % sid.stream_id[0].id) drone.addStream(sid) except RpcError as e: if ('Port Busy' in str(e)): passed = True else: raise finally: drone.stopTransmit(tx_port) suite.test_end(passed) passed = False suite.test_begin('modifyStreamDuringTransmitFails') scfg = ost_pb.StreamConfigList() scfg.port_id.CopyFrom(tx_port.port_id[0]) s = scfg.stream.add() s.stream_id.id = sid.stream_id[0].id s.protocol.add().protocol_id.id = ost_pb.Protocol.kMacFieldNumber s.protocol.add().protocol_id.id = ost_pb.Protocol.kArpFieldNumber s.protocol.add().protocol_id.id = ost_pb.Protocol.kPayloadFieldNumber drone.startTransmit(tx_port) try: log.info('configuring tx_stream %d' % sid.stream_id[0].id) drone.modifyStream(scfg) except RpcError as e: if ('Port Busy' in str(e)): passed = True else: raise finally: drone.stopTransmit(tx_port) suite.test_end(passed) passed = False suite.test_begin('deleteStreamDuringTransmitFails') drone.startTransmit(tx_port) try: log.info('deleting tx_stream %d' % sid.stream_id[0].id) drone.deleteStream(sid) except RpcError as e: if ('Port Busy' in str(e)): passed = True else: raise finally: drone.stopTransmit(tx_port) suite.test_end(passed) # ----------------------------------------------------------------- # # TESTCASE: Verify invoking startTransmit() during transmit is a NOP, # not a restart # ----------------------------------------------------------------- # passed = False suite.test_begin('startTransmitDuringTransmitIsNopNotRestart') drone.startCapture(rx_port) drone.startTransmit(tx_port) try: log.info('sleeping for 4s ...') time.sleep(4) log.info('starting transmit multiple times') drone.startTransmit(tx_port) time.sleep(1) drone.startTransmit(tx_port) time.sleep(1) drone.startTransmit(tx_port) time.sleep(1) log.info('waiting for transmit to finish ...') time.sleep(5) drone.stopTransmit(tx_port) drone.stopCapture(rx_port) buff = drone.getCaptureBuffer(rx_port.port_id[0]) drone.saveCaptureBuffer(buff, 'capture.pcap') log.info('dumping Rx capture buffer') cap_pkts = subprocess.check_output([tshark, '-r', 'capture.pcap']) print(cap_pkts) if '5.6.7.8' in cap_pkts: passed = True os.remove('capture.pcap') except RpcError as e: raise finally: drone.stopTransmit(tx_port) suite.test_end(passed) # ----------------------------------------------------------------- # # TESTCASE: Verify invoking startCapture() during capture is a NOP, # not a restart # ----------------------------------------------------------------- # passed = False suite.test_begin('startCaptureDuringTransmitIsNopNotRestart') try: drone.startCapture(rx_port) drone.startTransmit(tx_port) log.info('sleeping for 4s ...') time.sleep(4) log.info('starting capture multiple times') drone.startCapture(rx_port) time.sleep(1) drone.startCapture(rx_port) time.sleep(1) drone.startCapture(rx_port) time.sleep(1) log.info('waiting for transmit to finish ...') time.sleep(5) drone.stopTransmit(tx_port) drone.stopCapture(rx_port) buff = drone.getCaptureBuffer(rx_port.port_id[0]) drone.saveCaptureBuffer(buff, 'capture.pcap') log.info('dumping Rx capture buffer') cap_pkts = subprocess.check_output([tshark, '-r', 'capture.pcap']) print(cap_pkts) if '5.6.7.8' in cap_pkts: passed = True os.remove('capture.pcap') except RpcError as e: raise finally: drone.stopTransmit(tx_port) suite.test_end(passed) # ----------------------------------------------------------------- # # TESTCASE: Verify invoking stopTransmit() when transmit is not running # is a NOP # ----------------------------------------------------------------- # passed = False suite.test_begin('stopTransmitWhenTransmitNotRunningIsNop') try: tx_stats = drone.getStats(tx_port) log.info('--> (tx_stats)' + tx_stats.__str__()) if tx_stats.port_stats[0].state.is_transmit_on: raise Exception('Unexpected transmit ON state') log.info('stopping transmit multiple times') drone.stopTransmit(tx_port) time.sleep(1) drone.stopTransmit(tx_port) time.sleep(1) drone.stopTransmit(tx_port) # if we reached here, that means there was no exception passed = True except RpcError as e: raise finally: suite.test_end(passed) # ----------------------------------------------------------------- # # TESTCASE: Verify invoking stopCapture() when capture is not running # is a NOP # ----------------------------------------------------------------- # passed = False suite.test_begin('stopCaptureWhenCaptureNotRunningIsNop') try: rx_stats = drone.getStats(rx_port) log.info('--> (rx_stats)' + rx_stats.__str__()) if rx_stats.port_stats[0].state.is_capture_on: raise Exception('Unexpected capture ON state') log.info('stopping capture multiple times') drone.stopCapture(rx_port) time.sleep(1) drone.stopCapture(rx_port) time.sleep(1) drone.stopCapture(rx_port) # if we reached here, that means there was no exception passed = True except RpcError as e: raise finally: suite.test_end(passed) # ----------------------------------------------------------------- # # TESTCASE: Verify startCapture(), startTransmit() sequence captures the # first packet # TESTCASE: Verify stopTransmit(), stopCapture() sequence captures the # last packet # ----------------------------------------------------------------- # passed = False suite.test_begin('startStopTransmitCaptureOrderCapturesAllPackets') try: drone.startCapture(rx_port) drone.startTransmit(tx_port) log.info('waiting for transmit to finish ...') time.sleep(12) drone.stopTransmit(tx_port) drone.stopCapture(rx_port) log.info('getting Rx capture buffer') buff = drone.getCaptureBuffer(rx_port.port_id[0]) drone.saveCaptureBuffer(buff, 'capture.pcap') log.info('dumping Rx capture buffer') cap_pkts = subprocess.check_output([tshark, '-r', 'capture.pcap']) print(cap_pkts) if '5.6.7.8' in cap_pkts and '5.6.7.17' in cap_pkts: passed = True os.remove('capture.pcap') except RpcError as e: raise finally: drone.stopTransmit(tx_port) suite.test_end(passed) suite.complete() # delete streams log.info('deleting tx_stream %d' % stream_id.stream_id[0].id) drone.deleteStream(stream_id) # bye for now drone.disconnect() except Exception as ex: log.exception(ex) finally: suite.report() if not suite.passed: sys.exit(2); ostinato-0.7.1/test/test.pro0000700000175300010010000000220112537544001015350 0ustar srivatspNoneTEMPLATE = 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.7.1/test/vftest.py0000700000175300010010000011202312537544001015540 0ustar srivatspNone#! /usr/bin/env python # standard modules import logging import os import subprocess import sys import time from harness import Test, TestSuite, extract_column sys.path.insert(1, '../binding') from core import ost_pb, DroneProxy from rpc import RpcError from protocols.mac_pb2 import mac from protocols.arp_pb2 import arp from protocols.icmp_pb2 import icmp from protocols.ip4_pb2 import ip4, Ip4 from protocols.ip6_pb2 import ip6, Ip6 from protocols.payload_pb2 import payload, Payload # initialize defaults host_name = '127.0.0.1' tx_port_number = -1 rx_port_number = -1 drone_version = ['0', '0', '0'] if sys.platform == 'win32': tshark = r'C:\Program Files\Wireshark\tshark.exe' else: tshark = 'tshark' fmt_template = 'column.format:"Packet#","%m","Time","%t","Source","%uns","Destination","%und","Protocol","%p","Size","%L",,"Info","%i","Expert","%a"' fmt = fmt_template.replace('', '"Custom","%Cus:XXX"') fmt2 = fmt_template.replace('', '"Custom","%Cus:XXX",'*2).replace(',,',',') fmt4 = fmt_template.replace('', '"Custom","%Cus:XXX",'*4).replace(',,',',') fmt_col = 8 # col# in fmt for Custom (col numbers start from 1) # setup logging log = logging.getLogger(__name__) logging.basicConfig(level=logging.INFO) print('') print('This test uses the following topology -') print('') print(' +-------+ ') print(' | |Tx--->----+') print(' | Drone | |') print(' | |Rx---<----+') print(' +-------+ ') print('') print('A loopback port is used as both the Tx and Rx ports') print('') suite = TestSuite() drone = DroneProxy(host_name) try: # ----------------------------------------------------------------- # # Baseline Configuration for subsequent testcases # ----------------------------------------------------------------- # # connect to drone log.info('connecting to drone(%s:%d)' % (drone.hostName(), drone.portNumber())) drone.connect() # retreive port id list log.info('retreiving port list') port_id_list = drone.getPortIdList() # retreive port config list log.info('retreiving port config for all ports') port_config_list = drone.getPortConfig(port_id_list) if len(port_config_list.port) == 0: log.warning('drone has no ports!') sys.exit(1) # iterate port list to find a loopback port to use as the tx/rx port id print('Port List') print('---------') for port in port_config_list.port: print('%d.%s (%s)' % (port.port_id.id, port.name, port.description)) # use a loopback port as default tx/rx port if ('lo' in port.name or 'loopback' in port.description.lower()): tx_port_number = port.port_id.id rx_port_number = port.port_id.id if tx_port_number < 0 or rx_port_number < 0: log.warning('loopback port not found') sys.exit(1) print('Using port %d as tx/rx port(s)' % tx_port_number) tx_port = ost_pb.PortIdList() tx_port.port_id.add().id = tx_port_number; rx_port = ost_pb.PortIdList() rx_port.port_id.add().id = rx_port_number; # delete existing streams, if any, on tx port sid_list = drone.getStreamIdList(tx_port.port_id[0]) drone.deleteStream(sid_list) # add a stream stream_id = ost_pb.StreamIdList() stream_id.port_id.CopyFrom(tx_port.port_id[0]) stream_id.stream_id.add().id = 1 log.info('adding tx_stream %d' % stream_id.stream_id[0].id) drone.addStream(stream_id) # configure the stream stream_cfg = ost_pb.StreamConfigList() stream_cfg.port_id.CopyFrom(tx_port.port_id[0]) s = stream_cfg.stream.add() s.stream_id.id = stream_id.stream_id[0].id s.core.is_enabled = True s.core.frame_len = 128 s.control.packets_per_sec = 100 s.control.num_packets = 10 # all test cases will use this stream by modifying it as per its needs # ----------------------------------------------------------------- # # TESTCASE: Verify decrement counter8 variable field not affecting # any checksums - IPv6 HopLimit # ----------------------------------------------------------------- # # setup stream protocols as mac:eth2:ip6:udp:payload s.ClearField("protocol") p = s.protocol.add() p.protocol_id.id = ost_pb.Protocol.kMacFieldNumber p.Extensions[mac].dst_mac = 0x001122334455 p.Extensions[mac].src_mac = 0x00aabbccddee s.protocol.add().protocol_id.id = ost_pb.Protocol.kEth2FieldNumber p = s.protocol.add() p.protocol_id.id = ost_pb.Protocol.kIp6FieldNumber vf = p.variable_field.add() vf.type = ost_pb.VariableField.kCounter8 vf.offset = 7 vf.mask = 0xff vf.value = 255 vf.mode = ost_pb.VariableField.kDecrement vf.count = 4 s.protocol.add().protocol_id.id = ost_pb.Protocol.kUdpFieldNumber s.protocol.add().protocol_id.id = ost_pb.Protocol.kPayloadFieldNumber log.info('configuring tx_stream %d' % stream_id.stream_id[0].id) drone.modifyStream(stream_cfg) # clear tx/rx stats log.info('clearing tx/rx stats') drone.clearStats(tx_port) drone.clearStats(rx_port) passed = False suite.test_begin('counter8Decrement[Ip6HopLimit]') try: drone.startCapture(rx_port) drone.startTransmit(tx_port) log.info('waiting for transmit to finish ...') time.sleep(3) drone.stopTransmit(tx_port) drone.stopCapture(rx_port) log.info('getting Rx capture buffer') buff = drone.getCaptureBuffer(rx_port.port_id[0]) drone.saveCaptureBuffer(buff, 'capture.pcap') log.info('dumping Rx capture buffer') cap_pkts = subprocess.check_output([tshark, '-n', '-r', 'capture.pcap', '-R', 'udp && frame.len==124', '-o', fmt.replace('XXX', 'ipv6.hlim')]) print(cap_pkts) result = extract_column(cap_pkts, fmt_col) expected = ['255', '254', '253', '252', '255', '254', '253', '252', '255', '254'] log.info('result : %s' % result) log.info('expected: %s' % expected) if result == expected: passed = True os.remove('capture.pcap') except RpcError as e: raise finally: drone.stopTransmit(tx_port) suite.test_end(passed) # ----------------------------------------------------------------- # # TESTCASE: Verify increment bitmasked counter8 variable field not # affecting any checksums - Vlan Prio # ----------------------------------------------------------------- # # setup stream protocols as mac:vlan:eth2:ip4:udp:payload s.ClearField("protocol") p = s.protocol.add() p.protocol_id.id = ost_pb.Protocol.kMacFieldNumber p.Extensions[mac].dst_mac = 0x001122334455 p.Extensions[mac].src_mac = 0x00aabbccddee p = s.protocol.add() p.protocol_id.id = ost_pb.Protocol.kVlanFieldNumber vf = p.variable_field.add() vf.type = ost_pb.VariableField.kCounter8 vf.offset = 2 vf.mask = 0xe0 vf.value = 0x60 vf.mode = ost_pb.VariableField.kIncrement vf.count = 8 vf.step = 0x10 # step by 1 and repeat each value twice!!!!! s.protocol.add().protocol_id.id = ost_pb.Protocol.kEth2FieldNumber s.protocol.add().protocol_id.id = ost_pb.Protocol.kIp4FieldNumber s.protocol.add().protocol_id.id = ost_pb.Protocol.kUdpFieldNumber s.protocol.add().protocol_id.id = ost_pb.Protocol.kPayloadFieldNumber log.info('configuring tx_stream %d' % stream_id.stream_id[0].id) drone.modifyStream(stream_cfg) # clear tx/rx stats log.info('clearing tx/rx stats') drone.clearStats(tx_port) drone.clearStats(rx_port) passed = False suite.test_begin('counter8BitmaskIncrement[VlanPrio]') try: drone.startCapture(rx_port) drone.startTransmit(tx_port) log.info('waiting for transmit to finish ...') time.sleep(3) drone.stopTransmit(tx_port) drone.stopCapture(rx_port) log.info('getting Rx capture buffer') buff = drone.getCaptureBuffer(rx_port.port_id[0]) drone.saveCaptureBuffer(buff, 'capture.pcap') log.info('dumping Rx capture buffer') cap_pkts = subprocess.check_output([tshark, '-n', '-r', 'capture.pcap', '-R', 'udp && frame.len==124', '-o', fmt.replace('XXX', 'vlan.priority')]) print(cap_pkts) result = extract_column(cap_pkts, fmt_col) expected = ['Excellent', 'Excellent', 'Controlled', 'Controlled', 'Video,', 'Video,', 'Voice,', 'Voice,', 'Excellent', 'Excellent'] log.info('result : %s' % result) log.info('expected: %s' % expected) if result == expected: passed = True os.remove('capture.pcap') except RpcError as e: raise finally: drone.stopTransmit(tx_port) suite.test_end(passed) # ----------------------------------------------------------------- # # TESTCASE: Verify increment counter16 variable field not affecting # any checksums - vlan-id # ----------------------------------------------------------------- # # setup stream protocols as mac:vlan:eth2:ip4:udp:payload s.ClearField("protocol") p = s.protocol.add() p.protocol_id.id = ost_pb.Protocol.kMacFieldNumber p.Extensions[mac].dst_mac = 0x001122334455 p.Extensions[mac].src_mac = 0x00aabbccddee p = s.protocol.add() p.protocol_id.id = ost_pb.Protocol.kVlanFieldNumber vf = p.variable_field.add() vf.type = ost_pb.VariableField.kCounter16 vf.offset = 2 vf.mask = 0x0fff vf.value = 1234 vf.mode = ost_pb.VariableField.kIncrement vf.count = 7 s.protocol.add().protocol_id.id = ost_pb.Protocol.kEth2FieldNumber s.protocol.add().protocol_id.id = ost_pb.Protocol.kIp4FieldNumber s.protocol.add().protocol_id.id = ost_pb.Protocol.kUdpFieldNumber s.protocol.add().protocol_id.id = ost_pb.Protocol.kPayloadFieldNumber log.info('configuring tx_stream %d' % stream_id.stream_id[0].id) drone.modifyStream(stream_cfg) # clear tx/rx stats log.info('clearing tx/rx stats') drone.clearStats(tx_port) drone.clearStats(rx_port) passed = False suite.test_begin('counter16Increment[vlanid]') try: drone.startCapture(rx_port) drone.startTransmit(tx_port) log.info('waiting for transmit to finish ...') time.sleep(3) drone.stopTransmit(tx_port) drone.stopCapture(rx_port) log.info('getting Rx capture buffer') buff = drone.getCaptureBuffer(rx_port.port_id[0]) drone.saveCaptureBuffer(buff, 'capture.pcap') log.info('dumping Rx capture buffer') cap_pkts = subprocess.check_output([tshark, '-n', '-r', 'capture.pcap', '-R', 'udp && frame.len==124', '-o', fmt.replace('XXX', 'vlan.id')]) print(cap_pkts) result = extract_column(cap_pkts, fmt_col) expected = ['1234', '1235', '1236', '1237', '1238', '1239', '1240', '1234', '1235', '1236'] log.info('result : %s' % result) log.info('expected: %s' % expected) if result == expected: passed = True os.remove('capture.pcap') except RpcError as e: raise finally: drone.stopTransmit(tx_port) suite.test_end(passed) # ----------------------------------------------------------------- # # TESTCASE: Verify decrement counter32 step 3 variable field not # affecting any checksums - ARP Target IP # ----------------------------------------------------------------- # # setup stream protocols as mac:eth2:arp s.ClearField("protocol") p = s.protocol.add() p.protocol_id.id = ost_pb.Protocol.kMacFieldNumber p.Extensions[mac].dst_mac = 0x001122334455 p.Extensions[mac].src_mac = 0xffffffffffff s.protocol.add().protocol_id.id = ost_pb.Protocol.kEth2FieldNumber p = s.protocol.add() p.protocol_id.id = ost_pb.Protocol.kArpFieldNumber p.Extensions[arp].sender_hw_addr = 0x001122334455 p.Extensions[arp].sender_proto_addr = 0x01020304 p.Extensions[arp].target_proto_addr = 0x0a0b0c01 vf = p.variable_field.add() vf.type = ost_pb.VariableField.kCounter32 vf.offset = 24 vf.mask = 0x0000ff00 vf.value = 0x00006400 vf.mode = ost_pb.VariableField.kDecrement vf.count = 7 vf.step = 0x00000300 log.info('configuring tx_stream %d' % stream_id.stream_id[0].id) drone.modifyStream(stream_cfg) # clear tx/rx stats log.info('clearing tx/rx stats') drone.clearStats(tx_port) drone.clearStats(rx_port) passed = False suite.test_begin('counter32BitmaskDecrement[arpTargetIp]') try: drone.startCapture(rx_port) drone.startTransmit(tx_port) log.info('waiting for transmit to finish ...') time.sleep(3) drone.stopTransmit(tx_port) drone.stopCapture(rx_port) log.info('getting Rx capture buffer') buff = drone.getCaptureBuffer(rx_port.port_id[0]) drone.saveCaptureBuffer(buff, 'capture.pcap') log.info('dumping Rx capture buffer') cap_pkts = subprocess.check_output([tshark, '-n', '-r', 'capture.pcap', '-R', 'arp && frame.len==124', '-o', fmt.replace('XXX', 'arp.dst.proto_ipv4') .replace('un', 'uh')]) print(cap_pkts) result = extract_column(cap_pkts, fmt_col) expected = ['10.11.100.1', '10.11.97.1', '10.11.94.1', '10.11.91.1', '10.11.88.1', '10.11.85.1', '10.11.82.1', '10.11.100.1', '10.11.97.1', '10.11.94.1'] log.info('result : %s' % result) log.info('expected: %s' % expected) if result == expected: passed = True os.remove('capture.pcap') except RpcError as e: raise finally: drone.stopTransmit(tx_port) suite.test_end(passed) # ----------------------------------------------------------------- # # TESTCASE: Verify increment counter8 variable field affecting # IPv4 checksum - IPv4 TTL # ----------------------------------------------------------------- # # setup stream protocols as mac:eth2:ip4:udp:payload s.ClearField("protocol") p = s.protocol.add() p.protocol_id.id = ost_pb.Protocol.kMacFieldNumber p.Extensions[mac].dst_mac = 0x001122334455 p.Extensions[mac].src_mac = 0x00aabbccddee s.protocol.add().protocol_id.id = ost_pb.Protocol.kEth2FieldNumber p = s.protocol.add() p.protocol_id.id = ost_pb.Protocol.kIp4FieldNumber p.Extensions[ip4].src_ip = 0x01020304 p.Extensions[ip4].dst_ip = 0x05060708 vf = p.variable_field.add() vf.type = ost_pb.VariableField.kCounter8 vf.offset = 8 #vf.mask = 0xFF vf.value = 127 vf.mode = ost_pb.VariableField.kIncrement vf.count = 8 s.protocol.add().protocol_id.id = ost_pb.Protocol.kUdpFieldNumber s.protocol.add().protocol_id.id = ost_pb.Protocol.kPayloadFieldNumber log.info('configuring tx_stream %d' % stream_id.stream_id[0].id) drone.modifyStream(stream_cfg) # clear tx/rx stats log.info('clearing tx/rx stats') drone.clearStats(tx_port) drone.clearStats(rx_port) passed = False suite.test_begin('counter8IncrementIpCksum[IpTtl]') try: drone.startCapture(rx_port) drone.startTransmit(tx_port) log.info('waiting for transmit to finish ...') time.sleep(3) drone.stopTransmit(tx_port) drone.stopCapture(rx_port) log.info('getting Rx capture buffer') buff = drone.getCaptureBuffer(rx_port.port_id[0]) drone.saveCaptureBuffer(buff, 'capture.pcap') log.info('dumping Rx capture buffer') cap_pkts = subprocess.check_output([tshark, '-n', '-r', 'capture.pcap', '-R', 'udp && frame.len==124', '-o', fmt.replace('XXX', 'ip.ttl')]) print(cap_pkts) result = extract_column(cap_pkts, fmt_col) expected = ['127', '128', '129', '130', '131', '132', '133', '134', '127', '128'] log.info('result : %s' % result) log.info('expected: %s' % expected) if result == expected and 'Error' not in cap_pkts: passed = True os.remove('capture.pcap') except RpcError as e: raise finally: drone.stopTransmit(tx_port) suite.test_end(passed) # ----------------------------------------------------------------- # # TESTCASE: Verify decrement counter16 bitmask variable field affecting # IPv4 checksum - IPv4 FragOfs # ----------------------------------------------------------------- # # setup stream protocols as mac:eth2:ip4:udp:payload s.ClearField("protocol") p = s.protocol.add() p.protocol_id.id = ost_pb.Protocol.kMacFieldNumber p.Extensions[mac].dst_mac = 0x001122334455 p.Extensions[mac].src_mac = 0x00aabbccddee s.protocol.add().protocol_id.id = ost_pb.Protocol.kEth2FieldNumber p = s.protocol.add() p.protocol_id.id = ost_pb.Protocol.kIp4FieldNumber p.Extensions[ip4].src_ip = 0x01020304 p.Extensions[ip4].dst_ip = 0x05060708 vf = p.variable_field.add() vf.type = ost_pb.VariableField.kCounter16 vf.offset = 6 vf.mask = 0x1FFF vf.value = 1000 vf.mode = ost_pb.VariableField.kDecrement vf.count = 6 s.protocol.add().protocol_id.id = ost_pb.Protocol.kUdpFieldNumber s.protocol.add().protocol_id.id = ost_pb.Protocol.kPayloadFieldNumber log.info('configuring tx_stream %d' % stream_id.stream_id[0].id) drone.modifyStream(stream_cfg) # clear tx/rx stats log.info('clearing tx/rx stats') drone.clearStats(tx_port) drone.clearStats(rx_port) passed = False suite.test_begin('counter16BitmaskDecrementIpCksum[IpFragOfs]') try: drone.startCapture(rx_port) drone.startTransmit(tx_port) log.info('waiting for transmit to finish ...') time.sleep(3) drone.stopTransmit(tx_port) drone.stopCapture(rx_port) log.info('getting Rx capture buffer') buff = drone.getCaptureBuffer(rx_port.port_id[0]) drone.saveCaptureBuffer(buff, 'capture.pcap') log.info('dumping Rx capture buffer') cap_pkts = subprocess.check_output([tshark, '-n', '-r', 'capture.pcap', '-R', 'ip && frame.len==124', '-o', fmt.replace('XXX', 'ip.frag_offset')]) print(cap_pkts) result = extract_column(cap_pkts, fmt_col) expected = ['8000', '7992', '7984', '7976', '7968', '7960', '8000', '7992', '7984', '7976'] log.info('result : %s' % result) log.info('expected: %s' % expected) if result == expected and 'Error' not in cap_pkts: passed = True os.remove('capture.pcap') except RpcError as e: raise finally: drone.stopTransmit(tx_port) suite.test_end(passed) # ----------------------------------------------------------------- # # TESTCASE: Verify increment counter32 bitmask variable field affecting # IPv4 checksum - IPv4 Dst Addr # ----------------------------------------------------------------- # # setup stream protocols as mac:eth2:ip4:udp:payload s.ClearField("protocol") p = s.protocol.add() p.protocol_id.id = ost_pb.Protocol.kMacFieldNumber p.Extensions[mac].dst_mac = 0x001122334455 p.Extensions[mac].src_mac = 0x00aabbccddee s.protocol.add().protocol_id.id = ost_pb.Protocol.kEth2FieldNumber p = s.protocol.add() p.protocol_id.id = ost_pb.Protocol.kIp4FieldNumber p.Extensions[ip4].src_ip = 0x01020304 p.Extensions[ip4].dst_ip = 0x05060708 vf = p.variable_field.add() vf.type = ost_pb.VariableField.kCounter32 vf.offset = 16 vf.mask = 0x00FF0000 vf.value = 0x00ab0000 vf.mode = ost_pb.VariableField.kIncrement vf.count = 3 vf.step = 0x00020000 s.protocol.add().protocol_id.id = ost_pb.Protocol.kUdpFieldNumber s.protocol.add().protocol_id.id = ost_pb.Protocol.kPayloadFieldNumber log.info('configuring tx_stream %d' % stream_id.stream_id[0].id) drone.modifyStream(stream_cfg) # clear tx/rx stats log.info('clearing tx/rx stats') drone.clearStats(tx_port) drone.clearStats(rx_port) passed = False suite.test_begin('counter32BitmaskDecrementIpCksum[IpDstAddr]') try: drone.startCapture(rx_port) drone.startTransmit(tx_port) log.info('waiting for transmit to finish ...') time.sleep(3) drone.stopTransmit(tx_port) drone.stopCapture(rx_port) log.info('getting Rx capture buffer') buff = drone.getCaptureBuffer(rx_port.port_id[0]) drone.saveCaptureBuffer(buff, 'capture.pcap') log.info('dumping Rx capture buffer') cap_pkts = subprocess.check_output([tshark, '-n', '-r', 'capture.pcap', '-R', 'udp && frame.len==124', '-o', fmt.replace('XXX', 'ip.dst')]) print(cap_pkts) result = extract_column(cap_pkts, fmt_col) expected = ['5.171.7.8', '5.173.7.8', '5.175.7.8', '5.171.7.8', '5.173.7.8', '5.175.7.8', '5.171.7.8', '5.173.7.8', '5.175.7.8', '5.171.7.8'] log.info('result : %s' % result) log.info('expected: %s' % expected) if result == expected and 'Error' not in cap_pkts: passed = True os.remove('capture.pcap') except RpcError as e: raise finally: drone.stopTransmit(tx_port) suite.test_end(passed) # ----------------------------------------------------------------- # # TESTCASE: Verify increment counter32 variable field affecting # UDP checksum - seq# in payload # ----------------------------------------------------------------- # # setup stream protocols as mac:eth2:ip4:udp:payload s.ClearField("protocol") s.core.frame_len = 64 p = s.protocol.add() p.protocol_id.id = ost_pb.Protocol.kMacFieldNumber p.Extensions[mac].dst_mac = 0x001122334455 p.Extensions[mac].src_mac = 0x00aabbccddee s.protocol.add().protocol_id.id = ost_pb.Protocol.kVlanFieldNumber s.protocol.add().protocol_id.id = ost_pb.Protocol.kVlanFieldNumber s.protocol.add().protocol_id.id = ost_pb.Protocol.kVlanFieldNumber s.protocol.add().protocol_id.id = ost_pb.Protocol.kEth2FieldNumber p = s.protocol.add() p.protocol_id.id = ost_pb.Protocol.kIp4FieldNumber p.Extensions[ip4].src_ip = 0x01020304 p.Extensions[ip4].dst_ip = 0x05060708 s.protocol.add().protocol_id.id = ost_pb.Protocol.kUdpFieldNumber p = s.protocol.add() p.protocol_id.id = ost_pb.Protocol.kPayloadFieldNumber vf = p.variable_field.add() vf.type = ost_pb.VariableField.kCounter32 vf.offset = 0 vf.mode = ost_pb.VariableField.kIncrement vf.count = 100 log.info('configuring tx_stream %d' % stream_id.stream_id[0].id) drone.modifyStream(stream_cfg) # clear tx/rx stats log.info('clearing tx/rx stats') drone.clearStats(tx_port) drone.clearStats(rx_port) passed = False suite.test_begin('counter32IncrementUdpCksum[PayloadSeq]') try: drone.startCapture(rx_port) drone.startTransmit(tx_port) log.info('waiting for transmit to finish ...') time.sleep(3) drone.stopTransmit(tx_port) drone.stopCapture(rx_port) log.info('getting Rx capture buffer') buff = drone.getCaptureBuffer(rx_port.port_id[0]) drone.saveCaptureBuffer(buff, 'capture.pcap') log.info('dumping Rx capture buffer') cap_pkts = subprocess.check_output([tshark, '-n', '-r', 'capture.pcap', '-R', 'udp && frame.len==60', '-o', fmt.replace('XXX', 'data.data')]) print(cap_pkts) result = extract_column(cap_pkts, fmt_col) expected = ['000000000000', '000000010000', '000000020000', '000000030000', '000000040000', '000000050000', '000000060000', '000000070000', '000000080000', '000000090000'] log.info('result : %s' % result) log.info('expected: %s' % expected) if result == expected and 'Error' not in cap_pkts: passed = True os.remove('capture.pcap') except RpcError as e: raise finally: drone.stopTransmit(tx_port) s.core.frame_len = 128 # restore frame length suite.test_end(passed) # ----------------------------------------------------------------- # # TESTCASE: Verify increment counter32 variable field affecting # TCP checksum - TCP seq # ----------------------------------------------------------------- # # setup stream protocols as mac:eth2:ip4:tcp:payload s.ClearField("protocol") p = s.protocol.add() p.protocol_id.id = ost_pb.Protocol.kMacFieldNumber p.Extensions[mac].dst_mac = 0x001122334455 p.Extensions[mac].src_mac = 0x00aabbccddee s.protocol.add().protocol_id.id = ost_pb.Protocol.kEth2FieldNumber p = s.protocol.add() p.protocol_id.id = ost_pb.Protocol.kIp4FieldNumber p.Extensions[ip4].src_ip = 0x01020304 p.Extensions[ip4].dst_ip = 0x05060708 p = s.protocol.add() p.protocol_id.id = ost_pb.Protocol.kTcpFieldNumber vf = p.variable_field.add() vf.type = ost_pb.VariableField.kCounter32 vf.offset = 4 vf.value = 1000 vf.mode = ost_pb.VariableField.kIncrement vf.count = 5 vf.step = 5 s.protocol.add().protocol_id.id = ost_pb.Protocol.kPayloadFieldNumber log.info('configuring tx_stream %d' % stream_id.stream_id[0].id) drone.modifyStream(stream_cfg) # clear tx/rx stats log.info('clearing tx/rx stats') drone.clearStats(tx_port) drone.clearStats(rx_port) passed = False suite.test_begin('counter32IncrementTcpCksum[TcpSeq]') try: drone.startCapture(rx_port) drone.startTransmit(tx_port) log.info('waiting for transmit to finish ...') time.sleep(3) drone.stopTransmit(tx_port) drone.stopCapture(rx_port) log.info('getting Rx capture buffer') buff = drone.getCaptureBuffer(rx_port.port_id[0]) drone.saveCaptureBuffer(buff, 'capture.pcap') log.info('dumping Rx capture buffer') cap_pkts = subprocess.check_output([tshark, '-n', '-r', 'capture.pcap', '-R', 'tcp && frame.len==124', '-o', 'tcp.relative_sequence_numbers:0', '-o', fmt.replace('XXX', 'tcp.seq')]) print(cap_pkts) result = extract_column(cap_pkts, fmt_col) expected = ['1000', '1005', '1010', '1015', '1020', '1000', '1005', '1010', '1015', '1020'] log.info('result : %s' % result) log.info('expected: %s' % expected) if result == expected and 'Error' not in cap_pkts: passed = True os.remove('capture.pcap') except RpcError as e: raise finally: drone.stopTransmit(tx_port) suite.test_end(passed) # ----------------------------------------------------------------- # # TESTCASE: Verify increment counter16 variable field affecting # ICMP checksum - ICMP seq # ----------------------------------------------------------------- # # setup stream protocols as mac:eth2:ip4:icmp:payload s.ClearField("protocol") p = s.protocol.add() p.protocol_id.id = ost_pb.Protocol.kMacFieldNumber p.Extensions[mac].dst_mac = 0x001122334455 p.Extensions[mac].src_mac = 0x00aabbccddee s.protocol.add().protocol_id.id = ost_pb.Protocol.kEth2FieldNumber p = s.protocol.add() p.protocol_id.id = ost_pb.Protocol.kIp4FieldNumber p.Extensions[ip4].src_ip = 0x01020304 p.Extensions[ip4].dst_ip = 0x05060708 p = s.protocol.add() p.protocol_id.id = ost_pb.Protocol.kIcmpFieldNumber p.Extensions[icmp].identifier = 0xabcd vf = p.variable_field.add() vf.type = ost_pb.VariableField.kCounter16 vf.offset = 6 vf.value = 1 vf.count = 10 p = s.protocol.add() p.protocol_id.id = ost_pb.Protocol.kPayloadFieldNumber p.Extensions[payload].pattern_mode = Payload.e_dp_inc_byte log.info('configuring tx_stream %d' % stream_id.stream_id[0].id) drone.modifyStream(stream_cfg) # clear tx/rx stats log.info('clearing tx/rx stats') drone.clearStats(tx_port) drone.clearStats(rx_port) passed = False suite.test_begin('counter16IncrementIcmpCksum[IcmpSeq]') try: drone.startCapture(rx_port) drone.startTransmit(tx_port) log.info('waiting for transmit to finish ...') time.sleep(3) drone.stopTransmit(tx_port) drone.stopCapture(rx_port) log.info('getting Rx capture buffer') buff = drone.getCaptureBuffer(rx_port.port_id[0]) drone.saveCaptureBuffer(buff, 'capture.pcap') log.info('dumping Rx capture buffer') cap_pkts = subprocess.check_output([tshark, '-n', '-r', 'capture.pcap', '-R', 'icmp && frame.len==124', '-o', fmt.replace('XXX', 'icmp.seq')]) print(cap_pkts) result = extract_column(cap_pkts, fmt_col) expected = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10'] log.info('result : %s' % result) log.info('expected: %s' % expected) if result == expected and 'Error' not in cap_pkts: passed = True os.remove('capture.pcap') except RpcError as e: raise finally: drone.stopTransmit(tx_port) suite.test_end(passed) # ----------------------------------------------------------------- # # TESTCASE: Verify xcrement counter16 variable fields affecting # UDP checksum - UDP src/dst port # ----------------------------------------------------------------- # # setup stream protocols as mac:eth2:ip6:udp:payload s.ClearField("protocol") p = s.protocol.add() p.protocol_id.id = ost_pb.Protocol.kMacFieldNumber p.Extensions[mac].dst_mac = 0x001122334455 p.Extensions[mac].src_mac = 0x00aabbccddee s.protocol.add().protocol_id.id = ost_pb.Protocol.kEth2FieldNumber p = s.protocol.add() p.protocol_id.id = ost_pb.Protocol.kIp6FieldNumber ip = p.Extensions[ip6] ip.src_addr_hi = 0x2002000000000000 ip.src_addr_lo = 0x1001 ip.dst_addr_hi = 0x4004000000000000 ip.dst_addr_lo = 0x1001 p = s.protocol.add() p.protocol_id.id = ost_pb.Protocol.kUdpFieldNumber vf = p.variable_field.add() vf.type = ost_pb.VariableField.kCounter16 vf.offset = 0 vf.value = 5000 vf.mode = ost_pb.VariableField.kIncrement vf.count = 10 vf = p.variable_field.add() vf.type = ost_pb.VariableField.kCounter16 vf.offset = 2 vf.value = 6000 vf.mode = ost_pb.VariableField.kDecrement vf.count = 10 s.protocol.add().protocol_id.id = ost_pb.Protocol.kPayloadFieldNumber log.info('configuring tx_stream %d' % stream_id.stream_id[0].id) drone.modifyStream(stream_cfg) # clear tx/rx stats log.info('clearing tx/rx stats') drone.clearStats(tx_port) drone.clearStats(rx_port) passed = False suite.test_begin('counter16XcrementUdpCksum[UdpSrcDstPort]') try: drone.startCapture(rx_port) drone.startTransmit(tx_port) log.info('waiting for transmit to finish ...') time.sleep(3) drone.stopTransmit(tx_port) drone.stopCapture(rx_port) log.info('getting Rx capture buffer') buff = drone.getCaptureBuffer(rx_port.port_id[0]) drone.saveCaptureBuffer(buff, 'capture.pcap') log.info('dumping Rx capture buffer') cap_pkts = subprocess.check_output([tshark, '-n', '-r', 'capture.pcap', '-R', 'udp && frame.len==124', '-o', fmt2.replace('XXX', 'udp.srcport', 1) .replace('XXX', 'udp.dstport')]) print(cap_pkts) result1 = extract_column(cap_pkts, fmt_col) expected1 = ['5000', '5001', '5002', '5003', '5004', '5005', '5006', '5007', '5008', '5009'] log.info('result1 : %s' % result1) log.info('expected1: %s' % expected1) result2 = extract_column(cap_pkts, fmt_col+1) expected2 = ['6000', '5999', '5998', '5997', '5996', '5995', '5994', '5993', '5992', '5991'] log.info('result2 : %s' % result2) log.info('expected2: %s' % expected2) if result1 == expected1 and result2 == expected2 \ and 'Error' not in cap_pkts: passed = True os.remove('capture.pcap') except RpcError as e: raise finally: drone.stopTransmit(tx_port) suite.test_end(passed) # ----------------------------------------------------------------- # # TESTCASE: Verify multiple Xcrement counterX variable fields affecting # IP, UDP checksum - VlanId, SrcIp, TcpDstPort, PayloadSeq # ----------------------------------------------------------------- # s.core.frame_len = 64 # setup stream protocols as mac:vlan:eth2:ip4:udp:payload s.ClearField("protocol") p = s.protocol.add() p.protocol_id.id = ost_pb.Protocol.kMacFieldNumber p.Extensions[mac].dst_mac = 0x001122334455 p.Extensions[mac].src_mac = 0x00aabbccddee p = s.protocol.add() p.protocol_id.id = ost_pb.Protocol.kVlanFieldNumber vf = p.variable_field.add() vf.type = ost_pb.VariableField.kCounter16 vf.offset = 2 vf.value = 3 vf.mask = 0x0FFF vf.mode = ost_pb.VariableField.kDecrement vf.count = 10 s.protocol.add().protocol_id.id = ost_pb.Protocol.kEth2FieldNumber p = s.protocol.add() p.protocol_id.id = ost_pb.Protocol.kIp4FieldNumber p.Extensions[ip4].src_ip = 0x01020304 p.Extensions[ip4].dst_ip = 0x05060708 vf = p.variable_field.add() vf.type = ost_pb.VariableField.kCounter32 vf.offset = 12 vf.mask = 0x0000ff00 vf.value = 0x00000100 vf.mode = ost_pb.VariableField.kIncrement vf.count = 10 vf.step = 0x00000100 p = s.protocol.add() p.protocol_id.id = ost_pb.Protocol.kTcpFieldNumber vf = p.variable_field.add() vf.type = ost_pb.VariableField.kCounter16 vf.offset = 2 vf.value = 6666 vf.mode = ost_pb.VariableField.kDecrement vf.count = 10 p = s.protocol.add() p.protocol_id.id = ost_pb.Protocol.kPayloadFieldNumber vf = p.variable_field.add() vf.type = ost_pb.VariableField.kCounter8 vf.offset = 0 vf.mode = ost_pb.VariableField.kIncrement vf.count = 10 log.info('configuring tx_stream %d' % stream_id.stream_id[0].id) drone.modifyStream(stream_cfg) # clear tx/rx stats log.info('clearing tx/rx stats') drone.clearStats(tx_port) drone.clearStats(rx_port) passed = False suite.test_begin('counterXcrementIpTcpCksum[Multiple]') try: drone.startCapture(rx_port) drone.startTransmit(tx_port) log.info('waiting for transmit to finish ...') time.sleep(3) drone.stopTransmit(tx_port) drone.stopCapture(rx_port) log.info('getting Rx capture buffer') buff = drone.getCaptureBuffer(rx_port.port_id[0]) drone.saveCaptureBuffer(buff, 'capture.pcap') log.info('dumping Rx capture buffer') cap_pkts = subprocess.check_output([tshark, '-n', '-r', 'capture.pcap', '-R', 'tcp && frame.len==60', '-o', fmt4.replace('XXX', 'vlan.id', 1) .replace('XXX', 'ip.src', 1) .replace('XXX', 'tcp.dstport', 1) .replace('XXX', 'data.data', 1)]) print(cap_pkts) result = [] expected = [] expected.append(['3', '2', '1', '0', '4095', '4094', '4093', '4092', '4091', '4090']) expected.append(['1.2.1.4', '1.2.2.4', '1.2.3.4', '1.2.4.4', '1.2.5.4', '1.2.6.4', '1.2.7.4', '1.2.8.4', '1.2.9.4', '1.2.10.4']) expected.append(['6666', '6665', '6664', '6663', '6662', '6661', '6660', '6659', '6658', '6657']) expected.append(['0000', '0100', '0200', '0300', '0400', '0500', '0600', '0700', '0800', '0900']) passed = True for i in range(4): result.append(extract_column(cap_pkts, fmt_col+i)) log.info('result[%d] : %s' % (i, result[i])) log.info('expected[%d]: %s' % (i, expected[i])) if result[i] != expected[i]: passed = False if 'Error' in cap_pkts: passed = False os.remove('capture.pcap') except RpcError as e: raise finally: drone.stopTransmit(tx_port) s.core.frame_len = 128 suite.test_end(passed) suite.complete() # delete streams log.info('deleting tx_stream %d' % stream_id.stream_id[0].id) drone.deleteStream(stream_id) # bye for now drone.disconnect() except Exception as ex: log.exception(ex) finally: suite.report() if not suite.passed: sys.exit(2); ostinato-0.7.1/version.pri0000700000175300010010000000271112537544072015107 0ustar srivatspNoneAPP_VERSION = 0.7.1 #APP_REVISION = $(shell hg identify -i) #uncomment the below line in a source package and fill-in the correct revision APP_REVISION = da551b378f72@ 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 } pkg_info { PKG_INFO_FILE = pkg_info.json pkginfo.target = $$PKG_INFO_FILE pkginfo.CONFIG = recursive win32:pkginfo.commands = echo "{" \ " \"version\": \"$$APP_VERSION\"," \ " \"revision\": \"$$APP_REVISION\"" \ "}" \ > $$PKG_INFO_FILE unix:pkginfo.commands = echo "\"{" \ " \\\"version\\\": \\\"$$APP_VERSION\\\"," \ " \\\"revision\\\": \\\"$$APP_REVISION\\\"" \ "}\"" \ > $$PKG_INFO_FILE QMAKE_EXTRA_TARGETS += pkginfo POST_TARGETDEPS += $$PKG_INFO_FILE QMAKE_DISTCLEAN += $$PKG_INFO_FILE }